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