我们有一个启动守护程序(必然由于各种原因)以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
.
我们检查了一些额外的细节以及我们尝试过的东西,但无济于事:
-
守护进程二进制文件使用Developer Id证书进行签名 .
-
守护进程二进制文件包含一个嵌入式Info.plist部分,其中包含一个包ID和版本 .
-
我可以在Keychain Access.app中密码项的"Access Control"选项卡的"Always allow access by these applications"列表中看到守护进程二进制文件 .
-
如果我在Keychain Access中手动切换到"Allow all applications to access this item",它可以正常工作 . 然而,这在某种程度上违背了在钥匙串中保存密码的观点 .
-
我们试过玩
SecKeychainAddInternetPassword
的参数,但这似乎没有任何区别 . -
我们尝试使用
SecKeychainUnlock()
明确解锁钥匙串,但正如文档所示,这似乎是多余的 . -
删除
Keychain Access.app
中的项目会导致SecKeychainFindInternetPassword()
产生errSecItemNotFound
,正如您所期望的那样 . 所以它绝对可以 find 保存的项目,它只是不允许阅读它 .
钥匙串文档不是't exactly easy to read and in parts rather tautological. ( 337047 without mentioning why you'我想做Y.)尽管如此,我认为我已经详细介绍了(从守护进程访问),但是看起来非常清楚,访问之前由同一个应用程序保存的项目不需要任何特殊授权或认证 . 这与我们所看到的行为直接矛盾 .
有任何想法吗?
1 回答
在这几天花了几个小时后,我们终于弄明白了发生了什么 .
首先,我尝试构建一个可以重现问题的最小示例 . 这并没有因
errSecAuthFailed
而失败,因此没有重现问题 . 所以回到原来的守护进程,必须有一些特别关于它的东西出错了 .下一个想法是检查系统日志中调用
SecKeychainFindInternetPassword()
的时间 . 这出现了一些错误消息:这表明问题可能出在代码签名上 . 奇怪 . 使用
codesign -vv
检查二进制文件的代码签名没有问题 .在网络上搜索错误消息的各个部分之后,我找到了-67063 corresponds to errSecCSGuestInvalid . 评论内容为"code identity has been invalidated."
好的,肯定是一些代码签名错误,但它是什么意思,为什么会发生?
围绕一些更多的狩猎终于出现了解释,还有解决方案:http://lists.apple.com/archives/apple-cdsa/2010/Mar/msg00027.html
和
这解释了它 . 我正在使用将复制品的新版本复制到位
显然,它会就地覆盖文件而不是创建新的vnode,编写该文件,然后将其重命名为旧文件 . 所以解决方案是首先
cp
到临时目录,然后mv
到位 . 或者在使用cp
之前删除目标文件 .我一做到这一点就行了!