| Index: net/cert/cert_verify_proc_nss.cc
 | 
| diff --git a/net/cert/cert_verify_proc_nss.cc b/net/cert/cert_verify_proc_nss.cc
 | 
| index ca8e8116f0ef521f61539386abbc59dd57da2f21..cad20b9fa95fcc789159eba4fc947107269f383a 100644
 | 
| --- a/net/cert/cert_verify_proc_nss.cc
 | 
| +++ b/net/cert/cert_verify_proc_nss.cc
 | 
| @@ -279,7 +279,7 @@ enum CRLSetResult {
 | 
|  //       that some EV sites would otherwise take the hit of an OCSP lookup for
 | 
|  //       no reason.
 | 
|  //   kCRLSetOk: otherwise.
 | 
| -CRLSetResult CheckRevocationWithCRLSet(CERTCertList* cert_list,
 | 
| +CRLSetResult CheckRevocationWithCRLSet(const CERTCertList* cert_list,
 | 
|                                         CERTCertificate* root,
 | 
|                                         CRLSet* crl_set) {
 | 
|    std::vector<CERTCertificate*> certs;
 | 
| @@ -352,6 +352,50 @@ CRLSetResult CheckRevocationWithCRLSet(CERTCertList* cert_list,
 | 
|    return kCRLSetOk;
 | 
|  }
 | 
|  
 | 
| +// Arguments for CheckChainRevocationWithCRLSet that are curried within the
 | 
| +// CERTChainVerifyCallback's isChainValidArg.
 | 
| +struct CheckChainRevocationArgs {
 | 
| +  // The CRLSet to evaluate against.
 | 
| +  CRLSet* crl_set = nullptr;
 | 
| +
 | 
| +  // The next callback to invoke, if the CRLSet does not report any errors.
 | 
| +  CERTChainVerifyCallback* next_callback = nullptr;
 | 
| +
 | 
| +  // Indicates that the application callback failure was due to a CRLSet
 | 
| +  // revocation, rather than due to |next_callback| rejecting it. This is
 | 
| +  // used to map the error back to the proper caller-visible error code.
 | 
| +  bool was_revoked = false;
 | 
| +};
 | 
| +
 | 
| +SECStatus CheckChainRevocationWithCRLSet(void* is_chain_valid_arg,
 | 
| +                                         const CERTCertList* current_chain,
 | 
| +                                         PRBool* chain_ok) {
 | 
| +  CHECK(is_chain_valid_arg);
 | 
| +
 | 
| +  CheckChainRevocationArgs* args =
 | 
| +      static_cast<CheckChainRevocationArgs*>(is_chain_valid_arg);
 | 
| +
 | 
| +  CRLSetResult crlset_result = kCRLSetUnknown;
 | 
| +  if (args->crl_set) {
 | 
| +    crlset_result =
 | 
| +        CheckRevocationWithCRLSet(current_chain, nullptr, args->crl_set);
 | 
| +  }
 | 
| +
 | 
| +  if (crlset_result == kCRLSetRevoked) {
 | 
| +    args->was_revoked = true;
 | 
| +    *chain_ok = PR_FALSE;
 | 
| +    return SECSuccess;
 | 
| +  }
 | 
| +  args->was_revoked = false;
 | 
| +
 | 
| +  *chain_ok = PR_TRUE;
 | 
| +  if (!args->next_callback || !args->next_callback->isChainValid)
 | 
| +    return SECSuccess;
 | 
| +
 | 
| +  return (*args->next_callback->isChainValid)(
 | 
| +      args->next_callback->isChainValidArg, current_chain, chain_ok);
 | 
| +}
 | 
| +
 | 
|  // Forward declarations.
 | 
|  SECStatus RetryPKIXVerifyCertWithWorkarounds(
 | 
|      CERTCertificate* cert_handle, int num_policy_oids,
 | 
| @@ -825,6 +869,22 @@ int CertVerifyProcNSS::VerifyInternalImpl(
 | 
|      verify_result->cert_status |= CERT_STATUS_COMMON_NAME_INVALID;
 | 
|    }
 | 
|  
 | 
| +  // Setup a callback to call into CheckChainRevocationWithCRLSet with the
 | 
| +  // current CRLSet. If the CRLSet revokes a given chain, |was_revoked|
 | 
| +  // will be set to true.
 | 
| +  // The same callback and args are used for every invocation of
 | 
| +  // PKIXVerifyCert, as CheckChainRevocationWithCRLSet handles resetting
 | 
| +  // |was_revoked| as necessary.
 | 
| +  CheckChainRevocationArgs check_chain_revocation_args;
 | 
| +  check_chain_revocation_args.crl_set = crl_set;
 | 
| +  check_chain_revocation_args.next_callback = chain_verify_callback;
 | 
| +
 | 
| +  CERTChainVerifyCallback crlset_callback;
 | 
| +  memset(&crlset_callback, 0, sizeof(crlset_callback));
 | 
| +  crlset_callback.isChainValid = &CheckChainRevocationWithCRLSet;
 | 
| +  crlset_callback.isChainValidArg =
 | 
| +      static_cast<void*>(&check_chain_revocation_args);
 | 
| +
 | 
|    // Make sure that the cert is valid now.
 | 
|    SECCertTimeValidity validity = CERT_CheckCertValidTimes(
 | 
|        cert_handle, PR_Now(), PR_TRUE);
 | 
| @@ -862,15 +922,9 @@ int CertVerifyProcNSS::VerifyInternalImpl(
 | 
|          CertificateListToCERTCertList(additional_trust_anchors));
 | 
|    }
 | 
|  
 | 
| -  SECStatus status = PKIXVerifyCert(cert_handle,
 | 
| -                                    check_revocation,
 | 
| -                                    false,
 | 
| -                                    cert_io_enabled,
 | 
| -                                    NULL,
 | 
| -                                    0,
 | 
| -                                    trust_anchors.get(),
 | 
| -                                    chain_verify_callback,
 | 
| -                                    cvout);
 | 
| +  SECStatus status =
 | 
| +      PKIXVerifyCert(cert_handle, check_revocation, false, cert_io_enabled,
 | 
| +                     NULL, 0, trust_anchors.get(), &crlset_callback, cvout);
 | 
|  
 | 
|    if (status == SECSuccess &&
 | 
|        (flags & CertVerifier::VERIFY_REV_CHECKING_REQUIRED_LOCAL_ANCHORS) &&
 | 
| @@ -880,15 +934,8 @@ int CertVerifyProcNSS::VerifyInternalImpl(
 | 
|      // NSS tests for that feature.
 | 
|      scoped_cvout.Clear();
 | 
|      verify_result->cert_status |= CERT_STATUS_REV_CHECKING_ENABLED;
 | 
| -    status = PKIXVerifyCert(cert_handle,
 | 
| -                            true,
 | 
| -                            true,
 | 
| -                            cert_io_enabled,
 | 
| -                            NULL,
 | 
| -                            0,
 | 
| -                            trust_anchors.get(),
 | 
| -                            chain_verify_callback,
 | 
| -                            cvout);
 | 
| +    status = PKIXVerifyCert(cert_handle, true, true, cert_io_enabled, NULL, 0,
 | 
| +                            trust_anchors.get(), &crlset_callback, cvout);
 | 
|    }
 | 
|  
 | 
|    if (status == SECSuccess) {
 | 
| @@ -910,13 +957,25 @@ int CertVerifyProcNSS::VerifyInternalImpl(
 | 
|  
 | 
|    CRLSetResult crl_set_result = kCRLSetUnknown;
 | 
|    if (crl_set) {
 | 
| -    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 == kCRLSetRevoked) {
 | 
| +    if (status == SECSuccess) {
 | 
| +      // Reverify the returned chain; NSS should have already called
 | 
| +      // CheckChainRevocationWithCRLSet prior to returning, but given the
 | 
| +      // edge cases (self-signed certs that are trusted; cached chains;
 | 
| +      // unreadable code), this is more about defense in depth than
 | 
| +      // functional necessity.
 | 
| +      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 == kCRLSetRevoked) {
 | 
| +        PORT_SetError(SEC_ERROR_REVOKED_CERTIFICATE);
 | 
| +        status = SECFailure;
 | 
| +      }
 | 
| +    } else if (PORT_GetError() == SEC_ERROR_APPLICATION_CALLBACK_ERROR &&
 | 
| +               check_chain_revocation_args.was_revoked) {
 | 
| +      // If a CRLSet was supplied, and the error was an application callback
 | 
| +      // error, then it was directed through the CRLSet code and that
 | 
| +      // particular chain was revoked.
 | 
|        PORT_SetError(SEC_ERROR_REVOKED_CERTIFICATE);
 | 
| -      status = SECFailure;
 | 
|      }
 | 
|    }
 | 
|  
 | 
| @@ -949,14 +1008,8 @@ int CertVerifyProcNSS::VerifyInternalImpl(
 | 
|      if (check_revocation)
 | 
|        verify_result->cert_status |= CERT_STATUS_REV_CHECKING_ENABLED;
 | 
|  
 | 
| -    if (VerifyEV(cert_handle,
 | 
| -                 flags,
 | 
| -                 crl_set,
 | 
| -                 check_revocation,
 | 
| -                 metadata,
 | 
| -                 ev_policy_oid,
 | 
| -                 trust_anchors.get(),
 | 
| -                 chain_verify_callback)) {
 | 
| +    if (VerifyEV(cert_handle, flags, crl_set, check_revocation, metadata,
 | 
| +                 ev_policy_oid, trust_anchors.get(), &crlset_callback)) {
 | 
|        verify_result->cert_status |= CERT_STATUS_IS_EV;
 | 
|      }
 | 
|    }
 | 
| 
 |