Index: net/base/x509_certificate_nss.cc |
diff --git a/net/base/x509_certificate_nss.cc b/net/base/x509_certificate_nss.cc |
index 26b74c1e781e9ee1d9b450fc7b5392732dd78040..2ca31ba563b6a0647a75172236cd94aae4b68f86 100644 |
--- a/net/base/x509_certificate_nss.cc |
+++ b/net/base/x509_certificate_nss.cc |
@@ -21,8 +21,10 @@ |
#include "base/time.h" |
#include "crypto/nss_util.h" |
#include "crypto/rsa_private_key.h" |
+#include "net/base/asn1_util.h" |
#include "net/base/cert_status_flags.h" |
#include "net/base/cert_verify_result.h" |
+#include "net/base/crl_set.h" |
#include "net/base/ev_root_ca_metadata.h" |
#include "net/base/net_errors.h" |
#include "net/base/x509_util_nss.h" |
@@ -228,6 +230,67 @@ bool IsKnownRoot(CERTCertificate* root) { |
"NSS Builtin Objects"); |
} |
+enum CRLSetResult { |
+ kCRLSetRevoked, |
+ kCRLSetOk, |
+ kCRLSetError, |
+}; |
+ |
+// CheckRevocationWithCRLSet attempts to check each element of |cert_list| |
+// against |crl_set|. It returns: |
+// kCRLSetRevoked: if any element of the chain is known to have been revoked. |
+// kCRLSetError: if an error occurs in processing. |
+// kCRLSetOk: if no element in the chain is known to have been revoked. |
+CRLSetResult CheckRevocationWithCRLSet(CERTCertList* cert_list, |
+ CERTCertificate* root, |
+ CRLSet* crl_set) { |
+ std::vector<CERTCertificate*> certs; |
+ for (CERTCertListNode* node = CERT_LIST_HEAD(cert_list); |
+ !CERT_LIST_END(node, cert_list); |
+ node = CERT_LIST_NEXT(node)) { |
+ certs.push_back(node->cert); |
+ } |
+ certs.push_back(root); |
+ |
+ CERTCertificate* prev = NULL; |
+ for (std::vector<CERTCertificate*>::iterator i = certs.begin(); |
+ i != certs.end(); ++i) { |
+ CERTCertificate* cert = *i; |
+ CERTCertificate* child = prev; |
+ prev = cert; |
+ if (child == NULL) |
+ continue; |
+ |
+ base::StringPiece der(reinterpret_cast<char*>(cert->derCert.data), |
+ cert->derCert.len); |
+ |
+ base::StringPiece spki; |
+ if (!asn1::ExtractSPKIFromDERCert(der, &spki)) { |
+ NOTREACHED(); |
+ return kCRLSetError; |
+ } |
+ |
+ std::string serial_number( |
+ reinterpret_cast<char*>(child->serialNumber.data), |
+ child->serialNumber.len); |
+ |
+ CRLSet::Result result = crl_set->CheckCertificate(serial_number, spki); |
+ |
+ switch (result) { |
+ case CRLSet::REVOKED: |
+ return kCRLSetRevoked; |
+ case CRLSet::UNKNOWN: |
+ case CRLSet::GOOD: |
+ continue; |
+ default: |
+ NOTREACHED(); |
+ return kCRLSetError; |
+ } |
+ } |
+ |
+ return kCRLSetOk; |
+} |
+ |
void ParsePrincipal(CERTName* name, |
CertPrincipal* principal) { |
typedef char* (*CERTGetNameFunc)(CERTName* name); |
@@ -691,6 +754,7 @@ void X509Certificate::GetSubjectAltName( |
int X509Certificate::VerifyInternal(const std::string& hostname, |
int flags, |
+ CRLSet* crl_set, |
CertVerifyResult* verify_result) const { |
// Make sure that the hostname matches with the common name of the cert. |
SECStatus status = CERT_VerifyCertName(cert_handle_, hostname.c_str()); |
@@ -723,7 +787,34 @@ int X509Certificate::VerifyInternal(const std::string& hostname, |
// EV requires revocation checking. |
flags &= ~VERIFY_EV_CERT; |
} |
- status = PKIXVerifyCert(cert_handle_, check_revocation, NULL, 0, cvout); |
+ |
+ if (check_revocation && crl_set) { |
+ // We have a CRLSet so we build a chain without revocation checking in |
+ // order to try and check it ourselves. |
+ status = PKIXVerifyCert(cert_handle_, false /* no revocation checking */, |
+ NULL, 0, cvout); |
+ if (status == SECSuccess) { |
+ CRLSetResult crl_set_result = CheckRevocationWithCRLSet( |
+ cvout[cvout_cert_list_index].value.pointer.chain, |
+ cvout[cvout_trust_anchor_index].value.pointer.cert, |
+ crl_set); |
+ if (crl_set_result == kCRLSetError) { |
+ // An error occured during processing so we fall back to standard |
+ // revocation checking. |
+ status = PKIXVerifyCert(cert_handle_, check_revocation, NULL, 0, cvout); |
+ } else { |
+ DCHECK(crl_set_result == kCRLSetRevoked || crl_set_result == kCRLSetOk); |
+ if (crl_set_result == kCRLSetRevoked) { |
+ PORT_SetError(SEC_ERROR_REVOKED_CERTIFICATE); |
+ status = SECFailure; |
+ } |
+ } |
+ } |
+ } else { |
+ status = PKIXVerifyCert(cert_handle_, check_revocation, |
+ NULL, 0, cvout); |
+ } |
+ |
if (status != SECSuccess) { |
int err = PORT_GetError(); |
LOG(ERROR) << "CERT_PKIXVerifyCert for " << hostname |