cert_database_mac.cc 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "net/cert/cert_database.h"
  5. #include <Security/Security.h>
  6. #include "base/bind.h"
  7. #include "base/check.h"
  8. #include "base/location.h"
  9. #include "base/mac/mac_logging.h"
  10. #include "base/memory/raw_ptr.h"
  11. #include "base/notreached.h"
  12. #include "base/process/process_handle.h"
  13. #include "base/synchronization/lock.h"
  14. #include "base/task/current_thread.h"
  15. #include "base/task/single_thread_task_runner.h"
  16. #include "base/threading/thread_task_runner_handle.h"
  17. #include "crypto/mac_security_services_lock.h"
  18. #include "net/base/net_errors.h"
  19. #include "net/cert/x509_certificate.h"
  20. namespace net {
  21. // Helper that observes events from the Keychain and forwards them to the
  22. // given CertDatabase.
  23. class CertDatabase::Notifier {
  24. public:
  25. // Creates a new Notifier that will forward Keychain events to |cert_db|.
  26. // |message_loop| must refer to a thread with an associated CFRunLoop - a
  27. // TYPE_UI thread. Events will be dispatched from this message loop.
  28. Notifier(CertDatabase* cert_db,
  29. scoped_refptr<base::SingleThreadTaskRunner> task_runner)
  30. : cert_db_(cert_db), task_runner_(std::move(task_runner)) {
  31. // Ensure an associated CFRunLoop.
  32. DCHECK(base::CurrentUIThread::IsSet());
  33. DCHECK(task_runner_->BelongsToCurrentThread());
  34. task_runner_->PostTask(
  35. FROM_HERE, base::BindOnce(&Notifier::Init, base::Unretained(this)));
  36. }
  37. // Much of the Keychain API was marked deprecated as of the macOS 13 SDK.
  38. // Removal of its use is tracked in https://crbug.com/1348251 but deprecation
  39. // warnings are disabled in the meanwhile.
  40. #pragma clang diagnostic push
  41. #pragma clang diagnostic ignored "-Wdeprecated-declarations"
  42. // Should be called from the |task_runner_|'s sequence. Use Shutdown()
  43. // to shutdown on arbitrary sequence.
  44. ~Notifier() {
  45. DCHECK(called_shutdown_);
  46. // Only unregister from the same sequence where registration was performed.
  47. if (registered_ && task_runner_->RunsTasksInCurrentSequence())
  48. SecKeychainRemoveCallback(&Notifier::KeychainCallback);
  49. }
  50. #pragma clang diagnostic pop
  51. void Shutdown() {
  52. called_shutdown_ = true;
  53. if (!task_runner_->DeleteSoon(FROM_HERE, this)) {
  54. // If the task runner is no longer running, it's safe to just delete
  55. // the object, since no further events will or can be delivered by
  56. // Keychain Services.
  57. delete this;
  58. }
  59. }
  60. // Much of the Keychain API was marked deprecated as of the macOS 13 SDK.
  61. // Removal of its use is tracked in https://crbug.com/1348251 but deprecation
  62. // warnings are disabled in the meanwhile.
  63. #pragma clang diagnostic push
  64. #pragma clang diagnostic ignored "-Wdeprecated-declarations"
  65. private:
  66. void Init() {
  67. SecKeychainEventMask event_mask =
  68. kSecKeychainListChangedMask | kSecTrustSettingsChangedEventMask;
  69. OSStatus status = SecKeychainAddCallback(&Notifier::KeychainCallback,
  70. event_mask, this);
  71. if (status == noErr)
  72. registered_ = true;
  73. }
  74. #pragma clang diagnostic pop
  75. // SecKeychainCallback function that receives notifications from securityd
  76. // and forwards them to the |cert_db_|.
  77. static OSStatus KeychainCallback(SecKeychainEvent keychain_event,
  78. SecKeychainCallbackInfo* info,
  79. void* context);
  80. const raw_ptr<CertDatabase> cert_db_;
  81. scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
  82. bool registered_ = false;
  83. bool called_shutdown_ = false;
  84. };
  85. // static
  86. OSStatus CertDatabase::Notifier::KeychainCallback(
  87. SecKeychainEvent keychain_event,
  88. SecKeychainCallbackInfo* info,
  89. void* context) {
  90. Notifier* that = reinterpret_cast<Notifier*>(context);
  91. if (info->version > SEC_KEYCHAIN_SETTINGS_VERS1) {
  92. NOTREACHED();
  93. return errSecWrongSecVersion;
  94. }
  95. if (info->pid == base::GetCurrentProcId()) {
  96. // Ignore events generated by the current process, as the assumption is
  97. // that they have already been handled. This may miss events that
  98. // originated as a result of spawning native dialogs that allow the user
  99. // to modify Keychain settings. However, err on the side of missing
  100. // events rather than sending too many events.
  101. return errSecSuccess;
  102. }
  103. switch (keychain_event) {
  104. case kSecKeychainListChangedEvent:
  105. case kSecTrustSettingsChangedEvent:
  106. that->cert_db_->NotifyObserversCertDBChanged();
  107. break;
  108. default:
  109. break;
  110. }
  111. return errSecSuccess;
  112. }
  113. void CertDatabase::StartListeningForKeychainEvents() {
  114. ReleaseNotifier();
  115. notifier_ = new Notifier(this, base::ThreadTaskRunnerHandle::Get());
  116. }
  117. void CertDatabase::ReleaseNotifier() {
  118. // Shutdown will take care to delete the notifier on the right thread.
  119. if (notifier_) {
  120. notifier_->Shutdown();
  121. notifier_ = nullptr;
  122. }
  123. }
  124. } // namespace net