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

标签 macos keychain launchd launch-daemon security-framework

我们有一个启动守护进程(出于各种原因,它必须以 root 身份运行),并通过网络与服务器组件进行通信。它需要通过服务进行身份验证,因此当它第一次获取密码时,我们将其保存到系统钥匙串(keychain)中。在随后的发布中,想法是从钥匙串(keychain)中检索密码并使用它来通过网络服务进行身份验证。

这一直工作正常,但在 macOS 10.12 上,现有代码停止工作,我们完全不知道如何解决这个问题。归结起来是这样的:

无论我们是保存新密码还是检索旧密码,我们都会使用以下方法获取对系统钥匙串(keychain)的引用:

SecKeychainCopyDomainDefault(kSecPreferencesDomainSystem, &system_keychain);

我们还禁用用户交互以达到良好的效果,尽管我们希望它已经在守护进程的上下文中关闭。

SecKeychainSetUserInteractionAllowed(false);

将新密码保存到钥匙串(keychain)时,我们使用

OSStatus status = SecKeychainAddInternetPassword(
    system_keychain,
    urlLength, server_base_url,
    0, NULL,
    usernameLength, username,
    0, NULL,
    0,
    kSecProtocolTypeAny, kSecAuthenticationTypeAny,
    passwordLength, password,
    NULL);

这很有效。报告成功,我可以在 Keychain Access.app 的“系统”钥匙串(keychain)中看到该项目。

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

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

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

我们检查了一些其他详细信息并尝试了一些方法,但均无济于事:

  • 守护程序二进制文件使用开发者 ID 证书进行签名。
  • 守护程序二进制文件包含一个嵌入式 Info.plist 部分,其中包含 bundle ID 和版本。
  • 我可以在 Keychain Access.app 中密码项的“访问控制”选项卡的“始终允许这些应用程序访问”列表中看到守护程序二进制文件。
  • 如果我在钥匙串(keychain)访问中手动切换到“允许所有应用程序访问此项目”,它就会起作用。然而,这在某种程度上违背了将密码保存在钥匙串(keychain)中的目的。
  • 我们尝试过使用 SecKeychainAddInternetPassword 的参数,但这似乎没有任何区别。
  • 我们尝试使用 SecKeychainUnlock() 显式解锁钥匙串(keychain),但正如文档所示,这似乎是多余的。
  • 如您所料,删除 Keychain Access.app 中的项目会导致 SecKeychainFindInternetPassword() 产生 errSecItemNotFound。因此它绝对可以找到已保存的项目,只是不允许读取它。

钥匙串(keychain)文档并不容易阅读,而且部分内容相当同义反复。 (“为了做 Y,你需要做 Y”,但没有提及为什么你想做 Y。)尽管如此,我认为我已经完成并理解了其中的大部分内容。我们的特定设置的各个方面都没有详细介绍(从守护程序访问),但似乎很清楚,访问先前由同一应用程序保存的项目不需要任何特殊的授权或身份验证。这与我们所看到的行为直接矛盾。

有什么想法吗?

最佳答案

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

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

下一个想法是检查系统日志中调用 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 。评论内容为“代码身份已失效。”

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

又搜索了一些终于找到了解释,也是解决方案: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之前删除目标文件。

我一这么做,它就成功了!

关于macos - Mac 启动守护程序在将密码保存到系统钥匙串(keychain)后无法从系统钥匙串(keychain)中检索密码,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40638218/

相关文章:

macos - mac 安全命令在守护进程运行时需要写权限?

python - Plist一直加载不结束,满足条件也不退出

c++ - 如何在 OSX 上用 C++ 编写旋转光标?

macos - 如何使用终端执行.command 文件?

python - Python 导致 Mac 无法使用 Vim 打开 LaTeX 文件?

macos - 在Swift中捕获OSX媒体控制按钮

ios - 请求开发者证书的分发配置文件

ios - 问题将证书保存到 iOS 钥匙串(keychain) -25300(未找到)(如果删除),但 -25299(重复项)(如果添加)

launchd 运行的 iOS 控制台应用程序

ios - StartInterval 键如何影响启动的守护进程