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

2024-01-12

我们有一个启动守护进程(出于各种原因,必须)以 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。)尽管如此,我认为我已经完成并理解了其中的大部分内容。我们的特定设置的各个方面都没有详细介绍(从守护进程访问),但看起来很清楚,之前访问一个项目由同一应用程序保存不应要求任何特殊授权或认证。这与我们所看到的行为直接矛盾。

有任何想法吗?


在花了几天时间研究这个问题之后,我们终于弄清楚了发生了什么。

首先,我尝试构建一个可以重现该问题的最小示例。这并没有失败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对应于errSecCSGuestInvalid https://opensource.apple.com/source/libsecurity_codesigning/libsecurity_codesigning-55032/lib/CSCommon.h。评论内容为“代码身份已失效”。

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

经过一番查找终于找到了解释和解决方案:http://lists.apple.com/archives/apple-cdsa/2010/Mar/msg00027.html http://lists.apple.com/archives/apple-cdsa/2010/Mar/msg00027.html

这意味着在程序开始后的某个时刻,发生了一些事情 发生了使它无效的事情。

and

如果你运行一个签名的程序,then替换它(例如,构建一个 新版本就位:-),然后运行新版本,内核 仍将保留附加到可执行文件的 vnode 的旧签名。 如果这是您的情况,只需删除可执行文件并重新创建 它彻底解决了问题(直到您再次覆盖该文件 :-)。我们建议始终替换签名代码(mv(1),而不是 cp(1) 或等效项)。

这就解释了。我正在使用以下命令将守护进程的新版本复制到位

sudo cp path/to/built/daemon /usr/local/libexec/

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

我一这么做,就成功了!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Mac 启动守护程序在将密码保存到系统钥匙串后无法从系统钥匙串中检索密码 的相关文章

随机推荐