Home | History | Annotate | Download | only in base
      1 // Copyright (c) 2011 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/base/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/logging.h"
     14 #include "base/memory/scoped_ptr.h"
     15 #include "crypto/nss_util.h"
     16 #include "crypto/nss_util_internal.h"
     17 #include "net/base/crypto_module.h"
     18 #include "net/base/net_errors.h"
     19 #include "net/base/x509_certificate.h"
     20 #include "net/third_party/mozilla_security_manager/nsNSSCertificateDB.h"
     21 #include "net/third_party/mozilla_security_manager/nsNSSCertTrust.h"
     22 #include "net/third_party/mozilla_security_manager/nsPKCS12Blob.h"
     23 
     24 // PSM = Mozilla's Personal Security Manager.
     25 namespace psm = mozilla_security_manager;
     26 
     27 namespace net {
     28 
     29 CertDatabase::CertDatabase() {
     30   crypto::EnsureNSSInit();
     31   psm::EnsurePKCS12Init();
     32 }
     33 
     34 int CertDatabase::CheckUserCert(X509Certificate* cert_obj) {
     35   if (!cert_obj)
     36     return ERR_CERT_INVALID;
     37   if (cert_obj->HasExpired())
     38     return ERR_CERT_DATE_INVALID;
     39 
     40   // Check if the private key corresponding to the certificate exist
     41   // We shouldn't accept any random client certificate sent by a CA.
     42 
     43   // Note: The NSS source documentation wrongly suggests that this
     44   // also imports the certificate if the private key exists. This
     45   // doesn't seem to be the case.
     46 
     47   CERTCertificate* cert = cert_obj->os_cert_handle();
     48   PK11SlotInfo* slot = PK11_KeyForCertExists(cert, NULL, NULL);
     49   if (!slot) {
     50     LOG(ERROR) << "No corresponding private key in store";
     51     return ERR_NO_PRIVATE_KEY_FOR_CERT;
     52   }
     53   PK11_FreeSlot(slot);
     54 
     55   return OK;
     56 }
     57 
     58 int CertDatabase::AddUserCert(X509Certificate* cert_obj) {
     59   CERTCertificate* cert = cert_obj->os_cert_handle();
     60   PK11SlotInfo* slot = NULL;
     61   std::string nickname;
     62 
     63   // Create a nickname for this certificate.
     64   // We use the scheme used by Firefox:
     65   // --> <subject's common name>'s <issuer's common name> ID.
     66 
     67   std::string username, ca_name;
     68   char* temp_username = CERT_GetCommonName(&cert->subject);
     69   char* temp_ca_name = CERT_GetCommonName(&cert->issuer);
     70   if (temp_username) {
     71     username = temp_username;
     72     PORT_Free(temp_username);
     73   }
     74   if (temp_ca_name) {
     75     ca_name = temp_ca_name;
     76     PORT_Free(temp_ca_name);
     77   }
     78   nickname = username + "'s " + ca_name + " ID";
     79 
     80   {
     81     crypto::AutoNSSWriteLock lock;
     82     slot = PK11_ImportCertForKey(cert,
     83                                  const_cast<char*>(nickname.c_str()),
     84                                  NULL);
     85   }
     86 
     87   if (!slot) {
     88     LOG(ERROR) << "Couldn't import user certificate.";
     89     return ERR_ADD_USER_CERT_FAILED;
     90   }
     91   PK11_FreeSlot(slot);
     92   CertDatabase::NotifyObserversOfUserCertAdded(cert_obj);
     93   return OK;
     94 }
     95 
     96 void CertDatabase::ListCerts(CertificateList* certs) {
     97   certs->clear();
     98 
     99   CERTCertList* cert_list = PK11_ListCerts(PK11CertListUnique, NULL);
    100   CERTCertListNode* node;
    101   for (node = CERT_LIST_HEAD(cert_list);
    102        !CERT_LIST_END(node, cert_list);
    103        node = CERT_LIST_NEXT(node)) {
    104     certs->push_back(X509Certificate::CreateFromHandle(
    105         node->cert,
    106         X509Certificate::SOURCE_LONE_CERT_IMPORT,
    107         X509Certificate::OSCertHandles()));
    108   }
    109   CERT_DestroyCertList(cert_list);
    110 }
    111 
    112 CryptoModule* CertDatabase::GetPublicModule() const {
    113   CryptoModule* module =
    114       CryptoModule::CreateFromHandle(crypto::GetPublicNSSKeySlot());
    115   // The module is already referenced when returned from
    116   // GetPublicNSSKeySlot, so we need to deref it once.
    117   PK11_FreeSlot(module->os_module_handle());
    118 
    119   return module;
    120 }
    121 
    122 CryptoModule* CertDatabase::GetPrivateModule() const {
    123   CryptoModule* module =
    124       CryptoModule::CreateFromHandle(crypto::GetPrivateNSSKeySlot());
    125   // The module is already referenced when returned from
    126   // GetPrivateNSSKeySlot, so we need to deref it once.
    127   PK11_FreeSlot(module->os_module_handle());
    128 
    129   return module;
    130 }
    131 
    132 void CertDatabase::ListModules(CryptoModuleList* modules, bool need_rw) const {
    133   modules->clear();
    134 
    135   PK11SlotList* slot_list = NULL;
    136   // The wincx arg is unused since we don't call PK11_SetIsLoggedInFunc.
    137   slot_list = PK11_GetAllTokens(CKM_INVALID_MECHANISM,
    138                                 need_rw ? PR_TRUE : PR_FALSE,  // needRW
    139                                 PR_TRUE,  // loadCerts (unused)
    140                                 NULL);  // wincx
    141   if (!slot_list) {
    142     LOG(ERROR) << "PK11_GetAllTokens failed: " << PORT_GetError();
    143     return;
    144   }
    145 
    146   PK11SlotListElement* slot_element = PK11_GetFirstSafe(slot_list);
    147   while (slot_element) {
    148     modules->push_back(CryptoModule::CreateFromHandle(slot_element->slot));
    149     slot_element = PK11_GetNextSafe(slot_list, slot_element,
    150                                     PR_FALSE);  // restart
    151   }
    152 
    153   PK11_FreeSlotList(slot_list);
    154 }
    155 
    156 int CertDatabase::ImportFromPKCS12(
    157     CryptoModule* module,
    158     const std::string& data,
    159     const string16& password) {
    160   int result = psm::nsPKCS12Blob_Import(module->os_module_handle(),
    161                                         data.data(), data.size(),
    162                                         password);
    163   if (result == net::OK)
    164     CertDatabase::NotifyObserversOfUserCertAdded(NULL);
    165 
    166   return result;
    167 }
    168 
    169 int CertDatabase::ExportToPKCS12(
    170     const CertificateList& certs,
    171     const string16& password,
    172     std::string* output) const {
    173   return psm::nsPKCS12Blob_Export(output, certs, password);
    174 }
    175 
    176 X509Certificate* CertDatabase::FindRootInList(
    177     const CertificateList& certificates) const {
    178   DCHECK_GT(certificates.size(), 0U);
    179 
    180   if (certificates.size() == 1)
    181     return certificates[0].get();
    182 
    183   X509Certificate* cert0 = certificates[0];
    184   X509Certificate* cert1 = certificates[1];
    185   X509Certificate* certn_2 = certificates[certificates.size() - 2];
    186   X509Certificate* certn_1 = certificates[certificates.size() - 1];
    187 
    188   if (CERT_CompareName(&cert1->os_cert_handle()->issuer,
    189                        &cert0->os_cert_handle()->subject) == SECEqual)
    190     return cert0;
    191   if (CERT_CompareName(&certn_2->os_cert_handle()->issuer,
    192                        &certn_1->os_cert_handle()->subject) == SECEqual)
    193     return certn_1;
    194 
    195   VLOG(1) << "certificate list is not a hierarchy";
    196   return cert0;
    197 }
    198 
    199 bool CertDatabase::ImportCACerts(const CertificateList& certificates,
    200                                  unsigned int trust_bits,
    201                                  ImportCertFailureList* not_imported) {
    202   X509Certificate* root = FindRootInList(certificates);
    203   bool success = psm::ImportCACerts(certificates, root, trust_bits,
    204                                     not_imported);
    205   if (success)
    206     CertDatabase::NotifyObserversOfCertTrustChanged(NULL);
    207 
    208   return success;
    209 }
    210 
    211 bool CertDatabase::ImportServerCert(const CertificateList& certificates,
    212                                     ImportCertFailureList* not_imported) {
    213   return psm::ImportServerCert(certificates, not_imported);
    214 }
    215 
    216 unsigned int CertDatabase::GetCertTrust(
    217     const X509Certificate* cert, CertType type) const {
    218   CERTCertTrust nsstrust;
    219   SECStatus srv = CERT_GetCertTrust(cert->os_cert_handle(), &nsstrust);
    220   if (srv != SECSuccess) {
    221     LOG(ERROR) << "CERT_GetCertTrust failed with error " << PORT_GetError();
    222     return UNTRUSTED;
    223   }
    224   psm::nsNSSCertTrust trust(&nsstrust);
    225   switch (type) {
    226     case CA_CERT:
    227       return trust.HasTrustedCA(PR_TRUE, PR_FALSE, PR_FALSE) * TRUSTED_SSL +
    228           trust.HasTrustedCA(PR_FALSE, PR_TRUE, PR_FALSE) * TRUSTED_EMAIL +
    229           trust.HasTrustedCA(PR_FALSE, PR_FALSE, PR_TRUE) * TRUSTED_OBJ_SIGN;
    230     case SERVER_CERT:
    231       return trust.HasTrustedPeer(PR_TRUE, PR_FALSE, PR_FALSE) * TRUSTED_SSL +
    232           trust.HasTrustedPeer(PR_FALSE, PR_TRUE, PR_FALSE) * TRUSTED_EMAIL +
    233           trust.HasTrustedPeer(PR_FALSE, PR_FALSE, PR_TRUE) * TRUSTED_OBJ_SIGN;
    234     default:
    235       return UNTRUSTED;
    236   }
    237 }
    238 
    239 bool CertDatabase::SetCertTrust(const X509Certificate* cert,
    240                                 CertType type,
    241                                 unsigned int trusted) {
    242   bool success = psm::SetCertTrust(cert, type, trusted);
    243   if (success)
    244     CertDatabase::NotifyObserversOfCertTrustChanged(cert);
    245 
    246   return success;
    247 }
    248 
    249 // TODO(xiyuan): Add an Observer method for this event.
    250 bool CertDatabase::DeleteCertAndKey(const X509Certificate* cert) {
    251   // For some reason, PK11_DeleteTokenCertAndKey only calls
    252   // SEC_DeletePermCertificate if the private key is found.  So, we check
    253   // whether a private key exists before deciding which function to call to
    254   // delete the cert.
    255   SECKEYPrivateKey *privKey = PK11_FindKeyByAnyCert(cert->os_cert_handle(),
    256                                                     NULL);
    257   if (privKey) {
    258     SECKEY_DestroyPrivateKey(privKey);
    259     if (PK11_DeleteTokenCertAndKey(cert->os_cert_handle(), NULL)) {
    260       LOG(ERROR) << "PK11_DeleteTokenCertAndKey failed: " << PORT_GetError();
    261       return false;
    262     }
    263   } else {
    264     if (SEC_DeletePermCertificate(cert->os_cert_handle())) {
    265       LOG(ERROR) << "SEC_DeletePermCertificate failed: " << PORT_GetError();
    266       return false;
    267     }
    268   }
    269   return true;
    270 }
    271 
    272 bool CertDatabase::IsReadOnly(const X509Certificate* cert) const {
    273   PK11SlotInfo* slot = cert->os_cert_handle()->slot;
    274   return slot && PK11_IsReadOnly(slot);
    275 }
    276 
    277 }  // namespace net
    278