Keychain: store configurations in keychain instead of providerConfig

Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
This commit is contained in:
Jason A. Donenfeld
2019-02-04 07:37:26 +01:00
parent a26d620f11
commit 8c3557a907
8 changed files with 201 additions and 31 deletions
@@ -174,20 +174,32 @@ final class LegacyTunnelConfiguration: LegacyModel {
extension NETunnelProviderProtocol {
@discardableResult
func migrateConfigurationIfNeeded() -> Bool {
guard let configurationVersion = providerConfiguration?["tunnelConfigurationVersion"] as? Int else { return false }
if configurationVersion == 1 {
migrateFromConfigurationV1()
} else {
fatalError("No migration from configuration version \(configurationVersion) exists.")
func migrateConfigurationIfNeeded(called name: String) -> Bool {
var ret = false
if migrateFromConfigurationV1() {
ret = true
}
if migrateFromConfigurationV2(called: name) {
ret = true
}
return ret
}
private func migrateFromConfigurationV1() -> Bool {
guard let configurationVersion = providerConfiguration?["tunnelConfigurationVersion"] as? Int else { return false }
guard configurationVersion == 1 else { return false }
guard let serializedTunnelConfiguration = providerConfiguration?["tunnelConfiguration"] as? Data else { return false }
guard let configuration = try? JSONDecoder().decode(LegacyTunnelConfiguration.self, from: serializedTunnelConfiguration) else { return false }
providerConfiguration = ["WgQuickConfig": configuration.migrated.asWgQuickConfig()]
return true
}
private func migrateFromConfigurationV1() {
guard let serializedTunnelConfiguration = providerConfiguration?["tunnelConfiguration"] as? Data else { return }
guard let configuration = try? JSONDecoder().decode(LegacyTunnelConfiguration.self, from: serializedTunnelConfiguration) else { return }
providerConfiguration = [Keys.wgQuickConfig.rawValue: configuration.migrated.asWgQuickConfig()]
private func migrateFromConfigurationV2(called name: String) -> Bool {
guard let oldConfig = providerConfiguration?["WgQuickConfig"] as? String else { return false }
providerConfiguration = nil
guard passwordReference == nil else { return true }
passwordReference = Keychain.makeReference(containing: oldConfig, called: name)
return true
}
}
@@ -12,17 +12,16 @@ enum PacketTunnelProviderError: String, Error {
}
extension NETunnelProviderProtocol {
enum Keys: String {
case wgQuickConfig = "WgQuickConfig"
}
convenience init?(tunnelConfiguration: TunnelConfiguration) {
convenience init?(tunnelConfiguration: TunnelConfiguration, previouslyFrom old: NEVPNProtocol? = nil) {
self.init()
let appId = Bundle.main.bundleIdentifier!
guard let name = tunnelConfiguration.name else { return nil }
guard let appId = Bundle.main.bundleIdentifier else { return nil }
providerBundleIdentifier = "\(appId).network-extension"
providerConfiguration = [Keys.wgQuickConfig.rawValue: tunnelConfiguration.asWgQuickConfig()]
passwordReference = Keychain.makeReference(containing: tunnelConfiguration.asWgQuickConfig(), called: name, previouslyReferencedBy: old?.passwordReference)
if passwordReference == nil {
return nil
}
let endpoints = tunnelConfiguration.peers.compactMap { $0.endpoint }
if endpoints.count == 1 {
@@ -35,9 +34,26 @@ extension NETunnelProviderProtocol {
}
func asTunnelConfiguration(called name: String? = nil) -> TunnelConfiguration? {
migrateConfigurationIfNeeded()
guard let serializedConfig = providerConfiguration?[Keys.wgQuickConfig.rawValue] as? String else { return nil }
return try? TunnelConfiguration(fromWgQuickConfig: serializedConfig, called: name)
migrateConfigurationIfNeeded(called: name ?? "unknown")
//TODO: in the case where migrateConfigurationIfNeeded is called by the network extension,
// before the app has started, and when there is, in fact, configuration that needs to be
// put into the keychain, this will generate one new keychain item every time it is started,
// until finally the app is open. Would it be possible to call saveToPreferences here? Or is
// that generally not available to network extensions? In which case, what should our
// behavior be?
guard let passwordReference = passwordReference else { return nil }
guard let config = Keychain.openReference(called: passwordReference) else { return nil }
return try? TunnelConfiguration(fromWgQuickConfig: config, called: name)
}
func destroyConfigurationReference() {
guard let ref = passwordReference else { return }
Keychain.deleteReference(called: ref)
}
func verifyConfigurationReference() -> Data? {
guard let ref = passwordReference else { return nil }
return Keychain.verifyReference(called: ref) ? ref : nil
}
}