7

(必然的に、さまざまな理由から) root として実行され、ネットワーク経由でサーバー コンポーネントと通信する Launch Daemon があります。サービスで認証する必要があるため、最初にパスワードを取得するときに、それをシステム キーチェーンに保存します。その後の起動では、キーチェーンからパスワードを取得し、それを使用してネットワーク サービスで認証するという考え方です。

これは問題なく機能していましたが、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 証明書で署名されています。
  • デーモン バイナリには、バンドル ID とバージョンを含む Info.plist セクションが埋め込まれています。
  • Keychain Access.app のパスワード項目の [アクセス制御] タブにある [これらのアプリケーションによるアクセスを常に許可する] リストにデーモン バイナリが表示されます。
  • キーチェーンアクセスで「すべてのアプリケーションがこのアイテムにアクセスできるようにする」に手動で切り替えると、機能します。ただし、これは、パスワードをキーチェーンに保存するという点をやや無効にします。
  • パラメータを に変更してみSecKeychainAddInternetPasswordましたが、違いはないようです。
  • でキーチェーンを明示的にロック解除しようとしましSecKeychainUnlock()たが、ドキュメントが示唆するように、これは不必要なようです。
  • ご想像のとおり、アイテムを削除すると yieldがKeychain Access.app発生します。したがって、保存されたアイテムを確実に見つけることができますが、それを読み取ることは許可されていません。SecKeychainFindInternetPassword()errSecItemNotFound

キーチェーンのドキュメントは正確に読むのは簡単ではなく、部分的にはトートロジー的です。(「Y を行うには、Y を行う必要があります」と、Y を実行する理由については言及しません。) それでも、私はそれをやり遂げ、そのほとんどを理解したと思います。特定のセットアップのさまざまな側面 (デーモンからのアクセス) については詳しく説明していませんが、同じアプリによって以前に保存されたアイテムへのアクセスには、特別な承認や認証が必要ないことは明らかです。これは、私たちが見ている動作とは正反対です。

何か案は?

4

1 に答える 1

12

これに数日間さらに時間を費やした後、私たちは最終的に何が起こっているのかを突き止めました。

最初に、問題を再現する最小限の例を作成しようとしました。これは失敗し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も問題は返されませんでした。

エラー メッセージのさまざまな部分を Web で探したところ、 に-67063対応するerrSecCSGuestInvalidことがわかりました。コメントには、「コード ID が無効になりました」と書かれています。

確かにコード署名エラーですが、それは何を意味し、なぜ発生したのでしょうか?

もう少し探し回ると、最終的に説明と解決策が見つかりました:http://lists.apple.com/archives/apple-cdsa/2010/Mar/msg00027.html

これは、プログラムが開始されてからのある時点で、プログラムが無効になるようなことが起こったことを意味します。

署名されたプログラムを実行し、それを置き換えて (たとえば、新しいバージョンをその場でビルドして :-)、新しいバージョンを実行すると、カーネルは実行可能ファイルの vnode に添付された古い署名を保持したままになります。それがあなたの状況である場合は、実行可能ファイルを削除して再作成するだけで、問題が完全に解決されます (ファイルを再度上書きするまで:-)。署名されたコードは常に置き換えることをお勧めします (cp(1) ではなく mv(1)、または同等のもの)。

これはそれを説明しました。を使用してデーモンの新しいバージョンを所定の場所にコピーしていました

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

どうやら、新しい vnode を作成し、それを書き込んでから、古いファイルに名前を変更するのではなく、ファイルをその場で上書きします。したがって、解決策は、cp最初に一時ディレクトリに移動してから、適切なmv場所に移動することです。または、宛先ファイルを削除してから使用してcpください。

私がそれをするとすぐに、それはうまくいきました!

于 2016-11-20T14:42:55.297 に答える