Disable view source for Developer Tools.
[chromium-blink-merge.git] / net / cert / nss_cert_database.cc
blob935b271bd47b479aa46338de540737efe2ba33af
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.
5 #include "net/cert/nss_cert_database.h"
7 #include <cert.h>
8 #include <certdb.h>
9 #include <keyhi.h>
10 #include <pk11pub.h>
11 #include <secmod.h>
13 #include "base/lazy_instance.h"
14 #include "base/logging.h"
15 #include "base/memory/scoped_ptr.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"
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
33 // PSM = Mozilla's Personal Security Manager.
34 namespace psm = mozilla_security_manager;
36 namespace net {
38 namespace {
40 base::LazyInstance<NSSCertDatabase>::Leaky
41 g_nss_cert_database = LAZY_INSTANCE_INITIALIZER;
43 } // namespace
46 NSSCertDatabase::ImportCertFailure::ImportCertFailure(
47 const scoped_refptr<X509Certificate>& cert,
48 int err)
49 : certificate(cert), net_error(err) {}
51 NSSCertDatabase::ImportCertFailure::~ImportCertFailure() {}
53 // static
54 NSSCertDatabase* NSSCertDatabase::GetInstance() {
55 // TODO(mattm): Remove this ifdef guard once the linux impl of
56 // GetNSSCertDatabaseForResourceContext does not call GetInstance.
57 #if defined(OS_CHROMEOS)
58 LOG(ERROR) << "NSSCertDatabase::GetInstance() is deprecated."
59 << "See http://crbug.com/329735.";
60 #endif
61 return &g_nss_cert_database.Get();
64 NSSCertDatabase::NSSCertDatabase()
65 : observer_list_(new ObserverListThreadSafe<Observer>) {
66 // This also makes sure that NSS has been initialized.
67 CertDatabase::GetInstance()->ObserveNSSCertDatabase(this);
69 psm::EnsurePKCS12Init();
72 NSSCertDatabase::~NSSCertDatabase() {}
74 void NSSCertDatabase::ListCerts(CertificateList* certs) {
75 certs->clear();
77 CERTCertList* cert_list = PK11_ListCerts(PK11CertListUnique, NULL);
78 CERTCertListNode* node;
79 for (node = CERT_LIST_HEAD(cert_list);
80 !CERT_LIST_END(node, cert_list);
81 node = CERT_LIST_NEXT(node)) {
82 certs->push_back(X509Certificate::CreateFromHandle(
83 node->cert, X509Certificate::OSCertHandles()));
85 CERT_DestroyCertList(cert_list);
88 crypto::ScopedPK11Slot NSSCertDatabase::GetPublicSlot() const {
89 return crypto::ScopedPK11Slot(crypto::GetPublicNSSKeySlot());
92 crypto::ScopedPK11Slot NSSCertDatabase::GetPrivateSlot() const {
93 return crypto::ScopedPK11Slot(crypto::GetPrivateNSSKeySlot());
96 CryptoModule* NSSCertDatabase::GetPublicModule() const {
97 crypto::ScopedPK11Slot slot(GetPublicSlot());
98 return CryptoModule::CreateFromHandle(slot.get());
101 CryptoModule* NSSCertDatabase::GetPrivateModule() const {
102 crypto::ScopedPK11Slot slot(GetPrivateSlot());
103 return CryptoModule::CreateFromHandle(slot.get());
106 void NSSCertDatabase::ListModules(CryptoModuleList* modules,
107 bool need_rw) const {
108 modules->clear();
110 // The wincx arg is unused since we don't call PK11_SetIsLoggedInFunc.
111 crypto::ScopedPK11SlotList slot_list(
112 PK11_GetAllTokens(CKM_INVALID_MECHANISM,
113 need_rw ? PR_TRUE : PR_FALSE, // needRW
114 PR_TRUE, // loadCerts (unused)
115 NULL)); // wincx
116 if (!slot_list) {
117 LOG(ERROR) << "PK11_GetAllTokens failed: " << PORT_GetError();
118 return;
121 PK11SlotListElement* slot_element = PK11_GetFirstSafe(slot_list.get());
122 while (slot_element) {
123 modules->push_back(CryptoModule::CreateFromHandle(slot_element->slot));
124 slot_element = PK11_GetNextSafe(slot_list.get(), slot_element,
125 PR_FALSE); // restart
129 int NSSCertDatabase::ImportFromPKCS12(
130 CryptoModule* module,
131 const std::string& data,
132 const base::string16& password,
133 bool is_extractable,
134 net::CertificateList* imported_certs) {
135 DVLOG(1) << __func__ << " "
136 << PK11_GetModuleID(module->os_module_handle()) << ":"
137 << PK11_GetSlotID(module->os_module_handle());
138 int result = psm::nsPKCS12Blob_Import(module->os_module_handle(),
139 data.data(), data.size(),
140 password,
141 is_extractable,
142 imported_certs);
143 if (result == net::OK)
144 NotifyObserversOfCertAdded(NULL);
146 return result;
149 int NSSCertDatabase::ExportToPKCS12(
150 const CertificateList& certs,
151 const base::string16& password,
152 std::string* output) const {
153 return psm::nsPKCS12Blob_Export(output, certs, password);
156 X509Certificate* NSSCertDatabase::FindRootInList(
157 const CertificateList& certificates) const {
158 DCHECK_GT(certificates.size(), 0U);
160 if (certificates.size() == 1)
161 return certificates[0].get();
163 X509Certificate* cert0 = certificates[0].get();
164 X509Certificate* cert1 = certificates[1].get();
165 X509Certificate* certn_2 = certificates[certificates.size() - 2].get();
166 X509Certificate* certn_1 = certificates[certificates.size() - 1].get();
168 if (CERT_CompareName(&cert1->os_cert_handle()->issuer,
169 &cert0->os_cert_handle()->subject) == SECEqual)
170 return cert0;
171 if (CERT_CompareName(&certn_2->os_cert_handle()->issuer,
172 &certn_1->os_cert_handle()->subject) == SECEqual)
173 return certn_1;
175 LOG(WARNING) << "certificate list is not a hierarchy";
176 return cert0;
179 bool NSSCertDatabase::ImportCACerts(const CertificateList& certificates,
180 TrustBits trust_bits,
181 ImportCertFailureList* not_imported) {
182 crypto::ScopedPK11Slot slot(GetPublicSlot());
183 X509Certificate* root = FindRootInList(certificates);
184 bool success = psm::ImportCACerts(
185 slot.get(), certificates, root, trust_bits, not_imported);
186 if (success)
187 NotifyObserversOfCACertChanged(NULL);
189 return success;
192 bool NSSCertDatabase::ImportServerCert(const CertificateList& certificates,
193 TrustBits trust_bits,
194 ImportCertFailureList* not_imported) {
195 crypto::ScopedPK11Slot slot(GetPublicSlot());
196 return psm::ImportServerCert(
197 slot.get(), certificates, trust_bits, not_imported);
200 NSSCertDatabase::TrustBits NSSCertDatabase::GetCertTrust(
201 const X509Certificate* cert,
202 CertType type) const {
203 CERTCertTrust trust;
204 SECStatus srv = CERT_GetCertTrust(cert->os_cert_handle(), &trust);
205 if (srv != SECSuccess) {
206 LOG(ERROR) << "CERT_GetCertTrust failed with error " << PORT_GetError();
207 return TRUST_DEFAULT;
209 // We define our own more "friendly" TrustBits, which means we aren't able to
210 // round-trip all possible NSS trust flag combinations. We try to map them in
211 // a sensible way.
212 switch (type) {
213 case CA_CERT: {
214 const unsigned kTrustedCA = CERTDB_TRUSTED_CA | CERTDB_TRUSTED_CLIENT_CA;
215 const unsigned kCAFlags = kTrustedCA | CERTDB_TERMINAL_RECORD;
217 TrustBits trust_bits = TRUST_DEFAULT;
218 if ((trust.sslFlags & kCAFlags) == CERTDB_TERMINAL_RECORD)
219 trust_bits |= DISTRUSTED_SSL;
220 else if (trust.sslFlags & kTrustedCA)
221 trust_bits |= TRUSTED_SSL;
223 if ((trust.emailFlags & kCAFlags) == CERTDB_TERMINAL_RECORD)
224 trust_bits |= DISTRUSTED_EMAIL;
225 else if (trust.emailFlags & kTrustedCA)
226 trust_bits |= TRUSTED_EMAIL;
228 if ((trust.objectSigningFlags & kCAFlags) == CERTDB_TERMINAL_RECORD)
229 trust_bits |= DISTRUSTED_OBJ_SIGN;
230 else if (trust.objectSigningFlags & kTrustedCA)
231 trust_bits |= TRUSTED_OBJ_SIGN;
233 return trust_bits;
235 case SERVER_CERT:
236 if (trust.sslFlags & CERTDB_TERMINAL_RECORD) {
237 if (trust.sslFlags & CERTDB_TRUSTED)
238 return TRUSTED_SSL;
239 return DISTRUSTED_SSL;
241 return TRUST_DEFAULT;
242 default:
243 return TRUST_DEFAULT;
247 bool NSSCertDatabase::IsUntrusted(const X509Certificate* cert) const {
248 CERTCertTrust nsstrust;
249 SECStatus rv = CERT_GetCertTrust(cert->os_cert_handle(), &nsstrust);
250 if (rv != SECSuccess) {
251 LOG(ERROR) << "CERT_GetCertTrust failed with error " << PORT_GetError();
252 return false;
255 // The CERTCertTrust structure contains three trust records:
256 // sslFlags, emailFlags, and objectSigningFlags. The three
257 // trust records are independent of each other.
259 // If the CERTDB_TERMINAL_RECORD bit in a trust record is set,
260 // then that trust record is a terminal record. A terminal
261 // record is used for explicit trust and distrust of an
262 // end-entity or intermediate CA cert.
264 // In a terminal record, if neither CERTDB_TRUSTED_CA nor
265 // CERTDB_TRUSTED is set, then the terminal record means
266 // explicit distrust. On the other hand, if the terminal
267 // record has either CERTDB_TRUSTED_CA or CERTDB_TRUSTED bit
268 // set, then the terminal record means explicit trust.
270 // For a root CA, the trust record does not have
271 // the CERTDB_TERMINAL_RECORD bit set.
273 static const unsigned int kTrusted = CERTDB_TRUSTED_CA | CERTDB_TRUSTED;
274 if ((nsstrust.sslFlags & CERTDB_TERMINAL_RECORD) != 0 &&
275 (nsstrust.sslFlags & kTrusted) == 0) {
276 return true;
278 if ((nsstrust.emailFlags & CERTDB_TERMINAL_RECORD) != 0 &&
279 (nsstrust.emailFlags & kTrusted) == 0) {
280 return true;
282 if ((nsstrust.objectSigningFlags & CERTDB_TERMINAL_RECORD) != 0 &&
283 (nsstrust.objectSigningFlags & kTrusted) == 0) {
284 return true;
287 // Self-signed certificates that don't have any trust bits set are untrusted.
288 // Other certificates that don't have any trust bits set may still be trusted
289 // if they chain up to a trust anchor.
290 if (CERT_CompareName(&cert->os_cert_handle()->issuer,
291 &cert->os_cert_handle()->subject) == SECEqual) {
292 return (nsstrust.sslFlags & kTrusted) == 0 &&
293 (nsstrust.emailFlags & kTrusted) == 0 &&
294 (nsstrust.objectSigningFlags & kTrusted) == 0;
297 return false;
300 bool NSSCertDatabase::SetCertTrust(const X509Certificate* cert,
301 CertType type,
302 TrustBits trust_bits) {
303 bool success = psm::SetCertTrust(cert, type, trust_bits);
304 if (success)
305 NotifyObserversOfCACertChanged(cert);
307 return success;
310 bool NSSCertDatabase::DeleteCertAndKey(const X509Certificate* cert) {
311 // For some reason, PK11_DeleteTokenCertAndKey only calls
312 // SEC_DeletePermCertificate if the private key is found. So, we check
313 // whether a private key exists before deciding which function to call to
314 // delete the cert.
315 SECKEYPrivateKey *privKey = PK11_FindKeyByAnyCert(cert->os_cert_handle(),
316 NULL);
317 if (privKey) {
318 SECKEY_DestroyPrivateKey(privKey);
319 if (PK11_DeleteTokenCertAndKey(cert->os_cert_handle(), NULL)) {
320 LOG(ERROR) << "PK11_DeleteTokenCertAndKey failed: " << PORT_GetError();
321 return false;
323 } else {
324 if (SEC_DeletePermCertificate(cert->os_cert_handle())) {
325 LOG(ERROR) << "SEC_DeletePermCertificate failed: " << PORT_GetError();
326 return false;
330 NotifyObserversOfCertRemoved(cert);
332 return true;
335 bool NSSCertDatabase::IsReadOnly(const X509Certificate* cert) const {
336 PK11SlotInfo* slot = cert->os_cert_handle()->slot;
337 return slot && PK11_IsReadOnly(slot);
340 bool NSSCertDatabase::IsHardwareBacked(const X509Certificate* cert) const {
341 PK11SlotInfo* slot = cert->os_cert_handle()->slot;
342 return slot && PK11_IsHW(slot);
345 void NSSCertDatabase::AddObserver(Observer* observer) {
346 observer_list_->AddObserver(observer);
349 void NSSCertDatabase::RemoveObserver(Observer* observer) {
350 observer_list_->RemoveObserver(observer);
353 void NSSCertDatabase::NotifyObserversOfCertAdded(const X509Certificate* cert) {
354 observer_list_->Notify(&Observer::OnCertAdded, make_scoped_refptr(cert));
357 void NSSCertDatabase::NotifyObserversOfCertRemoved(
358 const X509Certificate* cert) {
359 observer_list_->Notify(&Observer::OnCertRemoved, make_scoped_refptr(cert));
362 void NSSCertDatabase::NotifyObserversOfCACertChanged(
363 const X509Certificate* cert) {
364 observer_list_->Notify(
365 &Observer::OnCACertChanged, make_scoped_refptr(cert));
368 } // namespace net