security/certverifier/CertVerifier.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/security/certverifier/CertVerifier.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,1017 @@
     1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* vim: set ts=8 sts=2 et sw=2 tw=80: */
     1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +#include "CertVerifier.h"
    1.11 +
    1.12 +#include <stdint.h>
    1.13 +
    1.14 +#include "pkix/pkix.h"
    1.15 +#include "ExtendedValidation.h"
    1.16 +#include "NSSCertDBTrustDomain.h"
    1.17 +#include "PublicKeyPinningService.h"
    1.18 +#include "cert.h"
    1.19 +#include "ocsp.h"
    1.20 +#include "secerr.h"
    1.21 +#include "pk11pub.h"
    1.22 +#include "prerror.h"
    1.23 +#include "sslerr.h"
    1.24 +
    1.25 +// ScopedXXX in this file are mozilla::pkix::ScopedXXX, not
    1.26 +// mozilla::ScopedXXX.
    1.27 +using namespace mozilla::pkix;
    1.28 +using namespace mozilla::psm;
    1.29 +
    1.30 +#ifdef PR_LOGGING
    1.31 +PRLogModuleInfo* gCertVerifierLog = nullptr;
    1.32 +#endif
    1.33 +
    1.34 +namespace mozilla { namespace psm {
    1.35 +
    1.36 +const CertVerifier::Flags CertVerifier::FLAG_LOCAL_ONLY = 1;
    1.37 +const CertVerifier::Flags CertVerifier::FLAG_MUST_BE_EV = 2;
    1.38 +
    1.39 +CertVerifier::CertVerifier(implementation_config ic,
    1.40 +#ifndef NSS_NO_LIBPKIX
    1.41 +                           missing_cert_download_config mcdc,
    1.42 +                           crl_download_config cdc,
    1.43 +#endif
    1.44 +                           ocsp_download_config odc,
    1.45 +                           ocsp_strict_config osc,
    1.46 +                           ocsp_get_config ogc,
    1.47 +                           pinning_enforcement_config pel)
    1.48 +  : mImplementation(ic)
    1.49 +#ifndef NSS_NO_LIBPKIX
    1.50 +  , mMissingCertDownloadEnabled(mcdc == missing_cert_download_on)
    1.51 +  , mCRLDownloadEnabled(cdc == crl_download_allowed)
    1.52 +#endif
    1.53 +  , mOCSPDownloadEnabled(odc == ocsp_on)
    1.54 +  , mOCSPStrict(osc == ocsp_strict)
    1.55 +  , mOCSPGETEnabled(ogc == ocsp_get_enabled)
    1.56 +  , mPinningEnforcementLevel(pel)
    1.57 +{
    1.58 +}
    1.59 +
    1.60 +CertVerifier::~CertVerifier()
    1.61 +{
    1.62 +}
    1.63 +
    1.64 +void
    1.65 +InitCertVerifierLog()
    1.66 +{
    1.67 +#ifdef PR_LOGGING
    1.68 +  if (!gCertVerifierLog) {
    1.69 +    gCertVerifierLog = PR_NewLogModule("certverifier");
    1.70 +  }
    1.71 +#endif
    1.72 +}
    1.73 +
    1.74 +// Once we migrate to mozilla::pkix or change the overridable error
    1.75 +// logic this will become unnecesary.
    1.76 +static SECStatus
    1.77 +insertErrorIntoVerifyLog(CERTCertificate* cert, const PRErrorCode err,
    1.78 +                         CERTVerifyLog* verifyLog){
    1.79 +  CERTVerifyLogNode* node;
    1.80 +  node = (CERTVerifyLogNode *)PORT_ArenaAlloc(verifyLog->arena,
    1.81 +                                              sizeof(CERTVerifyLogNode));
    1.82 +  if (!node) {
    1.83 +    PR_SetError(PR_UNKNOWN_ERROR, 0);
    1.84 +    return SECFailure;
    1.85 +  }
    1.86 +  node->cert = CERT_DupCertificate(cert);
    1.87 +  node->error = err;
    1.88 +  node->depth = 0;
    1.89 +  node->arg = nullptr;
    1.90 +  //and at to head!
    1.91 +  node->prev = nullptr;
    1.92 +  node->next = verifyLog->head;
    1.93 +  if (verifyLog->head) {
    1.94 +    verifyLog->head->prev = node;
    1.95 +  }
    1.96 +  verifyLog->head = node;
    1.97 +  if (!verifyLog->tail) {
    1.98 +    verifyLog->tail = node;
    1.99 +  }
   1.100 +  verifyLog->count++;
   1.101 +
   1.102 +  return SECSuccess;
   1.103 +}
   1.104 +
   1.105 +SECStatus
   1.106 +IsCertBuiltInRoot(CERTCertificate* cert, bool& result) {
   1.107 +  result = false;
   1.108 +  ScopedPtr<PK11SlotList, PK11_FreeSlotList> slots;
   1.109 +  slots = PK11_GetAllSlotsForCert(cert, nullptr);
   1.110 +  if (!slots) {
   1.111 +    if (PORT_GetError() == SEC_ERROR_NO_TOKEN) {
   1.112 +      // no list
   1.113 +      return SECSuccess;
   1.114 +    }
   1.115 +    return SECFailure;
   1.116 +  }
   1.117 +  for (PK11SlotListElement* le = slots->head; le; le = le->next) {
   1.118 +    char* token = PK11_GetTokenName(le->slot);
   1.119 +    PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
   1.120 +           ("BuiltInRoot? subject=%s token=%s",cert->subjectName, token));
   1.121 +    if (strcmp("Builtin Object Token", token) == 0) {
   1.122 +      result = true;
   1.123 +      return SECSuccess;
   1.124 +    }
   1.125 +  }
   1.126 +  return SECSuccess;
   1.127 +}
   1.128 +
   1.129 +struct ChainValidationCallbackState
   1.130 +{
   1.131 +  const char* hostname;
   1.132 +  const CertVerifier::pinning_enforcement_config pinningEnforcementLevel;
   1.133 +  const SECCertificateUsage usage;
   1.134 +  const PRTime time;
   1.135 +};
   1.136 +
   1.137 +SECStatus chainValidationCallback(void* state, const CERTCertList* certList,
   1.138 +                                  PRBool* chainOK)
   1.139 +{
   1.140 +  ChainValidationCallbackState* callbackState =
   1.141 +    reinterpret_cast<ChainValidationCallbackState*>(state);
   1.142 +
   1.143 +  *chainOK = PR_FALSE;
   1.144 +
   1.145 +  PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
   1.146 +         ("verifycert: Inside the Callback \n"));
   1.147 +
   1.148 +  // On sanity failure we fail closed.
   1.149 +  if (!certList) {
   1.150 +    PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
   1.151 +           ("verifycert: Short circuit, callback, sanity check failed \n"));
   1.152 +    PR_SetError(PR_INVALID_STATE_ERROR, 0);
   1.153 +    return SECFailure;
   1.154 +  }
   1.155 +  if (!callbackState) {
   1.156 +    PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
   1.157 +           ("verifycert: Short circuit, callback, no state! \n"));
   1.158 +    PR_SetError(PR_INVALID_STATE_ERROR, 0);
   1.159 +    return SECFailure;
   1.160 +  }
   1.161 +
   1.162 +  if (callbackState->usage != certificateUsageSSLServer ||
   1.163 +      callbackState->pinningEnforcementLevel == CertVerifier::pinningDisabled) {
   1.164 +    PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
   1.165 +           ("verifycert: Callback shortcut pel=%d \n",
   1.166 +            callbackState->pinningEnforcementLevel));
   1.167 +    *chainOK = PR_TRUE;
   1.168 +    return SECSuccess;
   1.169 +  }
   1.170 +
   1.171 +  for (CERTCertListNode* node = CERT_LIST_HEAD(certList);
   1.172 +       !CERT_LIST_END(node, certList);
   1.173 +       node = CERT_LIST_NEXT(node)) {
   1.174 +    CERTCertificate* currentCert = node->cert;
   1.175 +    if (CERT_LIST_END(CERT_LIST_NEXT(node), certList)) {
   1.176 +      bool isBuiltInRoot = false;
   1.177 +      SECStatus srv = IsCertBuiltInRoot(currentCert, isBuiltInRoot);
   1.178 +      if (srv != SECSuccess) {
   1.179 +        PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, ("Is BuiltInRoot failure"));
   1.180 +        return srv;
   1.181 +      }
   1.182 +      // If desired, the user can enable "allow user CA MITM mode", in which
   1.183 +      // case key pinning is not enforced for certificates that chain to trust
   1.184 +      // anchors that are not in Mozilla's root program
   1.185 +      if (!isBuiltInRoot &&
   1.186 +          (callbackState->pinningEnforcementLevel ==
   1.187 +             CertVerifier::pinningAllowUserCAMITM)) {
   1.188 +        *chainOK = PR_TRUE;
   1.189 +        return SECSuccess;
   1.190 +      }
   1.191 +    }
   1.192 +  }
   1.193 +
   1.194 +  const bool enforceTestMode = (callbackState->pinningEnforcementLevel ==
   1.195 +                                CertVerifier::pinningEnforceTestMode);
   1.196 +  *chainOK = PublicKeyPinningService::
   1.197 +    ChainHasValidPins(certList, callbackState->hostname, callbackState->time,
   1.198 +                      enforceTestMode);
   1.199 +
   1.200 +  return SECSuccess;
   1.201 +}
   1.202 +
   1.203 +// This always returns secfailure but its objective is to replate
   1.204 +// the PR_Error
   1.205 +static void
   1.206 +tryWorsenPRErrorInCallback(CERTCertificate* cert,
   1.207 +                           ChainValidationCallbackState* callbackState) {
   1.208 +  ScopedCERTCertificate certCopy(CERT_DupCertificate(cert));
   1.209 +  if (!certCopy) {
   1.210 +    return;
   1.211 +  }
   1.212 +  ScopedCERTCertList certList(CERT_NewCertList());
   1.213 +  if (!certList) {
   1.214 +    return;
   1.215 +  }
   1.216 +  SECStatus srv = CERT_AddCertToListTail(certList.get(), certCopy.get());
   1.217 +  if (srv != SECSuccess) {
   1.218 +    return;
   1.219 +  }
   1.220 +  certCopy.release(); // now owned by certList
   1.221 +  PRBool chainOK = false;
   1.222 +  srv = chainValidationCallback(&callbackState, certList.get(), &chainOK);
   1.223 +  if (srv != SECSuccess) {
   1.224 +    return;
   1.225 +  }
   1.226 +  if (!chainOK) {
   1.227 +    PR_SetError(SEC_ERROR_APPLICATION_CALLBACK_ERROR, 0); // same as libpkix
   1.228 +    return ;
   1.229 +  }
   1.230 +  return; // no change in PR_error
   1.231 +}
   1.232 +
   1.233 +static SECStatus
   1.234 +ClassicVerifyCert(CERTCertificate* cert,
   1.235 +                  const SECCertificateUsage usage,
   1.236 +                  const PRTime time,
   1.237 +                  void* pinArg,
   1.238 +                  ChainValidationCallbackState* callbackState,
   1.239 +                  /*optional out*/ ScopedCERTCertList* validationChain,
   1.240 +                  /*optional out*/ CERTVerifyLog* verifyLog)
   1.241 +{
   1.242 +  SECStatus rv;
   1.243 +  SECCertUsage enumUsage;
   1.244 +  switch (usage) {
   1.245 +    case certificateUsageSSLClient:
   1.246 +      enumUsage = certUsageSSLClient;
   1.247 +      break;
   1.248 +    case certificateUsageSSLServer:
   1.249 +      enumUsage = certUsageSSLServer;
   1.250 +      break;
   1.251 +    case certificateUsageSSLCA:
   1.252 +      enumUsage = certUsageSSLCA;
   1.253 +      break;
   1.254 +    case certificateUsageEmailSigner:
   1.255 +      enumUsage = certUsageEmailSigner;
   1.256 +      break;
   1.257 +    case certificateUsageEmailRecipient:
   1.258 +      enumUsage = certUsageEmailRecipient;
   1.259 +      break;
   1.260 +    case certificateUsageObjectSigner:
   1.261 +      enumUsage = certUsageObjectSigner;
   1.262 +      break;
   1.263 +    case certificateUsageVerifyCA:
   1.264 +      enumUsage = certUsageVerifyCA;
   1.265 +      break;
   1.266 +    case certificateUsageStatusResponder:
   1.267 +      enumUsage = certUsageStatusResponder;
   1.268 +      break;
   1.269 +    default:
   1.270 +      PR_NOT_REACHED("unexpected usage");
   1.271 +      PORT_SetError(SEC_ERROR_INVALID_ARGS);
   1.272 +      return SECFailure;
   1.273 +  }
   1.274 +  if (usage == certificateUsageSSLServer) {
   1.275 +    // SSL server cert verification has always used CERT_VerifyCert, so we
   1.276 +    // continue to use it for SSL cert verification to minimize the risk of
   1.277 +    // there being any differnce in results between CERT_VerifyCert and
   1.278 +    // CERT_VerifyCertificate.
   1.279 +    rv = CERT_VerifyCert(CERT_GetDefaultCertDB(), cert, true,
   1.280 +                         certUsageSSLServer, time, pinArg, verifyLog);
   1.281 +  } else {
   1.282 +    rv = CERT_VerifyCertificate(CERT_GetDefaultCertDB(), cert, true,
   1.283 +                                usage, time, pinArg, verifyLog, nullptr);
   1.284 +  }
   1.285 +
   1.286 +  if (rv == SECSuccess &&
   1.287 +      (validationChain || usage == certificateUsageSSLServer)) {
   1.288 +    PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
   1.289 +           ("VerifyCert: getting chain in 'classic' \n"));
   1.290 +    ScopedCERTCertList certChain(CERT_GetCertChainFromCert(cert, time,
   1.291 +                                                           enumUsage));
   1.292 +    if (!certChain) {
   1.293 +      return SECFailure;
   1.294 +    }
   1.295 +    if (usage == certificateUsageSSLServer) {
   1.296 +      PRBool chainOK = PR_FALSE;
   1.297 +      SECStatus srv = chainValidationCallback(callbackState, certChain.get(),
   1.298 +                                              &chainOK);
   1.299 +      if (srv != SECSuccess) {
   1.300 +        return srv;
   1.301 +      }
   1.302 +      if (chainOK != PR_TRUE) {
   1.303 +        if (verifyLog) {
   1.304 +          insertErrorIntoVerifyLog(cert,
   1.305 +                                   SEC_ERROR_APPLICATION_CALLBACK_ERROR,
   1.306 +                                   verifyLog);
   1.307 +        }
   1.308 +        PR_SetError(SEC_ERROR_APPLICATION_CALLBACK_ERROR, 0); // same as libpkix
   1.309 +        return SECFailure;
   1.310 +      }
   1.311 +    }
   1.312 +
   1.313 +    // If there is an error we may need to worsen to error to be a pinning failure
   1.314 +    if (rv != SECSuccess && usage == certificateUsageSSLServer) {
   1.315 +      tryWorsenPRErrorInCallback(cert, callbackState);
   1.316 +    }
   1.317 +
   1.318 +    if (rv == SECSuccess && validationChain) {
   1.319 +      *validationChain = certChain.release();
   1.320 +    }
   1.321 +  }
   1.322 +
   1.323 +  return rv;
   1.324 +}
   1.325 +
   1.326 +#ifndef NSS_NO_LIBPKIX
   1.327 +static void
   1.328 +destroyCertListThatShouldNotExist(CERTCertList** certChain)
   1.329 +{
   1.330 +  PR_ASSERT(certChain);
   1.331 +  PR_ASSERT(!*certChain);
   1.332 +  if (certChain && *certChain) {
   1.333 +    // There SHOULD not be a validation chain on failure, asserion here for
   1.334 +    // the debug builds AND a fallback for production builds
   1.335 +    CERT_DestroyCertList(*certChain);
   1.336 +    *certChain = nullptr;
   1.337 +  }
   1.338 +}
   1.339 +#endif
   1.340 +
   1.341 +static SECStatus
   1.342 +BuildCertChainForOneKeyUsage(TrustDomain& trustDomain, CERTCertificate* cert,
   1.343 +                             PRTime time, KeyUsage ku1, KeyUsage ku2,
   1.344 +                             KeyUsage ku3, SECOidTag eku,
   1.345 +                             SECOidTag requiredPolicy,
   1.346 +                             const SECItem* stapledOCSPResponse,
   1.347 +                             ScopedCERTCertList& builtChain)
   1.348 +{
   1.349 +  SECStatus rv = BuildCertChain(trustDomain, cert, time, MustBeEndEntity,
   1.350 +                                ku1, eku, requiredPolicy, stapledOCSPResponse,
   1.351 +                                builtChain);
   1.352 +  if (rv != SECSuccess && PR_GetError() == SEC_ERROR_INADEQUATE_KEY_USAGE) {
   1.353 +    rv = BuildCertChain(trustDomain, cert, time, MustBeEndEntity,
   1.354 +                        ku2, eku, requiredPolicy, stapledOCSPResponse,
   1.355 +                        builtChain);
   1.356 +    if (rv != SECSuccess && PR_GetError() == SEC_ERROR_INADEQUATE_KEY_USAGE) {
   1.357 +      rv = BuildCertChain(trustDomain, cert, time, MustBeEndEntity,
   1.358 +                          ku3, eku, requiredPolicy, stapledOCSPResponse,
   1.359 +                          builtChain);
   1.360 +      if (rv != SECSuccess) {
   1.361 +        PR_SetError(SEC_ERROR_INADEQUATE_KEY_USAGE, 0);
   1.362 +      }
   1.363 +    }
   1.364 +  }
   1.365 +  return rv;
   1.366 +}
   1.367 +
   1.368 +SECStatus
   1.369 +CertVerifier::MozillaPKIXVerifyCert(
   1.370 +                   CERTCertificate* cert,
   1.371 +                   const SECCertificateUsage usage,
   1.372 +                   const PRTime time,
   1.373 +                   void* pinArg,
   1.374 +                   const Flags flags,
   1.375 +                   ChainValidationCallbackState* callbackState,
   1.376 +      /*optional*/ const SECItem* stapledOCSPResponse,
   1.377 +  /*optional out*/ mozilla::pkix::ScopedCERTCertList* validationChain,
   1.378 +  /*optional out*/ SECOidTag* evOidPolicy)
   1.379 +{
   1.380 +  PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, ("Top of MozillaPKIXVerifyCert\n"));
   1.381 +
   1.382 +  PR_ASSERT(cert);
   1.383 +  PR_ASSERT(usage == certificateUsageSSLServer || !(flags & FLAG_MUST_BE_EV));
   1.384 +
   1.385 +  if (validationChain) {
   1.386 +    *validationChain = nullptr;
   1.387 +  }
   1.388 +  if (evOidPolicy) {
   1.389 +    *evOidPolicy = SEC_OID_UNKNOWN;
   1.390 +  }
   1.391 +
   1.392 +  if (!cert ||
   1.393 +      (usage != certificateUsageSSLServer && (flags & FLAG_MUST_BE_EV))) {
   1.394 +    PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
   1.395 +    return SECFailure;
   1.396 +  }
   1.397 +
   1.398 +  CERTChainVerifyCallback callbackContainer;
   1.399 +  callbackContainer.isChainValid = chainValidationCallback;
   1.400 +  callbackContainer.isChainValidArg = callbackState;
   1.401 +
   1.402 +  NSSCertDBTrustDomain::OCSPFetching ocspFetching
   1.403 +    = !mOCSPDownloadEnabled ||
   1.404 +      (flags & FLAG_LOCAL_ONLY) ? NSSCertDBTrustDomain::NeverFetchOCSP
   1.405 +    : !mOCSPStrict              ? NSSCertDBTrustDomain::FetchOCSPForDVSoftFail
   1.406 +                                : NSSCertDBTrustDomain::FetchOCSPForDVHardFail;
   1.407 +
   1.408 +  SECStatus rv;
   1.409 +
   1.410 +  // TODO(bug 970750): anyExtendedKeyUsage
   1.411 +  // TODO: encipherOnly/decipherOnly
   1.412 +  // S/MIME Key Usage: http://tools.ietf.org/html/rfc3850#section-4.4.2
   1.413 +  // S/MIME EKU:       http://tools.ietf.org/html/rfc3850#section-4.4.4
   1.414 +
   1.415 +  // TODO(bug 915931): Pass in stapled OCSP response in all calls to
   1.416 +  //                   BuildCertChain.
   1.417 +
   1.418 +  mozilla::pkix::ScopedCERTCertList builtChain;
   1.419 +  switch (usage) {
   1.420 +    case certificateUsageSSLClient: {
   1.421 +      // XXX: We don't really have a trust bit for SSL client authentication so
   1.422 +      // just use trustEmail as it is the closest alternative.
   1.423 +      NSSCertDBTrustDomain trustDomain(trustEmail, ocspFetching, mOCSPCache,
   1.424 +                                       pinArg);
   1.425 +      rv = BuildCertChain(trustDomain, cert, time, MustBeEndEntity,
   1.426 +                          KeyUsage::digitalSignature,
   1.427 +                          SEC_OID_EXT_KEY_USAGE_CLIENT_AUTH,
   1.428 +                          SEC_OID_X509_ANY_POLICY,
   1.429 +                          stapledOCSPResponse, builtChain);
   1.430 +      break;
   1.431 +    }
   1.432 +
   1.433 +    case certificateUsageSSLServer: {
   1.434 +      // TODO: When verifying a certificate in an SSL handshake, we should
   1.435 +      // restrict the acceptable key usage based on the key exchange method
   1.436 +      // chosen by the server.
   1.437 +
   1.438 +#ifndef MOZ_NO_EV_CERTS
   1.439 +      // Try to validate for EV first.
   1.440 +      SECOidTag evPolicy = SEC_OID_UNKNOWN;
   1.441 +      rv = GetFirstEVPolicy(cert, evPolicy);
   1.442 +      if (rv == SECSuccess && evPolicy != SEC_OID_UNKNOWN) {
   1.443 +        NSSCertDBTrustDomain
   1.444 +          trustDomain(trustSSL,
   1.445 +                      ocspFetching == NSSCertDBTrustDomain::NeverFetchOCSP
   1.446 +                        ? NSSCertDBTrustDomain::LocalOnlyOCSPForEV
   1.447 +                        : NSSCertDBTrustDomain::FetchOCSPForEV,
   1.448 +                      mOCSPCache, pinArg, &callbackContainer);
   1.449 +        rv = BuildCertChainForOneKeyUsage(trustDomain, cert, time,
   1.450 +                                          KeyUsage::digitalSignature, // ECDHE/DHE
   1.451 +                                          KeyUsage::keyEncipherment, // RSA
   1.452 +                                          KeyUsage::keyAgreement,    // (EC)DH
   1.453 +                                          SEC_OID_EXT_KEY_USAGE_SERVER_AUTH,
   1.454 +                                          evPolicy, stapledOCSPResponse,
   1.455 +                                          builtChain);
   1.456 +        if (rv == SECSuccess) {
   1.457 +          if (evOidPolicy) {
   1.458 +            *evOidPolicy = evPolicy;
   1.459 +          }
   1.460 +          break;
   1.461 +        }
   1.462 +        builtChain = nullptr; // clear built chain, just in case.
   1.463 +      }
   1.464 +#endif
   1.465 +
   1.466 +      if (flags & FLAG_MUST_BE_EV) {
   1.467 +        PR_SetError(SEC_ERROR_POLICY_VALIDATION_FAILED, 0);
   1.468 +        rv = SECFailure;
   1.469 +        break;
   1.470 +      }
   1.471 +
   1.472 +      // Now try non-EV.
   1.473 +      NSSCertDBTrustDomain trustDomain(trustSSL, ocspFetching, mOCSPCache,
   1.474 +                                       pinArg, &callbackContainer);
   1.475 +      rv = BuildCertChainForOneKeyUsage(trustDomain, cert, time,
   1.476 +                                        KeyUsage::digitalSignature, // (EC)DHE
   1.477 +                                        KeyUsage::keyEncipherment, // RSA
   1.478 +                                        KeyUsage::keyAgreement, // (EC)DH
   1.479 +                                        SEC_OID_EXT_KEY_USAGE_SERVER_AUTH,
   1.480 +                                        SEC_OID_X509_ANY_POLICY,
   1.481 +                                        stapledOCSPResponse, builtChain);
   1.482 +      break;
   1.483 +    }
   1.484 +
   1.485 +    case certificateUsageSSLCA: {
   1.486 +      NSSCertDBTrustDomain trustDomain(trustSSL, ocspFetching, mOCSPCache,
   1.487 +                                       pinArg);
   1.488 +      rv = BuildCertChain(trustDomain, cert, time, MustBeCA,
   1.489 +                          KeyUsage::keyCertSign,
   1.490 +                          SEC_OID_EXT_KEY_USAGE_SERVER_AUTH,
   1.491 +                          SEC_OID_X509_ANY_POLICY,
   1.492 +                          stapledOCSPResponse, builtChain);
   1.493 +      break;
   1.494 +    }
   1.495 +
   1.496 +    case certificateUsageEmailSigner: {
   1.497 +      NSSCertDBTrustDomain trustDomain(trustEmail, ocspFetching, mOCSPCache,
   1.498 +                                       pinArg);
   1.499 +      rv = BuildCertChain(trustDomain, cert, time, MustBeEndEntity,
   1.500 +                          KeyUsage::digitalSignature,
   1.501 +                          SEC_OID_EXT_KEY_USAGE_EMAIL_PROTECT,
   1.502 +                          SEC_OID_X509_ANY_POLICY,
   1.503 +                          stapledOCSPResponse, builtChain);
   1.504 +      break;
   1.505 +    }
   1.506 +
   1.507 +    case certificateUsageEmailRecipient: {
   1.508 +      // TODO: The higher level S/MIME processing should pass in which key
   1.509 +      // usage it is trying to verify for, and base its algorithm choices
   1.510 +      // based on the result of the verification(s).
   1.511 +      NSSCertDBTrustDomain trustDomain(trustEmail, ocspFetching, mOCSPCache,
   1.512 +                                       pinArg);
   1.513 +      rv = BuildCertChain(trustDomain, cert, time, MustBeEndEntity,
   1.514 +                          KeyUsage::keyEncipherment, // RSA
   1.515 +                          SEC_OID_EXT_KEY_USAGE_EMAIL_PROTECT,
   1.516 +                          SEC_OID_X509_ANY_POLICY,
   1.517 +                          stapledOCSPResponse, builtChain);
   1.518 +      if (rv != SECSuccess && PR_GetError() == SEC_ERROR_INADEQUATE_KEY_USAGE) {
   1.519 +        rv = BuildCertChain(trustDomain, cert, time, MustBeEndEntity,
   1.520 +                            KeyUsage::keyAgreement, // ECDH/DH
   1.521 +                            SEC_OID_EXT_KEY_USAGE_EMAIL_PROTECT,
   1.522 +                            SEC_OID_X509_ANY_POLICY,
   1.523 +                            stapledOCSPResponse, builtChain);
   1.524 +      }
   1.525 +      break;
   1.526 +    }
   1.527 +
   1.528 +    case certificateUsageObjectSigner: {
   1.529 +      NSSCertDBTrustDomain trustDomain(trustObjectSigning, ocspFetching,
   1.530 +                                       mOCSPCache, pinArg);
   1.531 +      rv = BuildCertChain(trustDomain, cert, time, MustBeEndEntity,
   1.532 +                          KeyUsage::digitalSignature,
   1.533 +                          SEC_OID_EXT_KEY_USAGE_CODE_SIGN,
   1.534 +                          SEC_OID_X509_ANY_POLICY,
   1.535 +                          stapledOCSPResponse, builtChain);
   1.536 +      break;
   1.537 +    }
   1.538 +
   1.539 +    case certificateUsageVerifyCA:
   1.540 +    case certificateUsageStatusResponder: {
   1.541 +      // XXX This is a pretty useless way to verify a certificate. It is used
   1.542 +      // by the implementation of window.crypto.importCertificates and in the
   1.543 +      // certificate viewer UI. Because we don't know what trust bit is
   1.544 +      // interesting, we just try them all.
   1.545 +      mozilla::pkix::EndEntityOrCA endEntityOrCA;
   1.546 +      mozilla::pkix::KeyUsage keyUsage;
   1.547 +      SECOidTag eku;
   1.548 +      if (usage == certificateUsageVerifyCA) {
   1.549 +        endEntityOrCA = MustBeCA;
   1.550 +        keyUsage = KeyUsage::keyCertSign;
   1.551 +        eku = SEC_OID_UNKNOWN;
   1.552 +      } else {
   1.553 +        endEntityOrCA = MustBeEndEntity;
   1.554 +        keyUsage = KeyUsage::digitalSignature;
   1.555 +        eku = SEC_OID_OCSP_RESPONDER;
   1.556 +      }
   1.557 +
   1.558 +      NSSCertDBTrustDomain sslTrust(trustSSL, ocspFetching, mOCSPCache,
   1.559 +                                    pinArg);
   1.560 +      rv = BuildCertChain(sslTrust, cert, time, endEntityOrCA,
   1.561 +                          keyUsage, eku, SEC_OID_X509_ANY_POLICY,
   1.562 +                          stapledOCSPResponse, builtChain);
   1.563 +      if (rv == SECFailure && PR_GetError() == SEC_ERROR_UNKNOWN_ISSUER) {
   1.564 +        NSSCertDBTrustDomain emailTrust(trustEmail, ocspFetching, mOCSPCache,
   1.565 +                                        pinArg);
   1.566 +        rv = BuildCertChain(emailTrust, cert, time, endEntityOrCA, keyUsage,
   1.567 +                            eku, SEC_OID_X509_ANY_POLICY,
   1.568 +                            stapledOCSPResponse, builtChain);
   1.569 +        if (rv == SECFailure && PR_GetError() == SEC_ERROR_UNKNOWN_ISSUER) {
   1.570 +          NSSCertDBTrustDomain objectSigningTrust(trustObjectSigning,
   1.571 +                                                  ocspFetching, mOCSPCache,
   1.572 +                                                  pinArg);
   1.573 +          rv = BuildCertChain(objectSigningTrust, cert, time, endEntityOrCA,
   1.574 +                              keyUsage, eku, SEC_OID_X509_ANY_POLICY,
   1.575 +                              stapledOCSPResponse, builtChain);
   1.576 +        }
   1.577 +      }
   1.578 +
   1.579 +      break;
   1.580 +    }
   1.581 +
   1.582 +    default:
   1.583 +      PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
   1.584 +      return SECFailure;
   1.585 +  }
   1.586 +
   1.587 +  // If there is an error we may need to worsen to error to be a pinning failure
   1.588 +  if (rv != SECSuccess && usage == certificateUsageSSLServer &&
   1.589 +      PR_GetError() != SEC_ERROR_APPLICATION_CALLBACK_ERROR) {
   1.590 +    tryWorsenPRErrorInCallback(cert, callbackState);
   1.591 +  }
   1.592 +
   1.593 +  if (validationChain && rv == SECSuccess) {
   1.594 +    *validationChain = builtChain.release();
   1.595 +  }
   1.596 +
   1.597 +  return rv;
   1.598 +}
   1.599 +
   1.600 +SECStatus
   1.601 +CertVerifier::VerifyCert(CERTCertificate* cert,
   1.602 +                         const SECCertificateUsage usage,
   1.603 +                         const PRTime time,
   1.604 +                         void* pinArg,
   1.605 +                         const char* hostname,
   1.606 +                         const Flags flags,
   1.607 +                         /*optional in*/ const SECItem* stapledOCSPResponse,
   1.608 +                         /*optional out*/ ScopedCERTCertList* validationChain,
   1.609 +                         /*optional out*/ SECOidTag* evOidPolicy,
   1.610 +                         /*optional out*/ CERTVerifyLog* verifyLog)
   1.611 +{
   1.612 +  ChainValidationCallbackState callbackState = { hostname,
   1.613 +                                                 mPinningEnforcementLevel,
   1.614 +                                                 usage,
   1.615 +                                                 time };
   1.616 +
   1.617 +  if (mImplementation == mozillapkix) {
   1.618 +    return MozillaPKIXVerifyCert(cert, usage, time, pinArg, flags,
   1.619 +                                 &callbackState, stapledOCSPResponse,
   1.620 +                                 validationChain, evOidPolicy);
   1.621 +  }
   1.622 +
   1.623 +  if (!cert)
   1.624 +  {
   1.625 +    PR_NOT_REACHED("Invalid arguments to CertVerifier::VerifyCert");
   1.626 +    PORT_SetError(SEC_ERROR_INVALID_ARGS);
   1.627 +    return SECFailure;
   1.628 +  }
   1.629 +  if (validationChain) {
   1.630 +    *validationChain = nullptr;
   1.631 +  }
   1.632 +  if (evOidPolicy) {
   1.633 +    *evOidPolicy = SEC_OID_UNKNOWN;
   1.634 +  }
   1.635 +
   1.636 +  switch(usage){
   1.637 +    case certificateUsageSSLClient:
   1.638 +    case certificateUsageSSLServer:
   1.639 +    case certificateUsageSSLCA:
   1.640 +    case certificateUsageEmailSigner:
   1.641 +    case certificateUsageEmailRecipient:
   1.642 +    case certificateUsageObjectSigner:
   1.643 +    case certificateUsageVerifyCA:
   1.644 +    case certificateUsageStatusResponder:
   1.645 +      break;
   1.646 +    default:
   1.647 +      PORT_SetError(SEC_ERROR_INVALID_ARGS);
   1.648 +      return SECFailure;
   1.649 +  }
   1.650 +
   1.651 +  if ((flags & FLAG_MUST_BE_EV) && usage != certificateUsageSSLServer) {
   1.652 +      PORT_SetError(SEC_ERROR_INVALID_ARGS);
   1.653 +      return SECFailure;
   1.654 +  }
   1.655 +
   1.656 +#ifndef NSS_NO_LIBPKIX
   1.657 +  ScopedCERTCertList trustAnchors;
   1.658 +  SECStatus rv;
   1.659 +  SECOidTag evPolicy = SEC_OID_UNKNOWN;
   1.660 +
   1.661 +  // Do EV checking only for sslserver usage
   1.662 +  if (usage == certificateUsageSSLServer) {
   1.663 +    SECStatus srv = GetFirstEVPolicy(cert, evPolicy);
   1.664 +    if (srv == SECSuccess) {
   1.665 +      if (evPolicy != SEC_OID_UNKNOWN) {
   1.666 +        trustAnchors = GetRootsForOid(evPolicy);
   1.667 +      }
   1.668 +      if (!trustAnchors) {
   1.669 +        return SECFailure;
   1.670 +      }
   1.671 +      // pkix ignores an empty trustanchors list and
   1.672 +      // decides then to use the whole set of trust in the DB
   1.673 +      // so we set the evPolicy to unkown in this case
   1.674 +      if (CERT_LIST_EMPTY(trustAnchors)) {
   1.675 +        evPolicy = SEC_OID_UNKNOWN;
   1.676 +      }
   1.677 +    } else {
   1.678 +      // No known EV policy found
   1.679 +      if (flags & FLAG_MUST_BE_EV) {
   1.680 +        PORT_SetError(SEC_ERROR_EXTENSION_NOT_FOUND);
   1.681 +        return SECFailure;
   1.682 +      }
   1.683 +      // Do not setup EV verification params
   1.684 +      evPolicy = SEC_OID_UNKNOWN;
   1.685 +    }
   1.686 +    if ((evPolicy == SEC_OID_UNKNOWN) && (flags & FLAG_MUST_BE_EV)) {
   1.687 +      PORT_SetError(SEC_ERROR_UNKNOWN_ISSUER);
   1.688 +      return SECFailure;
   1.689 +    }
   1.690 +  }
   1.691 +
   1.692 +  PR_ASSERT(evPolicy == SEC_OID_UNKNOWN || trustAnchors);
   1.693 +
   1.694 +  size_t i = 0;
   1.695 +  size_t validationChainLocation = 0;
   1.696 +  size_t validationTrustAnchorLocation = 0;
   1.697 +  CERTValOutParam cvout[4];
   1.698 +  if (verifyLog) {
   1.699 +     cvout[i].type = cert_po_errorLog;
   1.700 +     cvout[i].value.pointer.log = verifyLog;
   1.701 +     ++i;
   1.702 +  }
   1.703 +  if (validationChain) {
   1.704 +    PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, ("VerifyCert: setting up validation chain outparam.\n"));
   1.705 +    validationChainLocation = i;
   1.706 +    cvout[i].type = cert_po_certList;
   1.707 +    cvout[i].value.pointer.chain = nullptr;
   1.708 +    ++i;
   1.709 +    validationTrustAnchorLocation = i;
   1.710 +    cvout[i].type = cert_po_trustAnchor;
   1.711 +    cvout[i].value.pointer.cert = nullptr;
   1.712 +    ++i;
   1.713 +  }
   1.714 +  cvout[i].type = cert_po_end;
   1.715 +
   1.716 +  CERTRevocationFlags rev;
   1.717 +
   1.718 +  CERTRevocationMethodIndex revPreferredMethods[2];
   1.719 +  rev.leafTests.preferred_methods =
   1.720 +  rev.chainTests.preferred_methods = revPreferredMethods;
   1.721 +
   1.722 +  uint64_t revFlagsPerMethod[2];
   1.723 +  rev.leafTests.cert_rev_flags_per_method =
   1.724 +  rev.chainTests.cert_rev_flags_per_method = revFlagsPerMethod;
   1.725 +  rev.leafTests.number_of_preferred_methods =
   1.726 +  rev.chainTests.number_of_preferred_methods = 1;
   1.727 +
   1.728 +  rev.leafTests.number_of_defined_methods =
   1.729 +  rev.chainTests.number_of_defined_methods = cert_revocation_method_ocsp + 1;
   1.730 +
   1.731 +  const bool localOnly = flags & FLAG_LOCAL_ONLY;
   1.732 +  CERTValInParam cvin[7];
   1.733 +
   1.734 +  // Parameters for both EV and DV validation
   1.735 +  cvin[0].type = cert_pi_useAIACertFetch;
   1.736 +  cvin[0].value.scalar.b = mMissingCertDownloadEnabled && !localOnly;
   1.737 +  cvin[1].type = cert_pi_revocationFlags;
   1.738 +  cvin[1].value.pointer.revocation = &rev;
   1.739 +  cvin[2].type = cert_pi_date;
   1.740 +  cvin[2].value.scalar.time = time;
   1.741 +  i = 3;
   1.742 +
   1.743 +  CERTChainVerifyCallback callbackContainer;
   1.744 +  if (usage == certificateUsageSSLServer) {
   1.745 +    callbackContainer.isChainValid = chainValidationCallback;
   1.746 +    callbackContainer.isChainValidArg = &callbackState;
   1.747 +    cvin[i].type = cert_pi_chainVerifyCallback;
   1.748 +    cvin[i].value.pointer.chainVerifyCallback = &callbackContainer;
   1.749 +    ++i;
   1.750 +  }
   1.751 +
   1.752 +  const size_t evParamLocation = i;
   1.753 +
   1.754 +  if (evPolicy != SEC_OID_UNKNOWN) {
   1.755 +    // EV setup!
   1.756 +    // XXX 859872 The current flags are not quite correct. (use
   1.757 +    // of ocsp flags for crl preferences).
   1.758 +    uint64_t ocspRevMethodFlags =
   1.759 +      CERT_REV_M_TEST_USING_THIS_METHOD
   1.760 +      | ((mOCSPDownloadEnabled && !localOnly) ?
   1.761 +          CERT_REV_M_ALLOW_NETWORK_FETCHING : CERT_REV_M_FORBID_NETWORK_FETCHING)
   1.762 +      | CERT_REV_M_ALLOW_IMPLICIT_DEFAULT_SOURCE
   1.763 +      | CERT_REV_M_REQUIRE_INFO_ON_MISSING_SOURCE
   1.764 +      | CERT_REV_M_IGNORE_MISSING_FRESH_INFO
   1.765 +      | CERT_REV_M_STOP_TESTING_ON_FRESH_INFO
   1.766 +      | (mOCSPGETEnabled ? 0 : CERT_REV_M_FORCE_POST_METHOD_FOR_OCSP);
   1.767 +
   1.768 +    rev.leafTests.cert_rev_flags_per_method[cert_revocation_method_crl] =
   1.769 +    rev.chainTests.cert_rev_flags_per_method[cert_revocation_method_crl]
   1.770 +      = CERT_REV_M_DO_NOT_TEST_USING_THIS_METHOD;
   1.771 +
   1.772 +    rev.leafTests.cert_rev_flags_per_method[cert_revocation_method_ocsp] =
   1.773 +    rev.chainTests.cert_rev_flags_per_method[cert_revocation_method_ocsp]
   1.774 +      = ocspRevMethodFlags;
   1.775 +
   1.776 +    rev.leafTests.cert_rev_method_independent_flags =
   1.777 +    rev.chainTests.cert_rev_method_independent_flags =
   1.778 +      // avoiding the network is good, let's try local first
   1.779 +      CERT_REV_MI_TEST_ALL_LOCAL_INFORMATION_FIRST
   1.780 +      // is overall revocation requirement strict or relaxed?
   1.781 +      |  CERT_REV_MI_REQUIRE_SOME_FRESH_INFO_AVAILABLE
   1.782 +      ;
   1.783 +
   1.784 +    rev.leafTests.preferred_methods[0] =
   1.785 +    rev.chainTests.preferred_methods[0] = cert_revocation_method_ocsp;
   1.786 +
   1.787 +    cvin[i].type = cert_pi_policyOID;
   1.788 +    cvin[i].value.arraySize = 1;
   1.789 +    cvin[i].value.array.oids = &evPolicy;
   1.790 +    ++i;
   1.791 +    PR_ASSERT(trustAnchors);
   1.792 +    cvin[i].type = cert_pi_trustAnchors;
   1.793 +    cvin[i].value.pointer.chain = trustAnchors.get();
   1.794 +    ++i;
   1.795 +
   1.796 +    cvin[i].type = cert_pi_end;
   1.797 +
   1.798 +    rv = CERT_PKIXVerifyCert(cert, usage, cvin, cvout, pinArg);
   1.799 +    if (rv == SECSuccess) {
   1.800 +      if (evOidPolicy) {
   1.801 +        *evOidPolicy = evPolicy;
   1.802 +      }
   1.803 +      PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
   1.804 +             ("VerifyCert: successful CERT_PKIXVerifyCert(ev) \n"));
   1.805 +      goto pkix_done;
   1.806 +    }
   1.807 +    PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
   1.808 +           ("VerifyCert: failed CERT_PKIXVerifyCert(ev)\n"));
   1.809 +    if (flags & FLAG_MUST_BE_EV) {
   1.810 +      return rv;
   1.811 +    }
   1.812 +    if (validationChain) {
   1.813 +      destroyCertListThatShouldNotExist(
   1.814 +        &cvout[validationChainLocation].value.pointer.chain);
   1.815 +    }
   1.816 +
   1.817 +    if (verifyLog) {
   1.818 +      // Cleanup the log so that it is ready the the next validation
   1.819 +      CERTVerifyLogNode* i_node;
   1.820 +      for (i_node = verifyLog->head; i_node; i_node = i_node->next) {
   1.821 +         //destroy cert if any.
   1.822 +         if (i_node->cert) {
   1.823 +           CERT_DestroyCertificate(i_node->cert);
   1.824 +         }
   1.825 +         // No need to cleanup the actual nodes in the arena.
   1.826 +      }
   1.827 +      verifyLog->count = 0;
   1.828 +      verifyLog->head = nullptr;
   1.829 +      verifyLog->tail = nullptr;
   1.830 +    }
   1.831 +
   1.832 +  }
   1.833 +#endif
   1.834 +
   1.835 +  // If we're here, PKIX EV verification failed.
   1.836 +  // If requested, don't do DV fallback.
   1.837 +  if (flags & FLAG_MUST_BE_EV) {
   1.838 +    PR_ASSERT(*evOidPolicy == SEC_OID_UNKNOWN);
   1.839 +#ifdef NSS_NO_LIBPKIX
   1.840 +    PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
   1.841 +#else
   1.842 +    PR_SetError(PR_INVALID_STATE_ERROR, 0);
   1.843 +#endif
   1.844 +    return SECFailure;
   1.845 +  }
   1.846 +
   1.847 +  if (mImplementation == classic) {
   1.848 +    // XXX: we do not care about the localOnly flag (currently) as the
   1.849 +    // caller that wants localOnly should disable and reenable the fetching.
   1.850 +    return ClassicVerifyCert(cert, usage, time, pinArg, &callbackState,
   1.851 +                             validationChain, verifyLog);
   1.852 +  }
   1.853 +
   1.854 +#ifdef NSS_NO_LIBPKIX
   1.855 +  PR_NOT_REACHED("libpkix implementation chosen but not even compiled in");
   1.856 +  PR_SetError(PR_INVALID_STATE_ERROR, 0);
   1.857 +  return SECFailure;
   1.858 +#else
   1.859 +  PR_ASSERT(mImplementation == libpkix);
   1.860 +
   1.861 +  // The current flags check the chain the same way as the leafs
   1.862 +  rev.leafTests.cert_rev_flags_per_method[cert_revocation_method_crl] =
   1.863 +  rev.chainTests.cert_rev_flags_per_method[cert_revocation_method_crl] =
   1.864 +    // implicit default source - makes no sense for CRLs
   1.865 +    CERT_REV_M_IGNORE_IMPLICIT_DEFAULT_SOURCE
   1.866 +
   1.867 +    // let's not stop on fresh CRL. If OCSP is enabled, too, let's check it
   1.868 +    | CERT_REV_M_CONTINUE_TESTING_ON_FRESH_INFO
   1.869 +
   1.870 +    // no fresh CRL? well, let other flag decide whether to fail or not
   1.871 +    | CERT_REV_M_IGNORE_MISSING_FRESH_INFO
   1.872 +
   1.873 +    // testing using local CRLs is always allowed
   1.874 +    | CERT_REV_M_TEST_USING_THIS_METHOD
   1.875 +
   1.876 +    // no local crl and don't know where to get it from? ignore
   1.877 +    | CERT_REV_M_SKIP_TEST_ON_MISSING_SOURCE
   1.878 +
   1.879 +    // crl download based on parameter
   1.880 +    | ((mCRLDownloadEnabled && !localOnly) ?
   1.881 +        CERT_REV_M_ALLOW_NETWORK_FETCHING : CERT_REV_M_FORBID_NETWORK_FETCHING)
   1.882 +    ;
   1.883 +
   1.884 +  rev.leafTests.cert_rev_flags_per_method[cert_revocation_method_ocsp] =
   1.885 +  rev.chainTests.cert_rev_flags_per_method[cert_revocation_method_ocsp] =
   1.886 +    // use OCSP
   1.887 +      CERT_REV_M_TEST_USING_THIS_METHOD
   1.888 +
   1.889 +    // if app has a default OCSP responder configured, let's use it
   1.890 +    | CERT_REV_M_ALLOW_IMPLICIT_DEFAULT_SOURCE
   1.891 +
   1.892 +    // of course OCSP doesn't work without a source. let's accept such certs
   1.893 +    | CERT_REV_M_SKIP_TEST_ON_MISSING_SOURCE
   1.894 +
   1.895 +    // if ocsp is required stop on lack of freshness
   1.896 +    | (mOCSPStrict ?
   1.897 +       CERT_REV_M_FAIL_ON_MISSING_FRESH_INFO : CERT_REV_M_IGNORE_MISSING_FRESH_INFO)
   1.898 +
   1.899 +    // ocsp success is sufficient
   1.900 +    | CERT_REV_M_STOP_TESTING_ON_FRESH_INFO
   1.901 +
   1.902 +    // ocsp enabled controls network fetching, too
   1.903 +    | ((mOCSPDownloadEnabled && !localOnly) ?
   1.904 +        CERT_REV_M_ALLOW_NETWORK_FETCHING : CERT_REV_M_FORBID_NETWORK_FETCHING)
   1.905 +
   1.906 +    | (mOCSPGETEnabled ? 0 : CERT_REV_M_FORCE_POST_METHOD_FOR_OCSP);
   1.907 +    ;
   1.908 +
   1.909 +  rev.leafTests.preferred_methods[0] =
   1.910 +  rev.chainTests.preferred_methods[0] = cert_revocation_method_ocsp;
   1.911 +
   1.912 +  rev.leafTests.cert_rev_method_independent_flags =
   1.913 +  rev.chainTests.cert_rev_method_independent_flags =
   1.914 +    // avoiding the network is good, let's try local first
   1.915 +    CERT_REV_MI_TEST_ALL_LOCAL_INFORMATION_FIRST;
   1.916 +
   1.917 +  // Skip EV parameters
   1.918 +  cvin[evParamLocation].type = cert_pi_end;
   1.919 +
   1.920 +  PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, ("VerifyCert: calling CERT_PKIXVerifyCert(dv) \n"));
   1.921 +  rv = CERT_PKIXVerifyCert(cert, usage, cvin, cvout, pinArg);
   1.922 +
   1.923 +pkix_done:
   1.924 +  // If there is an error we may need to worsen to error to be a pinning failure
   1.925 +  if (rv != SECSuccess && usage == certificateUsageSSLServer &&
   1.926 +      PR_GetError() != SEC_ERROR_APPLICATION_CALLBACK_ERROR) {
   1.927 +    tryWorsenPRErrorInCallback(cert, &callbackState);
   1.928 +  }
   1.929 +
   1.930 +  if (validationChain) {
   1.931 +    PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, ("VerifyCert: validation chain requested\n"));
   1.932 +    ScopedCERTCertificate trustAnchor(cvout[validationTrustAnchorLocation].value.pointer.cert);
   1.933 +
   1.934 +    if (rv == SECSuccess) {
   1.935 +      if (! cvout[validationChainLocation].value.pointer.chain) {
   1.936 +        PR_SetError(PR_UNKNOWN_ERROR, 0);
   1.937 +        return SECFailure;
   1.938 +      }
   1.939 +      PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, ("VerifyCert: I have a chain\n"));
   1.940 +      *validationChain = cvout[validationChainLocation].value.pointer.chain;
   1.941 +      if (trustAnchor) {
   1.942 +        // we should only add the issuer to the chain if it is not already
   1.943 +        // present. On CA cert checking, the issuer is the same cert, so in
   1.944 +        // that case we do not add the cert to the chain.
   1.945 +        if (!CERT_CompareCerts(trustAnchor.get(), cert)) {
   1.946 +          PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, ("VerifyCert:  adding issuer to tail for display\n"));
   1.947 +          // note: rv is reused to catch errors on cert creation!
   1.948 +          ScopedCERTCertificate tempCert(CERT_DupCertificate(trustAnchor.get()));
   1.949 +          rv = CERT_AddCertToListTail(validationChain->get(), tempCert.get());
   1.950 +          if (rv == SECSuccess) {
   1.951 +            tempCert.release(); // ownership traferred to validationChain
   1.952 +          } else {
   1.953 +            *validationChain = nullptr;
   1.954 +          }
   1.955 +        }
   1.956 +      }
   1.957 +    } else {
   1.958 +      destroyCertListThatShouldNotExist(
   1.959 +        &cvout[validationChainLocation].value.pointer.chain);
   1.960 +    }
   1.961 +  }
   1.962 +
   1.963 +  return rv;
   1.964 +#endif
   1.965 +}
   1.966 +
   1.967 +SECStatus
   1.968 +CertVerifier::VerifySSLServerCert(CERTCertificate* peerCert,
   1.969 +                     /*optional*/ const SECItem* stapledOCSPResponse,
   1.970 +                                  PRTime time,
   1.971 +                     /*optional*/ void* pinarg,
   1.972 +                                  const char* hostname,
   1.973 +                                  bool saveIntermediatesInPermanentDatabase,
   1.974 +                 /*optional out*/ mozilla::pkix::ScopedCERTCertList* certChainOut,
   1.975 +                 /*optional out*/ SECOidTag* evOidPolicy)
   1.976 +{
   1.977 +  PR_ASSERT(peerCert);
   1.978 +  // XXX: PR_ASSERT(pinarg)
   1.979 +  PR_ASSERT(hostname);
   1.980 +  PR_ASSERT(hostname[0]);
   1.981 +
   1.982 +  if (certChainOut) {
   1.983 +    *certChainOut = nullptr;
   1.984 +  }
   1.985 +  if (evOidPolicy) {
   1.986 +    *evOidPolicy = SEC_OID_UNKNOWN;
   1.987 +  }
   1.988 +
   1.989 +  if (!hostname || !hostname[0]) {
   1.990 +    PR_SetError(SSL_ERROR_BAD_CERT_DOMAIN, 0);
   1.991 +    return SECFailure;
   1.992 +  }
   1.993 +
   1.994 +  // CreateCertErrorRunnable assumes that CERT_VerifyCertName is only called
   1.995 +  // if VerifyCert succeeded.
   1.996 +  ScopedCERTCertList validationChain;
   1.997 +  SECStatus rv = VerifyCert(peerCert, certificateUsageSSLServer, time, pinarg,
   1.998 +                            hostname, 0, stapledOCSPResponse, &validationChain,
   1.999 +                            evOidPolicy, nullptr);
  1.1000 +  if (rv != SECSuccess) {
  1.1001 +    return rv;
  1.1002 +  }
  1.1003 +
  1.1004 +  rv = CERT_VerifyCertName(peerCert, hostname);
  1.1005 +  if (rv != SECSuccess) {
  1.1006 +    return rv;
  1.1007 +  }
  1.1008 +
  1.1009 +  if (saveIntermediatesInPermanentDatabase) {
  1.1010 +    SaveIntermediateCerts(validationChain);
  1.1011 +  }
  1.1012 +
  1.1013 +  if (certChainOut) {
  1.1014 +    *certChainOut = validationChain.release();
  1.1015 +  }
  1.1016 +
  1.1017 +  return SECSuccess;
  1.1018 +}
  1.1019 +
  1.1020 +} } // namespace mozilla::psm

mercurial