Compare commits

..

6 Commits

Author SHA1 Message Date
Jason A. Donenfeld c6767d9007 Version bump
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2019-06-28 13:51:32 +02:00
Jason A. Donenfeld bb5760cca4 WgQuickConfig: Swift treats \r\n as a single character
let blah = "hello\nworld\ndoes\nthis\nwork"
print(blah.split(separator: "\n"))
//output: ["hello", "world", "does", "this", "work"]

let blah2 = "hello\r\nworld\r\ndoes\r\nthis\r\nwork"
print(blah2.split(separator: "\n"))
//output: ["hello\r\nworld\r\ndoes\r\nthis\r\nwork"]
//expected: ["hello\r", "world\r", "does\r", "this\r", "work\r"]

In blah2, the string splitting fails because swift considers \r\n to be
its own character.

Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2019-06-28 12:26:39 +02:00
Jason A. Donenfeld 26b7971ba6 UI: macOS: Show useful error message on .conf import
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2019-06-28 12:07:18 +02:00
Roopesh Chander b286ede3c6 iOS: Importing: If tunnelsManager isn't ready yet, we should wait for it
Signed-off-by: Roopesh Chander <roop@roopc.net>
2019-06-13 23:02:54 +05:30
Roopesh Chander 4ef7afe3ca macOS: Tunnel detail: Handle deletion outside app, again
This was previously done in commit f281b93, but the changes in commit
1507a97 for handling deletion of multiple tunnels undid this capability.

Signed-off-by: Roopesh Chander <roop@roopc.net>
2019-06-13 22:21:31 +05:30
Jason A. Donenfeld 377f2f0496 TunnelsManager: store UID on macOS for keychain availability
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2019-06-11 02:18:42 +02:00
10 changed files with 82 additions and 44 deletions
@@ -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 -1
View File
@@ -1,2 +1,2 @@
VERSION_NAME = 0.0.20190610
VERSION_ID = 12
VERSION_ID = 13
+20 -26
View File
@@ -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
}
}
+13 -3
View File
@@ -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()
+1 -4
View File
@@ -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)
}
}
}