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