TunnelsManager: store UID on macOS for keychain availability

Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
This commit is contained in:
Jason A. Donenfeld 2019-06-11 02:03:11 +02:00
parent 7ed5893fc6
commit 377f2f0496
4 changed files with 45 additions and 34 deletions

View File

@ -22,6 +22,9 @@ extension NETunnelProviderProtocol {
if passwordReference == nil { if passwordReference == nil {
return nil return nil
} }
#if os(macOS)
providerConfiguration = ["UID": getuid()]
#endif
let endpoints = tunnelConfiguration.peers.compactMap { $0.endpoint } let endpoints = tunnelConfiguration.peers.compactMap { $0.endpoint }
if endpoints.count == 1 { if endpoints.count == 1 {
@ -60,11 +63,25 @@ extension NETunnelProviderProtocol {
* in the keychain. But it's still useful to keep the migration * in the keychain. But it's still useful to keep the migration
* around so that .mobileconfig files are easier. * around so that .mobileconfig files are easier.
*/ */
guard let oldConfig = providerConfiguration?["WgQuickConfig"] as? String else { return false } if let oldConfig = providerConfiguration?["WgQuickConfig"] as? String {
#if os(macOS)
providerConfiguration = ["UID": getuid()]
#elseif os(iOS)
providerConfiguration = nil providerConfiguration = nil
#else
#error("Unimplemented")
#endif
guard passwordReference == nil else { return true } guard passwordReference == nil else { return true }
wg_log(.debug, message: "Migrating tunnel configuration '\(name)'") wg_log(.debug, message: "Migrating tunnel configuration '\(name)'")
passwordReference = Keychain.makeReference(containing: oldConfig, called: name) passwordReference = Keychain.makeReference(containing: oldConfig, called: name)
return true return true
} }
#if os(macOS)
if passwordReference != nil && providerConfiguration?["UID"] == nil && verifyConfigurationReference() {
providerConfiguration = ["UID": getuid()]
return true
}
#endif
return false
}
} }

View File

