Mac 启动守护程序在保存密码后无法从系统钥匙串中检索密码

Mac Launch Daemon unable to retrieve password from system keychain after saving it there

我们有一个 Launch Daemon(出于各种原因,必须)以 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.

中可以看到"system"钥匙串中的物品

在我们的守护程序的后续运行中检索它是通过以下行完成的:

status = SecKeychainFindInternetPassword(
    system_keychain,
    urlLength, url,
    0, NULL,
    usernameLength, username,
    0, NULL,
    0,
    kSecProtocolTypeAny, kSecAuthenticationTypeAny,
    &passwordLength, &password_data,
    NULL);

不幸的是,由于我们不清楚的原因,它已经开始返回 errSecAuthFailed

我们检查过的一些额外细节和我们尝试过的东西都无济于事:

钥匙串文档并不十分容易阅读,并且部分内容相当重复。 ("In order to do Y, you need to do Y," 没有提到你为什么想做 Y。)不过,我认为我已经完成并理解了大部分内容。我们特定设置的各个方面没有详细介绍(从守护程序访问),但很明显访问以前由同一应用程序保存的项目不需要任何特殊授权或认证。这与我们看到的行为直接矛盾。

有什么想法吗?

在这几天里又花了几个小时之后,我们终于搞清楚了是怎么回事。

首先,我尝试构建一个可以重现问题的最小示例。 这并没有因为 errSecAuthFailed 而失败,因此没有重现问题。 所以回到原来的守护进程,一定有什么特别的地方出了问题。

下一个想法是检查系统日志以了解调用 SecKeychainFindInternetPassword() 的时间。这出现了一些错误消息:

securityd   CSSM Exception: -2147411889 CSSMERR_CL_UNKNOWN_TAG
securityd   MacOS error: -67063
securityd   MacOS error: -67063
securityd   code requirement check failed (-67063), client is not Apple-signed
securityd   CSSM Exception: 32 CSSM_ERRCODE_OPERATION_AUTH_DENIED
OurDaemon   subsystem: com.apple.securityd, category: security_exception, enable_level: 0, persist_level: 0, default_ttl: 0, info_ttl: 0, debug_ttl: 0, generate_symptoms: 0, enable_oversize: 0, privacy_setting: 2, enable_private_data: 0
OurDaemon   CSSM Exception: -2147416032 CSSMERR_CSP_OPERATION_AUTH_DENIED

这表明问题可能出在代码签名上。奇怪的。使用 codesign -vv 检查二进制文件的代码签名没有返回任何问题。

在网上搜索错误消息的各个部分后,我找到了 -67063 corresponds to errSecCSGuestInvalid。评论是"code identity has been invalidated."

好吧,肯定是一些代码签名错误,但这是什么意思,为什么会发生?

再四处寻找终于找到了解释和解决方案:http://lists.apple.com/archives/apple-cdsa/2010/Mar/msg00027.html

It means that at some point since the program got started, something happened to it that made it invalid.

if you run a signed program, then replace it (by, say, building a new version in place :-), and then run the new version, the kernel will still hold the old signature attached to the executable's vnode. If that's your situation, just removing the executable and recreating it clears up the problem for good (until you overwrite the file again :-). We recommend that signed code always be replaced (mv(1), not cp(1), or equivalents).

这解释了。我正在使用

将新版本的守护程序复制到位
sudo cp path/to/built/daemon /usr/local/libexec/

显然,这会就地覆盖文件,而不是创建一个新的 vnode,写入它,然后在旧文件上重命名它。所以解决方案是先 cp 到临时目录,然后 mv 到位。或者在使用 cp.

之前删除目标文件

我一这样做,就奏效了!