OLD | NEW |
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "net/base/cert_database.h" | 5 #include "net/base/cert_database.h" |
6 | 6 |
| 7 #include <cert.h> |
| 8 #include <certdb.h> |
7 #include <pk11pub.h> | 9 #include <pk11pub.h> |
8 #include <secmod.h> | 10 #include <secmod.h> |
9 #include <ssl.h> | 11 #include <ssl.h> |
10 #include <nssb64.h> // NSSBase64_EncodeItem() | 12 #include <nssb64.h> // NSSBase64_EncodeItem() |
11 #include <secder.h> // DER_Encode() | 13 #include <secder.h> // DER_Encode() |
| 14 #include <secerr.h> |
12 #include <cryptohi.h> // SEC_DerSignData() | 15 #include <cryptohi.h> // SEC_DerSignData() |
13 #include <keyhi.h> // SECKEY_CreateSubjectPublicKeyInfo() | 16 #include <keyhi.h> // SECKEY_CreateSubjectPublicKeyInfo() |
14 | 17 |
15 #include "base/logging.h" | 18 #include "base/logging.h" |
16 #include "base/scoped_ptr.h" | 19 #include "base/scoped_ptr.h" |
17 #include "base/nss_util.h" | 20 #include "base/nss_util.h" |
| 21 #include "base/crypto/scoped_nss_types.h" |
18 #include "net/base/net_errors.h" | 22 #include "net/base/net_errors.h" |
19 #include "net/base/x509_certificate.h" | 23 #include "net/base/x509_certificate.h" |
20 | 24 |
21 namespace net { | 25 namespace net { |
22 | 26 |
| 27 namespace { |
| 28 |
| 29 typedef scoped_ptr_malloc<CERTCertList, |
| 30 base::NSSDestroyer<CERTCertList, |
| 31 CERT_DestroyCertList> > ScopedCERTCertList; |
| 32 |
| 33 typedef scoped_ptr_malloc<CERTCertificateList, |
| 34 base::NSSDestroyer<CERTCertificateList, |
| 35 CERT_DestroyCertificateList> > ScopedCERTCertificateList; |
| 36 |
| 37 // From an arbitrary, unordered list of certificate, attempt to import every |
| 38 // certificate that: |
| 39 // 1) Is a CA |
| 40 // 2) Results in a valid certificate chain |
| 41 // It is adapted from the PSM logic located in |
| 42 // security/manager/ssl/src/nsNSSCertificateDB.cpp |
| 43 void ImportValidIntermediates( |
| 44 const X509Certificate::OSCertHandles& intermediates) { |
| 45 if (intermediates.empty()) |
| 46 return; |
| 47 |
| 48 ScopedCERTCertList os_intermediates(CERT_NewCertList()); |
| 49 if (os_intermediates == NULL) |
| 50 return; |
| 51 |
| 52 for (X509Certificate::OSCertHandles::const_iterator it = |
| 53 intermediates.begin(); it != intermediates.end(); ++it) { |
| 54 // CERTCertList will free each certificate when it's destroyed, so we must |
| 55 // duplicate each cert prior to adding to the list, as we do not own the |
| 56 // intermediates (see x509_certificate.h) |
| 57 CERTCertificate* cert = CERT_DupCertificate( |
| 58 const_cast<CERTCertificate*>(*it)); |
| 59 if (cert) |
| 60 CERT_AddCertToListTail(os_intermediates.get(), cert); |
| 61 } |
| 62 |
| 63 // Filter out any certificates which are not valid CA certificates |
| 64 SECStatus rv = CERT_FilterCertListByUsage(os_intermediates.get(), |
| 65 certUsageAnyCA, |
| 66 PR_TRUE); |
| 67 if (rv != SECSuccess) |
| 68 return; |
| 69 |
| 70 PRTime now = PR_Now(); |
| 71 size_t offset = 0; |
| 72 for (CERTCertListNode* node = CERT_LIST_HEAD(os_intermediates); |
| 73 !CERT_LIST_END(node, os_intermediates); |
| 74 node = CERT_LIST_NEXT(node), ++offset) { |
| 75 // Validate the certificate |
| 76 SECStatus verify_result = CERT_VerifyCert(CERT_GetDefaultCertDB(), |
| 77 node->cert, PR_TRUE, |
| 78 certUsageVerifyCA, now, NULL, |
| 79 NULL); |
| 80 if (verify_result != SECSuccess) { |
| 81 DLOG(WARNING) << "Unable to validate the intermediate at " << offset |
| 82 << ". Skipping import of this certificate. Result was " |
| 83 << verify_result; |
| 84 continue; |
| 85 } |
| 86 |
| 87 // Attempt to construct a chain. The presumption here is that if |
| 88 // construction of chain depends on other certificates that were part of |
| 89 // |intermediates|, they will all be available in the same |
| 90 // CERTCertDBHandle. This is true if they were created via |
| 91 // X509Certificate::CreateFromBytes(), since under the hood that creates |
| 92 // temporary objects in CERT_GetDefaultCertDB(), but that may not be true |
| 93 // if they were obtained by other means. |
| 94 // |
| 95 // Note: If multiple CertDBs are ever supported, this chain building logic |
| 96 // will need to be adjusted accordingly. |
| 97 ScopedCERTCertificateList chain( |
| 98 CERT_CertChainFromCert(node->cert, certUsageAnyCA, PR_FALSE)); |
| 99 if (chain == NULL) { |
| 100 DLOG(WARNING) << "Unable to construct certificate chain for" |
| 101 << "intermediate " << offset << ". Skipping import of " |
| 102 << "this certificate."; |
| 103 continue; |
| 104 } |
| 105 |
| 106 // CertChainFromCert returns an array of SECItems, but ImportCerts |
| 107 // requires an array of SECItem pointers, so convert. |
| 108 scoped_array<SECItem*> items(new SECItem*[chain->len]); |
| 109 if (items == NULL) |
| 110 continue; |
| 111 |
| 112 for (int i = 0; i < chain->len; i++) { |
| 113 items[i] = &chain->certs[i]; |
| 114 } |
| 115 CERT_ImportCerts(CERT_GetDefaultCertDB(), certUsageAnyCA, chain->len, |
| 116 items.get(), NULL, PR_TRUE, PR_TRUE, NULL); |
| 117 } |
| 118 } |
| 119 |
| 120 } // namespace |
| 121 |
23 CertDatabase::CertDatabase() { | 122 CertDatabase::CertDatabase() { |
24 base::EnsureNSSInit(); | 123 base::EnsureNSSInit(); |
25 } | 124 } |
26 | 125 |
27 int CertDatabase::CheckUserCert(X509Certificate* cert_obj) { | 126 int CertDatabase::CheckUserCert(X509Certificate* cert_obj) { |
28 if (!cert_obj) | 127 if (!cert_obj) |
29 return ERR_CERT_INVALID; | 128 return ERR_CERT_INVALID; |
30 if (cert_obj->HasExpired()) | 129 if (cert_obj->HasExpired()) |
31 return ERR_CERT_DATE_INVALID; | 130 return ERR_CERT_DATE_INVALID; |
32 | 131 |
33 // Check if the private key corresponding to the certificate exist | 132 // Check if the private key corresponding to the certificate exist. We |
34 // We shouldn't accept any random client certificate sent by a CA. | 133 // shouldn't accept any random client certificate sent by a CA. |
35 | 134 // |
36 // Note: The NSS source documentation wrongly suggests that this | 135 // Note: The NSS source documentation wrongly suggests that this also |
37 // also imports the certificate if the private key exists. This | 136 // imports the certificate if the private key exists. This doesn't seem to |
38 // doesn't seem to be the case. | 137 // be the case. |
39 | |
40 CERTCertificate* cert = cert_obj->os_cert_handle(); | 138 CERTCertificate* cert = cert_obj->os_cert_handle(); |
41 PK11SlotInfo* slot = PK11_KeyForCertExists(cert, NULL, NULL); | 139 PK11SlotInfo* slot = PK11_KeyForCertExists(cert, NULL, NULL); |
42 if (!slot) { | 140 if (!slot) { |
43 LOG(ERROR) << "No corresponding private key in store"; | 141 LOG(ERROR) << "No corresponding private key in store"; |
44 return ERR_NO_PRIVATE_KEY_FOR_CERT; | 142 return ERR_NO_PRIVATE_KEY_FOR_CERT; |
45 } | 143 } |
46 PK11_FreeSlot(slot); | 144 PK11_FreeSlot(slot); |
47 | 145 |
48 return OK; | 146 return OK; |
49 } | 147 } |
(...skipping 21 matching lines...) Expand all Loading... |
71 nickname = username + "'s " + ca_name + " ID"; | 169 nickname = username + "'s " + ca_name + " ID"; |
72 | 170 |
73 slot = PK11_ImportCertForKey(cert, | 171 slot = PK11_ImportCertForKey(cert, |
74 const_cast<char*>(nickname.c_str()), | 172 const_cast<char*>(nickname.c_str()), |
75 NULL); | 173 NULL); |
76 if (!slot) { | 174 if (!slot) { |
77 LOG(ERROR) << "Couldn't import user certificate."; | 175 LOG(ERROR) << "Couldn't import user certificate."; |
78 return ERR_ADD_USER_CERT_FAILED; | 176 return ERR_ADD_USER_CERT_FAILED; |
79 } | 177 } |
80 PK11_FreeSlot(slot); | 178 PK11_FreeSlot(slot); |
| 179 |
| 180 ImportValidIntermediates(cert_obj->GetIntermediateCertificates()); |
81 return OK; | 181 return OK; |
82 } | 182 } |
83 | 183 |
84 } // namespace net | 184 } // namespace net |
OLD | NEW |