Home | History | Annotate | Download | only in cert
      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 
      5 #include "net/cert/nss_cert_database.h"
      6 
      7 #include <cert.h>
      8 #include <certdb.h>
      9 #include <keyhi.h>
     10 #include <pk11pub.h>
     11 #include <secmod.h>
     12 
     13 #include "base/bind.h"
     14 #include "base/callback.h"
     15 #include "base/logging.h"
     16 #include "base/memory/scoped_ptr.h"
     17 #include "base/observer_list_threadsafe.h"
     18 #include "base/task_runner.h"
     19 #include "base/task_runner_util.h"
     20 #include "base/threading/worker_pool.h"
     21 #include "crypto/scoped_nss_types.h"
     22 #include "net/base/crypto_module.h"
     23 #include "net/base/net_errors.h"
     24 #include "net/cert/cert_database.h"
     25 #include "net/cert/x509_certificate.h"
     26 #include "net/third_party/mozilla_security_manager/nsNSSCertificateDB.h"
     27 #include "net/third_party/mozilla_security_manager/nsPKCS12Blob.h"
     28 
     29 // In NSS 3.13, CERTDB_VALID_PEER was renamed CERTDB_TERMINAL_RECORD. So we use
     30 // the new name of the macro.
     31 #if !defined(CERTDB_TERMINAL_RECORD)
     32 #define CERTDB_TERMINAL_RECORD CERTDB_VALID_PEER
     33 #endif
     34 
     35 // PSM = Mozilla's Personal Security Manager.
     36 namespace psm = mozilla_security_manager;
     37 
     38 namespace net {
     39 
     40 namespace {
     41 
     42 // TODO(pneubeck): Move this class out of NSSCertDatabase and to the caller of
     43 // the c'tor of NSSCertDatabase, see https://crbug.com/395983 .
     44 // Helper that observes events from the NSSCertDatabase and forwards them to
     45 // the given CertDatabase.
     46 class CertNotificationForwarder : public NSSCertDatabase::Observer {
     47  public:
     48   explicit CertNotificationForwarder(CertDatabase* cert_db)
     49       : cert_db_(cert_db) {}
     50 
     51   virtual ~CertNotificationForwarder() {}
     52 
     53   // NSSCertDatabase::Observer implementation:
     54   virtual void OnCertAdded(const X509Certificate* cert) OVERRIDE {
     55     cert_db_->NotifyObserversOfCertAdded(cert);
     56   }
     57 
     58   virtual void OnCertRemoved(const X509Certificate* cert) OVERRIDE {
     59     cert_db_->NotifyObserversOfCertRemoved(cert);
     60   }
     61 
     62   virtual void OnCACertChanged(const X509Certificate* cert) OVERRIDE {
     63     cert_db_->NotifyObserversOfCACertChanged(cert);
     64   }
     65 
     66  private:
     67   CertDatabase* cert_db_;
     68 
     69   DISALLOW_COPY_AND_ASSIGN(CertNotificationForwarder);
     70 };
     71 
     72 }  // namespace
     73 
     74 NSSCertDatabase::ImportCertFailure::ImportCertFailure(
     75     const scoped_refptr<X509Certificate>& cert,
     76     int err)
     77     : certificate(cert), net_error(err) {}
     78 
     79 NSSCertDatabase::ImportCertFailure::~ImportCertFailure() {}
     80 
     81 NSSCertDatabase::NSSCertDatabase(crypto::ScopedPK11Slot public_slot,
     82                                  crypto::ScopedPK11Slot private_slot)
     83     : public_slot_(public_slot.Pass()),
     84       private_slot_(private_slot.Pass()),
     85       observer_list_(new ObserverListThreadSafe<Observer>),
     86       weak_factory_(this) {
     87   CHECK(public_slot_);
     88 
     89   // This also makes sure that NSS has been initialized.
     90   CertDatabase* cert_db = CertDatabase::GetInstance();
     91   cert_notification_forwarder_.reset(new CertNotificationForwarder(cert_db));
     92   AddObserver(cert_notification_forwarder_.get());
     93 
     94   psm::EnsurePKCS12Init();
     95 }
     96 
     97 NSSCertDatabase::~NSSCertDatabase() {}
     98 
     99 void NSSCertDatabase::ListCertsSync(CertificateList* certs) {
    100   ListCertsImpl(crypto::ScopedPK11Slot(), certs);
    101 }
    102 
    103 void NSSCertDatabase::ListCerts(
    104     const base::Callback<void(scoped_ptr<CertificateList> certs)>& callback) {
    105   scoped_ptr<CertificateList> certs(new CertificateList());
    106 
    107   // base::Passed will NULL out |certs|, so cache the underlying pointer here.
    108   CertificateList* raw_certs = certs.get();
    109   GetSlowTaskRunner()->PostTaskAndReply(
    110       FROM_HERE,
    111       base::Bind(&NSSCertDatabase::ListCertsImpl,
    112                  base::Passed(crypto::ScopedPK11Slot()),
    113                  base::Unretained(raw_certs)),
    114       base::Bind(callback, base::Passed(&certs)));
    115 }
    116 
    117 void NSSCertDatabase::ListCertsInSlot(const ListCertsCallback& callback,
    118                                       PK11SlotInfo* slot) {
    119   DCHECK(slot);
    120   scoped_ptr<CertificateList> certs(new CertificateList());
    121 
    122   // base::Passed will NULL out |certs|, so cache the underlying pointer here.
    123   CertificateList* raw_certs = certs.get();
    124   GetSlowTaskRunner()->PostTaskAndReply(
    125       FROM_HERE,
    126       base::Bind(&NSSCertDatabase::ListCertsImpl,
    127                  base::Passed(crypto::ScopedPK11Slot(PK11_ReferenceSlot(slot))),
    128                  base::Unretained(raw_certs)),
    129       base::Bind(callback, base::Passed(&certs)));
    130 }
    131 
    132 #if defined(OS_CHROMEOS)
    133 crypto::ScopedPK11Slot NSSCertDatabase::GetSystemSlot() const {
    134   return crypto::ScopedPK11Slot();
    135 }
    136 #endif
    137 
    138 crypto::ScopedPK11Slot NSSCertDatabase::GetPublicSlot() const {
    139   return crypto::ScopedPK11Slot(PK11_ReferenceSlot(public_slot_.get()));
    140 }
    141 
    142 crypto::ScopedPK11Slot NSSCertDatabase::GetPrivateSlot() const {
    143   if (!private_slot_)
    144     return crypto::ScopedPK11Slot();
    145   return crypto::ScopedPK11Slot(PK11_ReferenceSlot(private_slot_.get()));
    146 }
    147 
    148 CryptoModule* NSSCertDatabase::GetPublicModule() const {
    149   crypto::ScopedPK11Slot slot(GetPublicSlot());
    150   return CryptoModule::CreateFromHandle(slot.get());
    151 }
    152 
    153 CryptoModule* NSSCertDatabase::GetPrivateModule() const {
    154   crypto::ScopedPK11Slot slot(GetPrivateSlot());
    155   return CryptoModule::CreateFromHandle(slot.get());
    156 }
    157 
    158 void NSSCertDatabase::ListModules(CryptoModuleList* modules,
    159                                   bool need_rw) const {
    160   modules->clear();
    161 
    162   // The wincx arg is unused since we don't call PK11_SetIsLoggedInFunc.
    163   crypto::ScopedPK11SlotList slot_list(
    164       PK11_GetAllTokens(CKM_INVALID_MECHANISM,
    165                         need_rw ? PR_TRUE : PR_FALSE,  // needRW
    166                         PR_TRUE,                       // loadCerts (unused)
    167                         NULL));                        // wincx
    168   if (!slot_list) {
    169     LOG(ERROR) << "PK11_GetAllTokens failed: " << PORT_GetError();
    170     return;
    171   }
    172 
    173   PK11SlotListElement* slot_element = PK11_GetFirstSafe(slot_list.get());
    174   while (slot_element) {
    175     modules->push_back(CryptoModule::CreateFromHandle(slot_element->slot));
    176     slot_element = PK11_GetNextSafe(slot_list.get(), slot_element,
    177                                     PR_FALSE);  // restart
    178   }
    179 }
    180 
    181 int NSSCertDatabase::ImportFromPKCS12(
    182     CryptoModule* module,
    183     const std::string& data,
    184     const base::string16& password,
    185     bool is_extractable,
    186     net::CertificateList* imported_certs) {
    187   DVLOG(1) << __func__ << " "
    188            << PK11_GetModuleID(module->os_module_handle()) << ":"
    189            << PK11_GetSlotID(module->os_module_handle());
    190   int result = psm::nsPKCS12Blob_Import(module->os_module_handle(),
    191                                         data.data(), data.size(),
    192                                         password,
    193                                         is_extractable,
    194                                         imported_certs);
    195   if (result == net::OK)
    196     NotifyObserversOfCertAdded(NULL);
    197 
    198   return result;
    199 }
    200 
    201 int NSSCertDatabase::ExportToPKCS12(
    202     const CertificateList& certs,
    203     const base::string16& password,
    204     std::string* output) const {
    205   return psm::nsPKCS12Blob_Export(output, certs, password);
    206 }
    207 
    208 X509Certificate* NSSCertDatabase::FindRootInList(
    209     const CertificateList& certificates) const {
    210   DCHECK_GT(certificates.size(), 0U);
    211 
    212   if (certificates.size() == 1)
    213     return certificates[0].get();
    214 
    215   X509Certificate* cert0 = certificates[0].get();
    216   X509Certificate* cert1 = certificates[1].get();
    217   X509Certificate* certn_2 = certificates[certificates.size() - 2].get();
    218   X509Certificate* certn_1 = certificates[certificates.size() - 1].get();
    219 
    220   if (CERT_CompareName(&cert1->os_cert_handle()->issuer,
    221                        &cert0->os_cert_handle()->subject) == SECEqual)
    222     return cert0;
    223   if (CERT_CompareName(&certn_2->os_cert_handle()->issuer,
    224                        &certn_1->os_cert_handle()->subject) == SECEqual)
    225     return certn_1;
    226 
    227   LOG(WARNING) << "certificate list is not a hierarchy";
    228   return cert0;
    229 }
    230 
    231 bool NSSCertDatabase::ImportCACerts(const CertificateList& certificates,
    232                                     TrustBits trust_bits,
    233                                     ImportCertFailureList* not_imported) {
    234   crypto::ScopedPK11Slot slot(GetPublicSlot());
    235   X509Certificate* root = FindRootInList(certificates);
    236   bool success = psm::ImportCACerts(
    237       slot.get(), certificates, root, trust_bits, not_imported);
    238   if (success)
    239     NotifyObserversOfCACertChanged(NULL);
    240 
    241   return success;
    242 }
    243 
    244 bool NSSCertDatabase::ImportServerCert(const CertificateList& certificates,
    245                                        TrustBits trust_bits,
    246                                        ImportCertFailureList* not_imported) {
    247   crypto::ScopedPK11Slot slot(GetPublicSlot());
    248   return psm::ImportServerCert(
    249       slot.get(), certificates, trust_bits, not_imported);
    250 }
    251 
    252 NSSCertDatabase::TrustBits NSSCertDatabase::GetCertTrust(
    253     const X509Certificate* cert,
    254     CertType type) const {
    255   CERTCertTrust trust;
    256   SECStatus srv = CERT_GetCertTrust(cert->os_cert_handle(), &trust);
    257   if (srv != SECSuccess) {
    258     LOG(ERROR) << "CERT_GetCertTrust failed with error " << PORT_GetError();
    259     return TRUST_DEFAULT;
    260   }
    261   // We define our own more "friendly" TrustBits, which means we aren't able to
    262   // round-trip all possible NSS trust flag combinations.  We try to map them in
    263   // a sensible way.
    264   switch (type) {
    265     case CA_CERT: {
    266       const unsigned kTrustedCA = CERTDB_TRUSTED_CA | CERTDB_TRUSTED_CLIENT_CA;
    267       const unsigned kCAFlags = kTrustedCA | CERTDB_TERMINAL_RECORD;
    268 
    269       TrustBits trust_bits = TRUST_DEFAULT;
    270       if ((trust.sslFlags & kCAFlags) == CERTDB_TERMINAL_RECORD)
    271         trust_bits |= DISTRUSTED_SSL;
    272       else if (trust.sslFlags & kTrustedCA)
    273         trust_bits |= TRUSTED_SSL;
    274 
    275       if ((trust.emailFlags & kCAFlags) == CERTDB_TERMINAL_RECORD)
    276         trust_bits |= DISTRUSTED_EMAIL;
    277       else if (trust.emailFlags & kTrustedCA)
    278         trust_bits |= TRUSTED_EMAIL;
    279 
    280       if ((trust.objectSigningFlags & kCAFlags) == CERTDB_TERMINAL_RECORD)
    281         trust_bits |= DISTRUSTED_OBJ_SIGN;
    282       else if (trust.objectSigningFlags & kTrustedCA)
    283         trust_bits |= TRUSTED_OBJ_SIGN;
    284 
    285       return trust_bits;
    286     }
    287     case SERVER_CERT:
    288       if (trust.sslFlags & CERTDB_TERMINAL_RECORD) {
    289         if (trust.sslFlags & CERTDB_TRUSTED)
    290           return TRUSTED_SSL;
    291         return DISTRUSTED_SSL;
    292       }
    293       return TRUST_DEFAULT;
    294     default:
    295       return TRUST_DEFAULT;
    296   }
    297 }
    298 
    299 bool NSSCertDatabase::IsUntrusted(const X509Certificate* cert) const {
    300   CERTCertTrust nsstrust;
    301   SECStatus rv = CERT_GetCertTrust(cert->os_cert_handle(), &nsstrust);
    302   if (rv != SECSuccess) {
    303     LOG(ERROR) << "CERT_GetCertTrust failed with error " << PORT_GetError();
    304     return false;
    305   }
    306 
    307   // The CERTCertTrust structure contains three trust records:
    308   // sslFlags, emailFlags, and objectSigningFlags.  The three
    309   // trust records are independent of each other.
    310   //
    311   // If the CERTDB_TERMINAL_RECORD bit in a trust record is set,
    312   // then that trust record is a terminal record.  A terminal
    313   // record is used for explicit trust and distrust of an
    314   // end-entity or intermediate CA cert.
    315   //
    316   // In a terminal record, if neither CERTDB_TRUSTED_CA nor
    317   // CERTDB_TRUSTED is set, then the terminal record means
    318   // explicit distrust.  On the other hand, if the terminal
    319   // record has either CERTDB_TRUSTED_CA or CERTDB_TRUSTED bit
    320   // set, then the terminal record means explicit trust.
    321   //
    322   // For a root CA, the trust record does not have
    323   // the CERTDB_TERMINAL_RECORD bit set.
    324 
    325   static const unsigned int kTrusted = CERTDB_TRUSTED_CA | CERTDB_TRUSTED;
    326   if ((nsstrust.sslFlags & CERTDB_TERMINAL_RECORD) != 0 &&
    327       (nsstrust.sslFlags & kTrusted) == 0) {
    328     return true;
    329   }
    330   if ((nsstrust.emailFlags & CERTDB_TERMINAL_RECORD) != 0 &&
    331       (nsstrust.emailFlags & kTrusted) == 0) {
    332     return true;
    333   }
    334   if ((nsstrust.objectSigningFlags & CERTDB_TERMINAL_RECORD) != 0 &&
    335       (nsstrust.objectSigningFlags & kTrusted) == 0) {
    336     return true;
    337   }
    338 
    339   // Self-signed certificates that don't have any trust bits set are untrusted.
    340   // Other certificates that don't have any trust bits set may still be trusted
    341   // if they chain up to a trust anchor.
    342   if (CERT_CompareName(&cert->os_cert_handle()->issuer,
    343                        &cert->os_cert_handle()->subject) == SECEqual) {
    344     return (nsstrust.sslFlags & kTrusted) == 0 &&
    345            (nsstrust.emailFlags & kTrusted) == 0 &&
    346            (nsstrust.objectSigningFlags & kTrusted) == 0;
    347   }
    348 
    349   return false;
    350 }
    351 
    352 bool NSSCertDatabase::SetCertTrust(const X509Certificate* cert,
    353                                 CertType type,
    354                                 TrustBits trust_bits) {
    355   bool success = psm::SetCertTrust(cert, type, trust_bits);
    356   if (success)
    357     NotifyObserversOfCACertChanged(cert);
    358 
    359   return success;
    360 }
    361 
    362 bool NSSCertDatabase::DeleteCertAndKey(X509Certificate* cert) {
    363   if (!DeleteCertAndKeyImpl(cert))
    364     return false;
    365   NotifyObserversOfCertRemoved(cert);
    366   return true;
    367 }
    368 
    369 void NSSCertDatabase::DeleteCertAndKeyAsync(
    370     const scoped_refptr<X509Certificate>& cert,
    371     const DeleteCertCallback& callback) {
    372   base::PostTaskAndReplyWithResult(
    373       GetSlowTaskRunner().get(),
    374       FROM_HERE,
    375       base::Bind(&NSSCertDatabase::DeleteCertAndKeyImpl, cert),
    376       base::Bind(&NSSCertDatabase::NotifyCertRemovalAndCallBack,
    377                  weak_factory_.GetWeakPtr(),
    378                  cert,
    379                  callback));
    380 }
    381 
    382 bool NSSCertDatabase::IsReadOnly(const X509Certificate* cert) const {
    383   PK11SlotInfo* slot = cert->os_cert_handle()->slot;
    384   return slot && PK11_IsReadOnly(slot);
    385 }
    386 
    387 bool NSSCertDatabase::IsHardwareBacked(const X509Certificate* cert) const {
    388   PK11SlotInfo* slot = cert->os_cert_handle()->slot;
    389   return slot && PK11_IsHW(slot);
    390 }
    391 
    392 void NSSCertDatabase::AddObserver(Observer* observer) {
    393   observer_list_->AddObserver(observer);
    394 }
    395 
    396 void NSSCertDatabase::RemoveObserver(Observer* observer) {
    397   observer_list_->RemoveObserver(observer);
    398 }
    399 
    400 void NSSCertDatabase::SetSlowTaskRunnerForTest(
    401     const scoped_refptr<base::TaskRunner>& task_runner) {
    402   slow_task_runner_for_test_ = task_runner;
    403 }
    404 
    405 // static
    406 void NSSCertDatabase::ListCertsImpl(crypto::ScopedPK11Slot slot,
    407                                     CertificateList* certs) {
    408   certs->clear();
    409 
    410   CERTCertList* cert_list = NULL;
    411   if (slot)
    412     cert_list = PK11_ListCertsInSlot(slot.get());
    413   else
    414     cert_list = PK11_ListCerts(PK11CertListUnique, NULL);
    415 
    416   CERTCertListNode* node;
    417   for (node = CERT_LIST_HEAD(cert_list); !CERT_LIST_END(node, cert_list);
    418        node = CERT_LIST_NEXT(node)) {
    419     certs->push_back(X509Certificate::CreateFromHandle(
    420         node->cert, X509Certificate::OSCertHandles()));
    421   }
    422   CERT_DestroyCertList(cert_list);
    423 }
    424 
    425 scoped_refptr<base::TaskRunner> NSSCertDatabase::GetSlowTaskRunner() const {
    426   if (slow_task_runner_for_test_.get())
    427     return slow_task_runner_for_test_;
    428   return base::WorkerPool::GetTaskRunner(true /*task is slow*/);
    429 }
    430 
    431 void NSSCertDatabase::NotifyCertRemovalAndCallBack(
    432     scoped_refptr<X509Certificate> cert,
    433     const DeleteCertCallback& callback,
    434     bool success) {
    435   if (success)
    436     NotifyObserversOfCertRemoved(cert.get());
    437   callback.Run(success);
    438 }
    439 
    440 void NSSCertDatabase::NotifyObserversOfCertAdded(const X509Certificate* cert) {
    441   observer_list_->Notify(&Observer::OnCertAdded, make_scoped_refptr(cert));
    442 }
    443 
    444 void NSSCertDatabase::NotifyObserversOfCertRemoved(
    445     const X509Certificate* cert) {
    446   observer_list_->Notify(&Observer::OnCertRemoved, make_scoped_refptr(cert));
    447 }
    448 
    449 void NSSCertDatabase::NotifyObserversOfCACertChanged(
    450     const X509Certificate* cert) {
    451   observer_list_->Notify(
    452       &Observer::OnCACertChanged, make_scoped_refptr(cert));
    453 }
    454 
    455 // static
    456 bool NSSCertDatabase::DeleteCertAndKeyImpl(
    457     scoped_refptr<X509Certificate> cert) {
    458   // For some reason, PK11_DeleteTokenCertAndKey only calls
    459   // SEC_DeletePermCertificate if the private key is found.  So, we check
    460   // whether a private key exists before deciding which function to call to
    461   // delete the cert.
    462   SECKEYPrivateKey* privKey =
    463       PK11_FindKeyByAnyCert(cert->os_cert_handle(), NULL);
    464   if (privKey) {
    465     SECKEY_DestroyPrivateKey(privKey);
    466     if (PK11_DeleteTokenCertAndKey(cert->os_cert_handle(), NULL)) {
    467       LOG(ERROR) << "PK11_DeleteTokenCertAndKey failed: " << PORT_GetError();
    468       return false;
    469     }
    470   } else {
    471     if (SEC_DeletePermCertificate(cert->os_cert_handle())) {
    472       LOG(ERROR) << "SEC_DeletePermCertificate failed: " << PORT_GetError();
    473       return false;
    474     }
    475   }
    476   return true;
    477 }
    478 
    479 }  // namespace net
    480