Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c6767d9007 | |||
| bb5760cca4 | |||
| 26b7971ba6 | |||
| b286ede3c6 | |||
| 4ef7afe3ca | |||
| 377f2f0496 |
@@ -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 {
|
||||||
providerConfiguration = nil
|
#if os(macOS)
|
||||||
guard passwordReference == nil else { return true }
|
providerConfiguration = ["UID": getuid()]
|
||||||
wg_log(.debug, message: "Migrating tunnel configuration '\(name)'")
|
#elseif os(iOS)
|
||||||
passwordReference = Keychain.makeReference(containing: oldConfig, called: name)
|
providerConfiguration = nil
|
||||||
return true
|
#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 interfaceConfiguration: InterfaceConfiguration?
|
||||||
var peerConfigurations = [PeerConfiguration]()
|
var peerConfigurations = [PeerConfiguration]()
|
||||||
|
|
||||||
let lines = wgQuickConfig.split(separator: "\n")
|
let lines = wgQuickConfig.split { $0.isNewline }
|
||||||
|
|
||||||
var parserState = ParserState.notInASection
|
var parserState = ParserState.notInASection
|
||||||
var attributes = [String: String]()
|
var attributes = [String: String]()
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
VERSION_NAME = 0.0.20190610
|
VERSION_NAME = 0.0.20190610
|
||||||
VERSION_ID = 12
|
VERSION_ID = 13
|
||||||
|
|||||||
@@ -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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,10 +44,20 @@ class TunnelImporter {
|
|||||||
}
|
}
|
||||||
return
|
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 {
|
DispatchQueue.main.async {
|
||||||
if tunnelConfiguration == nil {
|
if parseError != nil {
|
||||||
lastFileImportErrorText = (title: tr("alertBadConfigImportTitle"), message: tr(format: "alertBadConfigImportMessage (%@)", fileName))
|
if let parseError = parseError as? WireGuardAppError {
|
||||||
|
lastFileImportErrorText = parseError.alertText
|
||||||
|
} else {
|
||||||
|
lastFileImportErrorText = (title: tr("alertBadConfigImportTitle"), message: tr(format: "alertBadConfigImportMessage (%@)", fileName))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
configs.append(tunnelConfiguration)
|
configs.append(tunnelConfiguration)
|
||||||
dispatchGroup.leave()
|
dispatchGroup.leave()
|
||||||
|
|||||||
@@ -34,10 +34,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
|
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
|
||||||
guard let tunnelsManager = mainVC?.tunnelsManager else { return true }
|
mainVC?.importFromDisposableFile(url: url)
|
||||||
TunnelImporter.importFromFile(urls: [url], into: tunnelsManager, sourceVC: mainVC, errorPresenterType: ErrorPresenter.self) {
|
|
||||||
_ = FileManager.deleteFile(at: url)
|
|
||||||
}
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -109,6 +109,19 @@ extension MainViewController {
|
|||||||
onTunnelsManagerReady = showTunnelDetailBlock
|
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 {
|
extension MainViewController: UISplitViewControllerDelegate {
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ class TunnelsListTableViewController: NSViewController {
|
|||||||
|
|
||||||
let tunnelsManager: TunnelsManager
|
let tunnelsManager: TunnelsManager
|
||||||
weak var delegate: TunnelsListTableViewControllerDelegate?
|
weak var delegate: TunnelsListTableViewControllerDelegate?
|
||||||
var isRemovingTunnels = false
|
var isRemovingTunnelsFromWithinTheApp = false
|
||||||
|
|
||||||
let tableView: NSTableView = {
|
let tableView: NSTableView = {
|
||||||
let tableView = NSTableView()
|
let tableView = NSTableView()
|
||||||
@@ -183,8 +183,10 @@ class TunnelsListTableViewController: NSViewController {
|
|||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
self.selectTunnel(at: nextSelection)
|
self.selectTunnel(at: nextSelection)
|
||||||
let selectedTunnels = selectedTunnelIndices.map { self.tunnelsManager.tunnel(at: $0) }
|
let selectedTunnels = selectedTunnelIndices.map { self.tunnelsManager.tunnel(at: $0) }
|
||||||
|
self.isRemovingTunnelsFromWithinTheApp = true
|
||||||
self.tunnelsManager.removeMultiple(tunnels: selectedTunnels) { [weak self] error in
|
self.tunnelsManager.removeMultiple(tunnels: selectedTunnels) { [weak self] error in
|
||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
|
self.isRemovingTunnelsFromWithinTheApp = false
|
||||||
defer { completion() }
|
defer { completion() }
|
||||||
if let error = error {
|
if let error = error {
|
||||||
ErrorPresenter.showErrorAlert(error: error, from: self)
|
ErrorPresenter.showErrorAlert(error: error, from: self)
|
||||||
@@ -281,9 +283,14 @@ extension TunnelsListTableViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func tunnelRemoved(at index: Int) {
|
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)
|
tableView.removeRows(at: IndexSet(integer: index), withAnimation: .slideLeft)
|
||||||
if tunnelsManager.numberOfTunnels() == 0 {
|
if tunnelsManager.numberOfTunnels() == 0 {
|
||||||
delegate?.tunnelsListEmpty()
|
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