// SPDX-License-Identifier: MIT // Copyright © 2018-2019 WireGuard LLC. All Rights Reserved. import NetworkExtension enum PacketTunnelProviderError: String, Error { case savedProtocolConfigurationIsInvalid case dnsResolutionFailure case couldNotStartBackend case couldNotDetermineFileDescriptor case couldNotSetNetworkSettings } extension NETunnelProviderProtocol { convenience init?(tunnelConfiguration: TunnelConfiguration, previouslyFrom old: NEVPNProtocol? = nil) { self.init() guard let name = tunnelConfiguration.name else { return nil } guard let appId = Bundle.main.bundleIdentifier else { return nil } providerBundleIdentifier = "\(appId).network-extension" passwordReference = Keychain.makeReference(containing: tunnelConfiguration.asWgQuickConfig(), called: name, previouslyReferencedBy: old?.passwordReference) if passwordReference == nil { return nil } #if os(macOS) providerConfiguration = ["UID": getuid()] #endif let endpoints = tunnelConfiguration.peers.compactMap { $0.endpoint } if endpoints.count == 1 { serverAddress = endpoints[0].stringRepresentation } else if endpoints.isEmpty { serverAddress = "Unspecified" } else { serverAddress = "Multiple endpoints" } } func asTunnelConfiguration(called name: String? = nil) -> TunnelConfiguration? { if let passwordReference = passwordReference, let config = Keychain.openReference(called: passwordReference) { return try? TunnelConfiguration(fromWgQuickConfig: config, called: name) } if let oldConfig = providerConfiguration?["WgQuickConfig"] as? String { return try? TunnelConfiguration(fromWgQuickConfig: oldConfig, called: name) } return nil } func destroyConfigurationReference() { guard let ref = passwordReference else { return } Keychain.deleteReference(called: ref) } func verifyConfigurationReference() -> Bool { guard let ref = passwordReference else { return false } return Keychain.verifyReference(called: ref) } @discardableResult func migrateConfigurationIfNeeded(called name: String) -> Bool { /* This is how we did things before we switched to putting items * in the keychain. But it's still useful to keep the migration * around so that .mobileconfig files are easier. */ if let oldConfig = providerConfiguration?["WgQuickConfig"] as? String { #if os(macOS) providerConfiguration = ["UID": getuid()] #elseif os(iOS) providerConfiguration = nil #else #error("Unimplemented") #endif guard passwordReference == nil else { return true } wg_log(.debug, message: "Migrating tunnel configuration '\(name)'") passwordReference = Keychain.makeReference(containing: oldConfig, called: name) return true } #if os(macOS) if passwordReference != nil && providerConfiguration?["UID"] == nil && verifyConfigurationReference() { providerConfiguration = ["UID": getuid()] return true } #endif return false } }