| 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;
|
| }
|
|
|
|
|