• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
25 namespace psm = mozilla_security_manager;
26 
27 namespace net {
28 
CertDatabase()29 CertDatabase::CertDatabase() {
30   crypto::EnsureNSSInit();
31   psm::EnsurePKCS12Init();
32 }
33 
CheckUserCert(X509Certificate * cert_obj)34 int 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 
AddUserCert(X509Certificate * cert_obj)58 int 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 
ListCerts(CertificateList * certs)96 void 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 
GetPublicModule() const112 CryptoModule* 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 
GetPrivateModule() const122 CryptoModule* 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 
ListModules(CryptoModuleList * modules,bool need_rw) const132 void 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 
ImportFromPKCS12(CryptoModule * module,const std::string & data,const string16 & password)156 int 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 
ExportToPKCS12(const CertificateList & certs,const string16 & password,std::string * output) const169 int 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 
FindRootInList(const CertificateList & certificates) const176 X509Certificate* 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 
ImportCACerts(const CertificateList & certificates,unsigned int trust_bits,ImportCertFailureList * not_imported)199 bool 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 
ImportServerCert(const CertificateList & certificates,ImportCertFailureList * not_imported)211 bool CertDatabase::ImportServerCert(const CertificateList& certificates,
212                                     ImportCertFailureList* not_imported) {
213   return psm::ImportServerCert(certificates, not_imported);
214 }
215 
GetCertTrust(const X509Certificate * cert,CertType type) const216 unsigned 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 
SetCertTrust(const X509Certificate * cert,CertType type,unsigned int trusted)239 bool 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.
DeleteCertAndKey(const X509Certificate * cert)250 bool 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 
IsReadOnly(const X509Certificate * cert) const272 bool 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