1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 *   http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19 
20 import Foundation
21 import CoreFoundation
22 
23 #if !swift(>=4.2)
24 // Swift 3/4 compatibility
25 fileprivate extension RunLoopMode {
26   static let `default` = defaultRunLoopMode
27 }
28 #endif
29 
30 #if os(Linux)
31 public class TSSLSocketTransport {
32   init(hostname: String, port: UInt16) {
33     // FIXME!
34     assert(false, "Security not available in Linux, TSSLSocketTransport Unavilable for now")
35   }
36 }
37 #else
38 let isLittleEndian = Int(OSHostByteOrder()) == OSLittleEndian
39 let htons  = isLittleEndian ? _OSSwapInt16 : { $0 }
40 let htonl  = isLittleEndian ? _OSSwapInt32 : { $0 }
41 
42 public class TSSLSocketTransport: TStreamTransport {
43   var sslHostname: String
44   var sd: Int32 = 0
45 
46   public init(hostname: String, port: UInt16) throws {
47     sslHostname = hostname
48     var readStream: Unmanaged<CFReadStream>?
49     var writeStream: Unmanaged<CFWriteStream>?
50 
51     /* create a socket structure */
52     var pin: sockaddr_in = sockaddr_in()
53     var hp: UnsafeMutablePointer<hostent>? = nil
54     for i in 0..<10 {
55 
56       hp = gethostbyname(hostname.cString(using: String.Encoding.utf8)!)
57       if hp == nil {
58         print("failed to resolve hostname \(hostname)")
59         herror("resolv")
60         if i == 9 {
61           super.init(inputStream: nil, outputStream: nil) // have to init before throwing
62           throw TSSLSocketTransportError(error: .hostanameResolution(hostname: hostname))
63         }
64         Thread.sleep(forTimeInterval: 0.2)
65       } else {
66         break
67       }
68     }
69     pin.sin_family  = UInt8(AF_INET)
70     pin.sin_addr    = in_addr(s_addr: UInt32((hp?.pointee.h_addr_list.pointee?.pointee)!)) // Is there a better way to get this???
71     pin.sin_port    = htons(port)
72 
73     /* create the socket */
74     sd = socket(Int32(AF_INET), Int32(SOCK_STREAM), Int32(IPPROTO_TCP))
75     if sd == -1 {
76       super.init(inputStream: nil, outputStream: nil) // have to init before throwing
77       throw TSSLSocketTransportError(error: .socketCreate(port: Int(port)))
78     }
79 
80     /* open a connection */
81     // need a non-self ref to sd, otherwise the j complains
82     let sd_local = sd
83     let connectResult = withUnsafePointer(to: &pin) {
84       connect(sd_local, UnsafePointer<sockaddr>(OpaquePointer($0)), socklen_t(MemoryLayout<sockaddr_in>.size))
85     }
86     if connectResult == -1 {
87       super.init(inputStream: nil, outputStream: nil) // have to init before throwing
88       throw TSSLSocketTransportError(error: .connect)
89     }
90 
91     CFStreamCreatePairWithSocket(kCFAllocatorDefault, sd, &readStream, &writeStream)
92 
93     CFReadStreamSetProperty(readStream?.takeRetainedValue(), .socketNativeHandle, kCFBooleanTrue)
94     CFWriteStreamSetProperty(writeStream?.takeRetainedValue(), .socketNativeHandle, kCFBooleanTrue)
95 
96     var inputStream: InputStream? = nil
97     var outputStream: OutputStream? = nil
98     if readStream != nil && writeStream != nil {
99 
100       CFReadStreamSetProperty(readStream?.takeRetainedValue(),
101                               .socketSecurityLevel,
102                               kCFStreamSocketSecurityLevelTLSv1)
103 
104       let settings: [String: Bool] = [kCFStreamSSLValidatesCertificateChain as String: true]
105 
106       CFReadStreamSetProperty(readStream?.takeRetainedValue(),
107                               .SSLSettings,
108                               settings as CFTypeRef)
109 
110       CFWriteStreamSetProperty(writeStream?.takeRetainedValue(),
111                               .SSLSettings,
112                               settings as CFTypeRef)
113 
114       inputStream = readStream!.takeRetainedValue()
115       inputStream?.schedule(in: .current, forMode: .default)
116       inputStream?.open()
117 
118       outputStream = writeStream!.takeRetainedValue()
119       outputStream?.schedule(in: .current, forMode: .default)
120       outputStream?.open()
121 
122       readStream?.release()
123       writeStream?.release()
124     }
125 
126 
127     super.init(inputStream: inputStream, outputStream: outputStream)
128     self.input?.delegate = self
129     self.output?.delegate = self
130   }
131 
recoverFromTrustFailurenull132   func recoverFromTrustFailure(_ myTrust: SecTrust, lastTrustResult: SecTrustResultType) -> Bool {
133     let trustTime = SecTrustGetVerifyTime(myTrust)
134     let currentTime = CFAbsoluteTimeGetCurrent()
135 
136     let timeIncrement = 31536000 // from TSSLSocketTransport.m
137     let newTime = currentTime - Double(timeIncrement)
138 
139     if trustTime - newTime != 0 {
140       let newDate = CFDateCreate(nil, newTime)
141       SecTrustSetVerifyDate(myTrust, newDate!)
142 
143       var tr = lastTrustResult
144       let success = withUnsafeMutablePointer(to: &tr) { trPtr -> Bool in
145         if SecTrustEvaluate(myTrust, trPtr) != errSecSuccess {
146           return false
147         }
148         return true
149       }
150       if !success { return false }
151     }
152     if lastTrustResult == .proceed || lastTrustResult == .unspecified {
153         return false
154     }
155 
156     print("TSSLSocketTransport: Unable to recover certificate trust failure")
157     return true
158   }
159 
isOpennull160   public func isOpen() -> Bool {
161     return sd > 0
162   }
163 }
164 
165 extension TSSLSocketTransport: StreamDelegate {
streamnull166   public func stream(_ aStream: Stream, handle eventCode: Stream.Event) {
167 
168     switch eventCode {
169     case Stream.Event(): break
170     case Stream.Event.hasBytesAvailable: break
171     case Stream.Event.openCompleted: break
172     case Stream.Event.hasSpaceAvailable:
173       var proceed = false
174       var trustResult: SecTrustResultType = .invalid
175 
176       var newPolicies: CFMutableArray?
177 
178       repeat {
179         let trust: SecTrust = aStream.property(forKey: .SSLPeerTrust) as! SecTrust
180 
181         // Add new policy to current list of policies
182         let policy = SecPolicyCreateSSL(false, sslHostname as CFString?)
183         var ppolicy = policy // mutable for pointer
184         let policies: UnsafeMutablePointer<CFArray?>? = nil
185         if SecTrustCopyPolicies(trust, policies!) != errSecSuccess {
186           break
187         }
188         withUnsafeMutablePointer(to: &ppolicy) { ptr in
189           newPolicies = CFArrayCreateMutableCopy(nil, 0, policies?.pointee)
190           CFArrayAppendValue(newPolicies, ptr)
191         }
192 
193         // update trust policies
194         if SecTrustSetPolicies(trust, newPolicies!) != errSecSuccess {
195           break
196         }
197 
198         // Evaluate the trust chain
199         let success = withUnsafeMutablePointer(to: &trustResult) { trustPtr -> Bool in
200           if SecTrustEvaluate(trust, trustPtr) != errSecSuccess {
201             return false
202           }
203           return true
204         }
205 
206         if !success {
207           break
208         }
209 
210 
211         switch trustResult {
212         case .proceed:      proceed = true
213         case .unspecified:  proceed = true
214         case .recoverableTrustFailure:
215           proceed = self.recoverFromTrustFailure(trust, lastTrustResult: trustResult)
216 
217         case .deny:         break
218         case .fatalTrustFailure: break
219         case .otherError:   break
220         case .invalid:      break
221         default: break
222         }
223       } while false
224 
225       if !proceed {
226         print("TSSLSocketTransport: Cannot trust certificate.  Result: \(trustResult)")
227         aStream.close()
228       }
229 
230     case Stream.Event.errorOccurred: break
231     case Stream.Event.endEncountered: break
232     default: break
233     }
234   }
235 }
236 #endif
237