OLD | NEW |
| (Empty) |
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: | |
48 explicit CertNotificationForwarder(CertDatabase* cert_db) | |
49 : cert_db_(cert_db) {} | |
50 | |
51 ~CertNotificationForwarder() override {} | |
52 | |
53 // NSSCertDatabase::Observer implementation: | |
54 void OnCertAdded(const X509Certificate* cert) override { | |
55 cert_db_->NotifyObserversOfCertAdded(cert); | |
56 } | |
57 | |
58 void OnCertRemoved(const X509Certificate* cert) override { | |
59 cert_db_->NotifyObserversOfCertRemoved(cert); | |
60 } | |
61 | |
62 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 | |
74 NSSCertDatabase::ImportCertFailure::ImportCertFailure( | |
75 const scoped_refptr<X509Certificate>& cert, | |
76 int err) | |
77 : certificate(cert), net_error(err) {} | |
78 | |
79 NSSCertDatabase::ImportCertFailure::~ImportCertFailure() {} | |
80 | |
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 | |
97 NSSCertDatabase::~NSSCertDatabase() {} | |
98 | |
99 void NSSCertDatabase::ListCertsSync(CertificateList* certs) { | |
100 ListCertsImpl(crypto::ScopedPK11Slot(), certs); | |
101 } | |
102 | |
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 | |
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) | |
133 crypto::ScopedPK11Slot NSSCertDatabase::GetSystemSlot() const { | |
134 return crypto::ScopedPK11Slot(); | |
135 } | |
136 #endif | |
137 | |
138 crypto::ScopedPK11Slot NSSCertDatabase::GetPublicSlot() const { | |
139 return crypto::ScopedPK11Slot(PK11_ReferenceSlot(public_slot_.get())); | |
140 } | |
141 | |
142 crypto::ScopedPK11Slot NSSCertDatabase::GetPrivateSlot() const { | |
143 if (!private_slot_) | |
144 return crypto::ScopedPK11Slot(); | |
145 return crypto::ScopedPK11Slot(PK11_ReferenceSlot(private_slot_.get())); | |
146 } | |
147 | |
148 CryptoModule* NSSCertDatabase::GetPublicModule() const { | |
149 crypto::ScopedPK11Slot slot(GetPublicSlot()); | |
150 return CryptoModule::CreateFromHandle(slot.get()); | |
151 } | |
152 | |
153 CryptoModule* NSSCertDatabase::GetPrivateModule() const { | |
154 crypto::ScopedPK11Slot slot(GetPrivateSlot()); | |
155 return CryptoModule::CreateFromHandle(slot.get()); | |
156 } | |
157 | |
158 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 | |
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 | |
201 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 | |
208 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 | |
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 | |
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 | |
252 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 | |
299 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 | |
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 | |
362 bool NSSCertDatabase::DeleteCertAndKey(X509Certificate* cert) { | |
363 if (!DeleteCertAndKeyImpl(cert)) | |
364 return false; | |
365 NotifyObserversOfCertRemoved(cert); | |
366 return true; | |
367 } | |
368 | |
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 | |
382 bool NSSCertDatabase::IsReadOnly(const X509Certificate* cert) const { | |
383 PK11SlotInfo* slot = cert->os_cert_handle()->slot; | |
384 return slot && PK11_IsReadOnly(slot); | |
385 } | |
386 | |
387 bool NSSCertDatabase::IsHardwareBacked(const X509Certificate* cert) const { | |
388 PK11SlotInfo* slot = cert->os_cert_handle()->slot; | |
389 return slot && PK11_IsHW(slot); | |
390 } | |
391 | |
392 void NSSCertDatabase::AddObserver(Observer* observer) { | |
393 observer_list_->AddObserver(observer); | |
394 } | |
395 | |
396 void NSSCertDatabase::RemoveObserver(Observer* observer) { | |
397 observer_list_->RemoveObserver(observer); | |
398 } | |
399 | |
400 void NSSCertDatabase::SetSlowTaskRunnerForTest( | |
401 const scoped_refptr<base::TaskRunner>& task_runner) { | |
402 slow_task_runner_for_test_ = task_runner; | |
403 } | |
404 | |
405 // static | |
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 | |
425 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 | |
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 | |
440 void NSSCertDatabase::NotifyObserversOfCertAdded(const X509Certificate* cert) { | |
441 observer_list_->Notify(FROM_HERE, &Observer::OnCertAdded, | |
442 make_scoped_refptr(cert)); | |
443 } | |
444 | |
445 void NSSCertDatabase::NotifyObserversOfCertRemoved( | |
446 const X509Certificate* cert) { | |
447 observer_list_->Notify(FROM_HERE, &Observer::OnCertRemoved, | |
448 make_scoped_refptr(cert)); | |
449 } | |
450 | |
451 void NSSCertDatabase::NotifyObserversOfCACertChanged( | |
452 const X509Certificate* cert) { | |
453 observer_list_->Notify(FROM_HERE, &Observer::OnCACertChanged, | |
454 make_scoped_refptr(cert)); | |
455 } | |
456 | |
457 // static | |
458 bool NSSCertDatabase::DeleteCertAndKeyImpl( | |
459 scoped_refptr<X509Certificate> cert) { | |
460 // For some reason, PK11_DeleteTokenCertAndKey only calls | |
461 // SEC_DeletePermCertificate if the private key is found. So, we check | |
462 // whether a private key exists before deciding which function to call to | |
463 // delete the cert. | |
464 SECKEYPrivateKey* privKey = | |
465 PK11_FindKeyByAnyCert(cert->os_cert_handle(), NULL); | |
466 if (privKey) { | |
467 SECKEY_DestroyPrivateKey(privKey); | |
468 if (PK11_DeleteTokenCertAndKey(cert->os_cert_handle(), NULL)) { | |
469 LOG(ERROR) << "PK11_DeleteTokenCertAndKey failed: " << PORT_GetError(); | |
470 return false; | |
471 } | |
472 } else { | |
473 if (SEC_DeletePermCertificate(cert->os_cert_handle())) { | |
474 LOG(ERROR) << "SEC_DeletePermCertificate failed: " << PORT_GetError(); | |
475 return false; | |
476 } | |
477 } | |
478 return true; | |
479 } | |
480 | |
481 } // namespace net | |
OLD | NEW |