Bug 1583945 - Import the RDM browser stylesheet along with the devtools-browser style...
[gecko.git] / security / certverifier / CertVerifier.cpp
blobb51f13cf7cc4b2a841a57c398f2347a47439b2ff
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "CertVerifier.h"
9 #include <stdint.h>
11 #include "CTDiversityPolicy.h"
12 #include "CTKnownLogs.h"
13 #include "CTLogVerifier.h"
14 #include "ExtendedValidation.h"
15 #include "MultiLogCTVerifier.h"
16 #include "NSSCertDBTrustDomain.h"
17 #include "NSSErrorsService.h"
18 #include "cert.h"
19 #include "mozilla/Assertions.h"
20 #include "mozilla/Casting.h"
21 #include "mozilla/IntegerPrintfMacros.h"
22 #include "nsNSSComponent.h"
23 #include "nsPromiseFlatString.h"
24 #include "nsServiceManagerUtils.h"
25 #include "pk11pub.h"
26 #include "mozpkix/pkix.h"
27 #include "mozpkix/pkixnss.h"
28 #include "secmod.h"
30 using namespace mozilla::ct;
31 using namespace mozilla::pkix;
32 using namespace mozilla::psm;
34 mozilla::LazyLogModule gCertVerifierLog("certverifier");
36 // Returns the certificate validity period in calendar months (rounded down).
37 // "extern" to allow unit tests in CTPolicyEnforcerTest.cpp.
38 extern mozilla::pkix::Result GetCertLifetimeInFullMonths(PRTime certNotBefore,
39 PRTime certNotAfter,
40 size_t& months) {
41 if (certNotBefore >= certNotAfter) {
42 MOZ_ASSERT_UNREACHABLE("Expected notBefore < notAfter");
43 return mozilla::pkix::Result::FATAL_ERROR_INVALID_ARGS;
46 PRExplodedTime explodedNotBefore;
47 PRExplodedTime explodedNotAfter;
49 PR_ExplodeTime(certNotBefore, PR_LocalTimeParameters, &explodedNotBefore);
50 PR_ExplodeTime(certNotAfter, PR_LocalTimeParameters, &explodedNotAfter);
52 PRInt32 signedMonths =
53 (explodedNotAfter.tm_year - explodedNotBefore.tm_year) * 12 +
54 (explodedNotAfter.tm_month - explodedNotBefore.tm_month);
55 if (explodedNotAfter.tm_mday < explodedNotBefore.tm_mday) {
56 --signedMonths;
59 // Can't use `mozilla::AssertedCast<size_t>(signedMonths)` below
60 // since it currently generates a warning on Win x64 debug.
61 if (signedMonths < 0) {
62 MOZ_ASSERT_UNREACHABLE("Expected explodedNotBefore < explodedNotAfter");
63 return mozilla::pkix::Result::FATAL_ERROR_LIBRARY_FAILURE;
65 months = static_cast<size_t>(signedMonths);
67 return Success;
70 namespace mozilla {
71 namespace psm {
73 const CertVerifier::Flags CertVerifier::FLAG_LOCAL_ONLY = 1;
74 const CertVerifier::Flags CertVerifier::FLAG_MUST_BE_EV = 2;
75 const CertVerifier::Flags CertVerifier::FLAG_TLS_IGNORE_STATUS_REQUEST = 4;
77 void CertificateTransparencyInfo::Reset() {
78 enabled = false;
79 verifyResult.Reset();
80 policyCompliance = CTPolicyCompliance::Unknown;
83 CertVerifier::CertVerifier(OcspDownloadConfig odc, OcspStrictConfig osc,
84 mozilla::TimeDuration ocspTimeoutSoft,
85 mozilla::TimeDuration ocspTimeoutHard,
86 uint32_t certShortLifetimeInDays,
87 PinningMode pinningMode, SHA1Mode sha1Mode,
88 BRNameMatchingPolicy::Mode nameMatchingMode,
89 NetscapeStepUpPolicy netscapeStepUpPolicy,
90 CertificateTransparencyMode ctMode,
91 DistrustedCAPolicy distrustedCAPolicy,
92 const Vector<EnterpriseCert>& thirdPartyCerts)
93 : mOCSPDownloadConfig(odc),
94 mOCSPStrict(osc == ocspStrict),
95 mOCSPTimeoutSoft(ocspTimeoutSoft),
96 mOCSPTimeoutHard(ocspTimeoutHard),
97 mCertShortLifetimeInDays(certShortLifetimeInDays),
98 mPinningMode(pinningMode),
99 mSHA1Mode(sha1Mode),
100 mNameMatchingMode(nameMatchingMode),
101 mNetscapeStepUpPolicy(netscapeStepUpPolicy),
102 mCTMode(ctMode),
103 mDistrustedCAPolicy(distrustedCAPolicy) {
104 LoadKnownCTLogs();
105 for (const auto& root : thirdPartyCerts) {
106 EnterpriseCert rootCopy;
107 // Best-effort. If we run out of memory, users might see untrusted issuer
108 // errors, but the browser will probably crash before then.
109 if (NS_SUCCEEDED(rootCopy.Init(root))) {
110 Unused << mThirdPartyCerts.append(std::move(rootCopy));
113 for (const auto& root : mThirdPartyCerts) {
114 Input input;
115 if (root.GetInput(input) == Success) {
116 // mThirdPartyCerts consists of roots and intermediates.
117 if (root.GetIsRoot()) {
118 // Best effort again.
119 Unused << mThirdPartyRootInputs.append(input);
120 } else {
121 Unused << mThirdPartyIntermediateInputs.append(input);
127 CertVerifier::~CertVerifier() {}
129 Result IsCertChainRootBuiltInRoot(const UniqueCERTCertList& chain,
130 bool& result) {
131 if (!chain || CERT_LIST_EMPTY(chain)) {
132 return Result::FATAL_ERROR_LIBRARY_FAILURE;
134 CERTCertListNode* rootNode = CERT_LIST_TAIL(chain);
135 if (!rootNode) {
136 return Result::FATAL_ERROR_LIBRARY_FAILURE;
138 CERTCertificate* root = rootNode->cert;
139 if (!root) {
140 return Result::FATAL_ERROR_LIBRARY_FAILURE;
142 return IsCertBuiltInRoot(root, result);
145 // The term "builtin root" traditionally refers to a root CA certificate that
146 // has been added to the NSS trust store, because it has been approved
147 // for inclusion according to the Mozilla CA policy, and might be accepted
148 // by Mozilla applications as an issuer for certificates seen on the public web.
149 Result IsCertBuiltInRoot(CERTCertificate* cert, bool& result) {
150 if (NS_FAILED(BlockUntilLoadableRootsLoaded())) {
151 return Result::FATAL_ERROR_LIBRARY_FAILURE;
154 result = false;
155 #ifdef DEBUG
156 nsCOMPtr<nsINSSComponent> component(do_GetService(PSM_COMPONENT_CONTRACTID));
157 if (!component) {
158 return Result::FATAL_ERROR_LIBRARY_FAILURE;
160 nsresult rv = component->IsCertTestBuiltInRoot(cert, &result);
161 if (NS_FAILED(rv)) {
162 return Result::FATAL_ERROR_LIBRARY_FAILURE;
164 if (result) {
165 return Success;
167 #endif // DEBUG
168 AutoSECMODListReadLock lock;
169 for (SECMODModuleList* list = SECMOD_GetDefaultModuleList(); list;
170 list = list->next) {
171 for (int i = 0; i < list->module->slotCount; i++) {
172 PK11SlotInfo* slot = list->module->slots[i];
173 // We're searching for the "builtin root module", which is a module that
174 // contains an object with a CKA_CLASS of CKO_NETSCAPE_BUILTIN_ROOT_LIST.
175 // We use PK11_HasRootCerts() to identify a module with that property.
176 // In the past, we exclusively used the PKCS#11 module named nssckbi,
177 // which is provided by the NSS library.
178 // Nowadays, some distributions use a replacement module, which contains
179 // the builtin roots, but which also contains additional CA certificates,
180 // such as CAs trusted in a local deployment.
181 // We want to be able to distinguish between these two categories,
182 // because a CA, which may issue certificates for the public web,
183 // is expected to comply with additional requirements.
184 // If the certificate has attribute CKA_NSS_MOZILLA_CA_POLICY set to true,
185 // then we treat it as a "builtin root".
186 if (PK11_IsPresent(slot) && PK11_HasRootCerts(slot)) {
187 CK_OBJECT_HANDLE handle = PK11_FindCertInSlot(slot, cert, nullptr);
188 if (handle != CK_INVALID_HANDLE &&
189 PK11_HasAttributeSet(slot, handle, CKA_NSS_MOZILLA_CA_POLICY,
190 false)) {
191 // Attribute was found, and is set to true
192 result = true;
193 break;
198 return Success;
201 static Result BuildCertChainForOneKeyUsage(
202 NSSCertDBTrustDomain& trustDomain, Input certDER, Time time, KeyUsage ku1,
203 KeyUsage ku2, KeyUsage ku3, KeyPurposeId eku,
204 const CertPolicyId& requiredPolicy, const Input* stapledOCSPResponse,
205 /*optional out*/ CertVerifier::OCSPStaplingStatus* ocspStaplingStatus) {
206 trustDomain.ResetAccumulatedState();
207 Result rv =
208 BuildCertChain(trustDomain, certDER, time, EndEntityOrCA::MustBeEndEntity,
209 ku1, eku, requiredPolicy, stapledOCSPResponse);
210 if (rv == Result::ERROR_INADEQUATE_KEY_USAGE) {
211 trustDomain.ResetAccumulatedState();
212 rv = BuildCertChain(trustDomain, certDER, time,
213 EndEntityOrCA::MustBeEndEntity, ku2, eku,
214 requiredPolicy, stapledOCSPResponse);
215 if (rv == Result::ERROR_INADEQUATE_KEY_USAGE) {
216 trustDomain.ResetAccumulatedState();
217 rv = BuildCertChain(trustDomain, certDER, time,
218 EndEntityOrCA::MustBeEndEntity, ku3, eku,
219 requiredPolicy, stapledOCSPResponse);
220 if (rv != Success) {
221 rv = Result::ERROR_INADEQUATE_KEY_USAGE;
225 if (ocspStaplingStatus) {
226 *ocspStaplingStatus = trustDomain.GetOCSPStaplingStatus();
228 return rv;
231 void CertVerifier::LoadKnownCTLogs() {
232 mCTVerifier = MakeUnique<MultiLogCTVerifier>();
233 for (const CTLogInfo& log : kCTLogList) {
234 Input publicKey;
235 Result rv = publicKey.Init(
236 BitwiseCast<const uint8_t*, const char*>(log.key), log.keyLength);
237 if (rv != Success) {
238 MOZ_ASSERT_UNREACHABLE("Failed reading a log key for a known CT Log");
239 continue;
242 CTLogVerifier logVerifier;
243 const CTLogOperatorInfo& logOperator =
244 kCTLogOperatorList[log.operatorIndex];
245 rv = logVerifier.Init(publicKey, logOperator.id, log.status,
246 log.disqualificationTime);
247 if (rv != Success) {
248 MOZ_ASSERT_UNREACHABLE("Failed initializing a known CT Log");
249 continue;
252 mCTVerifier->AddLog(std::move(logVerifier));
254 // TBD: Initialize mCTDiversityPolicy with the CA dependency map
255 // of the known CT logs operators.
256 mCTDiversityPolicy = MakeUnique<CTDiversityPolicy>();
259 Result CertVerifier::VerifyCertificateTransparencyPolicy(
260 NSSCertDBTrustDomain& trustDomain, const UniqueCERTCertList& builtChain,
261 Input sctsFromTLS, Time time,
262 /*optional out*/ CertificateTransparencyInfo* ctInfo) {
263 if (ctInfo) {
264 ctInfo->Reset();
266 if (mCTMode == CertificateTransparencyMode::Disabled) {
267 return Success;
269 if (ctInfo) {
270 ctInfo->enabled = true;
273 if (!builtChain || CERT_LIST_EMPTY(builtChain)) {
274 return Result::FATAL_ERROR_INVALID_ARGS;
277 Input embeddedSCTs = trustDomain.GetSCTListFromCertificate();
278 if (embeddedSCTs.GetLength() > 0) {
279 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
280 ("Got embedded SCT data of length %zu\n",
281 static_cast<size_t>(embeddedSCTs.GetLength())));
283 Input sctsFromOCSP = trustDomain.GetSCTListFromOCSPStapling();
284 if (sctsFromOCSP.GetLength() > 0) {
285 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
286 ("Got OCSP SCT data of length %zu\n",
287 static_cast<size_t>(sctsFromOCSP.GetLength())));
289 if (sctsFromTLS.GetLength() > 0) {
290 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
291 ("Got TLS SCT data of length %zu\n",
292 static_cast<size_t>(sctsFromTLS.GetLength())));
295 CERTCertListNode* endEntityNode = CERT_LIST_HEAD(builtChain);
296 if (!endEntityNode || CERT_LIST_END(endEntityNode, builtChain)) {
297 return Result::FATAL_ERROR_INVALID_ARGS;
299 CERTCertListNode* issuerNode = CERT_LIST_NEXT(endEntityNode);
300 if (!issuerNode || CERT_LIST_END(issuerNode, builtChain)) {
301 // Issuer certificate is required for SCT verification.
302 // If we've arrived here, we probably have a "trust chain" with only one
303 // certificate (i.e. a self-signed end-entity that has been set as a trust
304 // anchor either by a third party modifying our trust DB or via the
305 // enterprise roots feature). If this is the case, certificate transparency
306 // information will probably not be present, and it certainly won't verify
307 // correctly. To simplify things, we return an empty CTVerifyResult and a
308 // "not enough SCTs" CTPolicyCompliance result.
309 if (ctInfo) {
310 CTVerifyResult emptyResult;
311 ctInfo->verifyResult = std::move(emptyResult);
312 ctInfo->policyCompliance = CTPolicyCompliance::NotEnoughScts;
314 return Success;
317 CERTCertificate* endEntity = endEntityNode->cert;
318 CERTCertificate* issuer = issuerNode->cert;
319 if (!endEntity || !issuer) {
320 return Result::FATAL_ERROR_INVALID_ARGS;
323 if (endEntity->subjectName) {
324 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
325 ("Verifying CT Policy compliance of subject %s\n",
326 endEntity->subjectName));
329 Input endEntityDER;
330 Result rv =
331 endEntityDER.Init(endEntity->derCert.data, endEntity->derCert.len);
332 if (rv != Success) {
333 return rv;
336 Input issuerPublicKeyDER;
337 rv = issuerPublicKeyDER.Init(issuer->derPublicKey.data,
338 issuer->derPublicKey.len);
339 if (rv != Success) {
340 return rv;
343 CTVerifyResult result;
344 rv = mCTVerifier->Verify(endEntityDER, issuerPublicKeyDER, embeddedSCTs,
345 sctsFromOCSP, sctsFromTLS, time, result);
346 if (rv != Success) {
347 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
348 ("SCT verification failed with fatal error %" PRId32 "\n",
349 static_cast<uint32_t>(rv)));
350 return rv;
353 if (MOZ_LOG_TEST(gCertVerifierLog, LogLevel::Debug)) {
354 size_t validCount = 0;
355 size_t unknownLogCount = 0;
356 size_t disqualifiedLogCount = 0;
357 size_t invalidSignatureCount = 0;
358 size_t invalidTimestampCount = 0;
359 for (const VerifiedSCT& verifiedSct : result.verifiedScts) {
360 switch (verifiedSct.status) {
361 case VerifiedSCT::Status::Valid:
362 validCount++;
363 break;
364 case VerifiedSCT::Status::ValidFromDisqualifiedLog:
365 disqualifiedLogCount++;
366 break;
367 case VerifiedSCT::Status::UnknownLog:
368 unknownLogCount++;
369 break;
370 case VerifiedSCT::Status::InvalidSignature:
371 invalidSignatureCount++;
372 break;
373 case VerifiedSCT::Status::InvalidTimestamp:
374 invalidTimestampCount++;
375 break;
376 case VerifiedSCT::Status::None:
377 default:
378 MOZ_ASSERT_UNREACHABLE("Unexpected SCT verification status");
381 MOZ_LOG(
382 gCertVerifierLog, LogLevel::Debug,
383 ("SCT verification result: "
384 "valid=%zu unknownLog=%zu disqualifiedLog=%zu "
385 "invalidSignature=%zu invalidTimestamp=%zu "
386 "decodingErrors=%zu\n",
387 validCount, unknownLogCount, disqualifiedLogCount,
388 invalidSignatureCount, invalidTimestampCount, result.decodingErrors));
391 PRTime notBefore;
392 PRTime notAfter;
393 if (CERT_GetCertTimes(endEntity, &notBefore, &notAfter) != SECSuccess) {
394 return Result::FATAL_ERROR_LIBRARY_FAILURE;
396 size_t lifetimeInMonths;
397 rv = GetCertLifetimeInFullMonths(notBefore, notAfter, lifetimeInMonths);
398 if (rv != Success) {
399 return rv;
402 CTLogOperatorList allOperators;
403 GetCTLogOperatorsFromVerifiedSCTList(result.verifiedScts, allOperators);
405 CTLogOperatorList dependentOperators;
406 rv = mCTDiversityPolicy->GetDependentOperators(builtChain.get(), allOperators,
407 dependentOperators);
408 if (rv != Success) {
409 return rv;
412 CTPolicyEnforcer ctPolicyEnforcer;
413 CTPolicyCompliance ctPolicyCompliance;
414 ctPolicyEnforcer.CheckCompliance(result.verifiedScts, lifetimeInMonths,
415 dependentOperators, ctPolicyCompliance);
417 if (ctInfo) {
418 ctInfo->verifyResult = std::move(result);
419 ctInfo->policyCompliance = ctPolicyCompliance;
421 return Success;
424 bool CertVerifier::SHA1ModeMoreRestrictiveThanGivenMode(SHA1Mode mode) {
425 switch (mSHA1Mode) {
426 case SHA1Mode::Forbidden:
427 return mode != SHA1Mode::Forbidden;
428 case SHA1Mode::ImportedRoot:
429 return mode != SHA1Mode::Forbidden && mode != SHA1Mode::ImportedRoot;
430 case SHA1Mode::ImportedRootOrBefore2016:
431 return mode == SHA1Mode::Allowed;
432 case SHA1Mode::Allowed:
433 return false;
434 // MSVC warns unless we explicitly handle this now-unused option.
435 case SHA1Mode::UsedToBeBefore2016ButNowIsForbidden:
436 default:
437 MOZ_ASSERT(false, "unexpected SHA1Mode type");
438 return true;
442 static const unsigned int MIN_RSA_BITS = 2048;
443 static const unsigned int MIN_RSA_BITS_WEAK = 1024;
445 Result CertVerifier::VerifyCert(
446 CERTCertificate* cert, SECCertificateUsage usage, Time time, void* pinArg,
447 const char* hostname,
448 /*out*/ UniqueCERTCertList& builtChain,
449 /*optional*/ const Flags flags,
450 /*optional*/ const Maybe<nsTArray<uint8_t>>& stapledOCSPResponseArg,
451 /*optional*/ const Maybe<nsTArray<uint8_t>>& sctsFromTLS,
452 /*optional*/ const OriginAttributes& originAttributes,
453 /*optional out*/ SECOidTag* evOidPolicy,
454 /*optional out*/ OCSPStaplingStatus* ocspStaplingStatus,
455 /*optional out*/ KeySizeStatus* keySizeStatus,
456 /*optional out*/ SHA1ModeResult* sha1ModeResult,
457 /*optional out*/ PinningTelemetryInfo* pinningTelemetryInfo,
458 /*optional out*/ CertificateTransparencyInfo* ctInfo) {
459 MOZ_LOG(gCertVerifierLog, LogLevel::Debug, ("Top of VerifyCert\n"));
461 MOZ_ASSERT(cert);
462 MOZ_ASSERT(usage == certificateUsageSSLServer || !(flags & FLAG_MUST_BE_EV));
463 MOZ_ASSERT(usage == certificateUsageSSLServer || !keySizeStatus);
464 MOZ_ASSERT(usage == certificateUsageSSLServer || !sha1ModeResult);
466 if (NS_FAILED(BlockUntilLoadableRootsLoaded())) {
467 return Result::FATAL_ERROR_LIBRARY_FAILURE;
469 if (NS_FAILED(CheckForSmartCardChanges())) {
470 return Result::FATAL_ERROR_LIBRARY_FAILURE;
473 if (evOidPolicy) {
474 *evOidPolicy = SEC_OID_UNKNOWN;
476 if (ocspStaplingStatus) {
477 if (usage != certificateUsageSSLServer) {
478 return Result::FATAL_ERROR_INVALID_ARGS;
480 *ocspStaplingStatus = OCSP_STAPLING_NEVER_CHECKED;
483 if (keySizeStatus) {
484 if (usage != certificateUsageSSLServer) {
485 return Result::FATAL_ERROR_INVALID_ARGS;
487 *keySizeStatus = KeySizeStatus::NeverChecked;
490 if (sha1ModeResult) {
491 if (usage != certificateUsageSSLServer) {
492 return Result::FATAL_ERROR_INVALID_ARGS;
494 *sha1ModeResult = SHA1ModeResult::NeverChecked;
497 if (!cert ||
498 (usage != certificateUsageSSLServer && (flags & FLAG_MUST_BE_EV))) {
499 return Result::FATAL_ERROR_INVALID_ARGS;
502 Input certDER;
503 Result rv = certDER.Init(cert->derCert.data, cert->derCert.len);
504 if (rv != Success) {
505 return rv;
508 // We configure the OCSP fetching modes separately for EV and non-EV
509 // verifications.
510 NSSCertDBTrustDomain::OCSPFetching defaultOCSPFetching =
511 (mOCSPDownloadConfig == ocspOff) || (mOCSPDownloadConfig == ocspEVOnly) ||
512 (flags & FLAG_LOCAL_ONLY)
513 ? NSSCertDBTrustDomain::NeverFetchOCSP
514 : !mOCSPStrict ? NSSCertDBTrustDomain::FetchOCSPForDVSoftFail
515 : NSSCertDBTrustDomain::FetchOCSPForDVHardFail;
517 Input stapledOCSPResponseInput;
518 const Input* stapledOCSPResponse = nullptr;
519 if (stapledOCSPResponseArg) {
520 rv = stapledOCSPResponseInput.Init(stapledOCSPResponseArg->Elements(),
521 stapledOCSPResponseArg->Length());
522 if (rv != Success) {
523 // The stapled OCSP response was too big.
524 return Result::ERROR_OCSP_MALFORMED_RESPONSE;
526 stapledOCSPResponse = &stapledOCSPResponseInput;
529 Input sctsFromTLSInput;
530 if (sctsFromTLS) {
531 rv = sctsFromTLSInput.Init(sctsFromTLS->Elements(), sctsFromTLS->Length());
532 if (rv != Success && sctsFromTLSInput.GetLength() != 0) {
533 return Result::FATAL_ERROR_LIBRARY_FAILURE;
537 switch (usage) {
538 case certificateUsageSSLClient: {
539 // XXX: We don't really have a trust bit for SSL client authentication so
540 // just use trustEmail as it is the closest alternative.
541 NSSCertDBTrustDomain trustDomain(
542 trustEmail, defaultOCSPFetching, mOCSPCache, pinArg, mOCSPTimeoutSoft,
543 mOCSPTimeoutHard, mCertShortLifetimeInDays, pinningDisabled,
544 MIN_RSA_BITS_WEAK, ValidityCheckingMode::CheckingOff,
545 SHA1Mode::Allowed, NetscapeStepUpPolicy::NeverMatch,
546 mDistrustedCAPolicy, originAttributes, mThirdPartyRootInputs,
547 mThirdPartyIntermediateInputs, builtChain, nullptr, nullptr);
548 rv = BuildCertChain(
549 trustDomain, certDER, time, EndEntityOrCA::MustBeEndEntity,
550 KeyUsage::digitalSignature, KeyPurposeId::id_kp_clientAuth,
551 CertPolicyId::anyPolicy, stapledOCSPResponse);
552 break;
555 case certificateUsageSSLServer: {
556 // TODO: When verifying a certificate in an SSL handshake, we should
557 // restrict the acceptable key usage based on the key exchange method
558 // chosen by the server.
560 // These configurations are in order of most restrictive to least
561 // restrictive. This enables us to gather telemetry on the expected
562 // results of setting the default policy to a particular configuration.
563 SHA1Mode sha1ModeConfigurations[] = {
564 SHA1Mode::Forbidden,
565 SHA1Mode::ImportedRoot,
566 SHA1Mode::ImportedRootOrBefore2016,
567 SHA1Mode::Allowed,
570 SHA1ModeResult sha1ModeResults[] = {
571 SHA1ModeResult::SucceededWithoutSHA1,
572 SHA1ModeResult::SucceededWithImportedRoot,
573 SHA1ModeResult::SucceededWithImportedRootOrSHA1Before2016,
574 SHA1ModeResult::SucceededWithSHA1,
577 size_t sha1ModeConfigurationsCount =
578 MOZ_ARRAY_LENGTH(sha1ModeConfigurations);
580 static_assert(MOZ_ARRAY_LENGTH(sha1ModeConfigurations) ==
581 MOZ_ARRAY_LENGTH(sha1ModeResults),
582 "digestAlgorithm array lengths differ");
584 rv = Result::ERROR_UNKNOWN_ERROR;
586 // Try to validate for EV first.
587 NSSCertDBTrustDomain::OCSPFetching evOCSPFetching =
588 (mOCSPDownloadConfig == ocspOff) || (flags & FLAG_LOCAL_ONLY)
589 ? NSSCertDBTrustDomain::LocalOnlyOCSPForEV
590 : NSSCertDBTrustDomain::FetchOCSPForEV;
592 CertPolicyId evPolicy;
593 SECOidTag evPolicyOidTag;
594 bool foundEVPolicy = GetFirstEVPolicy(*cert, evPolicy, evPolicyOidTag);
595 for (size_t i = 0;
596 i < sha1ModeConfigurationsCount && rv != Success && foundEVPolicy;
597 i++) {
598 // Don't attempt verification if the SHA1 mode set by preferences
599 // (mSHA1Mode) is more restrictive than the SHA1 mode option we're on.
600 // (To put it another way, only attempt verification if the SHA1 mode
601 // option we're on is as restrictive or more restrictive than
602 // mSHA1Mode.) This allows us to gather telemetry information while
603 // still enforcing the mode set by preferences.
604 if (SHA1ModeMoreRestrictiveThanGivenMode(sha1ModeConfigurations[i])) {
605 continue;
608 // Because of the try-strict and fallback approach, we have to clear any
609 // previously noted telemetry information
610 if (pinningTelemetryInfo) {
611 pinningTelemetryInfo->Reset();
614 NSSCertDBTrustDomain trustDomain(
615 trustSSL, evOCSPFetching, mOCSPCache, pinArg, mOCSPTimeoutSoft,
616 mOCSPTimeoutHard, mCertShortLifetimeInDays, mPinningMode,
617 MIN_RSA_BITS, ValidityCheckingMode::CheckForEV,
618 sha1ModeConfigurations[i], mNetscapeStepUpPolicy,
619 mDistrustedCAPolicy, originAttributes, mThirdPartyRootInputs,
620 mThirdPartyIntermediateInputs, builtChain, pinningTelemetryInfo,
621 hostname);
622 rv = BuildCertChainForOneKeyUsage(
623 trustDomain, certDER, time,
624 KeyUsage::digitalSignature, // (EC)DHE
625 KeyUsage::keyEncipherment, // RSA
626 KeyUsage::keyAgreement, // (EC)DH
627 KeyPurposeId::id_kp_serverAuth, evPolicy, stapledOCSPResponse,
628 ocspStaplingStatus);
629 if (rv == Success &&
630 sha1ModeConfigurations[i] == SHA1Mode::ImportedRoot) {
631 bool isBuiltInRoot = false;
632 rv = IsCertChainRootBuiltInRoot(builtChain, isBuiltInRoot);
633 if (rv != Success) {
634 break;
636 if (isBuiltInRoot) {
637 rv = Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED;
640 if (rv == Success) {
641 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
642 ("cert is EV with status %i\n",
643 static_cast<int>(sha1ModeResults[i])));
644 if (evOidPolicy) {
645 *evOidPolicy = evPolicyOidTag;
647 if (sha1ModeResult) {
648 *sha1ModeResult = sha1ModeResults[i];
650 rv = VerifyCertificateTransparencyPolicy(
651 trustDomain, builtChain, sctsFromTLSInput, time, ctInfo);
652 if (rv != Success) {
653 break;
657 if (rv == Success) {
658 break;
661 if (flags & FLAG_MUST_BE_EV) {
662 rv = Result::ERROR_POLICY_VALIDATION_FAILED;
663 break;
666 // Now try non-EV.
667 unsigned int keySizeOptions[] = {MIN_RSA_BITS, MIN_RSA_BITS_WEAK};
669 KeySizeStatus keySizeStatuses[] = {KeySizeStatus::LargeMinimumSucceeded,
670 KeySizeStatus::CompatibilityRisk};
672 static_assert(
673 MOZ_ARRAY_LENGTH(keySizeOptions) == MOZ_ARRAY_LENGTH(keySizeStatuses),
674 "keySize array lengths differ");
676 size_t keySizeOptionsCount = MOZ_ARRAY_LENGTH(keySizeStatuses);
678 for (size_t i = 0; i < keySizeOptionsCount && rv != Success; i++) {
679 for (size_t j = 0; j < sha1ModeConfigurationsCount && rv != Success;
680 j++) {
681 // Don't attempt verification if the SHA1 mode set by preferences
682 // (mSHA1Mode) is more restrictive than the SHA1 mode option we're on.
683 // (To put it another way, only attempt verification if the SHA1 mode
684 // option we're on is as restrictive or more restrictive than
685 // mSHA1Mode.) This allows us to gather telemetry information while
686 // still enforcing the mode set by preferences.
687 if (SHA1ModeMoreRestrictiveThanGivenMode(sha1ModeConfigurations[j])) {
688 continue;
691 // invalidate any telemetry info relating to failed chains
692 if (pinningTelemetryInfo) {
693 pinningTelemetryInfo->Reset();
696 NSSCertDBTrustDomain trustDomain(
697 trustSSL, defaultOCSPFetching, mOCSPCache, pinArg,
698 mOCSPTimeoutSoft, mOCSPTimeoutHard, mCertShortLifetimeInDays,
699 mPinningMode, keySizeOptions[i],
700 ValidityCheckingMode::CheckingOff, sha1ModeConfigurations[j],
701 mNetscapeStepUpPolicy, mDistrustedCAPolicy, originAttributes,
702 mThirdPartyRootInputs, mThirdPartyIntermediateInputs, builtChain,
703 pinningTelemetryInfo, hostname);
704 rv = BuildCertChainForOneKeyUsage(
705 trustDomain, certDER, time,
706 KeyUsage::digitalSignature, //(EC)DHE
707 KeyUsage::keyEncipherment, // RSA
708 KeyUsage::keyAgreement, //(EC)DH
709 KeyPurposeId::id_kp_serverAuth, CertPolicyId::anyPolicy,
710 stapledOCSPResponse, ocspStaplingStatus);
711 if (rv != Success && !IsFatalError(rv) &&
712 rv != Result::ERROR_REVOKED_CERTIFICATE &&
713 trustDomain.GetIsErrorDueToDistrustedCAPolicy()) {
714 // Bug 1444440 - If there are multiple paths, at least one to a CA
715 // distrusted-by-policy, and none of them ending in a trusted root,
716 // then we might show a different error (UNKNOWN_ISSUER) than we
717 // intend, confusing users.
718 rv = Result::ERROR_ADDITIONAL_POLICY_CONSTRAINT_FAILED;
720 if (rv == Success &&
721 sha1ModeConfigurations[j] == SHA1Mode::ImportedRoot) {
722 bool isBuiltInRoot = false;
723 rv = IsCertChainRootBuiltInRoot(builtChain, isBuiltInRoot);
724 if (rv != Success) {
725 break;
727 if (isBuiltInRoot) {
728 rv = Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED;
731 if (rv == Success) {
732 if (keySizeStatus) {
733 *keySizeStatus = keySizeStatuses[i];
735 if (sha1ModeResult) {
736 *sha1ModeResult = sha1ModeResults[j];
738 rv = VerifyCertificateTransparencyPolicy(
739 trustDomain, builtChain, sctsFromTLSInput, time, ctInfo);
740 if (rv != Success) {
741 break;
747 if (rv == Success) {
748 break;
751 if (keySizeStatus) {
752 *keySizeStatus = KeySizeStatus::AlreadyBad;
754 // The telemetry probe CERT_CHAIN_SHA1_POLICY_STATUS gives us feedback on
755 // the result of setting a specific policy. However, we don't want noise
756 // from users who have manually set the policy to something other than the
757 // default, so we only collect for ImportedRoot (which is the default).
758 if (sha1ModeResult && mSHA1Mode == SHA1Mode::ImportedRoot) {
759 *sha1ModeResult = SHA1ModeResult::Failed;
762 break;
765 case certificateUsageSSLCA: {
766 NSSCertDBTrustDomain trustDomain(
767 trustSSL, defaultOCSPFetching, mOCSPCache, pinArg, mOCSPTimeoutSoft,
768 mOCSPTimeoutHard, mCertShortLifetimeInDays, pinningDisabled,
769 MIN_RSA_BITS_WEAK, ValidityCheckingMode::CheckingOff,
770 SHA1Mode::Allowed, mNetscapeStepUpPolicy, mDistrustedCAPolicy,
771 originAttributes, mThirdPartyRootInputs,
772 mThirdPartyIntermediateInputs, builtChain, nullptr, nullptr);
773 rv = BuildCertChain(trustDomain, certDER, time, EndEntityOrCA::MustBeCA,
774 KeyUsage::keyCertSign, KeyPurposeId::id_kp_serverAuth,
775 CertPolicyId::anyPolicy, stapledOCSPResponse);
776 break;
779 case certificateUsageEmailSigner: {
780 NSSCertDBTrustDomain trustDomain(
781 trustEmail, defaultOCSPFetching, mOCSPCache, pinArg, mOCSPTimeoutSoft,
782 mOCSPTimeoutHard, mCertShortLifetimeInDays, pinningDisabled,
783 MIN_RSA_BITS_WEAK, ValidityCheckingMode::CheckingOff,
784 SHA1Mode::Allowed, NetscapeStepUpPolicy::NeverMatch,
785 mDistrustedCAPolicy, originAttributes, mThirdPartyRootInputs,
786 mThirdPartyIntermediateInputs, builtChain, nullptr, nullptr);
787 rv = BuildCertChain(
788 trustDomain, certDER, time, EndEntityOrCA::MustBeEndEntity,
789 KeyUsage::digitalSignature, KeyPurposeId::id_kp_emailProtection,
790 CertPolicyId::anyPolicy, stapledOCSPResponse);
791 if (rv == Result::ERROR_INADEQUATE_KEY_USAGE) {
792 rv = BuildCertChain(
793 trustDomain, certDER, time, EndEntityOrCA::MustBeEndEntity,
794 KeyUsage::nonRepudiation, KeyPurposeId::id_kp_emailProtection,
795 CertPolicyId::anyPolicy, stapledOCSPResponse);
797 break;
800 case certificateUsageEmailRecipient: {
801 // TODO: The higher level S/MIME processing should pass in which key
802 // usage it is trying to verify for, and base its algorithm choices
803 // based on the result of the verification(s).
804 NSSCertDBTrustDomain trustDomain(
805 trustEmail, defaultOCSPFetching, mOCSPCache, pinArg, mOCSPTimeoutSoft,
806 mOCSPTimeoutHard, mCertShortLifetimeInDays, pinningDisabled,
807 MIN_RSA_BITS_WEAK, ValidityCheckingMode::CheckingOff,
808 SHA1Mode::Allowed, NetscapeStepUpPolicy::NeverMatch,
809 mDistrustedCAPolicy, originAttributes, mThirdPartyRootInputs,
810 mThirdPartyIntermediateInputs, builtChain, nullptr, nullptr);
811 rv = BuildCertChain(trustDomain, certDER, time,
812 EndEntityOrCA::MustBeEndEntity,
813 KeyUsage::keyEncipherment, // RSA
814 KeyPurposeId::id_kp_emailProtection,
815 CertPolicyId::anyPolicy, stapledOCSPResponse);
816 if (rv == Result::ERROR_INADEQUATE_KEY_USAGE) {
817 rv = BuildCertChain(trustDomain, certDER, time,
818 EndEntityOrCA::MustBeEndEntity,
819 KeyUsage::keyAgreement, // ECDH/DH
820 KeyPurposeId::id_kp_emailProtection,
821 CertPolicyId::anyPolicy, stapledOCSPResponse);
823 break;
826 default:
827 rv = Result::FATAL_ERROR_INVALID_ARGS;
830 if (rv != Success) {
831 return rv;
834 return Success;
837 static bool CertIsSelfSigned(const UniqueCERTCertificate& cert, void* pinarg) {
838 if (!SECITEM_ItemsAreEqual(&cert->derIssuer, &cert->derSubject)) {
839 return false;
842 // Check that the certificate is signed with the cert's spki.
843 SECStatus rv = CERT_VerifySignedDataWithPublicKeyInfo(
844 const_cast<CERTSignedData*>(&cert->signatureWrap),
845 const_cast<CERTSubjectPublicKeyInfo*>(&cert->subjectPublicKeyInfo),
846 pinarg);
847 if (rv != SECSuccess) {
848 return false;
851 return true;
854 Result CertVerifier::VerifySSLServerCert(
855 const UniqueCERTCertificate& peerCert,
856 /*optional*/ const Maybe<nsTArray<uint8_t>>& stapledOCSPResponse,
857 /*optional*/ const Maybe<nsTArray<uint8_t>>& sctsFromTLS, Time time,
858 /*optional*/ void* pinarg, const nsACString& hostname,
859 /*out*/ UniqueCERTCertList& builtChain,
860 /*optional*/ bool saveIntermediatesInPermanentDatabase,
861 /*optional*/ Flags flags,
862 /*optional*/ const OriginAttributes& originAttributes,
863 /*optional out*/ SECOidTag* evOidPolicy,
864 /*optional out*/ OCSPStaplingStatus* ocspStaplingStatus,
865 /*optional out*/ KeySizeStatus* keySizeStatus,
866 /*optional out*/ SHA1ModeResult* sha1ModeResult,
867 /*optional out*/ PinningTelemetryInfo* pinningTelemetryInfo,
868 /*optional out*/ CertificateTransparencyInfo* ctInfo) {
869 MOZ_ASSERT(peerCert);
870 // XXX: MOZ_ASSERT(pinarg);
871 MOZ_ASSERT(!hostname.IsEmpty());
873 if (evOidPolicy) {
874 *evOidPolicy = SEC_OID_UNKNOWN;
877 if (hostname.IsEmpty()) {
878 return Result::ERROR_BAD_CERT_DOMAIN;
881 // CreateCertErrorRunnable assumes that CheckCertHostname is only called
882 // if VerifyCert succeeded.
883 Result rv =
884 VerifyCert(peerCert.get(), certificateUsageSSLServer, time, pinarg,
885 PromiseFlatCString(hostname).get(), builtChain, flags,
886 stapledOCSPResponse, sctsFromTLS, originAttributes,
887 evOidPolicy, ocspStaplingStatus, keySizeStatus, sha1ModeResult,
888 pinningTelemetryInfo, ctInfo);
889 if (rv != Success) {
890 if (rv == Result::ERROR_UNKNOWN_ISSUER &&
891 CertIsSelfSigned(peerCert, pinarg)) {
892 // In this case we didn't find any issuer for the certificate and the
893 // certificate is self-signed.
894 return Result::ERROR_SELF_SIGNED_CERT;
896 if (rv == Result::ERROR_UNKNOWN_ISSUER) {
897 // In this case we didn't get any valid path for the cert. Let's see if
898 // the issuer is the same as the issuer for our canary probe. If yes, this
899 // connection is connecting via a misconfigured proxy.
900 // Note: The MitM canary might not be set. In this case we consider this
901 // an unknown issuer error.
902 nsCOMPtr<nsINSSComponent> component(
903 do_GetService(PSM_COMPONENT_CONTRACTID));
904 if (!component) {
905 return Result::FATAL_ERROR_LIBRARY_FAILURE;
907 // IssuerMatchesMitmCanary succeeds if the issuer matches the canary and
908 // the feature is enabled.
909 nsresult rv = component->IssuerMatchesMitmCanary(peerCert->issuerName);
910 if (NS_SUCCEEDED(rv)) {
911 return Result::ERROR_MITM_DETECTED;
914 return rv;
917 Input peerCertInput;
918 rv = peerCertInput.Init(peerCert->derCert.data, peerCert->derCert.len);
919 if (rv != Success) {
920 return rv;
923 Input stapledOCSPResponseInput;
924 Input* responseInputPtr = nullptr;
925 if (stapledOCSPResponse) {
926 rv = stapledOCSPResponseInput.Init(stapledOCSPResponse->Elements(),
927 stapledOCSPResponse->Length());
928 if (rv != Success) {
929 // The stapled OCSP response was too big.
930 return Result::ERROR_OCSP_MALFORMED_RESPONSE;
932 responseInputPtr = &stapledOCSPResponseInput;
935 if (!(flags & FLAG_TLS_IGNORE_STATUS_REQUEST)) {
936 rv = CheckTLSFeaturesAreSatisfied(peerCertInput, responseInputPtr);
937 if (rv != Success) {
938 return rv;
942 Input hostnameInput;
943 rv = hostnameInput.Init(
944 BitwiseCast<const uint8_t*, const char*>(hostname.BeginReading()),
945 hostname.Length());
946 if (rv != Success) {
947 return Result::FATAL_ERROR_INVALID_ARGS;
949 bool isBuiltInRoot;
950 rv = IsCertChainRootBuiltInRoot(builtChain, isBuiltInRoot);
951 if (rv != Success) {
952 return rv;
954 BRNameMatchingPolicy nameMatchingPolicy(
955 isBuiltInRoot ? mNameMatchingMode
956 : BRNameMatchingPolicy::Mode::DoNotEnforce);
957 rv = CheckCertHostname(peerCertInput, hostnameInput, nameMatchingPolicy);
958 if (rv != Success) {
959 // Treat malformed name information as a domain mismatch.
960 if (rv == Result::ERROR_BAD_DER) {
961 return Result::ERROR_BAD_CERT_DOMAIN;
964 return rv;
967 if (saveIntermediatesInPermanentDatabase) {
968 SaveIntermediateCerts(builtChain);
971 return Success;
974 } // namespace psm
975 } // namespace mozilla