blob: c2b590266a8143c6ca658c37456c3d959aa20926 [file] [log] [blame]
Chris Simpsona9b6c702018-04-08 07:11:37 -04001/*
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
20import Foundation
21import CoreFoundation
22
23#if os(Linux)
24public class TSSLSocketTransport {
25 init(hostname: String, port: UInt16) {
26 // FIXME!
27 assert(false, "Security not available in Linux, TSSLSocketTransport Unavilable for now")
28 }
29}
30#else
31let isLittleEndian = Int(OSHostByteOrder()) == OSLittleEndian
32let htons = isLittleEndian ? _OSSwapInt16 : { $0 }
33let htonl = isLittleEndian ? _OSSwapInt32 : { $0 }
34
35public class TSSLSocketTransport: TStreamTransport {
36 var sslHostname: String
37 var sd: Int32 = 0
38
39 public init(hostname: String, port: UInt16) throws {
40 sslHostname = hostname
41 var readStream: Unmanaged<CFReadStream>?
42 var writeStream: Unmanaged<CFWriteStream>?
43
44 /* create a socket structure */
45 var pin: sockaddr_in = sockaddr_in()
46 var hp: UnsafeMutablePointer<hostent>? = nil
47 for i in 0..<10 {
48
49 hp = gethostbyname(hostname.cString(using: String.Encoding.utf8)!)
50 if hp == nil {
51 print("failed to resolve hostname \(hostname)")
52 herror("resolv")
53 if i == 9 {
54 super.init(inputStream: nil, outputStream: nil) // have to init before throwing
55 throw TSSLSocketTransportError(error: .hostanameResolution(hostname: hostname))
56 }
57 Thread.sleep(forTimeInterval: 0.2)
58 } else {
59 break
60 }
61 }
62 pin.sin_family = UInt8(AF_INET)
63 pin.sin_addr = in_addr(s_addr: UInt32((hp?.pointee.h_addr_list.pointee?.pointee)!)) // Is there a better way to get this???
64 pin.sin_port = htons(port)
65
66 /* create the socket */
67 sd = socket(Int32(AF_INET), Int32(SOCK_STREAM), Int32(IPPROTO_TCP))
68 if sd == -1 {
69 super.init(inputStream: nil, outputStream: nil) // have to init before throwing
70 throw TSSLSocketTransportError(error: .socketCreate(port: Int(port)))
71 }
72
73 /* open a connection */
74 // need a non-self ref to sd, otherwise the j complains
75 let sd_local = sd
76 let connectResult = withUnsafePointer(to: &pin) {
77 connect(sd_local, UnsafePointer<sockaddr>(OpaquePointer($0)), socklen_t(MemoryLayout<sockaddr_in>.size))
78 }
79 if connectResult == -1 {
80 super.init(inputStream: nil, outputStream: nil) // have to init before throwing
81 throw TSSLSocketTransportError(error: .connect)
82 }
83
84 CFStreamCreatePairWithSocket(kCFAllocatorDefault, sd, &readStream, &writeStream)
85
86 CFReadStreamSetProperty(readStream?.takeRetainedValue(), .socketNativeHandle, kCFBooleanTrue)
87 CFWriteStreamSetProperty(writeStream?.takeRetainedValue(), .socketNativeHandle, kCFBooleanTrue)
88
89 var inputStream: InputStream? = nil
90 var outputStream: OutputStream? = nil
91 if readStream != nil && writeStream != nil {
92
93 CFReadStreamSetProperty(readStream?.takeRetainedValue(),
94 .socketSecurityLevel,
95 kCFStreamSocketSecurityLevelTLSv1)
96
97 let settings: [String: Bool] = [kCFStreamSSLValidatesCertificateChain as String: true]
98
99 CFReadStreamSetProperty(readStream?.takeRetainedValue(),
100 .SSLSettings,
101 settings as CFTypeRef!)
102
103 CFWriteStreamSetProperty(writeStream?.takeRetainedValue(),
104 .SSLSettings,
105 settings as CFTypeRef!)
106
107 inputStream = readStream!.takeRetainedValue()
108 inputStream?.schedule(in: .current, forMode: .defaultRunLoopMode)
109 inputStream?.open()
110
111 outputStream = writeStream!.takeRetainedValue()
112 outputStream?.schedule(in: .current, forMode: .defaultRunLoopMode)
113 outputStream?.open()
114
115 readStream?.release()
116 writeStream?.release()
117 }
118
119
120 super.init(inputStream: inputStream, outputStream: outputStream)
121 self.input?.delegate = self
122 self.output?.delegate = self
123 }
124
125 func recoverFromTrustFailure(_ myTrust: SecTrust, lastTrustResult: SecTrustResultType) -> Bool {
126 let trustTime = SecTrustGetVerifyTime(myTrust)
127 let currentTime = CFAbsoluteTimeGetCurrent()
128
129 let timeIncrement = 31536000 // from TSSLSocketTransport.m
130 let newTime = currentTime - Double(timeIncrement)
131
132 if trustTime - newTime != 0 {
133 let newDate = CFDateCreate(nil, newTime)
134 SecTrustSetVerifyDate(myTrust, newDate!)
135
136 var tr = lastTrustResult
137 let success = withUnsafeMutablePointer(to: &tr) { trPtr -> Bool in
138 if SecTrustEvaluate(myTrust, trPtr) != errSecSuccess {
139 return false
140 }
141 return true
142 }
143 if !success { return false }
144 }
145 if lastTrustResult == .proceed || lastTrustResult == .unspecified {
146 return false
147 }
148
149 print("TSSLSocketTransport: Unable to recover certificate trust failure")
150 return true
151 }
152
153 public func isOpen() -> Bool {
154 return sd > 0
155 }
156}
157
158extension TSSLSocketTransport: StreamDelegate {
159 public func stream(_ aStream: Stream, handle eventCode: Stream.Event) {
160
161 switch eventCode {
162 case Stream.Event(): break
163 case Stream.Event.hasBytesAvailable: break
164 case Stream.Event.openCompleted: break
165 case Stream.Event.hasSpaceAvailable:
166 var proceed = false
167 var trustResult: SecTrustResultType = .invalid
168
169 var newPolicies: CFMutableArray?
170
171 repeat {
172 let trust: SecTrust = aStream.property(forKey: .SSLPeerTrust) as! SecTrust
173
174 // Add new policy to current list of policies
175 let policy = SecPolicyCreateSSL(false, sslHostname as CFString?)
176 var ppolicy = policy // mutable for pointer
177 let policies: UnsafeMutablePointer<CFArray?>? = nil
178 if SecTrustCopyPolicies(trust, policies!) != errSecSuccess {
179 break
180 }
181 withUnsafeMutablePointer(to: &ppolicy) { ptr in
182 newPolicies = CFArrayCreateMutableCopy(nil, 0, policies?.pointee)
183 CFArrayAppendValue(newPolicies, ptr)
184 }
185
186 // update trust policies
187 if SecTrustSetPolicies(trust, newPolicies!) != errSecSuccess {
188 break
189 }
190
191 // Evaluate the trust chain
192 let success = withUnsafeMutablePointer(to: &trustResult) { trustPtr -> Bool in
193 if SecTrustEvaluate(trust, trustPtr) != errSecSuccess {
194 return false
195 }
196 return true
197 }
198
199 if !success {
200 break
201 }
202
203
204 switch trustResult {
205 case .proceed: proceed = true
206 case .unspecified: proceed = true
207 case .recoverableTrustFailure:
208 proceed = self.recoverFromTrustFailure(trust, lastTrustResult: trustResult)
209
210 case .deny: break
211 case .fatalTrustFailure: break
212 case .otherError: break
213 case .invalid: break
214 default: break
215 }
216 } while false
217
218 if !proceed {
219 print("TSSLSocketTransport: Cannot trust certificate. Result: \(trustResult)")
220 aStream.close()
221 }
222
223 case Stream.Event.errorOccurred: break
224 case Stream.Event.endEncountered: break
225 default: break
226 }
227 }
228}
229#endif