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;
76 static const unsigned int MIN_RSA_BITS
= 2048;
77 static const unsigned int MIN_RSA_BITS_WEAK
= 1024;
79 void CertificateTransparencyInfo::Reset() {
82 policyCompliance
= CTPolicyCompliance::Unknown
;
85 CertVerifier::CertVerifier(OcspDownloadConfig odc
, OcspStrictConfig osc
,
86 mozilla::TimeDuration ocspTimeoutSoft
,
87 mozilla::TimeDuration ocspTimeoutHard
,
88 uint32_t certShortLifetimeInDays
,
89 PinningMode pinningMode
, SHA1Mode sha1Mode
,
90 BRNameMatchingPolicy::Mode nameMatchingMode
,
91 NetscapeStepUpPolicy netscapeStepUpPolicy
,
92 CertificateTransparencyMode ctMode
,
93 DistrustedCAPolicy distrustedCAPolicy
,
94 CRLiteMode crliteMode
,
95 const Vector
<EnterpriseCert
>& thirdPartyCerts
)
96 : mOCSPDownloadConfig(odc
),
97 mOCSPStrict(osc
== ocspStrict
),
98 mOCSPTimeoutSoft(ocspTimeoutSoft
),
99 mOCSPTimeoutHard(ocspTimeoutHard
),
100 mCertShortLifetimeInDays(certShortLifetimeInDays
),
101 mPinningMode(pinningMode
),
103 mNameMatchingMode(nameMatchingMode
),
104 mNetscapeStepUpPolicy(netscapeStepUpPolicy
),
106 mDistrustedCAPolicy(distrustedCAPolicy
),
107 mCRLiteMode(crliteMode
) {
109 for (const auto& root
: thirdPartyCerts
) {
110 EnterpriseCert rootCopy
;
111 // Best-effort. If we run out of memory, users might see untrusted issuer
112 // errors, but the browser will probably crash before then.
113 if (NS_SUCCEEDED(rootCopy
.Init(root
))) {
114 Unused
<< mThirdPartyCerts
.append(std::move(rootCopy
));
117 for (const auto& root
: mThirdPartyCerts
) {
119 if (root
.GetInput(input
) == Success
) {
120 // mThirdPartyCerts consists of roots and intermediates.
121 if (root
.GetIsRoot()) {
122 // Best effort again.
123 Unused
<< mThirdPartyRootInputs
.append(input
);
125 Unused
<< mThirdPartyIntermediateInputs
.append(input
);
131 CertVerifier::~CertVerifier() = default;
133 Result
IsCertChainRootBuiltInRoot(const UniqueCERTCertList
& chain
,
135 if (!chain
|| CERT_LIST_EMPTY(chain
)) {
136 return Result::FATAL_ERROR_LIBRARY_FAILURE
;
138 CERTCertListNode
* rootNode
= CERT_LIST_TAIL(chain
);
140 return Result::FATAL_ERROR_LIBRARY_FAILURE
;
142 CERTCertificate
* root
= rootNode
->cert
;
144 return Result::FATAL_ERROR_LIBRARY_FAILURE
;
146 return IsCertBuiltInRoot(root
, result
);
149 Result
IsDelegatedCredentialAcceptable(const DelegatedCredentialInfo
& dcInfo
,
150 SECOidTag evOidPolicyTag
) {
151 bool isEcdsa
= dcInfo
.scheme
== ssl_sig_ecdsa_secp256r1_sha256
||
152 dcInfo
.scheme
== ssl_sig_ecdsa_secp384r1_sha384
||
153 dcInfo
.scheme
== ssl_sig_ecdsa_secp521r1_sha512
;
155 // Firefox currently does not advertise any RSA schemes for use
156 // with Delegated Credentials. As a secondary (on top of NSS)
157 // check, disallow any RSA SPKI here. When ssl_sig_rsa_pss_pss_*
158 // schemes are supported, check the modulus size and allow RSA here.
160 return Result::ERROR_INVALID_KEY
;
163 return Result::Success
;
166 // The term "builtin root" traditionally refers to a root CA certificate that
167 // has been added to the NSS trust store, because it has been approved
168 // for inclusion according to the Mozilla CA policy, and might be accepted
169 // by Mozilla applications as an issuer for certificates seen on the public web.
170 Result
IsCertBuiltInRoot(CERTCertificate
* cert
, bool& result
) {
171 if (NS_FAILED(BlockUntilLoadableCertsLoaded())) {
172 return Result::FATAL_ERROR_LIBRARY_FAILURE
;
177 nsCOMPtr
<nsINSSComponent
> component(do_GetService(PSM_COMPONENT_CONTRACTID
));
179 return Result::FATAL_ERROR_LIBRARY_FAILURE
;
181 nsresult rv
= component
->IsCertTestBuiltInRoot(cert
, &result
);
183 return Result::FATAL_ERROR_LIBRARY_FAILURE
;
189 AutoSECMODListReadLock lock
;
190 for (SECMODModuleList
* list
= SECMOD_GetDefaultModuleList(); list
;
192 for (int i
= 0; i
< list
->module
->slotCount
; i
++) {
193 PK11SlotInfo
* slot
= list
->module
->slots
[i
];
194 // We're searching for the "builtin root module", which is a module that
195 // contains an object with a CKA_CLASS of CKO_NETSCAPE_BUILTIN_ROOT_LIST.
196 // We use PK11_HasRootCerts() to identify a module with that property.
197 // In the past, we exclusively used the PKCS#11 module named nssckbi,
198 // which is provided by the NSS library.
199 // Nowadays, some distributions use a replacement module, which contains
200 // the builtin roots, but which also contains additional CA certificates,
201 // such as CAs trusted in a local deployment.
202 // We want to be able to distinguish between these two categories,
203 // because a CA, which may issue certificates for the public web,
204 // is expected to comply with additional requirements.
205 // If the certificate has attribute CKA_NSS_MOZILLA_CA_POLICY set to true,
206 // then we treat it as a "builtin root".
207 if (PK11_IsPresent(slot
) && PK11_HasRootCerts(slot
)) {
208 CK_OBJECT_HANDLE handle
= PK11_FindCertInSlot(slot
, cert
, nullptr);
209 if (handle
!= CK_INVALID_HANDLE
&&
210 PK11_HasAttributeSet(slot
, handle
, CKA_NSS_MOZILLA_CA_POLICY
,
212 // Attribute was found, and is set to true
222 static Result
BuildCertChainForOneKeyUsage(
223 NSSCertDBTrustDomain
& trustDomain
, Input certDER
, Time time
, KeyUsage ku1
,
224 KeyUsage ku2
, KeyUsage ku3
, KeyPurposeId eku
,
225 const CertPolicyId
& requiredPolicy
, const Input
* stapledOCSPResponse
,
226 /*optional out*/ CertVerifier::OCSPStaplingStatus
* ocspStaplingStatus
) {
227 trustDomain
.ResetAccumulatedState();
229 BuildCertChain(trustDomain
, certDER
, time
, EndEntityOrCA::MustBeEndEntity
,
230 ku1
, eku
, requiredPolicy
, stapledOCSPResponse
);
231 if (rv
== Result::ERROR_INADEQUATE_KEY_USAGE
) {
232 trustDomain
.ResetAccumulatedState();
233 rv
= BuildCertChain(trustDomain
, certDER
, time
,
234 EndEntityOrCA::MustBeEndEntity
, ku2
, eku
,
235 requiredPolicy
, stapledOCSPResponse
);
236 if (rv
== Result::ERROR_INADEQUATE_KEY_USAGE
) {
237 trustDomain
.ResetAccumulatedState();
238 rv
= BuildCertChain(trustDomain
, certDER
, time
,
239 EndEntityOrCA::MustBeEndEntity
, ku3
, eku
,
240 requiredPolicy
, stapledOCSPResponse
);
242 rv
= Result::ERROR_INADEQUATE_KEY_USAGE
;
246 if (ocspStaplingStatus
) {
247 *ocspStaplingStatus
= trustDomain
.GetOCSPStaplingStatus();
252 void CertVerifier::LoadKnownCTLogs() {
253 if (mCTMode
== CertificateTransparencyMode::Disabled
) {
256 mCTVerifier
= MakeUnique
<MultiLogCTVerifier
>();
257 for (const CTLogInfo
& log
: kCTLogList
) {
259 Result rv
= publicKey
.Init(
260 BitwiseCast
<const uint8_t*, const char*>(log
.key
), log
.keyLength
);
262 MOZ_ASSERT_UNREACHABLE("Failed reading a log key for a known CT Log");
266 CTLogVerifier logVerifier
;
267 const CTLogOperatorInfo
& logOperator
=
268 kCTLogOperatorList
[log
.operatorIndex
];
269 rv
= logVerifier
.Init(publicKey
, logOperator
.id
, log
.status
,
270 log
.disqualificationTime
);
272 MOZ_ASSERT_UNREACHABLE("Failed initializing a known CT Log");
276 mCTVerifier
->AddLog(std::move(logVerifier
));
278 // TBD: Initialize mCTDiversityPolicy with the CA dependency map
279 // of the known CT logs operators.
280 mCTDiversityPolicy
= MakeUnique
<CTDiversityPolicy
>();
283 Result
CertVerifier::VerifyCertificateTransparencyPolicy(
284 NSSCertDBTrustDomain
& trustDomain
, const UniqueCERTCertList
& builtChain
,
285 Input sctsFromTLS
, Time time
,
286 /*optional out*/ CertificateTransparencyInfo
* ctInfo
) {
290 if (mCTMode
== CertificateTransparencyMode::Disabled
) {
294 ctInfo
->enabled
= true;
297 if (!builtChain
|| CERT_LIST_EMPTY(builtChain
)) {
298 return Result::FATAL_ERROR_INVALID_ARGS
;
301 Input embeddedSCTs
= trustDomain
.GetSCTListFromCertificate();
302 if (embeddedSCTs
.GetLength() > 0) {
303 MOZ_LOG(gCertVerifierLog
, LogLevel::Debug
,
304 ("Got embedded SCT data of length %zu\n",
305 static_cast<size_t>(embeddedSCTs
.GetLength())));
307 Input sctsFromOCSP
= trustDomain
.GetSCTListFromOCSPStapling();
308 if (sctsFromOCSP
.GetLength() > 0) {
309 MOZ_LOG(gCertVerifierLog
, LogLevel::Debug
,
310 ("Got OCSP SCT data of length %zu\n",
311 static_cast<size_t>(sctsFromOCSP
.GetLength())));
313 if (sctsFromTLS
.GetLength() > 0) {
314 MOZ_LOG(gCertVerifierLog
, LogLevel::Debug
,
315 ("Got TLS SCT data of length %zu\n",
316 static_cast<size_t>(sctsFromTLS
.GetLength())));
319 CERTCertListNode
* endEntityNode
= CERT_LIST_HEAD(builtChain
);
320 if (!endEntityNode
|| CERT_LIST_END(endEntityNode
, builtChain
)) {
321 return Result::FATAL_ERROR_INVALID_ARGS
;
323 CERTCertListNode
* issuerNode
= CERT_LIST_NEXT(endEntityNode
);
324 if (!issuerNode
|| CERT_LIST_END(issuerNode
, builtChain
)) {
325 // Issuer certificate is required for SCT verification.
326 // If we've arrived here, we probably have a "trust chain" with only one
327 // certificate (i.e. a self-signed end-entity that has been set as a trust
328 // anchor either by a third party modifying our trust DB or via the
329 // enterprise roots feature). If this is the case, certificate transparency
330 // information will probably not be present, and it certainly won't verify
331 // correctly. To simplify things, we return an empty CTVerifyResult and a
332 // "not enough SCTs" CTPolicyCompliance result.
334 CTVerifyResult emptyResult
;
335 ctInfo
->verifyResult
= std::move(emptyResult
);
336 ctInfo
->policyCompliance
= CTPolicyCompliance::NotEnoughScts
;
341 CERTCertificate
* endEntity
= endEntityNode
->cert
;
342 CERTCertificate
* issuer
= issuerNode
->cert
;
343 if (!endEntity
|| !issuer
) {
344 return Result::FATAL_ERROR_INVALID_ARGS
;
347 if (endEntity
->subjectName
) {
348 MOZ_LOG(gCertVerifierLog
, LogLevel::Debug
,
349 ("Verifying CT Policy compliance of subject %s\n",
350 endEntity
->subjectName
));
355 endEntityDER
.Init(endEntity
->derCert
.data
, endEntity
->derCert
.len
);
360 Input issuerPublicKeyDER
;
361 rv
= issuerPublicKeyDER
.Init(issuer
->derPublicKey
.data
,
362 issuer
->derPublicKey
.len
);
367 CTVerifyResult result
;
368 rv
= mCTVerifier
->Verify(endEntityDER
, issuerPublicKeyDER
, embeddedSCTs
,
369 sctsFromOCSP
, sctsFromTLS
, time
, result
);
371 MOZ_LOG(gCertVerifierLog
, LogLevel::Debug
,
372 ("SCT verification failed with fatal error %" PRId32
"\n",
373 static_cast<uint32_t>(rv
)));
377 if (MOZ_LOG_TEST(gCertVerifierLog
, LogLevel::Debug
)) {
378 size_t validCount
= 0;
379 size_t unknownLogCount
= 0;
380 size_t disqualifiedLogCount
= 0;
381 size_t invalidSignatureCount
= 0;
382 size_t invalidTimestampCount
= 0;
383 for (const VerifiedSCT
& verifiedSct
: result
.verifiedScts
) {
384 switch (verifiedSct
.status
) {
385 case VerifiedSCT::Status::Valid
:
388 case VerifiedSCT::Status::ValidFromDisqualifiedLog
:
389 disqualifiedLogCount
++;
391 case VerifiedSCT::Status::UnknownLog
:
394 case VerifiedSCT::Status::InvalidSignature
:
395 invalidSignatureCount
++;
397 case VerifiedSCT::Status::InvalidTimestamp
:
398 invalidTimestampCount
++;
400 case VerifiedSCT::Status::None
:
402 MOZ_ASSERT_UNREACHABLE("Unexpected SCT verification status");
406 gCertVerifierLog
, LogLevel::Debug
,
407 ("SCT verification result: "
408 "valid=%zu unknownLog=%zu disqualifiedLog=%zu "
409 "invalidSignature=%zu invalidTimestamp=%zu "
410 "decodingErrors=%zu\n",
411 validCount
, unknownLogCount
, disqualifiedLogCount
,
412 invalidSignatureCount
, invalidTimestampCount
, result
.decodingErrors
));
417 if (CERT_GetCertTimes(endEntity
, ¬Before
, ¬After
) != SECSuccess
) {
418 return Result::FATAL_ERROR_LIBRARY_FAILURE
;
420 size_t lifetimeInMonths
;
421 rv
= GetCertLifetimeInFullMonths(notBefore
, notAfter
, lifetimeInMonths
);
426 CTLogOperatorList allOperators
;
427 GetCTLogOperatorsFromVerifiedSCTList(result
.verifiedScts
, allOperators
);
429 CTLogOperatorList dependentOperators
;
430 rv
= mCTDiversityPolicy
->GetDependentOperators(builtChain
.get(), allOperators
,
436 CTPolicyEnforcer ctPolicyEnforcer
;
437 CTPolicyCompliance ctPolicyCompliance
;
438 ctPolicyEnforcer
.CheckCompliance(result
.verifiedScts
, lifetimeInMonths
,
439 dependentOperators
, ctPolicyCompliance
);
442 ctInfo
->verifyResult
= std::move(result
);
443 ctInfo
->policyCompliance
= ctPolicyCompliance
;
448 bool CertVerifier::SHA1ModeMoreRestrictiveThanGivenMode(SHA1Mode mode
) {
450 case SHA1Mode::Forbidden
:
451 return mode
!= SHA1Mode::Forbidden
;
452 case SHA1Mode::ImportedRoot
:
453 return mode
!= SHA1Mode::Forbidden
&& mode
!= SHA1Mode::ImportedRoot
;
454 case SHA1Mode::ImportedRootOrBefore2016
:
455 return mode
== SHA1Mode::Allowed
;
456 case SHA1Mode::Allowed
:
458 // MSVC warns unless we explicitly handle this now-unused option.
459 case SHA1Mode::UsedToBeBefore2016ButNowIsForbidden
:
461 MOZ_ASSERT(false, "unexpected SHA1Mode type");
466 Result
CertVerifier::VerifyCert(
467 CERTCertificate
* cert
, SECCertificateUsage usage
, Time time
, void* pinArg
,
468 const char* hostname
,
469 /*out*/ UniqueCERTCertList
& builtChain
,
470 /*optional*/ const Flags flags
,
471 /*optional*/ const Maybe
<nsTArray
<nsTArray
<uint8_t>>>& extraCertificates
,
472 /*optional*/ const Maybe
<nsTArray
<uint8_t>>& stapledOCSPResponseArg
,
473 /*optional*/ const Maybe
<nsTArray
<uint8_t>>& sctsFromTLS
,
474 /*optional*/ const OriginAttributes
& originAttributes
,
475 /*optional out*/ SECOidTag
* evOidPolicy
,
476 /*optional out*/ OCSPStaplingStatus
* ocspStaplingStatus
,
477 /*optional out*/ KeySizeStatus
* keySizeStatus
,
478 /*optional out*/ SHA1ModeResult
* sha1ModeResult
,
479 /*optional out*/ PinningTelemetryInfo
* pinningTelemetryInfo
,
480 /*optional out*/ CertificateTransparencyInfo
* ctInfo
,
481 /*optional out*/ CRLiteTelemetryInfo
* crliteInfo
) {
482 MOZ_LOG(gCertVerifierLog
, LogLevel::Debug
, ("Top of VerifyCert\n"));
485 MOZ_ASSERT(usage
== certificateUsageSSLServer
|| !(flags
& FLAG_MUST_BE_EV
));
486 MOZ_ASSERT(usage
== certificateUsageSSLServer
|| !keySizeStatus
);
487 MOZ_ASSERT(usage
== certificateUsageSSLServer
|| !sha1ModeResult
);
489 if (NS_FAILED(BlockUntilLoadableCertsLoaded())) {
490 return Result::FATAL_ERROR_LIBRARY_FAILURE
;
492 if (NS_FAILED(CheckForSmartCardChanges())) {
493 return Result::FATAL_ERROR_LIBRARY_FAILURE
;
497 *evOidPolicy
= SEC_OID_UNKNOWN
;
499 if (ocspStaplingStatus
) {
500 if (usage
!= certificateUsageSSLServer
) {
501 return Result::FATAL_ERROR_INVALID_ARGS
;
503 *ocspStaplingStatus
= OCSP_STAPLING_NEVER_CHECKED
;
507 if (usage
!= certificateUsageSSLServer
) {
508 return Result::FATAL_ERROR_INVALID_ARGS
;
510 *keySizeStatus
= KeySizeStatus::NeverChecked
;
513 if (sha1ModeResult
) {
514 if (usage
!= certificateUsageSSLServer
) {
515 return Result::FATAL_ERROR_INVALID_ARGS
;
517 *sha1ModeResult
= SHA1ModeResult::NeverChecked
;
521 (usage
!= certificateUsageSSLServer
&& (flags
& FLAG_MUST_BE_EV
))) {
522 return Result::FATAL_ERROR_INVALID_ARGS
;
526 Result rv
= certDER
.Init(cert
->derCert
.data
, cert
->derCert
.len
);
531 // We configure the OCSP fetching modes separately for EV and non-EV
533 NSSCertDBTrustDomain::OCSPFetching defaultOCSPFetching
=
534 (mOCSPDownloadConfig
== ocspOff
) || (mOCSPDownloadConfig
== ocspEVOnly
) ||
535 (flags
& FLAG_LOCAL_ONLY
)
536 ? NSSCertDBTrustDomain::NeverFetchOCSP
537 : !mOCSPStrict
? NSSCertDBTrustDomain::FetchOCSPForDVSoftFail
538 : NSSCertDBTrustDomain::FetchOCSPForDVHardFail
;
540 Input stapledOCSPResponseInput
;
541 const Input
* stapledOCSPResponse
= nullptr;
542 if (stapledOCSPResponseArg
) {
543 rv
= stapledOCSPResponseInput
.Init(stapledOCSPResponseArg
->Elements(),
544 stapledOCSPResponseArg
->Length());
546 // The stapled OCSP response was too big.
547 return Result::ERROR_OCSP_MALFORMED_RESPONSE
;
549 stapledOCSPResponse
= &stapledOCSPResponseInput
;
552 Input sctsFromTLSInput
;
554 rv
= sctsFromTLSInput
.Init(sctsFromTLS
->Elements(), sctsFromTLS
->Length());
555 if (rv
!= Success
&& sctsFromTLSInput
.GetLength() != 0) {
556 return Result::FATAL_ERROR_LIBRARY_FAILURE
;
561 case certificateUsageSSLClient
: {
562 // XXX: We don't really have a trust bit for SSL client authentication so
563 // just use trustEmail as it is the closest alternative.
564 NSSCertDBTrustDomain
trustDomain(
565 trustEmail
, defaultOCSPFetching
, mOCSPCache
, pinArg
, mOCSPTimeoutSoft
,
566 mOCSPTimeoutHard
, mCertShortLifetimeInDays
, pinningDisabled
,
567 MIN_RSA_BITS_WEAK
, ValidityCheckingMode::CheckingOff
,
568 SHA1Mode::Allowed
, NetscapeStepUpPolicy::NeverMatch
,
569 mDistrustedCAPolicy
, mCRLiteMode
, originAttributes
,
570 mThirdPartyRootInputs
, mThirdPartyIntermediateInputs
,
571 extraCertificates
, builtChain
, nullptr, nullptr);
573 trustDomain
, certDER
, time
, EndEntityOrCA::MustBeEndEntity
,
574 KeyUsage::digitalSignature
, KeyPurposeId::id_kp_clientAuth
,
575 CertPolicyId::anyPolicy
, stapledOCSPResponse
);
579 case certificateUsageSSLServer
: {
580 // TODO: When verifying a certificate in an SSL handshake, we should
581 // restrict the acceptable key usage based on the key exchange method
582 // chosen by the server.
584 // These configurations are in order of most restrictive to least
585 // restrictive. This enables us to gather telemetry on the expected
586 // results of setting the default policy to a particular configuration.
587 SHA1Mode sha1ModeConfigurations
[] = {
589 SHA1Mode::ImportedRoot
,
590 SHA1Mode::ImportedRootOrBefore2016
,
594 SHA1ModeResult sha1ModeResults
[] = {
595 SHA1ModeResult::SucceededWithoutSHA1
,
596 SHA1ModeResult::SucceededWithImportedRoot
,
597 SHA1ModeResult::SucceededWithImportedRootOrSHA1Before2016
,
598 SHA1ModeResult::SucceededWithSHA1
,
601 size_t sha1ModeConfigurationsCount
=
602 MOZ_ARRAY_LENGTH(sha1ModeConfigurations
);
604 static_assert(MOZ_ARRAY_LENGTH(sha1ModeConfigurations
) ==
605 MOZ_ARRAY_LENGTH(sha1ModeResults
),
606 "digestAlgorithm array lengths differ");
608 rv
= Result::ERROR_UNKNOWN_ERROR
;
610 // Try to validate for EV first.
611 NSSCertDBTrustDomain::OCSPFetching evOCSPFetching
=
612 (mOCSPDownloadConfig
== ocspOff
) || (flags
& FLAG_LOCAL_ONLY
)
613 ? NSSCertDBTrustDomain::LocalOnlyOCSPForEV
614 : NSSCertDBTrustDomain::FetchOCSPForEV
;
616 CertPolicyId evPolicy
;
617 SECOidTag evPolicyOidTag
;
618 bool foundEVPolicy
= GetFirstEVPolicy(*cert
, evPolicy
, evPolicyOidTag
);
620 i
< sha1ModeConfigurationsCount
&& rv
!= Success
&& foundEVPolicy
;
622 // Don't attempt verification if the SHA1 mode set by preferences
623 // (mSHA1Mode) is more restrictive than the SHA1 mode option we're on.
624 // (To put it another way, only attempt verification if the SHA1 mode
625 // option we're on is as restrictive or more restrictive than
626 // mSHA1Mode.) This allows us to gather telemetry information while
627 // still enforcing the mode set by preferences.
628 if (SHA1ModeMoreRestrictiveThanGivenMode(sha1ModeConfigurations
[i
])) {
632 // Because of the try-strict and fallback approach, we have to clear any
633 // previously noted telemetry information.
634 if (pinningTelemetryInfo
) {
635 pinningTelemetryInfo
->Reset();
641 NSSCertDBTrustDomain
trustDomain(
642 trustSSL
, evOCSPFetching
, mOCSPCache
, pinArg
, mOCSPTimeoutSoft
,
643 mOCSPTimeoutHard
, mCertShortLifetimeInDays
, mPinningMode
,
644 MIN_RSA_BITS
, ValidityCheckingMode::CheckForEV
,
645 sha1ModeConfigurations
[i
], mNetscapeStepUpPolicy
,
646 mDistrustedCAPolicy
, mCRLiteMode
, originAttributes
,
647 mThirdPartyRootInputs
, mThirdPartyIntermediateInputs
,
648 extraCertificates
, builtChain
, pinningTelemetryInfo
, crliteInfo
,
650 rv
= BuildCertChainForOneKeyUsage(
651 trustDomain
, certDER
, time
,
652 KeyUsage::digitalSignature
, // (EC)DHE
653 KeyUsage::keyEncipherment
, // RSA
654 KeyUsage::keyAgreement
, // (EC)DH
655 KeyPurposeId::id_kp_serverAuth
, evPolicy
, stapledOCSPResponse
,
658 sha1ModeConfigurations
[i
] == SHA1Mode::ImportedRoot
) {
659 bool isBuiltInRoot
= false;
660 rv
= IsCertChainRootBuiltInRoot(builtChain
, isBuiltInRoot
);
665 rv
= Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED
;
669 MOZ_LOG(gCertVerifierLog
, LogLevel::Debug
,
670 ("cert is EV with status %i\n",
671 static_cast<int>(sha1ModeResults
[i
])));
673 *evOidPolicy
= evPolicyOidTag
;
675 if (sha1ModeResult
) {
676 *sha1ModeResult
= sha1ModeResults
[i
];
678 rv
= VerifyCertificateTransparencyPolicy(
679 trustDomain
, builtChain
, sctsFromTLSInput
, time
, ctInfo
);
689 if (flags
& FLAG_MUST_BE_EV
) {
690 rv
= Result::ERROR_POLICY_VALIDATION_FAILED
;
695 unsigned int keySizeOptions
[] = {MIN_RSA_BITS
, MIN_RSA_BITS_WEAK
};
697 KeySizeStatus keySizeStatuses
[] = {KeySizeStatus::LargeMinimumSucceeded
,
698 KeySizeStatus::CompatibilityRisk
};
701 MOZ_ARRAY_LENGTH(keySizeOptions
) == MOZ_ARRAY_LENGTH(keySizeStatuses
),
702 "keySize array lengths differ");
704 size_t keySizeOptionsCount
= MOZ_ARRAY_LENGTH(keySizeStatuses
);
706 for (size_t i
= 0; i
< keySizeOptionsCount
&& rv
!= Success
; i
++) {
707 for (size_t j
= 0; j
< sha1ModeConfigurationsCount
&& rv
!= Success
;
709 // Don't attempt verification if the SHA1 mode set by preferences
710 // (mSHA1Mode) is more restrictive than the SHA1 mode option we're on.
711 // (To put it another way, only attempt verification if the SHA1 mode
712 // option we're on is as restrictive or more restrictive than
713 // mSHA1Mode.) This allows us to gather telemetry information while
714 // still enforcing the mode set by preferences.
715 if (SHA1ModeMoreRestrictiveThanGivenMode(sha1ModeConfigurations
[j
])) {
719 // invalidate any telemetry info relating to failed chains
720 if (pinningTelemetryInfo
) {
721 pinningTelemetryInfo
->Reset();
727 NSSCertDBTrustDomain
trustDomain(
728 trustSSL
, defaultOCSPFetching
, mOCSPCache
, pinArg
,
729 mOCSPTimeoutSoft
, mOCSPTimeoutHard
, mCertShortLifetimeInDays
,
730 mPinningMode
, keySizeOptions
[i
],
731 ValidityCheckingMode::CheckingOff
, sha1ModeConfigurations
[j
],
732 mNetscapeStepUpPolicy
, mDistrustedCAPolicy
, mCRLiteMode
,
733 originAttributes
, mThirdPartyRootInputs
,
734 mThirdPartyIntermediateInputs
, extraCertificates
, builtChain
,
735 pinningTelemetryInfo
, crliteInfo
, hostname
);
736 rv
= BuildCertChainForOneKeyUsage(
737 trustDomain
, certDER
, time
,
738 KeyUsage::digitalSignature
, //(EC)DHE
739 KeyUsage::keyEncipherment
, // RSA
740 KeyUsage::keyAgreement
, //(EC)DH
741 KeyPurposeId::id_kp_serverAuth
, CertPolicyId::anyPolicy
,
742 stapledOCSPResponse
, ocspStaplingStatus
);
743 if (rv
!= Success
&& !IsFatalError(rv
) &&
744 rv
!= Result::ERROR_REVOKED_CERTIFICATE
&&
745 trustDomain
.GetIsErrorDueToDistrustedCAPolicy()) {
746 // Bug 1444440 - If there are multiple paths, at least one to a CA
747 // distrusted-by-policy, and none of them ending in a trusted root,
748 // then we might show a different error (UNKNOWN_ISSUER) than we
749 // intend, confusing users.
750 rv
= Result::ERROR_ADDITIONAL_POLICY_CONSTRAINT_FAILED
;
753 sha1ModeConfigurations
[j
] == SHA1Mode::ImportedRoot
) {
754 bool isBuiltInRoot
= false;
755 rv
= IsCertChainRootBuiltInRoot(builtChain
, isBuiltInRoot
);
760 rv
= Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED
;
765 *keySizeStatus
= keySizeStatuses
[i
];
767 if (sha1ModeResult
) {
768 *sha1ModeResult
= sha1ModeResults
[j
];
770 rv
= VerifyCertificateTransparencyPolicy(
771 trustDomain
, builtChain
, sctsFromTLSInput
, time
, ctInfo
);
784 *keySizeStatus
= KeySizeStatus::AlreadyBad
;
786 // The telemetry probe CERT_CHAIN_SHA1_POLICY_STATUS gives us feedback on
787 // the result of setting a specific policy. However, we don't want noise
788 // from users who have manually set the policy to something other than the
789 // default, so we only collect for ImportedRoot (which is the default).
790 if (sha1ModeResult
&& mSHA1Mode
== SHA1Mode::ImportedRoot
) {
791 *sha1ModeResult
= SHA1ModeResult::Failed
;
797 case certificateUsageSSLCA
: {
798 NSSCertDBTrustDomain
trustDomain(
799 trustSSL
, defaultOCSPFetching
, mOCSPCache
, pinArg
, mOCSPTimeoutSoft
,
800 mOCSPTimeoutHard
, mCertShortLifetimeInDays
, pinningDisabled
,
801 MIN_RSA_BITS_WEAK
, ValidityCheckingMode::CheckingOff
,
802 SHA1Mode::Allowed
, mNetscapeStepUpPolicy
, mDistrustedCAPolicy
,
803 mCRLiteMode
, originAttributes
, mThirdPartyRootInputs
,
804 mThirdPartyIntermediateInputs
, extraCertificates
, builtChain
, nullptr,
806 rv
= BuildCertChain(trustDomain
, certDER
, time
, EndEntityOrCA::MustBeCA
,
807 KeyUsage::keyCertSign
, KeyPurposeId::id_kp_serverAuth
,
808 CertPolicyId::anyPolicy
, stapledOCSPResponse
);
812 case certificateUsageEmailSigner
: {
813 NSSCertDBTrustDomain
trustDomain(
814 trustEmail
, defaultOCSPFetching
, mOCSPCache
, pinArg
, mOCSPTimeoutSoft
,
815 mOCSPTimeoutHard
, mCertShortLifetimeInDays
, pinningDisabled
,
816 MIN_RSA_BITS_WEAK
, ValidityCheckingMode::CheckingOff
,
817 SHA1Mode::Allowed
, NetscapeStepUpPolicy::NeverMatch
,
818 mDistrustedCAPolicy
, mCRLiteMode
, originAttributes
,
819 mThirdPartyRootInputs
, mThirdPartyIntermediateInputs
,
820 extraCertificates
, builtChain
, nullptr, nullptr);
822 trustDomain
, certDER
, time
, EndEntityOrCA::MustBeEndEntity
,
823 KeyUsage::digitalSignature
, KeyPurposeId::id_kp_emailProtection
,
824 CertPolicyId::anyPolicy
, stapledOCSPResponse
);
825 if (rv
== Result::ERROR_INADEQUATE_KEY_USAGE
) {
827 trustDomain
, certDER
, time
, EndEntityOrCA::MustBeEndEntity
,
828 KeyUsage::nonRepudiation
, KeyPurposeId::id_kp_emailProtection
,
829 CertPolicyId::anyPolicy
, stapledOCSPResponse
);
834 case certificateUsageEmailRecipient
: {
835 // TODO: The higher level S/MIME processing should pass in which key
836 // usage it is trying to verify for, and base its algorithm choices
837 // based on the result of the verification(s).
838 NSSCertDBTrustDomain
trustDomain(
839 trustEmail
, defaultOCSPFetching
, mOCSPCache
, pinArg
, mOCSPTimeoutSoft
,
840 mOCSPTimeoutHard
, mCertShortLifetimeInDays
, pinningDisabled
,
841 MIN_RSA_BITS_WEAK
, ValidityCheckingMode::CheckingOff
,
842 SHA1Mode::Allowed
, NetscapeStepUpPolicy::NeverMatch
,
843 mDistrustedCAPolicy
, mCRLiteMode
, originAttributes
,
844 mThirdPartyRootInputs
, mThirdPartyIntermediateInputs
,
845 extraCertificates
, builtChain
, nullptr, nullptr);
846 rv
= BuildCertChain(trustDomain
, certDER
, time
,
847 EndEntityOrCA::MustBeEndEntity
,
848 KeyUsage::keyEncipherment
, // RSA
849 KeyPurposeId::id_kp_emailProtection
,
850 CertPolicyId::anyPolicy
, stapledOCSPResponse
);
851 if (rv
== Result::ERROR_INADEQUATE_KEY_USAGE
) {
852 rv
= BuildCertChain(trustDomain
, certDER
, time
,
853 EndEntityOrCA::MustBeEndEntity
,
854 KeyUsage::keyAgreement
, // ECDH/DH
855 KeyPurposeId::id_kp_emailProtection
,
856 CertPolicyId::anyPolicy
, stapledOCSPResponse
);
862 rv
= Result::FATAL_ERROR_INVALID_ARGS
;
872 static bool CertIsSelfSigned(const UniqueCERTCertificate
& cert
, void* pinarg
) {
873 if (!SECITEM_ItemsAreEqual(&cert
->derIssuer
, &cert
->derSubject
)) {
877 // Check that the certificate is signed with the cert's spki.
878 SECStatus rv
= CERT_VerifySignedDataWithPublicKeyInfo(
879 const_cast<CERTSignedData
*>(&cert
->signatureWrap
),
880 const_cast<CERTSubjectPublicKeyInfo
*>(&cert
->subjectPublicKeyInfo
),
882 if (rv
!= SECSuccess
) {
889 Result
CertVerifier::VerifySSLServerCert(
890 const UniqueCERTCertificate
& peerCert
, Time time
,
891 /*optional*/ void* pinarg
, const nsACString
& hostname
,
892 /*out*/ UniqueCERTCertList
& builtChain
,
893 /*optional*/ Flags flags
,
894 /*optional*/ const Maybe
<nsTArray
<nsTArray
<uint8_t>>>& extraCertificates
,
895 /*optional*/ const Maybe
<nsTArray
<uint8_t>>& stapledOCSPResponse
,
896 /*optional*/ const Maybe
<nsTArray
<uint8_t>>& sctsFromTLS
,
897 /*optional*/ const Maybe
<DelegatedCredentialInfo
>& dcInfo
,
898 /*optional*/ const OriginAttributes
& originAttributes
,
899 /*optional*/ bool saveIntermediatesInPermanentDatabase
,
900 /*optional out*/ SECOidTag
* evOidPolicy
,
901 /*optional out*/ OCSPStaplingStatus
* ocspStaplingStatus
,
902 /*optional out*/ KeySizeStatus
* keySizeStatus
,
903 /*optional out*/ SHA1ModeResult
* sha1ModeResult
,
904 /*optional out*/ PinningTelemetryInfo
* pinningTelemetryInfo
,
905 /*optional out*/ CertificateTransparencyInfo
* ctInfo
,
906 /*optional out*/ CRLiteTelemetryInfo
* crliteInfo
,
907 /*optional out*/ bool* isBuiltCertChainRootBuiltInRoot
) {
908 MOZ_ASSERT(peerCert
);
909 // XXX: MOZ_ASSERT(pinarg);
910 MOZ_ASSERT(!hostname
.IsEmpty());
912 SECOidTag evPolicyOidTag
= SEC_OID_UNKNOWN
;
914 if (isBuiltCertChainRootBuiltInRoot
) {
915 *isBuiltCertChainRootBuiltInRoot
= false;
919 *evOidPolicy
= evPolicyOidTag
;
922 if (hostname
.IsEmpty()) {
923 return Result::ERROR_BAD_CERT_DOMAIN
;
926 // CreateCertErrorRunnable assumes that CheckCertHostname is only called
927 // if VerifyCert succeeded.
928 Result rv
= VerifyCert(peerCert
.get(), certificateUsageSSLServer
, time
,
929 pinarg
, PromiseFlatCString(hostname
).get(), builtChain
,
930 flags
, extraCertificates
, stapledOCSPResponse
,
931 sctsFromTLS
, originAttributes
, &evPolicyOidTag
,
932 ocspStaplingStatus
, keySizeStatus
, sha1ModeResult
,
933 pinningTelemetryInfo
, ctInfo
, crliteInfo
);
935 if (rv
== Result::ERROR_UNKNOWN_ISSUER
&&
936 CertIsSelfSigned(peerCert
, pinarg
)) {
937 // In this case we didn't find any issuer for the certificate and the
938 // certificate is self-signed.
939 return Result::ERROR_SELF_SIGNED_CERT
;
941 if (rv
== Result::ERROR_UNKNOWN_ISSUER
) {
942 // In this case we didn't get any valid path for the cert. Let's see if
943 // the issuer is the same as the issuer for our canary probe. If yes, this
944 // connection is connecting via a misconfigured proxy.
945 // Note: The MitM canary might not be set. In this case we consider this
946 // an unknown issuer error.
947 nsCOMPtr
<nsINSSComponent
> component(
948 do_GetService(PSM_COMPONENT_CONTRACTID
));
950 return Result::FATAL_ERROR_LIBRARY_FAILURE
;
952 // IssuerMatchesMitmCanary succeeds if the issuer matches the canary and
953 // the feature is enabled.
954 nsresult rv
= component
->IssuerMatchesMitmCanary(peerCert
->issuerName
);
955 if (NS_SUCCEEDED(rv
)) {
956 return Result::ERROR_MITM_DETECTED
;
963 rv
= IsDelegatedCredentialAcceptable(*dcInfo
, evPolicyOidTag
);
970 rv
= peerCertInput
.Init(peerCert
->derCert
.data
, peerCert
->derCert
.len
);
975 Input stapledOCSPResponseInput
;
976 Input
* responseInputPtr
= nullptr;
977 if (stapledOCSPResponse
) {
978 rv
= stapledOCSPResponseInput
.Init(stapledOCSPResponse
->Elements(),
979 stapledOCSPResponse
->Length());
981 // The stapled OCSP response was too big.
982 return Result::ERROR_OCSP_MALFORMED_RESPONSE
;
984 responseInputPtr
= &stapledOCSPResponseInput
;
987 if (!(flags
& FLAG_TLS_IGNORE_STATUS_REQUEST
)) {
988 rv
= CheckTLSFeaturesAreSatisfied(peerCertInput
, responseInputPtr
);
995 rv
= hostnameInput
.Init(
996 BitwiseCast
<const uint8_t*, const char*>(hostname
.BeginReading()),
999 return Result::FATAL_ERROR_INVALID_ARGS
;
1002 rv
= IsCertChainRootBuiltInRoot(builtChain
, isBuiltInRoot
);
1003 if (rv
!= Success
) {
1007 if (isBuiltCertChainRootBuiltInRoot
) {
1008 *isBuiltCertChainRootBuiltInRoot
= isBuiltInRoot
;
1011 BRNameMatchingPolicy
nameMatchingPolicy(
1012 isBuiltInRoot
? mNameMatchingMode
1013 : BRNameMatchingPolicy::Mode::DoNotEnforce
);
1014 rv
= CheckCertHostname(peerCertInput
, hostnameInput
, nameMatchingPolicy
);
1015 if (rv
!= Success
) {
1016 // Treat malformed name information as a domain mismatch.
1017 if (rv
== Result::ERROR_BAD_DER
) {
1018 return Result::ERROR_BAD_CERT_DOMAIN
;
1024 if (saveIntermediatesInPermanentDatabase
) {
1025 SaveIntermediateCerts(builtChain
);
1029 *evOidPolicy
= evPolicyOidTag
;
1036 } // namespace mozilla