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