Index: net/base/cert_database_nss.cc |
=================================================================== |
--- net/base/cert_database_nss.cc (revision 49024) |
+++ net/base/cert_database_nss.cc (working copy) |
@@ -4,22 +4,121 @@ |
#include "net/base/cert_database.h" |
+#include <cert.h> |
+#include <certdb.h> |
#include <pk11pub.h> |
#include <secmod.h> |
#include <ssl.h> |
#include <nssb64.h> // NSSBase64_EncodeItem() |
#include <secder.h> // DER_Encode() |
+#include <secerr.h> |
#include <cryptohi.h> // SEC_DerSignData() |
#include <keyhi.h> // SECKEY_CreateSubjectPublicKeyInfo() |
#include "base/logging.h" |
#include "base/scoped_ptr.h" |
#include "base/nss_util.h" |
+#include "base/crypto/scoped_nss_types.h" |
#include "net/base/net_errors.h" |
#include "net/base/x509_certificate.h" |
namespace net { |
+namespace { |
+ |
+typedef scoped_ptr_malloc<CERTCertList, |
+ base::NSSDestroyer<CERTCertList, |
+ CERT_DestroyCertList> > ScopedCERTCertList; |
+ |
+typedef scoped_ptr_malloc<CERTCertificateList, |
+ base::NSSDestroyer<CERTCertificateList, |
+ CERT_DestroyCertificateList> > ScopedCERTCertificateList; |
+ |
+// From an arbitrary, unordered list of certificate, attempt to import every |
+// certificate that: |
+// 1) Is a CA |
+// 2) Results in a valid certificate chain |
+// It is adapted from the PSM logic located in |
+// security/manager/ssl/src/nsNSSCertificateDB.cpp |
+void ImportValidIntermediates( |
+ const X509Certificate::OSCertHandles& intermediates) { |
+ if (intermediates.empty()) |
+ return; |
+ |
+ ScopedCERTCertList os_intermediates(CERT_NewCertList()); |
+ if (os_intermediates == NULL) |
+ return; |
+ |
+ for (X509Certificate::OSCertHandles::const_iterator it = |
+ intermediates.begin(); it != intermediates.end(); ++it) { |
+ // CERTCertList will free each certificate when it's destroyed, so we must |
+ // duplicate each cert prior to adding to the list, as we do not own the |
+ // intermediates (see x509_certificate.h) |
+ CERTCertificate* cert = CERT_DupCertificate( |
+ const_cast<CERTCertificate*>(*it)); |
+ if (cert) |
+ CERT_AddCertToListTail(os_intermediates.get(), cert); |
+ } |
+ |
+ // Filter out any certificates which are not valid CA certificates |
+ SECStatus rv = CERT_FilterCertListByUsage(os_intermediates.get(), |
+ certUsageAnyCA, |
+ PR_TRUE); |
+ if (rv != SECSuccess) |
+ return; |
+ |
+ PRTime now = PR_Now(); |
+ size_t offset = 0; |
+ for (CERTCertListNode* node = CERT_LIST_HEAD(os_intermediates); |
+ !CERT_LIST_END(node, os_intermediates); |
+ node = CERT_LIST_NEXT(node), ++offset) { |
+ // Validate the certificate |
+ SECStatus verify_result = CERT_VerifyCert(CERT_GetDefaultCertDB(), |
+ node->cert, PR_TRUE, |
+ certUsageVerifyCA, now, NULL, |
+ NULL); |
+ if (verify_result != SECSuccess) { |
+ DLOG(WARNING) << "Unable to validate the intermediate at " << offset |
+ << ". Skipping import of this certificate. Result was " |
+ << verify_result; |
+ continue; |
+ } |
+ |
+ // Attempt to construct a chain. The presumption here is that if |
+ // construction of chain depends on other certificates that were part of |
+ // |intermediates|, they will all be available in the same |
+ // CERTCertDBHandle. This is true if they were created via |
+ // X509Certificate::CreateFromBytes(), since under the hood that creates |
+ // temporary objects in CERT_GetDefaultCertDB(), but that may not be true |
+ // if they were obtained by other means. |
+ // |
+ // Note: If multiple CertDBs are ever supported, this chain building logic |
+ // will need to be adjusted accordingly. |
+ ScopedCERTCertificateList chain( |
+ CERT_CertChainFromCert(node->cert, certUsageAnyCA, PR_FALSE)); |
+ if (chain == NULL) { |
+ DLOG(WARNING) << "Unable to construct certificate chain for" |
+ << "intermediate " << offset << ". Skipping import of " |
+ << "this certificate."; |
+ continue; |
+ } |
+ |
+ // CertChainFromCert returns an array of SECItems, but ImportCerts |
+ // requires an array of SECItem pointers, so convert. |
+ scoped_array<SECItem*> items(new SECItem*[chain->len]); |
+ if (items == NULL) |
+ continue; |
+ |
+ for (int i = 0; i < chain->len; i++) { |
+ items[i] = &chain->certs[i]; |
+ } |
+ CERT_ImportCerts(CERT_GetDefaultCertDB(), certUsageAnyCA, chain->len, |
+ items.get(), NULL, PR_TRUE, PR_TRUE, NULL); |
+ } |
+} |
+ |
+} // namespace |
+ |
CertDatabase::CertDatabase() { |
base::EnsureNSSInit(); |
} |
@@ -30,13 +129,12 @@ |
if (cert_obj->HasExpired()) |
return ERR_CERT_DATE_INVALID; |
- // Check if the private key corresponding to the certificate exist |
- // We shouldn't accept any random client certificate sent by a CA. |
- |
- // Note: The NSS source documentation wrongly suggests that this |
- // also imports the certificate if the private key exists. This |
- // doesn't seem to be the case. |
- |
+ // Check if the private key corresponding to the certificate exist. We |
+ // shouldn't accept any random client certificate sent by a CA. |
+ // |
+ // Note: The NSS source documentation wrongly suggests that this also |
+ // imports the certificate if the private key exists. This doesn't seem to |
+ // be the case. |
CERTCertificate* cert = cert_obj->os_cert_handle(); |
PK11SlotInfo* slot = PK11_KeyForCertExists(cert, NULL, NULL); |
if (!slot) { |
@@ -78,6 +176,8 @@ |
return ERR_ADD_USER_CERT_FAILED; |
} |
PK11_FreeSlot(slot); |
+ |
+ ImportValidIntermediates(cert_obj->GetIntermediateCertificates()); |
return OK; |
} |