15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "net/cert/cert_database.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <Security/Security.h>
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/logging.h"
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/mac/mac_logging.h"
119ab5563a3196760eb381d102cbb2bc0f7abc6a50Ben Murdoch#include "base/message_loop/message_loop.h"
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/observer_list_threadsafe.h"
13a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch#include "base/process/process_handle.h"
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/single_thread_task_runner.h"
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/synchronization/lock.h"
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "crypto/mac_security_services_lock.h"
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/base/net_errors.h"
18c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "net/cert/x509_certificate.h"
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace net {
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Helper that observes events from the Keychain and forwards them to the
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// given CertDatabase.
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class CertDatabase::Notifier {
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public:
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Creates a new Notifier that will forward Keychain events to |cert_db|.
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // |message_loop| must refer to a thread with an associated CFRunLoop - a
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // TYPE_UI thread. Events will be dispatched from this message loop.
2990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  Notifier(CertDatabase* cert_db, base::MessageLoop* message_loop)
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      : cert_db_(cert_db),
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        registered_(false),
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        called_shutdown_(false) {
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Ensure an associated CFRunLoop.
345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    DCHECK(base::MessageLoopForUI::IsCurrent());
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    task_runner_ = message_loop->message_loop_proxy();
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    task_runner_->PostTask(FROM_HERE,
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                           base::Bind(&Notifier::Init,
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                      base::Unretained(this)));
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Should be called from the |task_runner_|'s thread. Use Shutdown()
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // to shutdown on arbitrary threads.
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ~Notifier() {
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DCHECK(called_shutdown_);
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Only unregister from the same thread where registration was performed.
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (registered_ && task_runner_->RunsTasksOnCurrentThread())
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      SecKeychainRemoveCallback(&Notifier::KeychainCallback);
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void Shutdown() {
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    called_shutdown_ = true;
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!task_runner_->DeleteSoon(FROM_HERE, this)) {
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // If the task runner is no longer running, it's safe to just delete
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // the object, since no further events will or can be delivered by
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Keychain Services.
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      delete this;
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) private:
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void Init() {
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SecKeychainEventMask event_mask =
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        kSecKeychainListChangedMask | kSecTrustSettingsChangedEventMask;
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    OSStatus status = SecKeychainAddCallback(&Notifier::KeychainCallback,
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                             event_mask, this);
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (status == noErr)
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      registered_ = true;
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // SecKeychainCallback function that receives notifications from securityd
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // and forwards them to the |cert_db_|.
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  static OSStatus KeychainCallback(SecKeychainEvent keychain_event,
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                   SecKeychainCallbackInfo* info,
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                   void* context);
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CertDatabase* const cert_db_;
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool registered_;
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool called_shutdown_;
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)OSStatus CertDatabase::Notifier::KeychainCallback(
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SecKeychainEvent keychain_event,
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SecKeychainCallbackInfo* info,
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    void* context) {
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Notifier* that = reinterpret_cast<Notifier*>(context);
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (info->version > SEC_KEYCHAIN_SETTINGS_VERS1) {
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    NOTREACHED();
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return errSecWrongSecVersion;
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (info->pid == base::GetCurrentProcId()) {
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Ignore events generated by the current process, as the assumption is
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // that they have already been handled. This may miss events that
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // originated as a result of spawning native dialogs that allow the user
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // to modify Keychain settings. However, err on the side of missing
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // events rather than sending too many events.
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return errSecSuccess;
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  switch (keychain_event) {
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case kSecKeychainListChangedEvent:
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case kSecTrustSettingsChangedEvent:
1061e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)      that->cert_db_->NotifyObserversOfCACertChanged(NULL);
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return errSecSuccess;
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void CertDatabase::SetMessageLoopForKeychainEvents() {
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Shutdown will take care to delete the notifier on the right thread.
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (notifier_.get())
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    notifier_.release()->Shutdown();
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  notifier_.reset(new Notifier(this, base::MessageLoopForUI::current()));
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)CertDatabase::CertDatabase()
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    : observer_list_(new ObserverListThreadSafe<Observer>) {
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)CertDatabase::~CertDatabase() {
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Shutdown will take care to delete the notifier on the right thread.
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (notifier_.get())
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    notifier_.release()->Shutdown();
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int CertDatabase::CheckUserCert(X509Certificate* cert) {
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!cert)
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return ERR_CERT_INVALID;
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (cert->HasExpired())
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return ERR_CERT_DATE_INVALID;
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Verify the Keychain already has the corresponding private key:
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SecIdentityRef identity = NULL;
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  OSStatus err = SecIdentityCreateWithCertificate(NULL, cert->os_cert_handle(),
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                  &identity);
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (err == errSecItemNotFound)
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return ERR_NO_PRIVATE_KEY_FOR_CERT;
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (err != noErr || !identity) {
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // TODO(snej): Map the error code more intelligently.
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return ERR_CERT_INVALID;
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CFRelease(identity);
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return OK;
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int CertDatabase::AddUserCert(X509Certificate* cert) {
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  OSStatus err;
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  {
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    base::AutoLock locked(crypto::GetMacSecurityServicesLock());
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    err = SecCertificateAddToKeychain(cert->os_cert_handle(), NULL);
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  switch (err) {
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case noErr:
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      CertDatabase::NotifyObserversOfCertAdded(cert);
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Fall through.
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case errSecDuplicateItem:
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return OK;
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    default:
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      OSSTATUS_LOG(ERROR, err) << "CertDatabase failed to add cert to keychain";
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // TODO(snej): Map the error code more intelligently.
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return ERR_ADD_USER_CERT_FAILED;
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace net
173