我们有一个启动守护进程(出于各种原因,必须)以 root 身份运行,并通过网络与服务器组件进行通信。它需要通过服务进行身份验证,因此当它第一次获取密码时,我们将其保存到系统钥匙串中。在随后的发布中,想法是从钥匙串中检索密码并使用它来通过网络服务进行身份验证。
这一直工作得很好,但在 macOS 10.12 上,现有代码停止工作,我们完全不知道如何解决这个问题。归结起来是这样的:
无论我们是保存新密码还是检索旧密码,我们都会使用以下方法获取对系统钥匙串的引用:
SecKeychainCopyDomainDefault(kSecPreferencesDomainSystem, &system_keychain);
我们还禁用用户交互以达到良好的效果,尽管我们希望它已经在守护进程的上下文中关闭。
SecKeychainSetUserInteractionAllowed(false);
将新密码保存到钥匙串时,我们使用
OSStatus status = SecKeychainAddInternetPassword(
system_keychain,
urlLength, server_base_url,
0, NULL,
usernameLength, username,
0, NULL,
0,
kSecProtocolTypeAny, kSecAuthenticationTypeAny,
passwordLength, password,
NULL);
这很有效。报告成功,我可以在 Keychain Access.app 的“系统”钥匙串中看到该项目。
在我们的守护进程的后续运行中检索它是通过以下行完成的:
status = SecKeychainFindInternetPassword(
system_keychain,
urlLength, url,
0, NULL,
usernameLength, username,
0, NULL,
0,
kSecProtocolTypeAny, kSecAuthenticationTypeAny,
&passwordLength, &password_data,
NULL);
不幸的是,这已经开始回归errSecAuthFailed
出于我们尚不清楚的原因。
我们检查了一些额外的细节,并尝试过一些措施,但均无济于事:
- 守护程序二进制文件使用开发人员 ID 证书进行签名。
- 守护程序二进制文件包含一个嵌入式 Info.plist 部分,其中包含包 ID 和版本。
- 我可以在 Keychain Access.app 中密码项的“访问控制”选项卡的“始终允许这些应用程序访问”列表中看到守护程序二进制文件。
- 如果我在钥匙串访问中手动切换到“允许所有应用程序访问此项目”,它就会起作用。然而,这在某种程度上违背了将密码保存在钥匙串中的目的。
- 我们尝试过调整参数
SecKeychainAddInternetPassword
,但这似乎没有任何区别。
- 我们尝试过显式解锁钥匙串
SecKeychainUnlock()
,但正如文档所示,这似乎是多余的。
- 删除中的项目
Keychain Access.app
causes SecKeychainFindInternetPassword()
屈服errSecItemNotFound
,如您所料。所以绝对可以find保存的项目,只是不允许读取它。
钥匙串文档并不容易阅读,而且部分内容相当同义反复。 (“为了做 Y,你需要做 Y”,但没有提及为什么你想做 Y。)尽管如此,我认为我已经完成并理解了其中的大部分内容。我们的特定设置的各个方面都没有详细介绍(从守护进程访问),但看起来很清楚,之前访问一个项目由同一应用程序保存不应要求任何特殊授权或认证。这与我们所看到的行为直接矛盾。
有任何想法吗?