Add client certificate to TunnelKitProvider

Refactor composition of temporary file URL.

Also fix missing LZOFraming from Configuration.builder().
This commit is contained in:
Davide De Rosa 2018-08-23 17:42:49 +02:00
parent a4c109a916
commit e6f509a26c
2 changed files with 88 additions and 10 deletions

View File

@ -154,6 +154,12 @@ extension TunnelKitProvider {
/// The optional CA certificate to validate server against. Set to `nil` to disable CA validation (default).
public var ca: Certificate?
/// The optional client certificate to authenticate with. Set to `nil` to disable client authentication (default).
public var clientCertificate: Certificate?
/// The optional key for `clientCertificate`. Set to `nil` if client authentication unused (default).
public var clientKey: Certificate?
/// The MTU of the link.
public var mtu: Int
@ -212,12 +218,25 @@ extension TunnelKitProvider {
}
let ca: Certificate?
if let caPEM = providerConfiguration[S.ca] as? String {
ca = Certificate(pem: caPEM)
let clientCertificate: Certificate?
let clientKey: Certificate?
if let pem = providerConfiguration[S.ca] as? String {
ca = Certificate(pem: pem)
} else {
ca = nil
}
if let pem = providerConfiguration[S.clientCertificate] as? String {
guard let keyPEM = providerConfiguration[S.clientKey] as? String else {
throw ProviderError.configuration(field: "protocolConfiguration.providerConfiguration[\(S.clientKey)]")
}
clientCertificate = Certificate(pem: pem)
clientKey = Certificate(pem: keyPEM)
} else {
clientCertificate = nil
clientKey = nil
}
prefersResolvedAddresses = providerConfiguration[S.prefersResolvedAddresses] as? Bool ?? false
resolvedAddresses = providerConfiguration[S.resolvedAddresses] as? [String]
guard let endpointProtocolsStrings = providerConfiguration[S.endpointProtocols] as? [String], !endpointProtocolsStrings.isEmpty else {
@ -243,6 +262,8 @@ extension TunnelKitProvider {
self.cipher = cipher
self.digest = digest
self.ca = ca
self.clientCertificate = clientCertificate
self.clientKey = clientKey
mtu = providerConfiguration[S.mtu] as? Int ?? 1250
LZOFraming = providerConfiguration[S.LZOFraming] as? Bool ?? false
renegotiatesAfterSeconds = providerConfiguration[S.renegotiatesAfter] as? Int
@ -277,6 +298,8 @@ extension TunnelKitProvider {
cipher: cipher,
digest: digest,
ca: ca,
clientCertificate: clientCertificate,
clientKey: clientKey,
mtu: mtu,
LZOFraming: LZOFraming,
renegotiatesAfterSeconds: renegotiatesAfterSeconds,
@ -304,6 +327,10 @@ extension TunnelKitProvider {
static let ca = "CA"
static let clientCertificate = "ClientCertificate"
static let clientKey = "ClientKey"
static let mtu = "MTU"
static let LZOFraming = "LZOFraming"
@ -338,6 +365,12 @@ extension TunnelKitProvider {
/// - Seealso: `TunnelKitProvider.ConfigurationBuilder.ca`
public let ca: Certificate?
/// - Seealso: `TunnelKitProvider.ConfigurationBuilder.clientCertificate`
public let clientCertificate: Certificate?
/// - Seealso: `TunnelKitProvider.ConfigurationBuilder.clientKey`
public let clientKey: Certificate?
/// - Seealso: `TunnelKitProvider.ConfigurationBuilder.mtu`
public let mtu: Int
@ -405,6 +438,12 @@ extension TunnelKitProvider {
if let ca = ca {
dict[S.ca] = ca.pem
}
if let clientCertificate = clientCertificate {
dict[S.clientCertificate] = clientCertificate.pem
}
if let clientKey = clientKey {
dict[S.clientKey] = clientKey.pem
}
if let resolvedAddresses = resolvedAddresses {
dict[S.resolvedAddresses] = resolvedAddresses
}
@ -464,6 +503,11 @@ extension TunnelKitProvider {
} else {
log.info("CA verification: disabled")
}
if let _ = clientCertificate {
log.info("Client verification: enabled")
} else {
log.info("Client verification: disabled")
}
log.info("MTU: \(mtu)")
log.info("LZO framing: \(LZOFraming ? "enabled" : "disabled")")
if let renegotiatesAfterSeconds = renegotiatesAfterSeconds {
@ -491,7 +535,10 @@ extension TunnelKitProvider.Configuration: Equatable {
builder.cipher = cipher
builder.digest = digest
builder.ca = ca
builder.clientCertificate = clientCertificate
builder.clientKey = clientKey
builder.mtu = mtu
builder.LZOFraming = LZOFraming
builder.renegotiatesAfterSeconds = renegotiatesAfterSeconds
builder.shouldDebug = shouldDebug
builder.debugLogKey = debugLogKey
@ -505,6 +552,8 @@ extension TunnelKitProvider.Configuration: Equatable {
(lhs.cipher == rhs.cipher) &&
(lhs.digest == rhs.digest) &&
(lhs.ca == rhs.ca) &&
(lhs.clientCertificate == rhs.clientCertificate) &&
(lhs.clientKey == rhs.clientKey) &&
(lhs.mtu == rhs.mtu) &&
(lhs.LZOFraming == rhs.LZOFraming) &&
(lhs.renegotiatesAfterSeconds == rhs.renegotiatesAfterSeconds)

View File

@ -82,14 +82,12 @@ open class TunnelKitProvider: NEPacketTunnelProvider {
private let prngSeedLength = 64
private let caTmpFilename = "CA.pem"
private var cachesURL: URL {
return URL(fileURLWithPath: NSSearchPathForDirectoriesInDomains(.cachesDirectory, .userDomainMask, true)[0])
}
private var tmpCaURL: URL {
return cachesURL.appendingPathComponent(caTmpFilename)
private func temporaryURL(forKey key: String) -> URL {
return cachesURL.appendingPathComponent("\(key).pem")
}
// MARK: Tunnel configuration
@ -169,10 +167,13 @@ open class TunnelKitProvider: NEPacketTunnelProvider {
}
let caPath: String?
let clientCertificatePath: String?
let clientKeyPath: String?
if let ca = cfg.ca {
do {
try ca.write(to: tmpCaURL)
caPath = tmpCaURL.path
let url = temporaryURL(forKey: Configuration.Keys.ca)
try ca.write(to: url)
caPath = url.path
} catch {
completionHandler(ProviderError.certificateSerialization)
return
@ -180,6 +181,30 @@ open class TunnelKitProvider: NEPacketTunnelProvider {
} else {
caPath = nil
}
if let clientCertificate = cfg.clientCertificate {
do {
let url = temporaryURL(forKey: Configuration.Keys.clientCertificate)
try clientCertificate.write(to: url)
clientCertificatePath = url.path
} catch {
completionHandler(ProviderError.certificateSerialization)
return
}
} else {
clientCertificatePath = nil
}
if let clientKey = cfg.clientKey {
do {
let url = temporaryURL(forKey: Configuration.Keys.clientKey)
try clientKey.write(to: url)
clientKeyPath = url.path
} catch {
completionHandler(ProviderError.certificateSerialization)
return
}
} else {
clientKeyPath = nil
}
cfg.print(appVersion: appVersion)
@ -188,6 +213,8 @@ open class TunnelKitProvider: NEPacketTunnelProvider {
sessionConfiguration.cipher = cfg.cipher
sessionConfiguration.digest = cfg.digest
sessionConfiguration.caPath = caPath
sessionConfiguration.clientCertificatePath = clientCertificatePath
sessionConfiguration.clientKeyPath = clientKeyPath
sessionConfiguration.LZOFraming = cfg.LZOFraming
if let renegotiatesAfterSeconds = cfg.renegotiatesAfterSeconds {
sessionConfiguration.renegotiatesAfter = Double(renegotiatesAfterSeconds)
@ -336,7 +363,9 @@ open class TunnelKitProvider: NEPacketTunnelProvider {
// stopped externally, unrecoverable
else {
let fm = FileManager.default
try? fm.removeItem(at: tmpCaURL)
for key in [Configuration.Keys.ca, Configuration.Keys.clientCertificate, Configuration.Keys.clientKey] {
try? fm.removeItem(at: temporaryURL(forKey: key))
}
cancelTunnelWithError(error)
}
}