Merge nucleic/quiet-opal-gecko-ah7w into dev
This commit is contained in:
@@ -20,6 +20,7 @@ import FoundationEssentials
|
||||
#else
|
||||
import Foundation
|
||||
#endif
|
||||
import Security // [Nucleic vendored patch] SecKeychain*UserInteractionAllowed for prompt-free reads
|
||||
|
||||
/// Holds the result of a query to the keychain.
|
||||
public struct KeychainQueryResult {
|
||||
@@ -68,7 +69,15 @@ public struct KeychainQuery {
|
||||
query[kSecAttrAccessGroup as String] = accessGroup
|
||||
}
|
||||
|
||||
let status = SecItemAdd(query as CFDictionary, nil)
|
||||
var status = SecItemAdd(query as CFDictionary, nil)
|
||||
// [Nucleic vendored patch] `exists` above no longer prompts, so it can under-report an
|
||||
// item this process isn't trusted to read; an explicit `login` re-save then hits
|
||||
// `errSecDuplicateItem`. Deleting (no read authorization required) and retrying once keeps
|
||||
// re-login working without ever raising a Keychain panel.
|
||||
if status == errSecDuplicateItem {
|
||||
try delete(securityDomain: securityDomain, accessGroup: accessGroup, hostname: hostname)
|
||||
status = SecItemAdd(query as CFDictionary, nil)
|
||||
}
|
||||
guard status == errSecSuccess else { throw Self.Error.unhandledError(status: status) }
|
||||
}
|
||||
|
||||
@@ -114,7 +123,7 @@ public struct KeychainQuery {
|
||||
query[kSecAttrAccessGroup as String] = accessGroup
|
||||
}
|
||||
var item: CFTypeRef?
|
||||
let status = SecItemCopyMatching(query as CFDictionary, &item)
|
||||
let status = Self.withoutInteractiveUI { SecItemCopyMatching(query as CFDictionary, &item) }
|
||||
let exists = try isQuerySuccessful(status)
|
||||
if !exists {
|
||||
return nil
|
||||
@@ -164,7 +173,7 @@ public struct KeychainQuery {
|
||||
query[kSecAttrAccessGroup as String] = accessGroup
|
||||
}
|
||||
var item: CFTypeRef?
|
||||
let status = SecItemCopyMatching(query as CFDictionary, &item)
|
||||
let status = Self.withoutInteractiveUI { SecItemCopyMatching(query as CFDictionary, &item) }
|
||||
let exists = try isQuerySuccessful(status)
|
||||
if !exists {
|
||||
return []
|
||||
@@ -217,12 +226,16 @@ public struct KeychainQuery {
|
||||
query[kSecAttrAccessGroup as String] = accessGroup
|
||||
}
|
||||
|
||||
let status = SecItemCopyMatching(query as CFDictionary, nil)
|
||||
let status = Self.withoutInteractiveUI { SecItemCopyMatching(query as CFDictionary, nil) }
|
||||
return try isQuerySuccessful(status)
|
||||
}
|
||||
|
||||
private func isQuerySuccessful(_ status: Int32) throws -> Bool {
|
||||
guard status != errSecItemNotFound else {
|
||||
// [Nucleic vendored patch] With interactive UI suppressed (see `withoutInteractiveUI`), a
|
||||
// matching item the caller isn't trusted to read returns `errSecInteractionNotAllowed`
|
||||
// instead of prompting. Treat it like "not found" so registry lookups degrade to anonymous
|
||||
// / env-var auth rather than surfacing an error — and never raise a Keychain panel.
|
||||
guard status != errSecItemNotFound, status != errSecInteractionNotAllowed else {
|
||||
return false
|
||||
}
|
||||
guard status == errSecSuccess else {
|
||||
@@ -230,6 +243,28 @@ public struct KeychainQuery {
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
/// [Nucleic vendored patch] Run `body` with the legacy login Keychain's interactive
|
||||
/// authorization panel disabled, restoring the prior setting afterward.
|
||||
///
|
||||
/// Nucleic requires that no automatic registry-credential lookup (image pull / push / list, via
|
||||
/// `KeychainHelper`) can ever raise the macOS "<app> wants to use your confidential information
|
||||
/// stored in 'ghcr.io' in your keychain" panel. That panel appears whenever a process that isn't
|
||||
/// on an internet-password item's ACL reads it — e.g. a `cctl` binary re-signed ad-hoc by a fresh
|
||||
/// `make vminit-image` reading a token an earlier build stored. The data-protection
|
||||
/// `kSecUseAuthenticationUI*` flags do NOT govern that legacy ACL/partition-list dialog; the only
|
||||
/// switch that does is `SecKeychainSetUserInteractionAllowed(false)`, which makes a read that
|
||||
/// would otherwise prompt fail with `errSecInteractionNotAllowed`. An already-trusted item still
|
||||
/// reads silently; anything else fails silently and the caller falls back to anonymous / env-var
|
||||
/// auth. `SecKeychain*` is deprecated but remains the only API covering this panel. Mirrors
|
||||
/// `KeychainOwnedAccess.withoutLegacyKeychainUI` in NucleicCore.
|
||||
private static func withoutInteractiveUI<T>(_ body: () -> T) -> T {
|
||||
var previous = DarwinBoolean(true)
|
||||
SecKeychainGetUserInteractionAllowed(&previous)
|
||||
SecKeychainSetUserInteractionAllowed(false)
|
||||
defer { SecKeychainSetUserInteractionAllowed(previous.boolValue) }
|
||||
return body()
|
||||
}
|
||||
}
|
||||
|
||||
extension KeychainQuery {
|
||||
|
||||
Reference in New Issue
Block a user