diff --git a/Sources/NIOSSL/SecurityFrameworkCertificateVerification.swift b/Sources/NIOSSL/SecurityFrameworkCertificateVerification.swift index b2aed91f..fc83a9e7 100644 --- a/Sources/NIOSSL/SecurityFrameworkCertificateVerification.swift +++ b/Sources/NIOSSL/SecurityFrameworkCertificateVerification.swift @@ -30,10 +30,12 @@ extension SSLConnection { preconditionFailure("This callback should only be used if we are using the system-default trust.") } + let expectedHostname = self.validateHostnames ? self.expectedHostname : nil + // This force-unwrap is safe as we must have decided if we're a client or a server before validation. var trust: SecTrust? = nil var result: OSStatus - let policy = SecPolicyCreateSSL(self.role! == .client, self.expectedHostname as CFString?) + let policy = SecPolicyCreateSSL(self.role! == .client, expectedHostname as CFString?) result = SecTrustCreateWithCertificates(peerCertificates as CFArray, policy, &trust) guard result == errSecSuccess, let actualTrust = trust else { throw NIOSSLError.unableToValidateCertificate diff --git a/Tests/NIOSSLTests/SecurityFrameworkVerificationTests.swift b/Tests/NIOSSLTests/SecurityFrameworkVerificationTests.swift index e721b976..13ce1241 100644 --- a/Tests/NIOSSLTests/SecurityFrameworkVerificationTests.swift +++ b/Tests/NIOSSLTests/SecurityFrameworkVerificationTests.swift @@ -73,6 +73,68 @@ final class SecurityFrameworkVerificationTests: XCTestCase { #endif } + func testDefaultVerificationCanValidateHostname() throws { + #if canImport(Darwin) + let group = MultiThreadedEventLoopGroup(numberOfThreads: 1) + defer { + try! group.syncShutdownGracefully() + } + + let p = group.next().makePromise(of: NIOSSLVerificationResult.self) + let context = try NIOSSLContext(configuration: .makeClientConfiguration()) + let connection = context.createConnection()! + connection.setConnectState() + connection.expectedHostname = "www.apple.com" + + connection.performSecurityFrameworkValidation(promise: p, peerCertificates: Self.appleComCertChain) + let result = try p.futureResult.wait() + + XCTAssertEqual(result, .certificateVerified) + #endif + } + + func testDefaultVerificationFailsOnInvalidHostname() throws { + #if canImport(Darwin) + let group = MultiThreadedEventLoopGroup(numberOfThreads: 1) + defer { + try! group.syncShutdownGracefully() + } + + let p = group.next().makePromise(of: NIOSSLVerificationResult.self) + let context = try NIOSSLContext(configuration: .makeClientConfiguration()) + let connection = context.createConnection()! + connection.setConnectState() + connection.expectedHostname = "www.swift-nio.io" + + connection.performSecurityFrameworkValidation(promise: p, peerCertificates: Self.appleComCertChain) + let result = try p.futureResult.wait() + + XCTAssertEqual(result, .failed) + #endif + } + + func testDefaultVerificationIgnoresHostnamesWhenConfiguredTo() throws { + #if canImport(Darwin) + let group = MultiThreadedEventLoopGroup(numberOfThreads: 1) + defer { + try! group.syncShutdownGracefully() + } + + let p = group.next().makePromise(of: NIOSSLVerificationResult.self) + var configuration = TLSConfiguration.makeClientConfiguration() + configuration.certificateVerification = .noHostnameVerification + let context = try NIOSSLContext(configuration: configuration) + let connection = context.createConnection()! + connection.setConnectState() + connection.expectedHostname = "www.swift-nio.io" + + connection.performSecurityFrameworkValidation(promise: p, peerCertificates: Self.appleComCertChain) + let result = try p.futureResult.wait() + + XCTAssertEqual(result, .certificateVerified) + #endif + } + func testDefaultVerificationPlusAdditionalCanUseAdditionalRoot() throws { #if canImport(Darwin) let group = MultiThreadedEventLoopGroup(numberOfThreads: 1)