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 "net/base/crypto_module.h"
20#include "net/base/net_errors.h"
21#include "net/cert/cert_database.h"
22#include "net/cert/x509_certificate.h"
23#include "net/third_party/mozilla_security_manager/nsNSSCertificateDB.h"
24#include "net/third_party/mozilla_security_manager/nsPKCS12Blob.h"
25
26// In NSS 3.13, CERTDB_VALID_PEER was renamed CERTDB_TERMINAL_RECORD. So we use
27// the new name of the macro.
28#if !defined(CERTDB_TERMINAL_RECORD)
29#define CERTDB_TERMINAL_RECORD CERTDB_VALID_PEER
30#endif
31
32// PSM = Mozilla's Personal Security Manager.
33namespace psm = mozilla_security_manager;
34
35namespace net {
36
37NSSCertDatabase::ImportCertFailure::ImportCertFailure(
38    const scoped_refptr<X509Certificate>& cert,
39    int err)
40    : certificate(cert), net_error(err) {}
41
42NSSCertDatabase::ImportCertFailure::~ImportCertFailure() {}
43
44// static
45NSSCertDatabase* NSSCertDatabase::GetInstance() {
46  return Singleton<NSSCertDatabase,
47                   LeakySingletonTraits<NSSCertDatabase> >::get();
48}
49
50NSSCertDatabase::NSSCertDatabase()
51    : observer_list_(new ObserverListThreadSafe<Observer>) {
52  crypto::EnsureNSSInit();
53  psm::EnsurePKCS12Init();
54}
55
56NSSCertDatabase::~NSSCertDatabase() {}
57
58void NSSCertDatabase::ListCerts(CertificateList* certs) {
59  certs->clear();
60
61  CERTCertList* cert_list = PK11_ListCerts(PK11CertListUnique, NULL);
62  CERTCertListNode* node;
63  for (node = CERT_LIST_HEAD(cert_list);
64       !CERT_LIST_END(node, cert_list);
65       node = CERT_LIST_NEXT(node)) {
66    certs->push_back(X509Certificate::CreateFromHandle(
67        node->cert, X509Certificate::OSCertHandles()));
68  }
69  CERT_DestroyCertList(cert_list);
70}
71
72CryptoModule* NSSCertDatabase::GetPublicModule() const {
73  CryptoModule* module =
74      CryptoModule::CreateFromHandle(crypto::GetPublicNSSKeySlot());
75  // The module is already referenced when returned from
76  // GetPublicNSSKeySlot, so we need to deref it once.
77  PK11_FreeSlot(module->os_module_handle());
78
79  return module;
80}
81
82CryptoModule* NSSCertDatabase::GetPrivateModule() const {
83  CryptoModule* module =
84      CryptoModule::CreateFromHandle(crypto::GetPrivateNSSKeySlot());
85  // The module is already referenced when returned from
86  // GetPrivateNSSKeySlot, so we need to deref it once.
87  PK11_FreeSlot(module->os_module_handle());
88
89  return module;
90}
91
92void NSSCertDatabase::ListModules(CryptoModuleList* modules,
93                                  bool need_rw) const {
94  modules->clear();
95
96  PK11SlotList* slot_list = NULL;
97  // The wincx arg is unused since we don't call PK11_SetIsLoggedInFunc.
98  slot_list = PK11_GetAllTokens(CKM_INVALID_MECHANISM,
99                                need_rw ? PR_TRUE : PR_FALSE,  // needRW
100                                PR_TRUE,  // loadCerts (unused)
101                                NULL);  // wincx
102  if (!slot_list) {
103    LOG(ERROR) << "PK11_GetAllTokens failed: " << PORT_GetError();
104    return;
105  }
106
107  PK11SlotListElement* slot_element = PK11_GetFirstSafe(slot_list);
108  while (slot_element) {
109    modules->push_back(CryptoModule::CreateFromHandle(slot_element->slot));
110    slot_element = PK11_GetNextSafe(slot_list, slot_element,
111                                    PR_FALSE);  // restart
112  }
113
114  PK11_FreeSlotList(slot_list);
115}
116
117int NSSCertDatabase::ImportFromPKCS12(
118    CryptoModule* module,
119    const std::string& data,
120    const base::string16& password,
121    bool is_extractable,
122    net::CertificateList* imported_certs) {
123  int result = psm::nsPKCS12Blob_Import(module->os_module_handle(),
124                                        data.data(), data.size(),
125                                        password,
126                                        is_extractable,
127                                        imported_certs);
128  if (result == net::OK)
129    NotifyObserversOfCertAdded(NULL);
130
131  return result;
132}
133
134int NSSCertDatabase::ExportToPKCS12(
135    const CertificateList& certs,
136    const base::string16& password,
137    std::string* output) const {
138  return psm::nsPKCS12Blob_Export(output, certs, password);
139}
140
141X509Certificate* NSSCertDatabase::FindRootInList(
142    const CertificateList& certificates) const {
143  DCHECK_GT(certificates.size(), 0U);
144
145  if (certificates.size() == 1)
146    return certificates[0].get();
147
148  X509Certificate* cert0 = certificates[0].get();
149  X509Certificate* cert1 = certificates[1].get();
150  X509Certificate* certn_2 = certificates[certificates.size() - 2].get();
151  X509Certificate* certn_1 = certificates[certificates.size() - 1].get();
152
153  if (CERT_CompareName(&cert1->os_cert_handle()->issuer,
154                       &cert0->os_cert_handle()->subject) == SECEqual)
155    return cert0;
156  if (CERT_CompareName(&certn_2->os_cert_handle()->issuer,
157                       &certn_1->os_cert_handle()->subject) == SECEqual)
158    return certn_1;
159
160  VLOG(1) << "certificate list is not a hierarchy";
161  return cert0;
162}
163
164bool NSSCertDatabase::ImportCACerts(const CertificateList& certificates,
165                                    TrustBits trust_bits,
166                                    ImportCertFailureList* not_imported) {
167  X509Certificate* root = FindRootInList(certificates);
168  bool success = psm::ImportCACerts(certificates, root, trust_bits,
169                                    not_imported);
170  if (success)
171    NotifyObserversOfCertTrustChanged(NULL);
172
173  return success;
174}
175
176bool NSSCertDatabase::ImportServerCert(const CertificateList& certificates,
177                                       TrustBits trust_bits,
178                                       ImportCertFailureList* not_imported) {
179  return psm::ImportServerCert(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    NotifyObserversOfCertTrustChanged(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
322void NSSCertDatabase::AddObserver(Observer* observer) {
323  observer_list_->AddObserver(observer);
324}
325
326void NSSCertDatabase::RemoveObserver(Observer* observer) {
327  observer_list_->RemoveObserver(observer);
328}
329
330void NSSCertDatabase::NotifyObserversOfCertAdded(const X509Certificate* cert) {
331  observer_list_->Notify(&Observer::OnCertAdded, make_scoped_refptr(cert));
332}
333
334void NSSCertDatabase::NotifyObserversOfCertRemoved(
335    const X509Certificate* cert) {
336  observer_list_->Notify(&Observer::OnCertRemoved, make_scoped_refptr(cert));
337}
338
339void NSSCertDatabase::NotifyObserversOfCertTrustChanged(
340    const X509Certificate* cert) {
341  observer_list_->Notify(
342      &Observer::OnCertTrustChanged, make_scoped_refptr(cert));
343}
344
345}  // namespace net
346