123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147 |
- // Copyright (c) 2012 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include "net/cert/cert_database.h"
- #include <Security/Security.h>
- #include "base/bind.h"
- #include "base/check.h"
- #include "base/location.h"
- #include "base/mac/mac_logging.h"
- #include "base/memory/raw_ptr.h"
- #include "base/notreached.h"
- #include "base/process/process_handle.h"
- #include "base/synchronization/lock.h"
- #include "base/task/current_thread.h"
- #include "base/task/single_thread_task_runner.h"
- #include "base/threading/thread_task_runner_handle.h"
- #include "crypto/mac_security_services_lock.h"
- #include "net/base/net_errors.h"
- #include "net/cert/x509_certificate.h"
- namespace net {
- // Helper that observes events from the Keychain and forwards them to the
- // given CertDatabase.
- class CertDatabase::Notifier {
- public:
- // Creates a new Notifier that will forward Keychain events to |cert_db|.
- // |message_loop| must refer to a thread with an associated CFRunLoop - a
- // TYPE_UI thread. Events will be dispatched from this message loop.
- Notifier(CertDatabase* cert_db,
- scoped_refptr<base::SingleThreadTaskRunner> task_runner)
- : cert_db_(cert_db), task_runner_(std::move(task_runner)) {
- // Ensure an associated CFRunLoop.
- DCHECK(base::CurrentUIThread::IsSet());
- DCHECK(task_runner_->BelongsToCurrentThread());
- task_runner_->PostTask(
- FROM_HERE, base::BindOnce(&Notifier::Init, base::Unretained(this)));
- }
- // Much of the Keychain API was marked deprecated as of the macOS 13 SDK.
- // Removal of its use is tracked in https://crbug.com/1348251 but deprecation
- // warnings are disabled in the meanwhile.
- #pragma clang diagnostic push
- #pragma clang diagnostic ignored "-Wdeprecated-declarations"
- // Should be called from the |task_runner_|'s sequence. Use Shutdown()
- // to shutdown on arbitrary sequence.
- ~Notifier() {
- DCHECK(called_shutdown_);
- // Only unregister from the same sequence where registration was performed.
- if (registered_ && task_runner_->RunsTasksInCurrentSequence())
- SecKeychainRemoveCallback(&Notifier::KeychainCallback);
- }
- #pragma clang diagnostic pop
- void Shutdown() {
- called_shutdown_ = true;
- if (!task_runner_->DeleteSoon(FROM_HERE, this)) {
- // If the task runner is no longer running, it's safe to just delete
- // the object, since no further events will or can be delivered by
- // Keychain Services.
- delete this;
- }
- }
- // Much of the Keychain API was marked deprecated as of the macOS 13 SDK.
- // Removal of its use is tracked in https://crbug.com/1348251 but deprecation
- // warnings are disabled in the meanwhile.
- #pragma clang diagnostic push
- #pragma clang diagnostic ignored "-Wdeprecated-declarations"
- private:
- void Init() {
- SecKeychainEventMask event_mask =
- kSecKeychainListChangedMask | kSecTrustSettingsChangedEventMask;
- OSStatus status = SecKeychainAddCallback(&Notifier::KeychainCallback,
- event_mask, this);
- if (status == noErr)
- registered_ = true;
- }
- #pragma clang diagnostic pop
- // SecKeychainCallback function that receives notifications from securityd
- // and forwards them to the |cert_db_|.
- static OSStatus KeychainCallback(SecKeychainEvent keychain_event,
- SecKeychainCallbackInfo* info,
- void* context);
- const raw_ptr<CertDatabase> cert_db_;
- scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
- bool registered_ = false;
- bool called_shutdown_ = false;
- };
- // static
- OSStatus CertDatabase::Notifier::KeychainCallback(
- SecKeychainEvent keychain_event,
- SecKeychainCallbackInfo* info,
- void* context) {
- Notifier* that = reinterpret_cast<Notifier*>(context);
- if (info->version > SEC_KEYCHAIN_SETTINGS_VERS1) {
- NOTREACHED();
- return errSecWrongSecVersion;
- }
- if (info->pid == base::GetCurrentProcId()) {
- // Ignore events generated by the current process, as the assumption is
- // that they have already been handled. This may miss events that
- // originated as a result of spawning native dialogs that allow the user
- // to modify Keychain settings. However, err on the side of missing
- // events rather than sending too many events.
- return errSecSuccess;
- }
- switch (keychain_event) {
- case kSecKeychainListChangedEvent:
- case kSecTrustSettingsChangedEvent:
- that->cert_db_->NotifyObserversCertDBChanged();
- break;
- default:
- break;
- }
- return errSecSuccess;
- }
- void CertDatabase::StartListeningForKeychainEvents() {
- ReleaseNotifier();
- notifier_ = new Notifier(this, base::ThreadTaskRunnerHandle::Get());
- }
- void CertDatabase::ReleaseNotifier() {
- // Shutdown will take care to delete the notifier on the right thread.
- if (notifier_) {
- notifier_->Shutdown();
- notifier_ = nullptr;
- }
- }
- } // namespace net
|