Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c6767d9007 | |||
| bb5760cca4 | |||
| 26b7971ba6 | |||
| b286ede3c6 | |||
| 4ef7afe3ca | |||
| 377f2f0496 |
@@ -22,6 +22,9 @@ extension NETunnelProviderProtocol {
|
||||
if passwordReference == nil {
|
||||
return nil
|
||||
}
|
||||
#if os(macOS)
|
||||
providerConfiguration = ["UID": getuid()]
|
||||
#endif
|
||||
|
||||
let endpoints = tunnelConfiguration.peers.compactMap { $0.endpoint }
|
||||
if endpoints.count == 1 {
|
||||
@@ -60,11 +63,25 @@ extension NETunnelProviderProtocol {
|
||||
* in the keychain. But it's still useful to keep the migration
|
||||
* around so that .mobileconfig files are easier.
|
||||
*/
|
||||
guard let oldConfig = providerConfiguration?["WgQuickConfig"] as? String else { return false }
|
||||
providerConfiguration = nil
|
||||
guard passwordReference == nil else { return true }
|
||||
wg_log(.debug, message: "Migrating tunnel configuration '\(name)'")
|
||||
passwordReference = Keychain.makeReference(containing: oldConfig, called: name)
|
||||
return true
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ extension TunnelConfiguration {
|
||||
var interfaceConfiguration: InterfaceConfiguration?
|
||||
var peerConfigurations = [PeerConfiguration]()
|
||||
|
||||
let lines = wgQuickConfig.split(separator: "\n")
|
||||
let lines = wgQuickConfig.split { $0.isNewline }
|
||||
|
||||
var parserState = ParserState.notInASection
|
||||
var attributes = [String: String]()
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
VERSION_NAME = 0.0.20190610
|
||||
VERSION_ID = 12
|
||||
VERSION_ID = 13
|
||||
|
||||
@@ -58,7 +58,12 @@ class TunnelsManager {
|
||||
#if os(iOS)
|
||||
let passwordRef = proto.verifyConfigurationReference() ? proto.passwordReference : nil
|
||||
#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
|
||||
#error("Unimplemented")
|
||||
#endif
|
||||
@@ -262,10 +267,15 @@ class TunnelsManager {
|
||||
|
||||
func remove(tunnel: TunnelContainer, completionHandler: @escaping (TunnelsManagerError?) -> Void) {
|
||||
let tunnelProviderManager = tunnel.tunnelProvider
|
||||
if tunnel.isTunnelConfigurationAvailableInKeychain {
|
||||
#if os(macOS)
|
||||
if tunnel.isTunnelAvailableToUser {
|
||||
(tunnelProviderManager.protocolConfiguration as? NETunnelProviderProtocol)?.destroyConfigurationReference()
|
||||
}
|
||||
|
||||
#elseif os(iOS)
|
||||
(tunnelProviderManager.protocolConfiguration as? NETunnelProviderProtocol)?.destroyConfigurationReference()
|
||||
#else
|
||||
#error("Unimplemented")
|
||||
#endif
|
||||
tunnelProviderManager.removeFromPreferences { [weak self] error in
|
||||
guard error == nil else {
|
||||
wg_log(.error, message: "Remove: Saving configuration failed: \(error!)")
|
||||
@@ -493,14 +503,16 @@ class TunnelContainer: NSObject {
|
||||
return tunnelProvider.tunnelConfiguration
|
||||
}
|
||||
|
||||
var isTunnelConfigurationAvailableInKeychain: Bool {
|
||||
return tunnelProvider.isTunnelConfigurationAvailableInKeychain
|
||||
}
|
||||
|
||||
var onDemandOption: ActivateOnDemandOption {
|
||||
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) {
|
||||
name = tunnel.localizedDescription ?? "Unnamed"
|
||||
let status = TunnelStatus(from: tunnel.connection.status)
|
||||
@@ -609,18 +621,8 @@ class TunnelContainer: NSObject {
|
||||
}
|
||||
|
||||
extension NETunnelProviderManager {
|
||||
private static var cachedIsConfigAvailableInKeychainKey: 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? {
|
||||
if let cached = objc_getAssociatedObject(self, &NETunnelProviderManager.cachedConfigKey) as? TunnelConfiguration {
|
||||
return cached
|
||||
@@ -636,17 +638,9 @@ extension NETunnelProviderManager {
|
||||
protocolConfiguration = NETunnelProviderProtocol(tunnelConfiguration: tunnelConfiguration, previouslyFrom: protocolConfiguration)
|
||||
localizedDescription = tunnelConfiguration.name
|
||||
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 {
|
||||
switch (isTunnelConfigurationAvailableInKeychain, tunnel.isTunnelConfigurationAvailableInKeychain) {
|
||||
case (true, true):
|
||||
return tunnelConfiguration == tunnel.tunnelConfiguration
|
||||
case (false, false):
|
||||
return localizedDescription == tunnel.name
|
||||
default:
|
||||
return false
|
||||
}
|
||||
return localizedDescription == tunnel.name && tunnelConfiguration == tunnel.tunnelConfiguration
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,10 +44,20 @@ class TunnelImporter {
|
||||
}
|
||||
return
|
||||
}
|
||||
let tunnelConfiguration = try? TunnelConfiguration(fromWgQuickConfig: fileContents, called: fileBaseName)
|
||||
var parseError: Error?
|
||||
var tunnelConfiguration: TunnelConfiguration?
|
||||
do {
|
||||
tunnelConfiguration = try TunnelConfiguration(fromWgQuickConfig: fileContents, called: fileBaseName)
|
||||
} catch let error {
|
||||
parseError = error
|
||||
}
|
||||
DispatchQueue.main.async {
|
||||
if tunnelConfiguration == nil {
|
||||
lastFileImportErrorText = (title: tr("alertBadConfigImportTitle"), message: tr(format: "alertBadConfigImportMessage (%@)", fileName))
|
||||
if parseError != nil {
|
||||
if let parseError = parseError as? WireGuardAppError {
|
||||
lastFileImportErrorText = parseError.alertText
|
||||
} else {
|
||||
lastFileImportErrorText = (title: tr("alertBadConfigImportTitle"), message: tr(format: "alertBadConfigImportMessage (%@)", fileName))
|
||||
}
|
||||
}
|
||||
configs.append(tunnelConfiguration)
|
||||
dispatchGroup.leave()
|
||||
|
||||
@@ -34,10 +34,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||
}
|
||||
|
||||
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
|
||||
guard let tunnelsManager = mainVC?.tunnelsManager else { return true }
|
||||
TunnelImporter.importFromFile(urls: [url], into: tunnelsManager, sourceVC: mainVC, errorPresenterType: ErrorPresenter.self) {
|
||||
_ = FileManager.deleteFile(at: url)
|
||||
}
|
||||
mainVC?.importFromDisposableFile(url: url)
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
@@ -109,6 +109,19 @@ extension MainViewController {
|
||||
onTunnelsManagerReady = showTunnelDetailBlock
|
||||
}
|
||||
}
|
||||
|
||||
func importFromDisposableFile(url: URL) {
|
||||
let importFromFileBlock: (TunnelsManager) -> Void = { [weak self] tunnelsManager in
|
||||
TunnelImporter.importFromFile(urls: [url], into: tunnelsManager, sourceVC: self, errorPresenterType: ErrorPresenter.self) {
|
||||
_ = FileManager.deleteFile(at: url)
|
||||
}
|
||||
}
|
||||
if let tunnelsManager = tunnelsManager {
|
||||
importFromFileBlock(tunnelsManager)
|
||||
} else {
|
||||
onTunnelsManagerReady = importFromFileBlock
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension MainViewController: UISplitViewControllerDelegate {
|
||||
|
||||
@@ -168,7 +168,7 @@ extension StatusMenu {
|
||||
func insertTunnelMenuItem(for tunnel: TunnelContainer, at tunnelIndex: Int) {
|
||||
let menuItem = TunnelMenuItem(tunnel: tunnel, action: #selector(tunnelClicked(sender:)))
|
||||
menuItem.target = self
|
||||
menuItem.isHidden = !tunnel.isTunnelConfigurationAvailableInKeychain
|
||||
menuItem.isHidden = !tunnel.isTunnelAvailableToUser
|
||||
insertItem(menuItem, at: firstTunnelMenuItemIndex + tunnelIndex)
|
||||
if numberOfTunnelMenuItems == 0 {
|
||||
insertItem(NSMenuItem.separator(), at: firstTunnelMenuItemIndex + tunnelIndex + 1)
|
||||
|
||||
@@ -81,7 +81,7 @@ extension ManageTunnelsRootViewController: TunnelsListTableViewControllerDelegat
|
||||
assert(!tunnelIndices.isEmpty)
|
||||
if tunnelIndices.count == 1 {
|
||||
let tunnel = tunnelsManager.tunnel(at: tunnelIndices.first!)
|
||||
if tunnel.isTunnelConfigurationAvailableInKeychain {
|
||||
if tunnel.isTunnelAvailableToUser {
|
||||
let tunnelDetailVC = TunnelDetailTableViewController(tunnelsManager: tunnelsManager, tunnel: tunnel)
|
||||
setTunnelDetailContentVC(tunnelDetailVC)
|
||||
self.tunnelDetailVC = tunnelDetailVC
|
||||
|
||||
@@ -12,7 +12,7 @@ class TunnelsListTableViewController: NSViewController {
|
||||
|
||||
let tunnelsManager: TunnelsManager
|
||||
weak var delegate: TunnelsListTableViewControllerDelegate?
|
||||
var isRemovingTunnels = false
|
||||
var isRemovingTunnelsFromWithinTheApp = false
|
||||
|
||||
let tableView: NSTableView = {
|
||||
let tableView = NSTableView()
|
||||
@@ -183,8 +183,10 @@ class TunnelsListTableViewController: NSViewController {
|
||||
guard let self = self else { return }
|
||||
self.selectTunnel(at: nextSelection)
|
||||
let selectedTunnels = selectedTunnelIndices.map { self.tunnelsManager.tunnel(at: $0) }
|
||||
self.isRemovingTunnelsFromWithinTheApp = true
|
||||
self.tunnelsManager.removeMultiple(tunnels: selectedTunnels) { [weak self] error in
|
||||
guard let self = self else { return }
|
||||
self.isRemovingTunnelsFromWithinTheApp = false
|
||||
defer { completion() }
|
||||
if let error = error {
|
||||
ErrorPresenter.showErrorAlert(error: error, from: self)
|
||||
@@ -281,9 +283,14 @@ extension TunnelsListTableViewController {
|
||||
}
|
||||
|
||||
func tunnelRemoved(at index: Int) {
|
||||
let selectedIndices = tableView.selectedRowIndexes
|
||||
let isSingleSelectedTunnelBeingRemoved = selectedIndices.contains(index) && selectedIndices.count == 1
|
||||
tableView.removeRows(at: IndexSet(integer: index), withAnimation: .slideLeft)
|
||||
if tunnelsManager.numberOfTunnels() == 0 {
|
||||
delegate?.tunnelsListEmpty()
|
||||
} else if !isRemovingTunnelsFromWithinTheApp && isSingleSelectedTunnelBeingRemoved {
|
||||
let newSelection = min(index, tunnelsManager.numberOfTunnels() - 1)
|
||||
tableView.selectRowIndexes(IndexSet(integer: newSelection), byExtendingSelection: false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user