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/logging.h" 14#include "base/memory/scoped_ptr.h" 15#include "base/memory/singleton.h" 16#include "base/observer_list_threadsafe.h" 17#include "crypto/nss_util.h" 18#include "crypto/nss_util_internal.h" 19#include "crypto/scoped_nss_types.h" 20#include "net/base/crypto_module.h" 21#include "net/base/net_errors.h" 22#include "net/cert/cert_database.h" 23#include "net/cert/x509_certificate.h" 24#include "net/third_party/mozilla_security_manager/nsNSSCertificateDB.h" 25#include "net/third_party/mozilla_security_manager/nsPKCS12Blob.h" 26 27// In NSS 3.13, CERTDB_VALID_PEER was renamed CERTDB_TERMINAL_RECORD. So we use 28// the new name of the macro. 29#if !defined(CERTDB_TERMINAL_RECORD) 30#define CERTDB_TERMINAL_RECORD CERTDB_VALID_PEER 31#endif 32 33// PSM = Mozilla's Personal Security Manager. 34namespace psm = mozilla_security_manager; 35 36namespace net { 37 38NSSCertDatabase::ImportCertFailure::ImportCertFailure( 39 const scoped_refptr<X509Certificate>& cert, 40 int err) 41 : certificate(cert), net_error(err) {} 42 43NSSCertDatabase::ImportCertFailure::~ImportCertFailure() {} 44 45// static 46NSSCertDatabase* NSSCertDatabase::GetInstance() { 47 return Singleton<NSSCertDatabase, 48 LeakySingletonTraits<NSSCertDatabase> >::get(); 49} 50 51NSSCertDatabase::NSSCertDatabase() 52 : observer_list_(new ObserverListThreadSafe<Observer>) { 53 crypto::EnsureNSSInit(); 54 psm::EnsurePKCS12Init(); 55} 56 57NSSCertDatabase::~NSSCertDatabase() {} 58 59void NSSCertDatabase::ListCerts(CertificateList* certs) { 60 certs->clear(); 61 62 CERTCertList* cert_list = PK11_ListCerts(PK11CertListUnique, NULL); 63 CERTCertListNode* node; 64 for (node = CERT_LIST_HEAD(cert_list); 65 !CERT_LIST_END(node, cert_list); 66 node = CERT_LIST_NEXT(node)) { 67 certs->push_back(X509Certificate::CreateFromHandle( 68 node->cert, X509Certificate::OSCertHandles())); 69 } 70 CERT_DestroyCertList(cert_list); 71} 72 73crypto::ScopedPK11Slot NSSCertDatabase::GetPublicSlot() const { 74 return crypto::ScopedPK11Slot(crypto::GetPublicNSSKeySlot()); 75} 76 77crypto::ScopedPK11Slot NSSCertDatabase::GetPrivateSlot() const { 78 return crypto::ScopedPK11Slot(crypto::GetPrivateNSSKeySlot()); 79} 80 81CryptoModule* NSSCertDatabase::GetPublicModule() const { 82 crypto::ScopedPK11Slot slot(GetPublicSlot()); 83 return CryptoModule::CreateFromHandle(slot.get()); 84} 85 86CryptoModule* NSSCertDatabase::GetPrivateModule() const { 87 crypto::ScopedPK11Slot slot(GetPrivateSlot()); 88 return CryptoModule::CreateFromHandle(slot.get()); 89} 90 91void NSSCertDatabase::ListModules(CryptoModuleList* modules, 92 bool need_rw) const { 93 modules->clear(); 94 95 // The wincx arg is unused since we don't call PK11_SetIsLoggedInFunc. 96 crypto::ScopedPK11SlotList slot_list( 97 PK11_GetAllTokens(CKM_INVALID_MECHANISM, 98 need_rw ? PR_TRUE : PR_FALSE, // needRW 99 PR_TRUE, // loadCerts (unused) 100 NULL)); // wincx 101 if (!slot_list) { 102 LOG(ERROR) << "PK11_GetAllTokens failed: " << PORT_GetError(); 103 return; 104 } 105 106 PK11SlotListElement* slot_element = PK11_GetFirstSafe(slot_list.get()); 107 while (slot_element) { 108 modules->push_back(CryptoModule::CreateFromHandle(slot_element->slot)); 109 slot_element = PK11_GetNextSafe(slot_list.get(), slot_element, 110 PR_FALSE); // restart 111 } 112} 113 114int NSSCertDatabase::ImportFromPKCS12( 115 CryptoModule* module, 116 const std::string& data, 117 const base::string16& password, 118 bool is_extractable, 119 net::CertificateList* imported_certs) { 120 int result = psm::nsPKCS12Blob_Import(module->os_module_handle(), 121 data.data(), data.size(), 122 password, 123 is_extractable, 124 imported_certs); 125 if (result == net::OK) 126 NotifyObserversOfCertAdded(NULL); 127 128 return result; 129} 130 131int NSSCertDatabase::ExportToPKCS12( 132 const CertificateList& certs, 133 const base::string16& password, 134 std::string* output) const { 135 return psm::nsPKCS12Blob_Export(output, certs, password); 136} 137 138X509Certificate* NSSCertDatabase::FindRootInList( 139 const CertificateList& certificates) const { 140 DCHECK_GT(certificates.size(), 0U); 141 142 if (certificates.size() == 1) 143 return certificates[0].get(); 144 145 X509Certificate* cert0 = certificates[0].get(); 146 X509Certificate* cert1 = certificates[1].get(); 147 X509Certificate* certn_2 = certificates[certificates.size() - 2].get(); 148 X509Certificate* certn_1 = certificates[certificates.size() - 1].get(); 149 150 if (CERT_CompareName(&cert1->os_cert_handle()->issuer, 151 &cert0->os_cert_handle()->subject) == SECEqual) 152 return cert0; 153 if (CERT_CompareName(&certn_2->os_cert_handle()->issuer, 154 &certn_1->os_cert_handle()->subject) == SECEqual) 155 return certn_1; 156 157 VLOG(1) << "certificate list is not a hierarchy"; 158 return cert0; 159} 160 161bool NSSCertDatabase::ImportCACerts(const CertificateList& certificates, 162 TrustBits trust_bits, 163 ImportCertFailureList* not_imported) { 164 crypto::ScopedPK11Slot slot(GetPublicSlot()); 165 X509Certificate* root = FindRootInList(certificates); 166 bool success = psm::ImportCACerts( 167 slot.get(), certificates, root, trust_bits, not_imported); 168 if (success) 169 NotifyObserversOfCACertChanged(NULL); 170 171 return success; 172} 173 174bool NSSCertDatabase::ImportServerCert(const CertificateList& certificates, 175 TrustBits trust_bits, 176 ImportCertFailureList* not_imported) { 177 crypto::ScopedPK11Slot slot(GetPublicSlot()); 178 return psm::ImportServerCert( 179 slot.get(), certificates, trust_bits, not_imported); 180} 181 182NSSCertDatabase::TrustBits NSSCertDatabase::GetCertTrust( 183 const X509Certificate* cert, 184 CertType type) const { 185 CERTCertTrust trust; 186 SECStatus srv = CERT_GetCertTrust(cert->os_cert_handle(), &trust); 187 if (srv != SECSuccess) { 188 LOG(ERROR) << "CERT_GetCertTrust failed with error " << PORT_GetError(); 189 return TRUST_DEFAULT; 190 } 191 // We define our own more "friendly" TrustBits, which means we aren't able to 192 // round-trip all possible NSS trust flag combinations. We try to map them in 193 // a sensible way. 194 switch (type) { 195 case CA_CERT: { 196 const unsigned kTrustedCA = CERTDB_TRUSTED_CA | CERTDB_TRUSTED_CLIENT_CA; 197 const unsigned kCAFlags = kTrustedCA | CERTDB_TERMINAL_RECORD; 198 199 TrustBits trust_bits = TRUST_DEFAULT; 200 if ((trust.sslFlags & kCAFlags) == CERTDB_TERMINAL_RECORD) 201 trust_bits |= DISTRUSTED_SSL; 202 else if (trust.sslFlags & kTrustedCA) 203 trust_bits |= TRUSTED_SSL; 204 205 if ((trust.emailFlags & kCAFlags) == CERTDB_TERMINAL_RECORD) 206 trust_bits |= DISTRUSTED_EMAIL; 207 else if (trust.emailFlags & kTrustedCA) 208 trust_bits |= TRUSTED_EMAIL; 209 210 if ((trust.objectSigningFlags & kCAFlags) == CERTDB_TERMINAL_RECORD) 211 trust_bits |= DISTRUSTED_OBJ_SIGN; 212 else if (trust.objectSigningFlags & kTrustedCA) 213 trust_bits |= TRUSTED_OBJ_SIGN; 214 215 return trust_bits; 216 } 217 case SERVER_CERT: 218 if (trust.sslFlags & CERTDB_TERMINAL_RECORD) { 219 if (trust.sslFlags & CERTDB_TRUSTED) 220 return TRUSTED_SSL; 221 return DISTRUSTED_SSL; 222 } 223 return TRUST_DEFAULT; 224 default: 225 return TRUST_DEFAULT; 226 } 227} 228 229bool NSSCertDatabase::IsUntrusted(const X509Certificate* cert) const { 230 CERTCertTrust nsstrust; 231 SECStatus rv = CERT_GetCertTrust(cert->os_cert_handle(), &nsstrust); 232 if (rv != SECSuccess) { 233 LOG(ERROR) << "CERT_GetCertTrust failed with error " << PORT_GetError(); 234 return false; 235 } 236 237 // The CERTCertTrust structure contains three trust records: 238 // sslFlags, emailFlags, and objectSigningFlags. The three 239 // trust records are independent of each other. 240 // 241 // If the CERTDB_TERMINAL_RECORD bit in a trust record is set, 242 // then that trust record is a terminal record. A terminal 243 // record is used for explicit trust and distrust of an 244 // end-entity or intermediate CA cert. 245 // 246 // In a terminal record, if neither CERTDB_TRUSTED_CA nor 247 // CERTDB_TRUSTED is set, then the terminal record means 248 // explicit distrust. On the other hand, if the terminal 249 // record has either CERTDB_TRUSTED_CA or CERTDB_TRUSTED bit 250 // set, then the terminal record means explicit trust. 251 // 252 // For a root CA, the trust record does not have 253 // the CERTDB_TERMINAL_RECORD bit set. 254 255 static const unsigned int kTrusted = CERTDB_TRUSTED_CA | CERTDB_TRUSTED; 256 if ((nsstrust.sslFlags & CERTDB_TERMINAL_RECORD) != 0 && 257 (nsstrust.sslFlags & kTrusted) == 0) { 258 return true; 259 } 260 if ((nsstrust.emailFlags & CERTDB_TERMINAL_RECORD) != 0 && 261 (nsstrust.emailFlags & kTrusted) == 0) { 262 return true; 263 } 264 if ((nsstrust.objectSigningFlags & CERTDB_TERMINAL_RECORD) != 0 && 265 (nsstrust.objectSigningFlags & kTrusted) == 0) { 266 return true; 267 } 268 269 // Self-signed certificates that don't have any trust bits set are untrusted. 270 // Other certificates that don't have any trust bits set may still be trusted 271 // if they chain up to a trust anchor. 272 if (CERT_CompareName(&cert->os_cert_handle()->issuer, 273 &cert->os_cert_handle()->subject) == SECEqual) { 274 return (nsstrust.sslFlags & kTrusted) == 0 && 275 (nsstrust.emailFlags & kTrusted) == 0 && 276 (nsstrust.objectSigningFlags & kTrusted) == 0; 277 } 278 279 return false; 280} 281 282bool NSSCertDatabase::SetCertTrust(const X509Certificate* cert, 283 CertType type, 284 TrustBits trust_bits) { 285 bool success = psm::SetCertTrust(cert, type, trust_bits); 286 if (success) 287 NotifyObserversOfCACertChanged(cert); 288 289 return success; 290} 291 292bool NSSCertDatabase::DeleteCertAndKey(const X509Certificate* cert) { 293 // For some reason, PK11_DeleteTokenCertAndKey only calls 294 // SEC_DeletePermCertificate if the private key is found. So, we check 295 // whether a private key exists before deciding which function to call to 296 // delete the cert. 297 SECKEYPrivateKey *privKey = PK11_FindKeyByAnyCert(cert->os_cert_handle(), 298 NULL); 299 if (privKey) { 300 SECKEY_DestroyPrivateKey(privKey); 301 if (PK11_DeleteTokenCertAndKey(cert->os_cert_handle(), NULL)) { 302 LOG(ERROR) << "PK11_DeleteTokenCertAndKey failed: " << PORT_GetError(); 303 return false; 304 } 305 } else { 306 if (SEC_DeletePermCertificate(cert->os_cert_handle())) { 307 LOG(ERROR) << "SEC_DeletePermCertificate failed: " << PORT_GetError(); 308 return false; 309 } 310 } 311 312 NotifyObserversOfCertRemoved(cert); 313 314 return true; 315} 316 317bool NSSCertDatabase::IsReadOnly(const X509Certificate* cert) const { 318 PK11SlotInfo* slot = cert->os_cert_handle()->slot; 319 return slot && PK11_IsReadOnly(slot); 320} 321 322bool NSSCertDatabase::IsHardwareBacked(const X509Certificate* cert) const { 323 PK11SlotInfo* slot = cert->os_cert_handle()->slot; 324 return slot && PK11_IsHW(slot); 325} 326 327void NSSCertDatabase::AddObserver(Observer* observer) { 328 observer_list_->AddObserver(observer); 329} 330 331void NSSCertDatabase::RemoveObserver(Observer* observer) { 332 observer_list_->RemoveObserver(observer); 333} 334 335void NSSCertDatabase::NotifyObserversOfCertAdded(const X509Certificate* cert) { 336 observer_list_->Notify(&Observer::OnCertAdded, make_scoped_refptr(cert)); 337} 338 339void NSSCertDatabase::NotifyObserversOfCertRemoved( 340 const X509Certificate* cert) { 341 observer_list_->Notify(&Observer::OnCertRemoved, make_scoped_refptr(cert)); 342} 343 344void NSSCertDatabase::NotifyObserversOfCACertChanged( 345 const X509Certificate* cert) { 346 observer_list_->Notify( 347 &Observer::OnCACertChanged, make_scoped_refptr(cert)); 348} 349 350} // namespace net 351