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"
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"
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"
26 #include "mozpkix/pkix.h"
27 #include "mozpkix/pkixnss.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
,
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
) {
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
);
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() {
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
),
100 mNameMatchingMode(nameMatchingMode
),
101 mNetscapeStepUpPolicy(netscapeStepUpPolicy
),
103 mDistrustedCAPolicy(distrustedCAPolicy
) {
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
) {
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
);
121 Unused
<< mThirdPartyIntermediateInputs
.append(input
);
127 CertVerifier::~CertVerifier() {}
129 Result
IsCertChainRootBuiltInRoot(const UniqueCERTCertList
& chain
,
131 if (!chain
|| CERT_LIST_EMPTY(chain
)) {
132 return Result::FATAL_ERROR_LIBRARY_FAILURE
;
134 CERTCertListNode
* rootNode
= CERT_LIST_TAIL(chain
);
136 return Result::FATAL_ERROR_LIBRARY_FAILURE
;
138 CERTCertificate
* root
= rootNode
->cert
;
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
;
156 nsCOMPtr
<nsINSSComponent
> component(do_GetService(PSM_COMPONENT_CONTRACTID
));
158 return Result::FATAL_ERROR_LIBRARY_FAILURE
;
160 nsresult rv
= component
->IsCertTestBuiltInRoot(cert
, &result
);
162 return Result::FATAL_ERROR_LIBRARY_FAILURE
;
168 AutoSECMODListReadLock lock
;
169 for (SECMODModuleList
* list
= SECMOD_GetDefaultModuleList(); list
;
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
,
191 // Attribute was found, and is set to true
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();
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
);
221 rv
= Result::ERROR_INADEQUATE_KEY_USAGE
;
225 if (ocspStaplingStatus
) {
226 *ocspStaplingStatus
= trustDomain
.GetOCSPStaplingStatus();
231 void CertVerifier::LoadKnownCTLogs() {
232 mCTVerifier
= MakeUnique
<MultiLogCTVerifier
>();
233 for (const CTLogInfo
& log
: kCTLogList
) {
235 Result rv
= publicKey
.Init(
236 BitwiseCast
<const uint8_t*, const char*>(log
.key
), log
.keyLength
);
238 MOZ_ASSERT_UNREACHABLE("Failed reading a log key for a known CT Log");
242 CTLogVerifier logVerifier
;
243 const CTLogOperatorInfo
& logOperator
=
244 kCTLogOperatorList
[log
.operatorIndex
];
245 rv
= logVerifier
.Init(publicKey
, logOperator
.id
, log
.status
,
246 log
.disqualificationTime
);
248 MOZ_ASSERT_UNREACHABLE("Failed initializing a known CT Log");
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
) {
266 if (mCTMode
== CertificateTransparencyMode::Disabled
) {
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.
310 CTVerifyResult emptyResult
;
311 ctInfo
->verifyResult
= std::move(emptyResult
);
312 ctInfo
->policyCompliance
= CTPolicyCompliance::NotEnoughScts
;
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
));
331 endEntityDER
.Init(endEntity
->derCert
.data
, endEntity
->derCert
.len
);
336 Input issuerPublicKeyDER
;
337 rv
= issuerPublicKeyDER
.Init(issuer
->derPublicKey
.data
,
338 issuer
->derPublicKey
.len
);
343 CTVerifyResult result
;
344 rv
= mCTVerifier
->Verify(endEntityDER
, issuerPublicKeyDER
, embeddedSCTs
,
345 sctsFromOCSP
, sctsFromTLS
, time
, result
);
347 MOZ_LOG(gCertVerifierLog
, LogLevel::Debug
,
348 ("SCT verification failed with fatal error %" PRId32
"\n",
349 static_cast<uint32_t>(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
:
364 case VerifiedSCT::Status::ValidFromDisqualifiedLog
:
365 disqualifiedLogCount
++;
367 case VerifiedSCT::Status::UnknownLog
:
370 case VerifiedSCT::Status::InvalidSignature
:
371 invalidSignatureCount
++;
373 case VerifiedSCT::Status::InvalidTimestamp
:
374 invalidTimestampCount
++;
376 case VerifiedSCT::Status::None
:
378 MOZ_ASSERT_UNREACHABLE("Unexpected SCT verification status");
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
));
393 if (CERT_GetCertTimes(endEntity
, ¬Before
, ¬After
) != SECSuccess
) {
394 return Result::FATAL_ERROR_LIBRARY_FAILURE
;
396 size_t lifetimeInMonths
;
397 rv
= GetCertLifetimeInFullMonths(notBefore
, notAfter
, lifetimeInMonths
);
402 CTLogOperatorList allOperators
;
403 GetCTLogOperatorsFromVerifiedSCTList(result
.verifiedScts
, allOperators
);
405 CTLogOperatorList dependentOperators
;
406 rv
= mCTDiversityPolicy
->GetDependentOperators(builtChain
.get(), allOperators
,
412 CTPolicyEnforcer ctPolicyEnforcer
;
413 CTPolicyCompliance ctPolicyCompliance
;
414 ctPolicyEnforcer
.CheckCompliance(result
.verifiedScts
, lifetimeInMonths
,
415 dependentOperators
, ctPolicyCompliance
);
418 ctInfo
->verifyResult
= std::move(result
);
419 ctInfo
->policyCompliance
= ctPolicyCompliance
;
424 bool CertVerifier::SHA1ModeMoreRestrictiveThanGivenMode(SHA1Mode mode
) {
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
:
434 // MSVC warns unless we explicitly handle this now-unused option.
435 case SHA1Mode::UsedToBeBefore2016ButNowIsForbidden
:
437 MOZ_ASSERT(false, "unexpected SHA1Mode type");
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"));
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
;
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
;
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
;
498 (usage
!= certificateUsageSSLServer
&& (flags
& FLAG_MUST_BE_EV
))) {
499 return Result::FATAL_ERROR_INVALID_ARGS
;
503 Result rv
= certDER
.Init(cert
->derCert
.data
, cert
->derCert
.len
);
508 // We configure the OCSP fetching modes separately for EV and non-EV
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());
523 // The stapled OCSP response was too big.
524 return Result::ERROR_OCSP_MALFORMED_RESPONSE
;
526 stapledOCSPResponse
= &stapledOCSPResponseInput
;
529 Input sctsFromTLSInput
;
531 rv
= sctsFromTLSInput
.Init(sctsFromTLS
->Elements(), sctsFromTLS
->Length());
532 if (rv
!= Success
&& sctsFromTLSInput
.GetLength() != 0) {
533 return Result::FATAL_ERROR_LIBRARY_FAILURE
;
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);
549 trustDomain
, certDER
, time
, EndEntityOrCA::MustBeEndEntity
,
550 KeyUsage::digitalSignature
, KeyPurposeId::id_kp_clientAuth
,
551 CertPolicyId::anyPolicy
, stapledOCSPResponse
);
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
[] = {
565 SHA1Mode::ImportedRoot
,
566 SHA1Mode::ImportedRootOrBefore2016
,
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
);
596 i
< sha1ModeConfigurationsCount
&& rv
!= Success
&& foundEVPolicy
;
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
])) {
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
,
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
,
630 sha1ModeConfigurations
[i
] == SHA1Mode::ImportedRoot
) {
631 bool isBuiltInRoot
= false;
632 rv
= IsCertChainRootBuiltInRoot(builtChain
, isBuiltInRoot
);
637 rv
= Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED
;
641 MOZ_LOG(gCertVerifierLog
, LogLevel::Debug
,
642 ("cert is EV with status %i\n",
643 static_cast<int>(sha1ModeResults
[i
])));
645 *evOidPolicy
= evPolicyOidTag
;
647 if (sha1ModeResult
) {
648 *sha1ModeResult
= sha1ModeResults
[i
];
650 rv
= VerifyCertificateTransparencyPolicy(
651 trustDomain
, builtChain
, sctsFromTLSInput
, time
, ctInfo
);
661 if (flags
& FLAG_MUST_BE_EV
) {
662 rv
= Result::ERROR_POLICY_VALIDATION_FAILED
;
667 unsigned int keySizeOptions
[] = {MIN_RSA_BITS
, MIN_RSA_BITS_WEAK
};
669 KeySizeStatus keySizeStatuses
[] = {KeySizeStatus::LargeMinimumSucceeded
,
670 KeySizeStatus::CompatibilityRisk
};
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
;
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
])) {
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
;
721 sha1ModeConfigurations
[j
] == SHA1Mode::ImportedRoot
) {
722 bool isBuiltInRoot
= false;
723 rv
= IsCertChainRootBuiltInRoot(builtChain
, isBuiltInRoot
);
728 rv
= Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED
;
733 *keySizeStatus
= keySizeStatuses
[i
];
735 if (sha1ModeResult
) {
736 *sha1ModeResult
= sha1ModeResults
[j
];
738 rv
= VerifyCertificateTransparencyPolicy(
739 trustDomain
, builtChain
, sctsFromTLSInput
, time
, ctInfo
);
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
;
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
);
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);
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
) {
793 trustDomain
, certDER
, time
, EndEntityOrCA::MustBeEndEntity
,
794 KeyUsage::nonRepudiation
, KeyPurposeId::id_kp_emailProtection
,
795 CertPolicyId::anyPolicy
, stapledOCSPResponse
);
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
);
827 rv
= Result::FATAL_ERROR_INVALID_ARGS
;
837 static bool CertIsSelfSigned(const UniqueCERTCertificate
& cert
, void* pinarg
) {
838 if (!SECITEM_ItemsAreEqual(&cert
->derIssuer
, &cert
->derSubject
)) {
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
),
847 if (rv
!= SECSuccess
) {
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());
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.
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
);
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
));
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
;
918 rv
= peerCertInput
.Init(peerCert
->derCert
.data
, peerCert
->derCert
.len
);
923 Input stapledOCSPResponseInput
;
924 Input
* responseInputPtr
= nullptr;
925 if (stapledOCSPResponse
) {
926 rv
= stapledOCSPResponseInput
.Init(stapledOCSPResponse
->Elements(),
927 stapledOCSPResponse
->Length());
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
);
943 rv
= hostnameInput
.Init(
944 BitwiseCast
<const uint8_t*, const char*>(hostname
.BeginReading()),
947 return Result::FATAL_ERROR_INVALID_ARGS
;
950 rv
= IsCertChainRootBuiltInRoot(builtChain
, isBuiltInRoot
);
954 BRNameMatchingPolicy
nameMatchingPolicy(
955 isBuiltInRoot
? mNameMatchingMode
956 : BRNameMatchingPolicy::Mode::DoNotEnforce
);
957 rv
= CheckCertHostname(peerCertInput
, hostnameInput
, nameMatchingPolicy
);
959 // Treat malformed name information as a domain mismatch.
960 if (rv
== Result::ERROR_BAD_DER
) {
961 return Result::ERROR_BAD_CERT_DOMAIN
;
967 if (saveIntermediatesInPermanentDatabase
) {
968 SaveIntermediateCerts(builtChain
);
975 } // namespace mozilla