Return password reference at the time of setting
Simplifies app/extension IPC.
This commit is contained in:
parent
8e6624e113
commit
bb5cd1e1ab
|
@ -98,12 +98,11 @@ class ViewController: UIViewController, URLSessionDataDelegate {
|
||||||
|
|
||||||
let credentials = OpenVPN.Credentials(textUsername.text!, textPassword.text!)
|
let credentials = OpenVPN.Credentials(textUsername.text!, textPassword.text!)
|
||||||
let cfg = Configuration.make(hostname: hostname, port: port, socketType: socketType)
|
let cfg = Configuration.make(hostname: hostname, port: port, socketType: socketType)
|
||||||
try? keychain.set(password: credentials.password, for: credentials.username, context: tunnelIdentifier)
|
|
||||||
let proto = try! cfg.generatedTunnelProtocol(
|
let proto = try! cfg.generatedTunnelProtocol(
|
||||||
withBundleIdentifier: tunnelIdentifier,
|
withBundleIdentifier: tunnelIdentifier,
|
||||||
appGroup: appGroup,
|
appGroup: appGroup,
|
||||||
context: tunnelIdentifier,
|
context: tunnelIdentifier,
|
||||||
username: credentials.username
|
credentials: credentials
|
||||||
)
|
)
|
||||||
let neCfg = NetworkExtensionVPNConfiguration(title: "BasicTunnel", protocolConfiguration: proto, onDemandRules: [])
|
let neCfg = NetworkExtensionVPNConfiguration(title: "BasicTunnel", protocolConfiguration: proto, onDemandRules: [])
|
||||||
vpn.reconnect(configuration: neCfg) { (error) in
|
vpn.reconnect(configuration: neCfg) { (error) in
|
||||||
|
@ -147,15 +146,11 @@ class ViewController: UIViewController, URLSessionDataDelegate {
|
||||||
let username = "foo"
|
let username = "foo"
|
||||||
let password = "bar"
|
let password = "bar"
|
||||||
|
|
||||||
guard let _ = try? keychain.set(password: password, for: username, context: tunnelIdentifier) else {
|
guard let ref = try? keychain.set(password: password, for: username, context: tunnelIdentifier) else {
|
||||||
print("Couldn't set password")
|
print("Couldn't set password")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
guard let passwordReference = try? keychain.passwordReference(for: username, context: tunnelIdentifier) else {
|
guard let fetchedPassword = try? Keychain.password(forReference: ref) else {
|
||||||
print("Couldn't get password reference")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
guard let fetchedPassword = try? keychain.password(for: username, reference: passwordReference, context: tunnelIdentifier) else {
|
|
||||||
print("Couldn't fetch password")
|
print("Couldn't fetch password")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -89,12 +89,11 @@ class ViewController: NSViewController {
|
||||||
|
|
||||||
let credentials = OpenVPN.Credentials(textUsername.stringValue, textPassword.stringValue)
|
let credentials = OpenVPN.Credentials(textUsername.stringValue, textPassword.stringValue)
|
||||||
let cfg = Configuration.make(hostname: hostname, port: port, socketType: .udp)
|
let cfg = Configuration.make(hostname: hostname, port: port, socketType: .udp)
|
||||||
try? keychain.set(password: credentials.password, for: credentials.username, context: tunnelIdentifier)
|
|
||||||
let proto = try! cfg.generatedTunnelProtocol(
|
let proto = try! cfg.generatedTunnelProtocol(
|
||||||
withBundleIdentifier: tunnelIdentifier,
|
withBundleIdentifier: tunnelIdentifier,
|
||||||
appGroup: appGroup,
|
appGroup: appGroup,
|
||||||
context: tunnelIdentifier,
|
context: tunnelIdentifier,
|
||||||
username: credentials.username
|
credentials: credentials
|
||||||
)
|
)
|
||||||
let neCfg = NetworkExtensionVPNConfiguration(title: "BasicTunnel", protocolConfiguration: proto, onDemandRules: [])
|
let neCfg = NetworkExtensionVPNConfiguration(title: "BasicTunnel", protocolConfiguration: proto, onDemandRules: [])
|
||||||
vpn.reconnect(configuration: neCfg) { (error) in
|
vpn.reconnect(configuration: neCfg) { (error) in
|
||||||
|
@ -132,15 +131,11 @@ class ViewController: NSViewController {
|
||||||
let username = "foo"
|
let username = "foo"
|
||||||
let password = "bar"
|
let password = "bar"
|
||||||
|
|
||||||
guard let _ = try? keychain.set(password: password, for: username, context: tunnelIdentifier) else {
|
guard let ref = try? keychain.set(password: password, for: username, context: tunnelIdentifier) else {
|
||||||
print("Couldn't set password")
|
print("Couldn't set password")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
guard let passwordReference = try? keychain.passwordReference(for: username, context: tunnelIdentifier) else {
|
guard let fetchedPassword = try? Keychain.password(forReference: ref) else {
|
||||||
print("Couldn't get password reference")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
guard let fetchedPassword = try? keychain.password(for: username, reference: passwordReference, context: tunnelIdentifier) else {
|
|
||||||
print("Couldn't fetch password")
|
print("Couldn't fetch password")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -74,13 +74,15 @@ public class Keychain {
|
||||||
- Parameter password: The password to set.
|
- Parameter password: The password to set.
|
||||||
- Parameter username: The username to set the password for.
|
- Parameter username: The username to set the password for.
|
||||||
- Parameter context: An optional context.
|
- Parameter context: An optional context.
|
||||||
|
- Returns: The reference to the password.
|
||||||
- Throws: `KeychainError.add` if unable to add the password to the keychain.
|
- Throws: `KeychainError.add` if unable to add the password to the keychain.
|
||||||
**/
|
**/
|
||||||
public func set(password: String, for username: String, context: String? = nil) throws {
|
@discardableResult
|
||||||
|
public func set(password: String, for username: String, context: String? = nil) throws -> Data {
|
||||||
do {
|
do {
|
||||||
let currentPassword = try self.password(for: username, context: context)
|
let currentPassword = try self.password(for: username, context: context)
|
||||||
guard password != currentPassword else {
|
guard password != currentPassword else {
|
||||||
return
|
return try passwordReference(for: username, context: context)
|
||||||
}
|
}
|
||||||
removePassword(for: username, context: context)
|
removePassword(for: username, context: context)
|
||||||
} catch let e as KeychainError {
|
} catch let e as KeychainError {
|
||||||
|
@ -99,11 +101,14 @@ public class Keychain {
|
||||||
query[kSecAttrAccount as String] = username
|
query[kSecAttrAccount as String] = username
|
||||||
query[kSecAttrAccessible as String] = kSecAttrAccessibleAfterFirstUnlock
|
query[kSecAttrAccessible as String] = kSecAttrAccessibleAfterFirstUnlock
|
||||||
query[kSecValueData as String] = password.data(using: .utf8)
|
query[kSecValueData as String] = password.data(using: .utf8)
|
||||||
|
query[kSecReturnPersistentRef as String] = true
|
||||||
|
|
||||||
let status = SecItemAdd(query as CFDictionary, nil)
|
var ref: CFTypeRef?
|
||||||
guard status == errSecSuccess else {
|
let status = SecItemAdd(query as CFDictionary, &ref)
|
||||||
|
guard status == errSecSuccess, let refData = ref as? Data else {
|
||||||
throw KeychainError.add
|
throw KeychainError.add
|
||||||
}
|
}
|
||||||
|
return refData
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -195,18 +200,13 @@ public class Keychain {
|
||||||
/**
|
/**
|
||||||
Gets a password associated with a password reference.
|
Gets a password associated with a password reference.
|
||||||
|
|
||||||
- Parameter username: The username to get the password for.
|
|
||||||
- Parameter reference: The password reference.
|
- Parameter reference: The password reference.
|
||||||
- Parameter context: An optional context.
|
- Returns: The password for the input reference.
|
||||||
- Returns: The password for the input username and reference.
|
|
||||||
- Throws: `KeychainError.notFound` if unable to find the password in the keychain.
|
- Throws: `KeychainError.notFound` if unable to find the password in the keychain.
|
||||||
**/
|
**/
|
||||||
public func password(for username: String, reference: Data, context: String? = nil) throws -> String {
|
public static func password(forReference reference: Data) throws -> String {
|
||||||
var query = [String: Any]()
|
var query = [String: Any]()
|
||||||
setScope(query: &query, context: context)
|
query[kSecValuePersistentRef as String] = reference
|
||||||
query[kSecClass as String] = kSecClassGenericPassword
|
|
||||||
query[kSecAttrAccount as String] = username
|
|
||||||
query[kSecMatchItemList as String] = [reference]
|
|
||||||
query[kSecReturnData as String] = true
|
query[kSecReturnData as String] = true
|
||||||
|
|
||||||
var result: AnyObject?
|
var result: AnyObject?
|
||||||
|
|
|
@ -217,9 +217,8 @@ open class OpenVPNTunnelProvider: NEPacketTunnelProvider {
|
||||||
// optional credentials
|
// optional credentials
|
||||||
let credentials: OpenVPN.Credentials?
|
let credentials: OpenVPN.Credentials?
|
||||||
if let username = protocolConfiguration.username, let passwordReference = protocolConfiguration.passwordReference {
|
if let username = protocolConfiguration.username, let passwordReference = protocolConfiguration.passwordReference {
|
||||||
let keychain = Keychain(group: appGroup)
|
guard let password = try? Keychain.password(forReference: passwordReference) else {
|
||||||
guard let password = try? keychain.password(for: username, reference: passwordReference) else {
|
completionHandler(OpenVPNProviderConfigurationError.credentials(details: "Keychain.password(forReference:)"))
|
||||||
completionHandler(OpenVPNProviderConfigurationError.credentials(details: "keychain.password(for:, reference:)"))
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
credentials = OpenVPN.Credentials(username, password)
|
credentials = OpenVPN.Credentials(username, password)
|
||||||
|
|
|
@ -276,7 +276,7 @@ extension OpenVPNProvider {
|
||||||
- Parameter bundleIdentifier: The provider bundle identifier required to locate the tunnel extension.
|
- Parameter bundleIdentifier: The provider bundle identifier required to locate the tunnel extension.
|
||||||
- Parameter appGroup: The name of the app group in which the tunnel extension lives in.
|
- Parameter appGroup: The name of the app group in which the tunnel extension lives in.
|
||||||
- Parameter context: The keychain context where to look for the password reference.
|
- Parameter context: The keychain context where to look for the password reference.
|
||||||
- Parameter username: The username to authenticate with.
|
- Parameter credentials: The credentials to authenticate with.
|
||||||
- Returns: The generated `NETunnelProviderProtocol` object.
|
- Returns: The generated `NETunnelProviderProtocol` object.
|
||||||
- Throws: `OpenVPNProviderError.credentials` if unable to store `credentials.password` to the `appGroup` keychain.
|
- Throws: `OpenVPNProviderError.credentials` if unable to store `credentials.password` to the `appGroup` keychain.
|
||||||
*/
|
*/
|
||||||
|
@ -284,16 +284,18 @@ extension OpenVPNProvider {
|
||||||
withBundleIdentifier bundleIdentifier: String,
|
withBundleIdentifier bundleIdentifier: String,
|
||||||
appGroup: String,
|
appGroup: String,
|
||||||
context: String,
|
context: String,
|
||||||
username: String?) throws -> NETunnelProviderProtocol {
|
credentials: OpenVPN.Credentials?) throws -> NETunnelProviderProtocol {
|
||||||
|
|
||||||
let protocolConfiguration = NETunnelProviderProtocol()
|
let protocolConfiguration = NETunnelProviderProtocol()
|
||||||
let keychain = Keychain(group: appGroup)
|
let keychain = Keychain(group: appGroup)
|
||||||
|
|
||||||
protocolConfiguration.providerBundleIdentifier = bundleIdentifier
|
protocolConfiguration.providerBundleIdentifier = bundleIdentifier
|
||||||
protocolConfiguration.serverAddress = sessionConfiguration.hostname ?? resolvedAddresses?.first
|
protocolConfiguration.serverAddress = sessionConfiguration.hostname ?? resolvedAddresses?.first
|
||||||
if let username = username {
|
if let username = credentials?.username {
|
||||||
protocolConfiguration.username = username
|
protocolConfiguration.username = username
|
||||||
protocolConfiguration.passwordReference = try? keychain.passwordReference(for: username, context: context)
|
if let password = credentials?.password {
|
||||||
|
protocolConfiguration.passwordReference = try? keychain.set(password: password, for: username, context: context)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
protocolConfiguration.providerConfiguration = generatedProviderConfiguration(appGroup: appGroup)
|
protocolConfiguration.providerConfiguration = generatedProviderConfiguration(appGroup: appGroup)
|
||||||
|
|
||||||
|
|
|
@ -81,7 +81,7 @@ class AppExtensionTests: XCTestCase {
|
||||||
withBundleIdentifier: identifier,
|
withBundleIdentifier: identifier,
|
||||||
appGroup: appGroup,
|
appGroup: appGroup,
|
||||||
context: context,
|
context: context,
|
||||||
username: credentials.username
|
credentials: credentials
|
||||||
)
|
)
|
||||||
XCTAssertNotNil(proto)
|
XCTAssertNotNil(proto)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue