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