Make CA non-optional

Fix up nullability qualifiers in TLSBox.

Fixes #26
This commit is contained in:
Davide De Rosa 2018-10-05 17:35:40 +02:00
parent d0a46fe20e
commit 093774535d
6 changed files with 50 additions and 59 deletions

View File

@ -164,8 +164,8 @@ extension TunnelKitProvider {
/// The message digest algorithm. /// The message digest algorithm.
public var digest: SessionProxy.Digest public var digest: SessionProxy.Digest
/// The optional CA certificate to validate server against. Set to `nil` to disable CA validation (default). /// The CA certificate to validate server against.
public var ca: CryptoContainer? public let ca: CryptoContainer
/// The optional client certificate to authenticate with. Set to `nil` to disable client authentication (default). /// The optional client certificate to authenticate with. Set to `nil` to disable client authentication (default).
public var clientCertificate: CryptoContainer? public var clientCertificate: CryptoContainer?
@ -200,14 +200,16 @@ extension TunnelKitProvider {
/** /**
Default initializer. Default initializer.
- Parameter ca: The CA certificate.
*/ */
public init() { public init(ca: CryptoContainer) {
prefersResolvedAddresses = false prefersResolvedAddresses = false
resolvedAddresses = nil resolvedAddresses = nil
endpointProtocols = [EndpointProtocol(.udp, 1194)] endpointProtocols = [EndpointProtocol(.udp, 1194)]
cipher = .aes128cbc cipher = .aes128cbc
digest = .sha1 digest = .sha1
ca = nil self.ca = ca
clientCertificate = nil clientCertificate = nil
clientKey = nil clientKey = nil
mtu = 1500 mtu = 1500
@ -229,20 +231,19 @@ extension TunnelKitProvider {
throw ProviderError.configuration(field: "protocolConfiguration.providerConfiguration[\(S.digestAlgorithm)]") throw ProviderError.configuration(field: "protocolConfiguration.providerConfiguration[\(S.digestAlgorithm)]")
} }
let ca: CryptoContainer? let ca: CryptoContainer
let clientCertificate: CryptoContainer? let clientCertificate: CryptoContainer?
let clientKey: CryptoContainer? let clientKey: CryptoContainer?
if let pem = providerConfiguration[S.ca] as? String { guard let caPEM = providerConfiguration[S.ca] as? String else {
ca = CryptoContainer(pem: pem) throw ProviderError.configuration(field: "protocolConfiguration.providerConfiguration[\(S.ca)]")
} else {
ca = nil
} }
if let pem = providerConfiguration[S.clientCertificate] as? String { ca = CryptoContainer(pem: caPEM)
if let clientPEM = providerConfiguration[S.clientCertificate] as? String {
guard let keyPEM = providerConfiguration[S.clientKey] as? String else { guard let keyPEM = providerConfiguration[S.clientKey] as? String else {
throw ProviderError.configuration(field: "protocolConfiguration.providerConfiguration[\(S.clientKey)]") throw ProviderError.configuration(field: "protocolConfiguration.providerConfiguration[\(S.clientKey)]")
} }
clientCertificate = CryptoContainer(pem: pem) clientCertificate = CryptoContainer(pem: clientPEM)
clientKey = CryptoContainer(pem: keyPEM) clientKey = CryptoContainer(pem: keyPEM)
} else { } else {
clientCertificate = nil clientCertificate = nil
@ -365,7 +366,7 @@ extension TunnelKitProvider {
public let digest: SessionProxy.Digest public let digest: SessionProxy.Digest
/// - Seealso: `TunnelKitProvider.ConfigurationBuilder.ca` /// - Seealso: `TunnelKitProvider.ConfigurationBuilder.ca`
public let ca: CryptoContainer? public let ca: CryptoContainer
/// - Seealso: `TunnelKitProvider.ConfigurationBuilder.clientCertificate` /// - Seealso: `TunnelKitProvider.ConfigurationBuilder.clientCertificate`
public let clientCertificate: CryptoContainer? public let clientCertificate: CryptoContainer?
@ -446,12 +447,10 @@ extension TunnelKitProvider {
S.endpointProtocols: endpointProtocols.map { $0.serialized() }, S.endpointProtocols: endpointProtocols.map { $0.serialized() },
S.cipherAlgorithm: cipher.rawValue, S.cipherAlgorithm: cipher.rawValue,
S.digestAlgorithm: digest.rawValue, S.digestAlgorithm: digest.rawValue,
S.ca: ca.pem,
S.mtu: mtu, S.mtu: mtu,
S.debug: shouldDebug S.debug: shouldDebug
] ]
if let ca = ca {
dict[S.ca] = ca.pem
}
if let clientCertificate = clientCertificate { if let clientCertificate = clientCertificate {
dict[S.clientCertificate] = clientCertificate.pem dict[S.clientCertificate] = clientCertificate.pem
} }
@ -514,11 +513,6 @@ extension TunnelKitProvider {
log.info("\tProtocols: \(endpointProtocols)") log.info("\tProtocols: \(endpointProtocols)")
log.info("\tCipher: \(cipher)") log.info("\tCipher: \(cipher)")
log.info("\tDigest: \(digest)") log.info("\tDigest: \(digest)")
if let _ = ca {
log.info("\tCA verification: enabled")
} else {
log.info("\tCA verification: disabled")
}
if let _ = clientCertificate { if let _ = clientCertificate {
log.info("\tClient verification: enabled") log.info("\tClient verification: enabled")
} else { } else {
@ -551,11 +545,10 @@ extension TunnelKitProvider.Configuration: Equatable {
- Returns: An editable `TunnelKitProvider.ConfigurationBuilder` initialized with this configuration. - Returns: An editable `TunnelKitProvider.ConfigurationBuilder` initialized with this configuration.
*/ */
public func builder() -> TunnelKitProvider.ConfigurationBuilder { public func builder() -> TunnelKitProvider.ConfigurationBuilder {
var builder = TunnelKitProvider.ConfigurationBuilder() var builder = TunnelKitProvider.ConfigurationBuilder(ca: ca)
builder.endpointProtocols = endpointProtocols builder.endpointProtocols = endpointProtocols
builder.cipher = cipher builder.cipher = cipher
builder.digest = digest builder.digest = digest
builder.ca = ca
builder.clientCertificate = clientCertificate builder.clientCertificate = clientCertificate
builder.clientKey = clientKey builder.clientKey = clientKey
builder.mtu = mtu builder.mtu = mtu

View File

@ -171,20 +171,16 @@ open class TunnelKitProvider: NEPacketTunnelProvider {
return return
} }
let caPath: String? let caPath: String
let clientCertificatePath: String? let clientCertificatePath: String?
let clientKeyPath: String? let clientKeyPath: String?
if let ca = cfg.ca { do {
do { let url = temporaryURL(forKey: Configuration.Keys.ca)
let url = temporaryURL(forKey: Configuration.Keys.ca) try cfg.ca.write(to: url)
try ca.write(to: url) caPath = url.path
caPath = url.path } catch {
} catch { completionHandler(ProviderError.certificateSerialization)
completionHandler(ProviderError.certificateSerialization) return
return
}
} else {
caPath = nil
} }
if let clientCertificate = cfg.clientCertificate { if let clientCertificate = cfg.clientCertificate {
do { do {
@ -214,10 +210,13 @@ open class TunnelKitProvider: NEPacketTunnelProvider {
cfg.print(appVersion: appVersion) cfg.print(appVersion: appVersion)
// log.info("Temporary CA is stored to: \(caPath)") // log.info("Temporary CA is stored to: \(caPath)")
var sessionConfiguration = SessionProxy.ConfigurationBuilder(username: endpoint.username, password: endpoint.password) var sessionConfiguration = SessionProxy.ConfigurationBuilder(
username: endpoint.username,
password: endpoint.password,
caPath: caPath
)
sessionConfiguration.cipher = cfg.cipher sessionConfiguration.cipher = cfg.cipher
sessionConfiguration.digest = cfg.digest sessionConfiguration.digest = cfg.digest
sessionConfiguration.caPath = caPath
sessionConfiguration.clientCertificatePath = clientCertificatePath sessionConfiguration.clientCertificatePath = clientCertificatePath
sessionConfiguration.clientKeyPath = clientKeyPath sessionConfiguration.clientKeyPath = clientKeyPath
sessionConfiguration.compressionFraming = cfg.compressionFraming sessionConfiguration.compressionFraming = cfg.compressionFraming

View File

@ -124,8 +124,8 @@ extension SessionProxy {
/// The digest algorithm for HMAC. /// The digest algorithm for HMAC.
public var digest: Digest public var digest: Digest
/// The path to the optional CA for TLS negotiation (PEM format). /// The path to the CA for TLS negotiation (PEM format).
public var caPath: String? public let caPath: String
/// The path to the optional client certificate for TLS negotiation (PEM format). /// The path to the optional client certificate for TLS negotiation (PEM format).
public var clientCertificatePath: String? public var clientCertificatePath: String?
@ -143,12 +143,12 @@ extension SessionProxy {
public var renegotiatesAfter: TimeInterval? public var renegotiatesAfter: TimeInterval?
/// :nodoc: /// :nodoc:
public init(username: String, password: String) { public init(username: String, password: String, caPath: String) {
self.username = username self.username = username
self.password = password self.password = password
cipher = .aes128cbc cipher = .aes128cbc
digest = .sha1 digest = .sha1
caPath = nil self.caPath = caPath
clientCertificatePath = nil clientCertificatePath = nil
clientKeyPath = nil clientKeyPath = nil
compressionFraming = .disabled compressionFraming = .disabled
@ -193,7 +193,7 @@ extension SessionProxy {
public let digest: Digest public let digest: Digest
/// - Seealso: `SessionProxy.ConfigurationBuilder.caPath` /// - Seealso: `SessionProxy.ConfigurationBuilder.caPath`
public let caPath: String? public let caPath: String
/// - Seealso: `SessionProxy.ConfigurationBuilder.clientCertificatePath` /// - Seealso: `SessionProxy.ConfigurationBuilder.clientCertificatePath`
public let clientCertificatePath: String? public let clientCertificatePath: String?

View File

@ -37,6 +37,8 @@
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
extern const NSInteger TLSBoxMaxBufferLength; extern const NSInteger TLSBoxMaxBufferLength;
extern NSString *const TLSBoxPeerVerificationErrorNotification; extern NSString *const TLSBoxPeerVerificationErrorNotification;
@ -50,12 +52,12 @@ extern NSString *const TLSBoxPeerVerificationErrorNotification;
@interface TLSBox : NSObject @interface TLSBox : NSObject
- (instancetype)initWithCAPath:(NSString *)caPath - (instancetype)initWithCAPath:(NSString *)caPath
clientCertificatePath:(NSString *)clientCertificatePath clientCertificatePath:(nullable NSString *)clientCertificatePath
clientKeyPath:(NSString *)clientKeyPath; clientKeyPath:(nullable NSString *)clientKeyPath;
- (BOOL)startWithError:(NSError **)error; - (BOOL)startWithError:(NSError **)error;
- (NSData *)pullCipherTextWithError:(NSError **)error; - (nullable NSData *)pullCipherTextWithError:(NSError **)error;
// WARNING: text must be able to hold plain text output // WARNING: text must be able to hold plain text output
- (BOOL)pullRawPlainText:(uint8_t *)text length:(NSInteger *)length error:(NSError **)error; - (BOOL)pullRawPlainText:(uint8_t *)text length:(NSInteger *)length error:(NSError **)error;
@ -67,3 +69,5 @@ extern NSString *const TLSBoxPeerVerificationErrorNotification;
- (BOOL)isConnected; - (BOOL)isConnected;
@end @end
NS_ASSUME_NONNULL_END

View File

@ -77,7 +77,8 @@ int TLSBoxVerifyPeer(int ok, X509_STORE_CTX *ctx) {
- (instancetype)init - (instancetype)init
{ {
return [self initWithCAPath:nil clientCertificatePath:nil clientKeyPath:nil]; [NSException raise:NSInvalidArgumentException format:@"Use initWithCAPath:clientCertificatePath:clientKeyPath:"];
return nil;
} }
- (instancetype)initWithCAPath:(NSString *)caPath clientCertificatePath:(NSString *)clientCertificatePath clientKeyPath:(NSString *)clientKeyPath - (instancetype)initWithCAPath:(NSString *)caPath clientCertificatePath:(NSString *)clientCertificatePath clientKeyPath:(NSString *)clientKeyPath
@ -115,18 +116,13 @@ int TLSBoxVerifyPeer(int ok, X509_STORE_CTX *ctx) {
self.ctx = SSL_CTX_new(TLS_client_method()); self.ctx = SSL_CTX_new(TLS_client_method());
SSL_CTX_set_options(self.ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_COMPRESSION); SSL_CTX_set_options(self.ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_COMPRESSION);
if (self.caPath) { SSL_CTX_set_verify(self.ctx, SSL_VERIFY_PEER, TLSBoxVerifyPeer);
SSL_CTX_set_verify(self.ctx, SSL_VERIFY_PEER, TLSBoxVerifyPeer); if (!SSL_CTX_load_verify_locations(self.ctx, [self.caPath cStringUsingEncoding:NSASCIIStringEncoding], NULL)) {
if (!SSL_CTX_load_verify_locations(self.ctx, [self.caPath cStringUsingEncoding:NSASCIIStringEncoding], NULL)) { ERR_print_errors_fp(stdout);
ERR_print_errors_fp(stdout); if (error) {
if (error) { *error = TunnelKitErrorWithCode(TunnelKitErrorCodeTLSBoxCA);
*error = TunnelKitErrorWithCode(TunnelKitErrorCodeTLSBoxCA);
}
return NO;
} }
} return NO;
else {
SSL_CTX_set_verify(self.ctx, SSL_VERIFY_NONE, NULL);
} }
if (self.clientCertificatePath) { if (self.clientCertificatePath) {

View File

@ -63,12 +63,11 @@ class AppExtensionTests: XCTestCase {
password: "bar" password: "bar"
) )
builder = TunnelKitProvider.ConfigurationBuilder() builder = TunnelKitProvider.ConfigurationBuilder(ca: CryptoContainer(pem: "abcdef"))
XCTAssertNotNil(builder) XCTAssertNotNil(builder)
builder.cipher = .aes128cbc builder.cipher = .aes128cbc
builder.digest = .sha256 builder.digest = .sha256
builder.ca = CryptoContainer(pem: "abcdef")
cfg = builder.build() cfg = builder.build()
let proto = try? cfg.generatedTunnelProtocol(withBundleIdentifier: identifier, appGroup: appGroup, endpoint: endpoint) let proto = try? cfg.generatedTunnelProtocol(withBundleIdentifier: identifier, appGroup: appGroup, endpoint: endpoint)
@ -87,7 +86,7 @@ class AppExtensionTests: XCTestCase {
XCTAssertEqual(proto?.providerConfiguration?[K.appGroup] as? String, appGroup) XCTAssertEqual(proto?.providerConfiguration?[K.appGroup] as? String, appGroup)
XCTAssertEqual(proto?.providerConfiguration?[K.cipherAlgorithm] as? String, cfg.cipher.rawValue) XCTAssertEqual(proto?.providerConfiguration?[K.cipherAlgorithm] as? String, cfg.cipher.rawValue)
XCTAssertEqual(proto?.providerConfiguration?[K.digestAlgorithm] as? String, cfg.digest.rawValue) XCTAssertEqual(proto?.providerConfiguration?[K.digestAlgorithm] as? String, cfg.digest.rawValue)
XCTAssertEqual(proto?.providerConfiguration?[K.ca] as? String, cfg.ca?.pem) XCTAssertEqual(proto?.providerConfiguration?[K.ca] as? String, cfg.ca.pem)
XCTAssertEqual(proto?.providerConfiguration?[K.mtu] as? Int, cfg.mtu) XCTAssertEqual(proto?.providerConfiguration?[K.mtu] as? Int, cfg.mtu)
XCTAssertEqual(proto?.providerConfiguration?[K.renegotiatesAfter] as? Int, cfg.renegotiatesAfterSeconds) XCTAssertEqual(proto?.providerConfiguration?[K.renegotiatesAfter] as? Int, cfg.renegotiatesAfterSeconds)
XCTAssertEqual(proto?.providerConfiguration?[K.debug] as? Bool, cfg.shouldDebug) XCTAssertEqual(proto?.providerConfiguration?[K.debug] as? Bool, cfg.shouldDebug)