Return password reference at the time of setting

Simplifies app/extension IPC.
This commit is contained in:
Davide De Rosa 2021-11-23 00:24:01 +01:00
parent 8e6624e113
commit bb5cd1e1ab
6 changed files with 27 additions and 36 deletions

View File

@ -98,12 +98,11 @@ class ViewController: UIViewController, URLSessionDataDelegate {
let credentials = OpenVPN.Credentials(textUsername.text!, textPassword.text!)
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(
withBundleIdentifier: tunnelIdentifier,
appGroup: appGroup,
context: tunnelIdentifier,
username: credentials.username
credentials: credentials
)
let neCfg = NetworkExtensionVPNConfiguration(title: "BasicTunnel", protocolConfiguration: proto, onDemandRules: [])
vpn.reconnect(configuration: neCfg) { (error) in
@ -147,15 +146,11 @@ class ViewController: UIViewController, URLSessionDataDelegate {
let username = "foo"
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")
return
}
guard let passwordReference = try? keychain.passwordReference(for: username, context: tunnelIdentifier) else {
print("Couldn't get password reference")
return
}
guard let fetchedPassword = try? keychain.password(for: username, reference: passwordReference, context: tunnelIdentifier) else {
guard let fetchedPassword = try? Keychain.password(forReference: ref) else {
print("Couldn't fetch password")
return
}

View File

@ -89,12 +89,11 @@ class ViewController: NSViewController {
let credentials = OpenVPN.Credentials(textUsername.stringValue, textPassword.stringValue)
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(
withBundleIdentifier: tunnelIdentifier,
appGroup: appGroup,
context: tunnelIdentifier,
username: credentials.username
credentials: credentials
)
let neCfg = NetworkExtensionVPNConfiguration(title: "BasicTunnel", protocolConfiguration: proto, onDemandRules: [])
vpn.reconnect(configuration: neCfg) { (error) in
@ -132,15 +131,11 @@ class ViewController: NSViewController {
let username = "foo"
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")
return
}
guard let passwordReference = try? keychain.passwordReference(for: username, context: tunnelIdentifier) else {
print("Couldn't get password reference")
return
}
guard let fetchedPassword = try? keychain.password(for: username, reference: passwordReference, context: tunnelIdentifier) else {
guard let fetchedPassword = try? Keychain.password(forReference: ref) else {
print("Couldn't fetch password")
return
}

View File

@ -74,13 +74,15 @@ public class Keychain {
- Parameter password: The password to set.
- Parameter username: The username to set the password for.
- Parameter context: An optional context.
- Returns: The reference to the password.
- 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 {
let currentPassword = try self.password(for: username, context: context)
guard password != currentPassword else {
return
return try passwordReference(for: username, context: context)
}
removePassword(for: username, context: context)
} catch let e as KeychainError {
@ -99,11 +101,14 @@ public class Keychain {
query[kSecAttrAccount as String] = username
query[kSecAttrAccessible as String] = kSecAttrAccessibleAfterFirstUnlock
query[kSecValueData as String] = password.data(using: .utf8)
query[kSecReturnPersistentRef as String] = true
let status = SecItemAdd(query as CFDictionary, nil)
guard status == errSecSuccess else {
var ref: CFTypeRef?
let status = SecItemAdd(query as CFDictionary, &ref)
guard status == errSecSuccess, let refData = ref as? Data else {
throw KeychainError.add
}
return refData
}
/**
@ -195,18 +200,13 @@ public class Keychain {
/**
Gets a password associated with a password reference.
- Parameter username: The username to get the password for.
- Parameter reference: The password reference.
- Parameter context: An optional context.
- Returns: The password for the input username and reference.
- Returns: The password for the input reference.
- 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]()
setScope(query: &query, context: context)
query[kSecClass as String] = kSecClassGenericPassword
query[kSecAttrAccount as String] = username
query[kSecMatchItemList as String] = [reference]
query[kSecValuePersistentRef as String] = reference
query[kSecReturnData as String] = true
var result: AnyObject?

View File

@ -217,9 +217,8 @@ open class OpenVPNTunnelProvider: NEPacketTunnelProvider {
// optional credentials
let credentials: OpenVPN.Credentials?
if let username = protocolConfiguration.username, let passwordReference = protocolConfiguration.passwordReference {
let keychain = Keychain(group: appGroup)
guard let password = try? keychain.password(for: username, reference: passwordReference) else {
completionHandler(OpenVPNProviderConfigurationError.credentials(details: "keychain.password(for:, reference:)"))
guard let password = try? Keychain.password(forReference: passwordReference) else {
completionHandler(OpenVPNProviderConfigurationError.credentials(details: "Keychain.password(forReference:)"))
return
}
credentials = OpenVPN.Credentials(username, password)

View File

@ -276,7 +276,7 @@ extension OpenVPNProvider {
- 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 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.
- Throws: `OpenVPNProviderError.credentials` if unable to store `credentials.password` to the `appGroup` keychain.
*/
@ -284,16 +284,18 @@ extension OpenVPNProvider {
withBundleIdentifier bundleIdentifier: String,
appGroup: String,
context: String,
username: String?) throws -> NETunnelProviderProtocol {
credentials: OpenVPN.Credentials?) throws -> NETunnelProviderProtocol {
let protocolConfiguration = NETunnelProviderProtocol()
let keychain = Keychain(group: appGroup)
protocolConfiguration.providerBundleIdentifier = bundleIdentifier
protocolConfiguration.serverAddress = sessionConfiguration.hostname ?? resolvedAddresses?.first
if let username = username {
if let username = credentials?.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)

View File

@ -81,7 +81,7 @@ class AppExtensionTests: XCTestCase {
withBundleIdentifier: identifier,
appGroup: appGroup,
context: context,
username: credentials.username
credentials: credentials
)
XCTAssertNotNil(proto)