1// Copyright (c) 2011 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/base/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 "crypto/nss_util.h"
16#include "crypto/nss_util_internal.h"
17#include "net/base/crypto_module.h"
18#include "net/base/net_errors.h"
19#include "net/base/x509_certificate.h"
20#include "net/third_party/mozilla_security_manager/nsNSSCertificateDB.h"
21#include "net/third_party/mozilla_security_manager/nsNSSCertTrust.h"
22#include "net/third_party/mozilla_security_manager/nsPKCS12Blob.h"
23
24// PSM = Mozilla's Personal Security Manager.
25namespace psm = mozilla_security_manager;
26
27namespace net {
28
29CertDatabase::CertDatabase() {
30  crypto::EnsureNSSInit();
31  psm::EnsurePKCS12Init();
32}
33
34int CertDatabase::CheckUserCert(X509Certificate* cert_obj) {
35  if (!cert_obj)
36    return ERR_CERT_INVALID;
37  if (cert_obj->HasExpired())
38    return ERR_CERT_DATE_INVALID;
39
40  // Check if the private key corresponding to the certificate exist
41  // We shouldn't accept any random client certificate sent by a CA.
42
43  // Note: The NSS source documentation wrongly suggests that this
44  // also imports the certificate if the private key exists. This
45  // doesn't seem to be the case.
46
47  CERTCertificate* cert = cert_obj->os_cert_handle();
48  PK11SlotInfo* slot = PK11_KeyForCertExists(cert, NULL, NULL);
49  if (!slot) {
50    LOG(ERROR) << "No corresponding private key in store";
51    return ERR_NO_PRIVATE_KEY_FOR_CERT;
52  }
53  PK11_FreeSlot(slot);
54
55  return OK;
56}
57
58int CertDatabase::AddUserCert(X509Certificate* cert_obj) {
59  CERTCertificate* cert = cert_obj->os_cert_handle();
60  PK11SlotInfo* slot = NULL;
61  std::string nickname;
62
63  // Create a nickname for this certificate.
64  // We use the scheme used by Firefox:
65  // --> <subject's common name>'s <issuer's common name> ID.
66
67  std::string username, ca_name;
68  char* temp_username = CERT_GetCommonName(&cert->subject);
69  char* temp_ca_name = CERT_GetCommonName(&cert->issuer);
70  if (temp_username) {
71    username = temp_username;
72    PORT_Free(temp_username);
73  }
74  if (temp_ca_name) {
75    ca_name = temp_ca_name;
76    PORT_Free(temp_ca_name);
77  }
78  nickname = username + "'s " + ca_name + " ID";
79
80  {
81    crypto::AutoNSSWriteLock lock;
82    slot = PK11_ImportCertForKey(cert,
83                                 const_cast<char*>(nickname.c_str()),
84                                 NULL);
85  }
86
87  if (!slot) {
88    LOG(ERROR) << "Couldn't import user certificate.";
89    return ERR_ADD_USER_CERT_FAILED;
90  }
91  PK11_FreeSlot(slot);
92  CertDatabase::NotifyObserversOfUserCertAdded(cert_obj);
93  return OK;
94}
95
96void CertDatabase::ListCerts(CertificateList* certs) {
97  certs->clear();
98
99  CERTCertList* cert_list = PK11_ListCerts(PK11CertListUnique, NULL);
100  CERTCertListNode* node;
101  for (node = CERT_LIST_HEAD(cert_list);
102       !CERT_LIST_END(node, cert_list);
103       node = CERT_LIST_NEXT(node)) {
104    certs->push_back(X509Certificate::CreateFromHandle(
105        node->cert,
106        X509Certificate::SOURCE_LONE_CERT_IMPORT,
107        X509Certificate::OSCertHandles()));
108  }
109  CERT_DestroyCertList(cert_list);
110}
111
112CryptoModule* CertDatabase::GetPublicModule() const {
113  CryptoModule* module =
114      CryptoModule::CreateFromHandle(crypto::GetPublicNSSKeySlot());
115  // The module is already referenced when returned from
116  // GetPublicNSSKeySlot, so we need to deref it once.
117  PK11_FreeSlot(module->os_module_handle());
118
119  return module;
120}
121
122CryptoModule* CertDatabase::GetPrivateModule() const {
123  CryptoModule* module =
124      CryptoModule::CreateFromHandle(crypto::GetPrivateNSSKeySlot());
125  // The module is already referenced when returned from
126  // GetPrivateNSSKeySlot, so we need to deref it once.
127  PK11_FreeSlot(module->os_module_handle());
128
129  return module;
130}
131
132void CertDatabase::ListModules(CryptoModuleList* modules, bool need_rw) const {
133  modules->clear();
134
135  PK11SlotList* slot_list = NULL;
136  // The wincx arg is unused since we don't call PK11_SetIsLoggedInFunc.
137  slot_list = PK11_GetAllTokens(CKM_INVALID_MECHANISM,
138                                need_rw ? PR_TRUE : PR_FALSE,  // needRW
139                                PR_TRUE,  // loadCerts (unused)
140                                NULL);  // wincx
141  if (!slot_list) {
142    LOG(ERROR) << "PK11_GetAllTokens failed: " << PORT_GetError();
143    return;
144  }
145
146  PK11SlotListElement* slot_element = PK11_GetFirstSafe(slot_list);
147  while (slot_element) {
148    modules->push_back(CryptoModule::CreateFromHandle(slot_element->slot));
149    slot_element = PK11_GetNextSafe(slot_list, slot_element,
150                                    PR_FALSE);  // restart
151  }
152
153  PK11_FreeSlotList(slot_list);
154}
155
156int CertDatabase::ImportFromPKCS12(
157    CryptoModule* module,
158    const std::string& data,
159    const string16& password) {
160  int result = psm::nsPKCS12Blob_Import(module->os_module_handle(),
161                                        data.data(), data.size(),
162                                        password);
163  if (result == net::OK)
164    CertDatabase::NotifyObserversOfUserCertAdded(NULL);
165
166  return result;
167}
168
169int CertDatabase::ExportToPKCS12(
170    const CertificateList& certs,
171    const string16& password,
172    std::string* output) const {
173  return psm::nsPKCS12Blob_Export(output, certs, password);
174}
175
176X509Certificate* CertDatabase::FindRootInList(
177    const CertificateList& certificates) const {
178  DCHECK_GT(certificates.size(), 0U);
179
180  if (certificates.size() == 1)
181    return certificates[0].get();
182
183  X509Certificate* cert0 = certificates[0];
184  X509Certificate* cert1 = certificates[1];
185  X509Certificate* certn_2 = certificates[certificates.size() - 2];
186  X509Certificate* certn_1 = certificates[certificates.size() - 1];
187
188  if (CERT_CompareName(&cert1->os_cert_handle()->issuer,
189                       &cert0->os_cert_handle()->subject) == SECEqual)
190    return cert0;
191  if (CERT_CompareName(&certn_2->os_cert_handle()->issuer,
192                       &certn_1->os_cert_handle()->subject) == SECEqual)
193    return certn_1;
194
195  VLOG(1) << "certificate list is not a hierarchy";
196  return cert0;
197}
198
199bool CertDatabase::ImportCACerts(const CertificateList& certificates,
200                                 unsigned int trust_bits,
201                                 ImportCertFailureList* not_imported) {
202  X509Certificate* root = FindRootInList(certificates);
203  bool success = psm::ImportCACerts(certificates, root, trust_bits,
204                                    not_imported);
205  if (success)
206    CertDatabase::NotifyObserversOfCertTrustChanged(NULL);
207
208  return success;
209}
210
211bool CertDatabase::ImportServerCert(const CertificateList& certificates,
212                                    ImportCertFailureList* not_imported) {
213  return psm::ImportServerCert(certificates, not_imported);
214}
215
216unsigned int CertDatabase::GetCertTrust(
217    const X509Certificate* cert, CertType type) const {
218  CERTCertTrust nsstrust;
219  SECStatus srv = CERT_GetCertTrust(cert->os_cert_handle(), &nsstrust);
220  if (srv != SECSuccess) {
221    LOG(ERROR) << "CERT_GetCertTrust failed with error " << PORT_GetError();
222    return UNTRUSTED;
223  }
224  psm::nsNSSCertTrust trust(&nsstrust);
225  switch (type) {
226    case CA_CERT:
227      return trust.HasTrustedCA(PR_TRUE, PR_FALSE, PR_FALSE) * TRUSTED_SSL +
228          trust.HasTrustedCA(PR_FALSE, PR_TRUE, PR_FALSE) * TRUSTED_EMAIL +
229          trust.HasTrustedCA(PR_FALSE, PR_FALSE, PR_TRUE) * TRUSTED_OBJ_SIGN;
230    case SERVER_CERT:
231      return trust.HasTrustedPeer(PR_TRUE, PR_FALSE, PR_FALSE) * TRUSTED_SSL +
232          trust.HasTrustedPeer(PR_FALSE, PR_TRUE, PR_FALSE) * TRUSTED_EMAIL +
233          trust.HasTrustedPeer(PR_FALSE, PR_FALSE, PR_TRUE) * TRUSTED_OBJ_SIGN;
234    default:
235      return UNTRUSTED;
236  }
237}
238
239bool CertDatabase::SetCertTrust(const X509Certificate* cert,
240                                CertType type,
241                                unsigned int trusted) {
242  bool success = psm::SetCertTrust(cert, type, trusted);
243  if (success)
244    CertDatabase::NotifyObserversOfCertTrustChanged(cert);
245
246  return success;
247}
248
249// TODO(xiyuan): Add an Observer method for this event.
250bool CertDatabase::DeleteCertAndKey(const X509Certificate* cert) {
251  // For some reason, PK11_DeleteTokenCertAndKey only calls
252  // SEC_DeletePermCertificate if the private key is found.  So, we check
253  // whether a private key exists before deciding which function to call to
254  // delete the cert.
255  SECKEYPrivateKey *privKey = PK11_FindKeyByAnyCert(cert->os_cert_handle(),
256                                                    NULL);
257  if (privKey) {
258    SECKEY_DestroyPrivateKey(privKey);
259    if (PK11_DeleteTokenCertAndKey(cert->os_cert_handle(), NULL)) {
260      LOG(ERROR) << "PK11_DeleteTokenCertAndKey failed: " << PORT_GetError();
261      return false;
262    }
263  } else {
264    if (SEC_DeletePermCertificate(cert->os_cert_handle())) {
265      LOG(ERROR) << "SEC_DeletePermCertificate failed: " << PORT_GetError();
266      return false;
267    }
268  }
269  return true;
270}
271
272bool CertDatabase::IsReadOnly(const X509Certificate* cert) const {
273  PK11SlotInfo* slot = cert->os_cert_handle()->slot;
274  return slot && PK11_IsReadOnly(slot);
275}
276
277}  // namespace net
278