diff options
author | phajdan.jr@chromium.org <phajdan.jr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-03-29 21:48:11 +0000 |
---|---|---|
committer | phajdan.jr@chromium.org <phajdan.jr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-03-29 21:48:11 +0000 |
commit | 6e7845aed4759ab35d722ce0551b5a90d21e7640 (patch) | |
tree | f29a3f007f7ded842d2096446ff7ecaf186cb362 /net/cert/cert_database_mac.cc | |
parent | a6b4f91d970aa2b71b0f3552dbc11e94f7650fd5 (diff) | |
download | chromium_src-6e7845aed4759ab35d722ce0551b5a90d21e7640.zip chromium_src-6e7845aed4759ab35d722ce0551b5a90d21e7640.tar.gz chromium_src-6e7845aed4759ab35d722ce0551b5a90d21e7640.tar.bz2 |
net: extract net/cert out of net/base
This introduces the following dependency of net/base on things outside:
net/base/openssl_client_key_store.cc:#include "net/cert/x509_certificate.h"
BUG=70818
Review URL: https://codereview.chromium.org/13006020
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@191450 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/cert/cert_database_mac.cc')
-rw-r--r-- | net/cert/cert_database_mac.cc | 172 |
1 files changed, 172 insertions, 0 deletions
diff --git a/net/cert/cert_database_mac.cc b/net/cert/cert_database_mac.cc new file mode 100644 index 0000000..c8fcf47 --- /dev/null +++ b/net/cert/cert_database_mac.cc @@ -0,0 +1,172 @@ +// 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/logging.h" +#include "base/mac/mac_logging.h" +#include "base/message_loop.h" +#include "base/observer_list_threadsafe.h" +#include "base/process_util.h" +#include "base/single_thread_task_runner.h" +#include "base/synchronization/lock.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, MessageLoop* message_loop) + : cert_db_(cert_db), + registered_(false), + called_shutdown_(false) { + // Ensure an associated CFRunLoop. + DCHECK(message_loop->IsType(MessageLoop::TYPE_UI)); + task_runner_ = message_loop->message_loop_proxy(); + task_runner_->PostTask(FROM_HERE, + base::Bind(&Notifier::Init, + base::Unretained(this))); + } + + // Should be called from the |task_runner_|'s thread. Use Shutdown() + // to shutdown on arbitrary threads. + ~Notifier() { + DCHECK(called_shutdown_); + // Only unregister from the same thread where registration was performed. + if (registered_ && task_runner_->RunsTasksOnCurrentThread()) + SecKeychainRemoveCallback(&Notifier::KeychainCallback); + } + + 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; + } + } + + private: + void Init() { + SecKeychainEventMask event_mask = + kSecKeychainListChangedMask | kSecTrustSettingsChangedEventMask; + OSStatus status = SecKeychainAddCallback(&Notifier::KeychainCallback, + event_mask, this); + if (status == noErr) + registered_ = true; + } + + // SecKeychainCallback function that receives notifications from securityd + // and forwards them to the |cert_db_|. + static OSStatus KeychainCallback(SecKeychainEvent keychain_event, + SecKeychainCallbackInfo* info, + void* context); + + CertDatabase* const cert_db_; + scoped_refptr<base::SingleThreadTaskRunner> task_runner_; + bool registered_; + bool called_shutdown_; +}; + +// 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_->NotifyObserversOfCertTrustChanged(NULL); + break; + } + + return errSecSuccess; +} + +void CertDatabase::SetMessageLoopForKeychainEvents() { + // Shutdown will take care to delete the notifier on the right thread. + if (notifier_.get()) + notifier_.release()->Shutdown(); + + notifier_.reset(new Notifier(this, MessageLoopForUI::current())); +} + +CertDatabase::CertDatabase() + : observer_list_(new ObserverListThreadSafe<Observer>) { +} + +CertDatabase::~CertDatabase() { + // Shutdown will take care to delete the notifier on the right thread. + if (notifier_.get()) + notifier_.release()->Shutdown(); +} + +int CertDatabase::CheckUserCert(X509Certificate* cert) { + if (!cert) + return ERR_CERT_INVALID; + if (cert->HasExpired()) + return ERR_CERT_DATE_INVALID; + + // Verify the Keychain already has the corresponding private key: + SecIdentityRef identity = NULL; + OSStatus err = SecIdentityCreateWithCertificate(NULL, cert->os_cert_handle(), + &identity); + if (err == errSecItemNotFound) + return ERR_NO_PRIVATE_KEY_FOR_CERT; + + if (err != noErr || !identity) { + // TODO(snej): Map the error code more intelligently. + return ERR_CERT_INVALID; + } + + CFRelease(identity); + return OK; +} + +int CertDatabase::AddUserCert(X509Certificate* cert) { + OSStatus err; + { + base::AutoLock locked(crypto::GetMacSecurityServicesLock()); + err = SecCertificateAddToKeychain(cert->os_cert_handle(), NULL); + } + switch (err) { + case noErr: + CertDatabase::NotifyObserversOfCertAdded(cert); + // Fall through. + case errSecDuplicateItem: + return OK; + default: + OSSTATUS_LOG(ERROR, err) << "CertDatabase failed to add cert to keychain"; + // TODO(snej): Map the error code more intelligently. + return ERR_ADD_USER_CERT_FAILED; + } +} + +} // namespace net |