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