From 9d5b376dcf4d711cbb8d57473f6c869908f1f9cb Mon Sep 17 00:00:00 2001 From: "Jason A. Donenfeld" Date: Fri, 11 Dec 2020 12:50:31 +0100 Subject: Revert "[REVERT ME SOON] TunnelsManager: Workaround for macOS Catalina deleting tunnels arbitrarily" This reverts commit 028e76eb3fda127d84eb88dc5cb96d4278f37b96. It's been over a year. I really hope this is fixed by Apple. Signed-off-by: Jason A. Donenfeld --- Sources/WireGuardApp/Tunnel/TunnelsManager.swift | 163 +---------------------- 1 file changed, 2 insertions(+), 161 deletions(-) (limited to 'Sources/WireGuardApp/Tunnel/TunnelsManager.swift') diff --git a/Sources/WireGuardApp/Tunnel/TunnelsManager.swift b/Sources/WireGuardApp/Tunnel/TunnelsManager.swift index dea541f..024c3fd 100644 --- a/Sources/WireGuardApp/Tunnel/TunnelsManager.swift +++ b/Sources/WireGuardApp/Tunnel/TunnelsManager.swift @@ -20,23 +20,17 @@ protocol TunnelsManagerActivationDelegate: class { } class TunnelsManager { - fileprivate var tunnels: [TunnelContainer] + private var tunnels: [TunnelContainer] weak var tunnelsListDelegate: TunnelsManagerListDelegate? weak var activationDelegate: TunnelsManagerActivationDelegate? private var statusObservationToken: AnyObject? private var waiteeObservationToken: AnyObject? private var configurationsObservationToken: AnyObject? - private var catalinaWorkaround: Any? init(tunnelProviders: [NETunnelProviderManager]) { tunnels = tunnelProviders.map { TunnelContainer(tunnel: $0) }.sorted { TunnelsManager.tunnelNameIsLessThan($0.name, $1.name) } startObservingTunnelStatuses() startObservingTunnelConfigurations() - #if os(macOS) - if #available(macOS 10.15, *) { - self.catalinaWorkaround = CatalinaWorkaround(tunnelsManager: self) - } - #endif } static func create(completionHandler: @escaping (Result) -> Void) { @@ -81,15 +75,7 @@ class TunnelsManager { tunnelManagers.remove(at: index) } } - #if os(macOS) - if #available(macOS 10.15, *) { - // Don't delete orphaned keychain refs. We need them to restore tunnels as a workaround. - } else { - Keychain.deleteReferences(except: refs) - } - #else Keychain.deleteReferences(except: refs) - #endif #if os(iOS) RecentTunnelsTracker.cleanupTunnels(except: tunnelNames) #endif @@ -636,7 +622,7 @@ class TunnelContainer: NSObject { } extension NETunnelProviderManager { - fileprivate static var cachedConfigKey: UInt8 = 0 + private static var cachedConfigKey: UInt8 = 0 var tunnelConfiguration: TunnelConfiguration? { if let cached = objc_getAssociatedObject(self, &NETunnelProviderManager.cachedConfigKey) as? TunnelConfiguration { @@ -659,148 +645,3 @@ extension NETunnelProviderManager { return localizedDescription == tunnel.name && tunnelConfiguration == tunnel.tunnelConfiguration } } - -#if os(macOS) -@available(macOS 10.15, *) -class CatalinaWorkaround { - - // In macOS Catalina, for some users, the tunnels get deleted arbitrarily - // by the OS. It's not clear what triggers that. - - // As a workaround, in macOS Catalina, when we realize that tunnels have been - // deleted outside the app, we reinstate those tunnels using the information - // in the keychain. - - unowned let tunnelsManager: TunnelsManager - private var configChangeSubscriber: Any? - - struct ReinstationData { - let tunnelConfiguration: TunnelConfiguration - let keychainPasswordRef: Data - } - - init(tunnelsManager: TunnelsManager) { - self.tunnelsManager = tunnelsManager - - // Attempt reinstation when there's a change in tunnel configurations, - // which indicates that tunnels may have been deleted outside the app. - // We use debounce to wait for all change notifications to arrive - // before attempting to reinstate, so that we don't have saveToPreferences - // being called while another saveToPreferences is in progress. - self.configChangeSubscriber = NotificationCenter.default - .publisher(for: .NEVPNConfigurationChange, object: nil) - .debounce(for: .seconds(1), scheduler: RunLoop.main) - .subscribe(on: RunLoop.main) - .sink { [weak self] _ in - self?.reinstateTunnelsDeletedOutsideApp() - } - - // Attempt reinstation on app launch - reinstateTunnelsDeletedOutsideApp() - } - - func reinstateTunnelsDeletedOutsideApp() { - let data = reinstationDataForTunnelsDeletedOutsideApp() - reinstateTunnels(ArraySlice(data), completionHandler: nil) - } - - private func reinstateTunnels(_ rdArray: ArraySlice, completionHandler: (() -> Void)?) { - guard let head = rdArray.first else { - completionHandler?() - return - } - let tail = rdArray.dropFirst() - self.tunnelsManager.reinstateTunnel(reinstationData: head) { _ in - DispatchQueue.main.async { - self.reinstateTunnels(tail, completionHandler: completionHandler) - } - } - } - - private func reinstationDataForTunnelsDeletedOutsideApp() -> [ReinstationData] { - let knownRefs: [Data] = self.tunnelsManager.tunnels - .compactMap { $0.tunnelProvider.protocolConfiguration as? NETunnelProviderProtocol } - .compactMap { $0.passwordReference } - let knownRefsSet: Set = Set(knownRefs) - var result: CFTypeRef? - let ret = SecItemCopyMatching([kSecClass as String: kSecClassGenericPassword, - kSecAttrService as String: Bundle.main.bundleIdentifier as Any, - kSecMatchLimit as String: kSecMatchLimitAll, - kSecReturnAttributes as String: true, - kSecReturnPersistentRef as String: true] as CFDictionary, - &result) - guard ret == errSecSuccess, let resultDicts = result as? [[String: Any]] else { return [] } - let labelPrefix = "WireGuard Tunnel: " - var reinstationData: [ReinstationData] = [] - for resultDict in resultDicts { - guard let ref = resultDict[kSecValuePersistentRef as String] as? Data else { continue } - guard let label = resultDict[kSecAttrLabel as String] as? String else { continue } - guard label.hasPrefix(labelPrefix) else { continue } - if !knownRefsSet.contains(ref) { - let tunnelName = String(label.dropFirst(labelPrefix.count)) - if let configStr = Keychain.openReference(called: ref), - let config = try? TunnelConfiguration(fromWgQuickConfig: configStr, called: tunnelName) { - reinstationData.append(ReinstationData(tunnelConfiguration: config, keychainPasswordRef: ref)) - } - } - } - return reinstationData - } -} -#endif - -#if os(macOS) -@available(macOS 10.15, *) -extension TunnelsManager { - fileprivate func reinstateTunnel(reinstationData: CatalinaWorkaround.ReinstationData, completionHandler: @escaping (Bool) -> Void) { - let tunnelName = reinstationData.tunnelConfiguration.name ?? "" - if tunnelName.isEmpty { - completionHandler(false) - return - } - - if tunnels.contains(where: { $0.name == tunnelName }) { - completionHandler(false) - return - } - - let tunnelProviderProtocol = NETunnelProviderProtocol() - guard let appId = Bundle.main.bundleIdentifier else { fatalError() } - tunnelProviderProtocol.providerBundleIdentifier = "\(appId).network-extension" - tunnelProviderProtocol.passwordReference = reinstationData.keychainPasswordRef - tunnelProviderProtocol.providerConfiguration = ["UID": getuid()] - tunnelProviderProtocol.serverAddress = { - let endpoints = reinstationData.tunnelConfiguration.peers.compactMap { $0.endpoint } - if endpoints.count == 1 { - return endpoints[0].stringRepresentation - } else if endpoints.isEmpty { - return "Unspecified" - } else { - return "Multiple endpoints" - } - }() - - let tunnelProvider = NETunnelProviderManager() - tunnelProvider.localizedDescription = tunnelName - tunnelProvider.protocolConfiguration = tunnelProviderProtocol - objc_setAssociatedObject(tunnelProvider, &NETunnelProviderManager.cachedConfigKey, reinstationData.tunnelConfiguration, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC) - tunnelProvider.isEnabled = true - - tunnelProvider.saveToPreferences { [weak self] error in - guard error == nil else { - wg_log(.error, message: "Reinstate: Saving configuration failed: \(error!)") - completionHandler(false) - return - } - - guard let self = self else { return } - - let tunnel = TunnelContainer(tunnel: tunnelProvider) - self.tunnels.append(tunnel) - self.tunnels.sort { TunnelsManager.tunnelNameIsLessThan($0.name, $1.name) } - self.tunnelsListDelegate?.tunnelAdded(at: self.tunnels.firstIndex(of: tunnel)!) - completionHandler(true) - } - } -} -#endif -- cgit v1.2.3-59-g8ed1b