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. 25namespace psm = mozilla_security_manager; 26 27namespace net { 28 29CertDatabase::CertDatabase() { 30 crypto::EnsureNSSInit(); 31 psm::EnsurePKCS12Init(); 32} 33 34int 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 58int 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 96void 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 112CryptoModule* 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 122CryptoModule* 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 132void 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 156int 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 169int 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 176X509Certificate* 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 199bool 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 211bool CertDatabase::ImportServerCert(const CertificateList& certificates, 212 ImportCertFailureList* not_imported) { 213 return psm::ImportServerCert(certificates, not_imported); 214} 215 216unsigned 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 239bool 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. 250bool 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 272bool 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