@ -58,7 +58,12 @@ class TunnelsManager {
#if os(iOS) #if os(iOS)
let passwordRef = proto.verifyConfigurationReference() ? proto.passwordReference : nil let passwordRef = proto.verifyConfigurationReference() ? proto.passwordReference : nil
#elseif os(macOS) #elseif os(macOS)
let passwordRef = proto.passwordReference // To handle multiple users in macOS, we skip verifying let passwordRef: Data?
if proto.providerConfiguration?["UID"] as? uid_t == getuid() {
passwordRef = proto.verifyConfigurationReference() ? proto.passwordReference : nil
} else {
passwordRef = proto.passwordReference // To handle multiple users in macOS, we skip verifying
}
#else #else
#error("Unimplemented") #error("Unimplemented")
#endif #endif
@ -262,10 +267,15 @@ class TunnelsManager {
func remove(tunnel: TunnelContainer, completionHandler: @escaping (TunnelsManagerError?) -> Void) { func remove(tunnel: TunnelContainer, completionHandler: @escaping (TunnelsManagerError?) -> Void) {
let tunnelProviderManager = tunnel.tunnelProvider let tunnelProviderManager = tunnel.tunnelProvider
if tunnel.isTunnelConfigurationAvailableInKeychain { #if os(macOS)
if tunnel.isTunnelAvailableToUser {
(tunnelProviderManager.protocolConfiguration as? NETunnelProviderProtocol)?.destroyConfigurationReference() (tunnelProviderManager.protocolConfiguration as? NETunnelProviderProtocol)?.destroyConfigurationReference()
} }
#elseif os(iOS)
(tunnelProviderManager.protocolConfiguration as? NETunnelProviderProtocol)?.destroyConfigurationReference()
#else
#error("Unimplemented")
#endif
tunnelProviderManager.removeFromPreferences { [weak self] error in tunnelProviderManager.removeFromPreferences { [weak self] error in
guard error == nil else { guard error == nil else {
wg_log(.error, message: "Remove: Saving configuration failed: \(error!)") wg_log(.error, message: "Remove: Saving configuration failed: \(error!)")
@ -493,14 +503,16 @@ class TunnelContainer: NSObject {
return tunnelProvider.tunnelConfiguration return tunnelProvider.tunnelConfiguration
} }
var isTunnelConfigurationAvailableInKeychain: Bool {
return tunnelProvider.isTunnelConfigurationAvailableInKeychain
}
var onDemandOption: ActivateOnDemandOption { var onDemandOption: ActivateOnDemandOption {
return ActivateOnDemandOption(from: tunnelProvider) return ActivateOnDemandOption(from: tunnelProvider)
} }
#if os(macOS)
var isTunnelAvailableToUser: Bool {
return (tunnelProvider.protocolConfiguration as? NETunnelProviderProtocol)?.providerConfiguration?["UID"] as? uid_t == getuid()
}
#endif
init(tunnel: NETunnelProviderManager) { init(tunnel: NETunnelProviderManager) {
name = tunnel.localizedDescription ?? "Unnamed" name = tunnel.localizedDescription ?? "Unnamed"
let status = TunnelStatus(from: tunnel.connection.status) let status = TunnelStatus(from: tunnel.connection.status)
@ -609,18 +621,8 @@ class TunnelContainer: NSObject {
} }
extension NETunnelProviderManager { extension NETunnelProviderManager {
private static var cachedIsConfigAvailableInKeychainKey: UInt8 = 0
private static var cachedConfigKey: UInt8 = 0 private static var cachedConfigKey: UInt8 = 0
var isTunnelConfigurationAvailableInKeychain: Bool {
if let cachedNumber = objc_getAssociatedObject(self, &NETunnelProviderManager.cachedIsConfigAvailableInKeychainKey) as? NSNumber {
return cachedNumber.boolValue
}
let isAvailable = (protocolConfiguration as? NETunnelProviderProtocol)?.verifyConfigurationReference() ?? false
objc_setAssociatedObject(self, &NETunnelProviderManager.cachedIsConfigAvailableInKeychainKey, NSNumber(value: isAvailable), objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
return isAvailable
}
var tunnelConfiguration: TunnelConfiguration? { var tunnelConfiguration: TunnelConfiguration? {
if let cached = objc_getAssociatedObject(self, &NETunnelProviderManager.cachedConfigKey) as? TunnelConfiguration { if let cached = objc_getAssociatedObject(self, &NETunnelProviderManager.cachedConfigKey) as? TunnelConfiguration {
return cached return cached
@ -636,17 +638,9 @@ extension NETunnelProviderManager {
protocolConfiguration = NETunnelProviderProtocol(tunnelConfiguration: tunnelConfiguration, previouslyFrom: protocolConfiguration) protocolConfiguration = NETunnelProviderProtocol(tunnelConfiguration: tunnelConfiguration, previouslyFrom: protocolConfiguration)
localizedDescription = tunnelConfiguration.name localizedDescription = tunnelConfiguration.name
objc_setAssociatedObject(self, &NETunnelProviderManager.cachedConfigKey, tunnelConfiguration, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC) objc_setAssociatedObject(self, &NETunnelProviderManager.cachedConfigKey, tunnelConfiguration, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
objc_setAssociatedObject(self, &NETunnelProviderManager.cachedIsConfigAvailableInKeychainKey, NSNumber(value: true), objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
} }
func isEquivalentTo(_ tunnel: TunnelContainer) -> Bool { func isEquivalentTo(_ tunnel: TunnelContainer) -> Bool {
switch (isTunnelConfigurationAvailableInKeychain, tunnel.isTunnelConfigurationAvailableInKeychain) { return localizedDescription == tunnel.name && tunnelConfiguration == tunnel.tunnelConfiguration
case (true, true):
return tunnelConfiguration == tunnel.tunnelConfiguration
case (false, false):
return localizedDescription == tunnel.name
default:
return false
}
} }
} }

View File

@ -168,7 +168,7 @@ extension StatusMenu {
func insertTunnelMenuItem(for tunnel: TunnelContainer, at tunnelIndex: Int) { func insertTunnelMenuItem(for tunnel: TunnelContainer, at tunnelIndex: Int) {
let menuItem = TunnelMenuItem(tunnel: tunnel, action: #selector(tunnelClicked(sender:))) let menuItem = TunnelMenuItem(tunnel: tunnel, action: #selector(tunnelClicked(sender:)))
menuItem.target = self menuItem.target = self
menuItem.isHidden = !tunnel.isTunnelConfigurationAvailableInKeychain menuItem.isHidden = !tunnel.isTunnelAvailableToUser
insertItem(menuItem, at: firstTunnelMenuItemIndex + tunnelIndex) insertItem(menuItem, at: firstTunnelMenuItemIndex + tunnelIndex)
if numberOfTunnelMenuItems == 0 { if numberOfTunnelMenuItems == 0 {
insertItem(NSMenuItem.separator(), at: firstTunnelMenuItemIndex + tunnelIndex + 1) insertItem(NSMenuItem.separator(), at: firstTunnelMenuItemIndex + tunnelIndex + 1)

View File

@ -81,7 +81,7 @@ extension ManageTunnelsRootViewController: TunnelsListTableViewControllerDelegat
assert(!tunnelIndices.isEmpty) assert(!tunnelIndices.isEmpty)
if tunnelIndices.count == 1 { if tunnelIndices.count == 1 {
let tunnel = tunnelsManager.tunnel(at: tunnelIndices.first!) let tunnel = tunnelsManager.tunnel(at: tunnelIndices.first!)
if tunnel.isTunnelConfigurationAvailableInKeychain { if tunnel.isTunnelAvailableToUser {
let tunnelDetailVC = TunnelDetailTableViewController(tunnelsManager: tunnelsManager, tunnel: tunnel) let tunnelDetailVC = TunnelDetailTableViewController(tunnelsManager: tunnelsManager, tunnel: tunnel)
setTunnelDetailContentVC(tunnelDetailVC) setTunnelDetailContentVC(tunnelDetailVC)
self.tunnelDetailVC = tunnelDetailVC self.tunnelDetailVC = tunnelDetailVC