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