Line data Source code
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/. */
6 :
7 : #include "NSSCertDBTrustDomain.h"
8 :
9 : #include <stdint.h>
10 :
11 : #include "ExtendedValidation.h"
12 : #include "NSSErrorsService.h"
13 : #include "OCSPRequestor.h"
14 : #include "OCSPVerificationTrustDomain.h"
15 : #include "PublicKeyPinningService.h"
16 : #include "cert.h"
17 : #include "certdb.h"
18 : #include "mozilla/Assertions.h"
19 : #include "mozilla/Casting.h"
20 : #include "mozilla/Move.h"
21 : #include "mozilla/PodOperations.h"
22 : #include "mozilla/TimeStamp.h"
23 : #include "mozilla/Unused.h"
24 : #include "nsNSSCertificate.h"
25 : #include "nsServiceManagerUtils.h"
26 : #include "nss.h"
27 : #include "pk11pub.h"
28 : #include "pkix/Result.h"
29 : #include "pkix/pkix.h"
30 : #include "pkix/pkixnss.h"
31 : #include "prerror.h"
32 : #include "secerr.h"
33 :
34 : #include "CNNICHashWhitelist.inc"
35 : #include "StartComAndWoSignData.inc"
36 :
37 : using namespace mozilla;
38 : using namespace mozilla::pkix;
39 :
40 : extern LazyLogModule gCertVerifierLog;
41 :
42 : static const uint64_t ServerFailureDelaySeconds = 5 * 60;
43 :
44 : namespace mozilla { namespace psm {
45 :
46 0 : NSSCertDBTrustDomain::NSSCertDBTrustDomain(SECTrustType certDBTrustType,
47 : OCSPFetching ocspFetching,
48 : OCSPCache& ocspCache,
49 : /*optional but shouldn't be*/ void* pinArg,
50 : CertVerifier::OcspGetConfig ocspGETConfig,
51 : TimeDuration ocspTimeoutSoft,
52 : TimeDuration ocspTimeoutHard,
53 : uint32_t certShortLifetimeInDays,
54 : CertVerifier::PinningMode pinningMode,
55 : unsigned int minRSABits,
56 : ValidityCheckingMode validityCheckingMode,
57 : CertVerifier::SHA1Mode sha1Mode,
58 : NetscapeStepUpPolicy netscapeStepUpPolicy,
59 : const OriginAttributes& originAttributes,
60 : UniqueCERTCertList& builtChain,
61 : /*optional*/ UniqueCERTCertList* peerCertChain,
62 : /*optional*/ PinningTelemetryInfo* pinningTelemetryInfo,
63 0 : /*optional*/ const char* hostname)
64 : : mCertDBTrustType(certDBTrustType)
65 : , mOCSPFetching(ocspFetching)
66 : , mOCSPCache(ocspCache)
67 : , mPinArg(pinArg)
68 : , mOCSPGetConfig(ocspGETConfig)
69 : , mOCSPTimeoutSoft(ocspTimeoutSoft)
70 : , mOCSPTimeoutHard(ocspTimeoutHard)
71 : , mCertShortLifetimeInDays(certShortLifetimeInDays)
72 : , mPinningMode(pinningMode)
73 : , mMinRSABits(minRSABits)
74 : , mValidityCheckingMode(validityCheckingMode)
75 : , mSHA1Mode(sha1Mode)
76 : , mNetscapeStepUpPolicy(netscapeStepUpPolicy)
77 : , mOriginAttributes(originAttributes)
78 : , mBuiltChain(builtChain)
79 : , mPeerCertChain(peerCertChain)
80 : , mPinningTelemetryInfo(pinningTelemetryInfo)
81 : , mHostname(hostname)
82 : , mCertBlocklist(do_GetService(NS_CERTBLOCKLIST_CONTRACTID))
83 : , mOCSPStaplingStatus(CertVerifier::OCSP_STAPLING_NEVER_CHECKED)
84 : , mSCTListFromCertificate()
85 0 : , mSCTListFromOCSPStapling()
86 : {
87 0 : }
88 :
89 : // If useRoots is true, we only use root certificates in the candidate list.
90 : // If useRoots is false, we only use non-root certificates in the list.
91 : static Result
92 0 : FindIssuerInner(const UniqueCERTCertList& candidates, bool useRoots,
93 : Input encodedIssuerName, TrustDomain::IssuerChecker& checker,
94 : /*out*/ bool& keepGoing)
95 : {
96 0 : keepGoing = true;
97 0 : for (CERTCertListNode* n = CERT_LIST_HEAD(candidates);
98 0 : !CERT_LIST_END(n, candidates); n = CERT_LIST_NEXT(n)) {
99 0 : bool candidateIsRoot = !!n->cert->isRoot;
100 0 : if (candidateIsRoot != useRoots) {
101 0 : continue;
102 : }
103 0 : Input certDER;
104 0 : Result rv = certDER.Init(n->cert->derCert.data, n->cert->derCert.len);
105 0 : if (rv != Success) {
106 0 : continue; // probably too big
107 : }
108 :
109 : const SECItem encodedIssuerNameItem = {
110 : siBuffer,
111 0 : const_cast<unsigned char*>(encodedIssuerName.UnsafeGetData()),
112 0 : encodedIssuerName.GetLength()
113 0 : };
114 0 : ScopedAutoSECItem nameConstraints;
115 : SECStatus srv = CERT_GetImposedNameConstraints(&encodedIssuerNameItem,
116 0 : &nameConstraints);
117 0 : if (srv != SECSuccess) {
118 0 : if (PR_GetError() != SEC_ERROR_EXTENSION_NOT_FOUND) {
119 0 : return Result::FATAL_ERROR_LIBRARY_FAILURE;
120 : }
121 :
122 : // If no imposed name constraints were found, continue without them
123 0 : rv = checker.Check(certDER, nullptr, keepGoing);
124 : } else {
125 : // Otherwise apply the constraints
126 0 : Input nameConstraintsInput;
127 0 : if (nameConstraintsInput.Init(nameConstraints.data, nameConstraints.len)
128 : != Success) {
129 0 : return Result::FATAL_ERROR_LIBRARY_FAILURE;
130 : }
131 0 : rv = checker.Check(certDER, &nameConstraintsInput, keepGoing);
132 : }
133 0 : if (rv != Success) {
134 0 : return rv;
135 : }
136 0 : if (!keepGoing) {
137 0 : break;
138 : }
139 : }
140 :
141 0 : return Success;
142 : }
143 :
144 : // Remove from newCandidates any CERTCertificates in alreadyTried.
145 : // alreadyTried is likely to be small or empty.
146 : static void
147 0 : RemoveCandidatesAlreadyTried(UniqueCERTCertList& newCandidates,
148 : const UniqueCERTCertList& alreadyTried)
149 : {
150 0 : for (const CERTCertListNode* triedNode = CERT_LIST_HEAD(alreadyTried);
151 0 : !CERT_LIST_END(triedNode, alreadyTried);
152 0 : triedNode = CERT_LIST_NEXT(triedNode)) {
153 0 : CERTCertListNode* newNode = CERT_LIST_HEAD(newCandidates);
154 0 : while (!CERT_LIST_END(newNode, newCandidates)) {
155 0 : CERTCertListNode* savedNode = CERT_LIST_NEXT(newNode);
156 0 : if (CERT_CompareCerts(triedNode->cert, newNode->cert)) {
157 0 : CERT_RemoveCertListNode(newNode);
158 : }
159 0 : newNode = savedNode;
160 : }
161 : }
162 0 : }
163 :
164 : // Add to matchingCandidates any CERTCertificates from candidatesIn that have a
165 : // DER-encoded subject name equal to the given subject name.
166 : static Result
167 0 : AddMatchingCandidates(UniqueCERTCertList& matchingCandidates,
168 : const UniqueCERTCertList& candidatesIn,
169 : Input subjectName)
170 : {
171 0 : for (const CERTCertListNode* node = CERT_LIST_HEAD(candidatesIn);
172 0 : !CERT_LIST_END(node, candidatesIn); node = CERT_LIST_NEXT(node)) {
173 0 : Input candidateSubjectName;
174 0 : Result rv = candidateSubjectName.Init(node->cert->derSubject.data,
175 0 : node->cert->derSubject.len);
176 0 : if (rv != Success) {
177 0 : continue; // probably just too big - continue processing other candidates
178 : }
179 0 : if (InputsAreEqual(candidateSubjectName, subjectName)) {
180 0 : UniqueCERTCertificate certDuplicate(CERT_DupCertificate(node->cert));
181 0 : if (!certDuplicate) {
182 0 : return Result::FATAL_ERROR_NO_MEMORY;
183 : }
184 0 : SECStatus srv = CERT_AddCertToListTail(matchingCandidates.get(),
185 0 : certDuplicate.get());
186 0 : if (srv != SECSuccess) {
187 0 : return MapPRErrorCodeToResult(PR_GetError());
188 : }
189 : // matchingCandidates now owns certDuplicate
190 0 : Unused << certDuplicate.release();
191 : }
192 : }
193 0 : return Success;
194 : }
195 :
196 : Result
197 0 : NSSCertDBTrustDomain::FindIssuer(Input encodedIssuerName,
198 : IssuerChecker& checker, Time)
199 : {
200 : // If the peer certificate chain was specified, try to use it before falling
201 : // back to CERT_CreateSubjectCertList.
202 : bool keepGoing;
203 0 : UniqueCERTCertList peerCertChainCandidates(CERT_NewCertList());
204 0 : if (!peerCertChainCandidates) {
205 0 : return Result::FATAL_ERROR_NO_MEMORY;
206 : }
207 0 : if (mPeerCertChain) {
208 : // Build a candidate list that consists only of certificates with a subject
209 : // matching the issuer we're looking for.
210 0 : Result rv = AddMatchingCandidates(peerCertChainCandidates, *mPeerCertChain,
211 0 : encodedIssuerName);
212 0 : if (rv != Success) {
213 0 : return rv;
214 : }
215 : rv = FindIssuerInner(peerCertChainCandidates, true, encodedIssuerName,
216 0 : checker, keepGoing);
217 0 : if (rv != Success) {
218 0 : return rv;
219 : }
220 0 : if (keepGoing) {
221 : rv = FindIssuerInner(peerCertChainCandidates, false, encodedIssuerName,
222 0 : checker, keepGoing);
223 0 : if (rv != Success) {
224 0 : return rv;
225 : }
226 : }
227 0 : if (!keepGoing) {
228 0 : return Success;
229 : }
230 : }
231 : // TODO: NSS seems to be ambiguous between "no potential issuers found" and
232 : // "there was an error trying to retrieve the potential issuers."
233 0 : SECItem encodedIssuerNameItem = UnsafeMapInputToSECItem(encodedIssuerName);
234 : UniqueCERTCertList
235 : candidates(CERT_CreateSubjectCertList(nullptr, CERT_GetDefaultCertDB(),
236 : &encodedIssuerNameItem, 0,
237 0 : false));
238 0 : if (candidates) {
239 0 : RemoveCandidatesAlreadyTried(candidates, peerCertChainCandidates);
240 : // First, try all the root certs; then try all the non-root certs.
241 : Result rv = FindIssuerInner(candidates, true, encodedIssuerName, checker,
242 0 : keepGoing);
243 0 : if (rv != Success) {
244 0 : return rv;
245 : }
246 0 : if (keepGoing) {
247 : rv = FindIssuerInner(candidates, false, encodedIssuerName, checker,
248 0 : keepGoing);
249 0 : if (rv != Success) {
250 0 : return rv;
251 : }
252 : }
253 : }
254 :
255 0 : return Success;
256 : }
257 :
258 : Result
259 0 : NSSCertDBTrustDomain::GetCertTrust(EndEntityOrCA endEntityOrCA,
260 : const CertPolicyId& policy,
261 : Input candidateCertDER,
262 : /*out*/ TrustLevel& trustLevel)
263 : {
264 : // XXX: This would be cleaner and more efficient if we could get the trust
265 : // information without constructing a CERTCertificate here, but NSS doesn't
266 : // expose it in any other easy-to-use fashion. The use of
267 : // CERT_NewTempCertificate to get a CERTCertificate shouldn't be a
268 : // performance problem because NSS will just find the existing
269 : // CERTCertificate in its in-memory cache and return it.
270 0 : SECItem candidateCertDERSECItem = UnsafeMapInputToSECItem(candidateCertDER);
271 : UniqueCERTCertificate candidateCert(
272 : CERT_NewTempCertificate(CERT_GetDefaultCertDB(), &candidateCertDERSECItem,
273 0 : nullptr, false, true));
274 0 : if (!candidateCert) {
275 0 : return MapPRErrorCodeToResult(PR_GetError());
276 : }
277 :
278 : // Check the certificate against the OneCRL cert blocklist
279 0 : if (!mCertBlocklist) {
280 0 : return Result::FATAL_ERROR_LIBRARY_FAILURE;
281 : }
282 :
283 : // The certificate blocklist currently only applies to TLS server
284 : // certificates.
285 0 : if (mCertDBTrustType == trustSSL) {
286 : bool isCertRevoked;
287 0 : nsresult nsrv = mCertBlocklist->IsCertRevoked(
288 0 : candidateCert->derIssuer.data,
289 0 : candidateCert->derIssuer.len,
290 0 : candidateCert->serialNumber.data,
291 0 : candidateCert->serialNumber.len,
292 0 : candidateCert->derSubject.data,
293 0 : candidateCert->derSubject.len,
294 0 : candidateCert->derPublicKey.data,
295 0 : candidateCert->derPublicKey.len,
296 0 : &isCertRevoked);
297 0 : if (NS_FAILED(nsrv)) {
298 0 : return Result::FATAL_ERROR_LIBRARY_FAILURE;
299 : }
300 :
301 0 : if (isCertRevoked) {
302 0 : MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
303 : ("NSSCertDBTrustDomain: certificate is in blocklist"));
304 0 : return Result::ERROR_REVOKED_CERTIFICATE;
305 : }
306 : }
307 :
308 : // XXX: CERT_GetCertTrust seems to be abusing SECStatus as a boolean, where
309 : // SECSuccess means that there is a trust record and SECFailure means there
310 : // is not a trust record. I looked at NSS's internal uses of
311 : // CERT_GetCertTrust, and all that code uses the result as a boolean meaning
312 : // "We have a trust record."
313 : CERTCertTrust trust;
314 0 : if (CERT_GetCertTrust(candidateCert.get(), &trust) == SECSuccess) {
315 0 : uint32_t flags = SEC_GET_TRUST_FLAGS(&trust, mCertDBTrustType);
316 :
317 : // For DISTRUST, we use the CERTDB_TRUSTED or CERTDB_TRUSTED_CA bit,
318 : // because we can have active distrust for either type of cert. Note that
319 : // CERTDB_TERMINAL_RECORD means "stop trying to inherit trust" so if the
320 : // relevant trust bit isn't set then that means the cert must be considered
321 : // distrusted.
322 : uint32_t relevantTrustBit =
323 0 : endEntityOrCA == EndEntityOrCA::MustBeCA ? CERTDB_TRUSTED_CA
324 0 : : CERTDB_TRUSTED;
325 0 : if (((flags & (relevantTrustBit|CERTDB_TERMINAL_RECORD)))
326 : == CERTDB_TERMINAL_RECORD) {
327 0 : trustLevel = TrustLevel::ActivelyDistrusted;
328 0 : return Success;
329 : }
330 :
331 : // For TRUST, we only use the CERTDB_TRUSTED_CA bit, because Gecko hasn't
332 : // needed to consider end-entity certs to be their own trust anchors since
333 : // Gecko implemented nsICertOverrideService.
334 : // Of course, for this to work as expected, we need to make sure we're
335 : // inquiring about the trust of a CA and not an end-entity. If an end-entity
336 : // has the CERTDB_TRUSTED_CA bit set, Gecko does not consider it to be a
337 : // trust anchor; it must inherit its trust.
338 0 : if (flags & CERTDB_TRUSTED_CA && endEntityOrCA == EndEntityOrCA::MustBeCA) {
339 0 : if (policy.IsAnyPolicy()) {
340 0 : trustLevel = TrustLevel::TrustAnchor;
341 0 : return Success;
342 : }
343 0 : if (CertIsAuthoritativeForEVPolicy(candidateCert, policy)) {
344 0 : trustLevel = TrustLevel::TrustAnchor;
345 0 : return Success;
346 : }
347 : }
348 : }
349 :
350 0 : trustLevel = TrustLevel::InheritsTrust;
351 0 : return Success;
352 : }
353 :
354 : Result
355 0 : NSSCertDBTrustDomain::DigestBuf(Input item, DigestAlgorithm digestAlg,
356 : /*out*/ uint8_t* digestBuf, size_t digestBufLen)
357 : {
358 0 : return DigestBufNSS(item, digestAlg, digestBuf, digestBufLen);
359 : }
360 :
361 : TimeDuration
362 0 : NSSCertDBTrustDomain::GetOCSPTimeout() const
363 : {
364 0 : switch (mOCSPFetching) {
365 : case NSSCertDBTrustDomain::FetchOCSPForDVSoftFail:
366 0 : return mOCSPTimeoutSoft;
367 : case NSSCertDBTrustDomain::FetchOCSPForEV:
368 : case NSSCertDBTrustDomain::FetchOCSPForDVHardFail:
369 0 : return mOCSPTimeoutHard;
370 : // The rest of these are error cases. Assert in debug builds, but return
371 : // the soft timeout value in release builds.
372 : case NSSCertDBTrustDomain::NeverFetchOCSP:
373 : case NSSCertDBTrustDomain::LocalOnlyOCSPForEV:
374 0 : MOZ_ASSERT_UNREACHABLE("we should never see this OCSPFetching type here");
375 : break;
376 : }
377 :
378 0 : MOZ_ASSERT_UNREACHABLE("we're not handling every OCSPFetching type");
379 : return mOCSPTimeoutSoft;
380 : }
381 :
382 : // Copied and modified from CERT_GetOCSPAuthorityInfoAccessLocation and
383 : // CERT_GetGeneralNameByType. Returns a non-Result::Success result on error,
384 : // Success with url == nullptr when an OCSP URI was not found, and Success with
385 : // url != nullptr when an OCSP URI was found. The output url will be owned
386 : // by the arena.
387 : static Result
388 0 : GetOCSPAuthorityInfoAccessLocation(const UniquePLArenaPool& arena,
389 : Input aiaExtension,
390 : /*out*/ char const*& url)
391 : {
392 0 : MOZ_ASSERT(arena.get());
393 0 : if (!arena.get()) {
394 0 : return Result::FATAL_ERROR_INVALID_ARGS;
395 : }
396 :
397 0 : url = nullptr;
398 0 : SECItem aiaExtensionSECItem = UnsafeMapInputToSECItem(aiaExtension);
399 : CERTAuthInfoAccess** aia =
400 0 : CERT_DecodeAuthInfoAccessExtension(arena.get(), &aiaExtensionSECItem);
401 0 : if (!aia) {
402 0 : return Result::ERROR_CERT_BAD_ACCESS_LOCATION;
403 : }
404 0 : for (size_t i = 0; aia[i]; ++i) {
405 0 : if (SECOID_FindOIDTag(&aia[i]->method) == SEC_OID_PKIX_OCSP) {
406 : // NSS chooses the **last** OCSP URL; we choose the **first**
407 0 : CERTGeneralName* current = aia[i]->location;
408 0 : if (!current) {
409 0 : continue;
410 : }
411 0 : do {
412 0 : if (current->type == certURI) {
413 0 : const SECItem& location = current->name.other;
414 : // (location.len + 1) must be small enough to fit into a uint32_t,
415 : // but we limit it to a smaller bound to reduce OOM risk.
416 0 : if (location.len > 1024 || memchr(location.data, 0, location.len)) {
417 : // Reject embedded nulls. (NSS doesn't do this)
418 0 : return Result::ERROR_CERT_BAD_ACCESS_LOCATION;
419 : }
420 : // Copy the non-null-terminated SECItem into a null-terminated string.
421 : char* nullTerminatedURL(
422 0 : static_cast<char*>(PORT_ArenaAlloc(arena.get(), location.len + 1)));
423 0 : if (!nullTerminatedURL) {
424 0 : return Result::FATAL_ERROR_NO_MEMORY;
425 : }
426 0 : memcpy(nullTerminatedURL, location.data, location.len);
427 0 : nullTerminatedURL[location.len] = 0;
428 0 : url = nullTerminatedURL;
429 0 : return Success;
430 : }
431 0 : current = CERT_GetNextGeneralName(current);
432 0 : } while (current != aia[i]->location);
433 : }
434 : }
435 :
436 0 : return Success;
437 : }
438 :
439 : Result
440 0 : NSSCertDBTrustDomain::CheckRevocation(EndEntityOrCA endEntityOrCA,
441 : const CertID& certID, Time time,
442 : Duration validityDuration,
443 : /*optional*/ const Input* stapledOCSPResponse,
444 : /*optional*/ const Input* aiaExtension)
445 : {
446 : // Actively distrusted certificates will have already been blocked by
447 : // GetCertTrust.
448 :
449 : // TODO: need to verify that IsRevoked isn't called for trust anchors AND
450 : // that that fact is documented in mozillapkix.
451 :
452 0 : MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
453 : ("NSSCertDBTrustDomain: Top of CheckRevocation\n"));
454 :
455 : // Bug 991815: The BR allow OCSP for intermediates to be up to one year old.
456 : // Since this affects EV there is no reason why DV should be more strict
457 : // so all intermediatates are allowed to have OCSP responses up to one year
458 : // old.
459 0 : uint16_t maxOCSPLifetimeInDays = 10;
460 0 : if (endEntityOrCA == EndEntityOrCA::MustBeCA) {
461 0 : maxOCSPLifetimeInDays = 365;
462 : }
463 :
464 : // If we have a stapled OCSP response then the verification of that response
465 : // determines the result unless the OCSP response is expired. We make an
466 : // exception for expired responses because some servers, nginx in particular,
467 : // are known to serve expired responses due to bugs.
468 : // We keep track of the result of verifying the stapled response but don't
469 : // immediately return failure if the response has expired.
470 : //
471 : // We only set the OCSP stapling status if we're validating the end-entity
472 : // certificate. Non-end-entity certificates would always be
473 : // OCSP_STAPLING_NONE unless/until we implement multi-stapling.
474 0 : Result stapledOCSPResponseResult = Success;
475 0 : if (stapledOCSPResponse) {
476 0 : MOZ_ASSERT(endEntityOrCA == EndEntityOrCA::MustBeEndEntity);
477 : bool expired;
478 : stapledOCSPResponseResult =
479 0 : VerifyAndMaybeCacheEncodedOCSPResponse(certID, time,
480 : maxOCSPLifetimeInDays,
481 : *stapledOCSPResponse,
482 0 : ResponseWasStapled, expired);
483 0 : if (stapledOCSPResponseResult == Success) {
484 : // stapled OCSP response present and good
485 0 : mOCSPStaplingStatus = CertVerifier::OCSP_STAPLING_GOOD;
486 0 : MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
487 : ("NSSCertDBTrustDomain: stapled OCSP response: good"));
488 0 : return Success;
489 : }
490 0 : if (stapledOCSPResponseResult == Result::ERROR_OCSP_OLD_RESPONSE ||
491 : expired) {
492 : // stapled OCSP response present but expired
493 0 : mOCSPStaplingStatus = CertVerifier::OCSP_STAPLING_EXPIRED;
494 0 : MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
495 : ("NSSCertDBTrustDomain: expired stapled OCSP response"));
496 : } else {
497 : // stapled OCSP response present but invalid for some reason
498 0 : mOCSPStaplingStatus = CertVerifier::OCSP_STAPLING_INVALID;
499 0 : MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
500 : ("NSSCertDBTrustDomain: stapled OCSP response: failure"));
501 0 : return stapledOCSPResponseResult;
502 : }
503 0 : } else if (endEntityOrCA == EndEntityOrCA::MustBeEndEntity) {
504 : // no stapled OCSP response
505 0 : mOCSPStaplingStatus = CertVerifier::OCSP_STAPLING_NONE;
506 0 : MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
507 : ("NSSCertDBTrustDomain: no stapled OCSP response"));
508 : }
509 :
510 0 : Result cachedResponseResult = Success;
511 0 : Time cachedResponseValidThrough(Time::uninitialized);
512 0 : bool cachedResponsePresent = mOCSPCache.Get(certID, mOriginAttributes,
513 : cachedResponseResult,
514 0 : cachedResponseValidThrough);
515 0 : if (cachedResponsePresent) {
516 0 : if (cachedResponseResult == Success && cachedResponseValidThrough >= time) {
517 0 : MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
518 : ("NSSCertDBTrustDomain: cached OCSP response: good"));
519 0 : return Success;
520 : }
521 : // If we have a cached revoked response, use it.
522 0 : if (cachedResponseResult == Result::ERROR_REVOKED_CERTIFICATE) {
523 0 : MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
524 : ("NSSCertDBTrustDomain: cached OCSP response: revoked"));
525 0 : return Result::ERROR_REVOKED_CERTIFICATE;
526 : }
527 : // The cached response may indicate an unknown certificate or it may be
528 : // expired. Don't return with either of these statuses yet - we may be
529 : // able to fetch a more recent one.
530 0 : MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
531 : ("NSSCertDBTrustDomain: cached OCSP response: error %d",
532 : static_cast<int>(cachedResponseResult)));
533 : // When a good cached response has expired, it is more convenient
534 : // to convert that to an error code and just deal with
535 : // cachedResponseResult from here on out.
536 0 : if (cachedResponseResult == Success && cachedResponseValidThrough < time) {
537 0 : cachedResponseResult = Result::ERROR_OCSP_OLD_RESPONSE;
538 : }
539 : // We may have a cached indication of server failure. Ignore it if
540 : // it has expired.
541 0 : if (cachedResponseResult != Success &&
542 0 : cachedResponseResult != Result::ERROR_OCSP_UNKNOWN_CERT &&
543 0 : cachedResponseResult != Result::ERROR_OCSP_OLD_RESPONSE &&
544 0 : cachedResponseValidThrough < time) {
545 0 : cachedResponseResult = Success;
546 0 : cachedResponsePresent = false;
547 : }
548 : } else {
549 0 : MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
550 : ("NSSCertDBTrustDomain: no cached OCSP response"));
551 : }
552 : // At this point, if and only if cachedErrorResult is Success, there was no
553 : // cached response.
554 0 : MOZ_ASSERT((!cachedResponsePresent && cachedResponseResult == Success) ||
555 : (cachedResponsePresent && cachedResponseResult != Success));
556 :
557 : // If we have a fresh OneCRL Blocklist we can skip OCSP for CA certs
558 : bool blocklistIsFresh;
559 0 : nsresult nsrv = mCertBlocklist->IsBlocklistFresh(&blocklistIsFresh);
560 0 : if (NS_FAILED(nsrv)) {
561 0 : return Result::FATAL_ERROR_LIBRARY_FAILURE;
562 : }
563 :
564 : // TODO: We still need to handle the fallback for expired responses. But,
565 : // if/when we disable OCSP fetching by default, it would be ambiguous whether
566 : // security.OCSP.enable==0 means "I want the default" or "I really never want
567 : // you to ever fetch OCSP."
568 :
569 0 : Duration shortLifetime(mCertShortLifetimeInDays * Time::ONE_DAY_IN_SECONDS);
570 :
571 0 : if ((mOCSPFetching == NeverFetchOCSP) ||
572 0 : (validityDuration < shortLifetime) ||
573 0 : (endEntityOrCA == EndEntityOrCA::MustBeCA &&
574 0 : (mOCSPFetching == FetchOCSPForDVHardFail ||
575 0 : mOCSPFetching == FetchOCSPForDVSoftFail ||
576 : blocklistIsFresh))) {
577 : // We're not going to be doing any fetching, so if there was a cached
578 : // "unknown" response, say so.
579 0 : if (cachedResponseResult == Result::ERROR_OCSP_UNKNOWN_CERT) {
580 0 : return Result::ERROR_OCSP_UNKNOWN_CERT;
581 : }
582 : // If we're doing hard-fail, we want to know if we have a cached response
583 : // that has expired.
584 0 : if (mOCSPFetching == FetchOCSPForDVHardFail &&
585 0 : cachedResponseResult == Result::ERROR_OCSP_OLD_RESPONSE) {
586 0 : return Result::ERROR_OCSP_OLD_RESPONSE;
587 : }
588 :
589 0 : return Success;
590 : }
591 :
592 0 : if (mOCSPFetching == LocalOnlyOCSPForEV) {
593 0 : if (cachedResponseResult != Success) {
594 0 : return cachedResponseResult;
595 : }
596 0 : return Result::ERROR_OCSP_UNKNOWN_CERT;
597 : }
598 :
599 0 : UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
600 0 : if (!arena) {
601 0 : return Result::FATAL_ERROR_NO_MEMORY;
602 : }
603 :
604 : Result rv;
605 0 : const char* url = nullptr; // owned by the arena
606 :
607 0 : if (aiaExtension) {
608 0 : rv = GetOCSPAuthorityInfoAccessLocation(arena, *aiaExtension, url);
609 0 : if (rv != Success) {
610 0 : return rv;
611 : }
612 : }
613 :
614 0 : if (!url) {
615 0 : if (mOCSPFetching == FetchOCSPForEV ||
616 0 : cachedResponseResult == Result::ERROR_OCSP_UNKNOWN_CERT) {
617 0 : return Result::ERROR_OCSP_UNKNOWN_CERT;
618 : }
619 0 : if (cachedResponseResult == Result::ERROR_OCSP_OLD_RESPONSE) {
620 0 : return Result::ERROR_OCSP_OLD_RESPONSE;
621 : }
622 0 : if (stapledOCSPResponseResult != Success) {
623 0 : return stapledOCSPResponseResult;
624 : }
625 :
626 : // Nothing to do if we don't have an OCSP responder URI for the cert; just
627 : // assume it is good. Note that this is the confusing, but intended,
628 : // interpretation of "strict" revocation checking in the face of a
629 : // certificate that lacks an OCSP responder URI.
630 0 : return Success;
631 : }
632 :
633 : // Only request a response if we didn't have a cached indication of failure
634 : // (don't keep requesting responses from a failing server).
635 0 : Input response;
636 : bool attemptedRequest;
637 0 : if (cachedResponseResult == Success ||
638 0 : cachedResponseResult == Result::ERROR_OCSP_UNKNOWN_CERT ||
639 0 : cachedResponseResult == Result::ERROR_OCSP_OLD_RESPONSE) {
640 : uint8_t ocspRequest[OCSP_REQUEST_MAX_LENGTH];
641 : size_t ocspRequestLength;
642 0 : rv = CreateEncodedOCSPRequest(*this, certID, ocspRequest,
643 0 : ocspRequestLength);
644 0 : if (rv != Success) {
645 0 : return rv;
646 : }
647 : SECItem ocspRequestItem = {
648 : siBuffer,
649 : ocspRequest,
650 : static_cast<unsigned int>(ocspRequestLength)
651 0 : };
652 : // Owned by arena
653 0 : SECItem* responseSECItem = nullptr;
654 : Result tempRV =
655 0 : DoOCSPRequest(arena, url, mOriginAttributes, &ocspRequestItem,
656 : GetOCSPTimeout(),
657 0 : mOCSPGetConfig == CertVerifier::ocspGetEnabled,
658 0 : responseSECItem);
659 0 : MOZ_ASSERT((tempRV != Success) || responseSECItem);
660 0 : if (tempRV != Success) {
661 0 : rv = tempRV;
662 0 : } else if (response.Init(responseSECItem->data, responseSECItem->len)
663 : != Success) {
664 0 : rv = Result::ERROR_OCSP_MALFORMED_RESPONSE; // too big
665 : }
666 0 : attemptedRequest = true;
667 : } else {
668 0 : rv = cachedResponseResult;
669 0 : attemptedRequest = false;
670 : }
671 :
672 0 : if (response.GetLength() == 0) {
673 0 : Result error = rv;
674 0 : if (attemptedRequest) {
675 0 : Time timeout(time);
676 0 : if (timeout.AddSeconds(ServerFailureDelaySeconds) != Success) {
677 0 : return Result::FATAL_ERROR_LIBRARY_FAILURE; // integer overflow
678 : }
679 0 : rv = mOCSPCache.Put(certID, mOriginAttributes, error, time, timeout);
680 0 : if (rv != Success) {
681 0 : return rv;
682 : }
683 : }
684 0 : if (mOCSPFetching != FetchOCSPForDVSoftFail) {
685 0 : MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
686 : ("NSSCertDBTrustDomain: returning SECFailure after "
687 : "OCSP request failure"));
688 0 : return error;
689 : }
690 0 : if (cachedResponseResult == Result::ERROR_OCSP_UNKNOWN_CERT) {
691 0 : MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
692 : ("NSSCertDBTrustDomain: returning SECFailure from cached "
693 : "response after OCSP request failure"));
694 0 : return cachedResponseResult;
695 : }
696 0 : if (stapledOCSPResponseResult != Success) {
697 0 : MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
698 : ("NSSCertDBTrustDomain: returning SECFailure from expired "
699 : "stapled response after OCSP request failure"));
700 0 : return stapledOCSPResponseResult;
701 : }
702 :
703 0 : MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
704 : ("NSSCertDBTrustDomain: returning SECSuccess after "
705 : "OCSP request failure"));
706 0 : return Success; // Soft fail -> success :(
707 : }
708 :
709 : // If the response from the network has expired but indicates a revoked
710 : // or unknown certificate, PR_GetError() will return the appropriate error.
711 : // We actually ignore expired here.
712 : bool expired;
713 0 : rv = VerifyAndMaybeCacheEncodedOCSPResponse(certID, time,
714 : maxOCSPLifetimeInDays,
715 : response, ResponseIsFromNetwork,
716 0 : expired);
717 0 : if (rv == Success || mOCSPFetching != FetchOCSPForDVSoftFail) {
718 0 : MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
719 : ("NSSCertDBTrustDomain: returning after VerifyEncodedOCSPResponse"));
720 0 : return rv;
721 : }
722 :
723 0 : if (rv == Result::ERROR_OCSP_UNKNOWN_CERT ||
724 : rv == Result::ERROR_REVOKED_CERTIFICATE) {
725 0 : return rv;
726 : }
727 0 : if (stapledOCSPResponseResult != Success) {
728 0 : MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
729 : ("NSSCertDBTrustDomain: returning SECFailure from expired stapled "
730 : "response after OCSP request verification failure"));
731 0 : return stapledOCSPResponseResult;
732 : }
733 :
734 0 : MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
735 : ("NSSCertDBTrustDomain: end of CheckRevocation"));
736 :
737 0 : return Success; // Soft fail -> success :(
738 : }
739 :
740 : Result
741 0 : NSSCertDBTrustDomain::VerifyAndMaybeCacheEncodedOCSPResponse(
742 : const CertID& certID, Time time, uint16_t maxLifetimeInDays,
743 : Input encodedResponse, EncodedResponseSource responseSource,
744 : /*out*/ bool& expired)
745 : {
746 0 : Time thisUpdate(Time::uninitialized);
747 0 : Time validThrough(Time::uninitialized);
748 :
749 : // We use a try and fallback approach which first mandates good signature
750 : // digest algorithms, then falls back to SHA-1 if this fails. If a delegated
751 : // OCSP response signing certificate was issued with a SHA-1 signature,
752 : // verification initially fails. We cache the failure and then re-use that
753 : // result even when doing fallback (i.e. when weak signature digest algorithms
754 : // should succeed). To address this we use an OCSPVerificationTrustDomain
755 : // here, rather than using *this, to ensure verification succeeds for all
756 : // allowed signature digest algorithms.
757 0 : OCSPVerificationTrustDomain trustDomain(*this);
758 0 : Result rv = VerifyEncodedOCSPResponse(trustDomain, certID, time,
759 : maxLifetimeInDays, encodedResponse,
760 0 : expired, &thisUpdate, &validThrough);
761 : // If a response was stapled and expired, we don't want to cache it. Return
762 : // early to simplify the logic here.
763 0 : if (responseSource == ResponseWasStapled && expired) {
764 0 : MOZ_ASSERT(rv != Success);
765 0 : return rv;
766 : }
767 : // validThrough is only trustworthy if the response successfully verifies
768 : // or it indicates a revoked or unknown certificate.
769 : // If this isn't the case, store an indication of failure (to prevent
770 : // repeatedly requesting a response from a failing server).
771 0 : if (rv != Success && rv != Result::ERROR_REVOKED_CERTIFICATE &&
772 : rv != Result::ERROR_OCSP_UNKNOWN_CERT) {
773 0 : validThrough = time;
774 0 : if (validThrough.AddSeconds(ServerFailureDelaySeconds) != Success) {
775 0 : return Result::FATAL_ERROR_LIBRARY_FAILURE; // integer overflow
776 : }
777 : }
778 0 : if (responseSource == ResponseIsFromNetwork ||
779 0 : rv == Success ||
780 0 : rv == Result::ERROR_REVOKED_CERTIFICATE ||
781 : rv == Result::ERROR_OCSP_UNKNOWN_CERT) {
782 0 : MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
783 : ("NSSCertDBTrustDomain: caching OCSP response"));
784 0 : Result putRV = mOCSPCache.Put(certID, mOriginAttributes, rv, thisUpdate,
785 0 : validThrough);
786 0 : if (putRV != Success) {
787 0 : return putRV;
788 : }
789 : }
790 :
791 0 : return rv;
792 : }
793 :
794 : static const uint8_t CNNIC_ROOT_CA_SUBJECT_DATA[] =
795 : "\x30\x32\x31\x0B\x30\x09\x06\x03\x55\x04\x06\x13\x02\x43\x4E\x31\x0E\x30"
796 : "\x0C\x06\x03\x55\x04\x0A\x13\x05\x43\x4E\x4E\x49\x43\x31\x13\x30\x11\x06"
797 : "\x03\x55\x04\x03\x13\x0A\x43\x4E\x4E\x49\x43\x20\x52\x4F\x4F\x54";
798 :
799 : static const uint8_t CNNIC_EV_ROOT_CA_SUBJECT_DATA[] =
800 : "\x30\x81\x8A\x31\x0B\x30\x09\x06\x03\x55\x04\x06\x13\x02\x43\x4E\x31\x32"
801 : "\x30\x30\x06\x03\x55\x04\x0A\x0C\x29\x43\x68\x69\x6E\x61\x20\x49\x6E\x74"
802 : "\x65\x72\x6E\x65\x74\x20\x4E\x65\x74\x77\x6F\x72\x6B\x20\x49\x6E\x66\x6F"
803 : "\x72\x6D\x61\x74\x69\x6F\x6E\x20\x43\x65\x6E\x74\x65\x72\x31\x47\x30\x45"
804 : "\x06\x03\x55\x04\x03\x0C\x3E\x43\x68\x69\x6E\x61\x20\x49\x6E\x74\x65\x72"
805 : "\x6E\x65\x74\x20\x4E\x65\x74\x77\x6F\x72\x6B\x20\x49\x6E\x66\x6F\x72\x6D"
806 : "\x61\x74\x69\x6F\x6E\x20\x43\x65\x6E\x74\x65\x72\x20\x45\x56\x20\x43\x65"
807 : "\x72\x74\x69\x66\x69\x63\x61\x74\x65\x73\x20\x52\x6F\x6F\x74";
808 :
809 : class WhitelistedCNNICHashBinarySearchComparator
810 : {
811 : public:
812 0 : explicit WhitelistedCNNICHashBinarySearchComparator(const uint8_t* aTarget,
813 : size_t aTargetLength)
814 0 : : mTarget(aTarget)
815 : {
816 0 : MOZ_ASSERT(aTargetLength == CNNIC_WHITELIST_HASH_LEN,
817 : "Hashes should be of the same length.");
818 0 : }
819 :
820 0 : int operator()(const WhitelistedCNNICHash val) const {
821 0 : return memcmp(mTarget, val.hash, CNNIC_WHITELIST_HASH_LEN);
822 : }
823 :
824 : private:
825 : const uint8_t* mTarget;
826 : };
827 :
828 : static bool
829 0 : CertIsStartComOrWoSign(const CERTCertificate* cert)
830 : {
831 0 : for (const DataAndLength& dn : StartComAndWoSignDNs) {
832 0 : if (cert->derSubject.len == dn.len &&
833 0 : PodEqual(cert->derSubject.data, dn.data, dn.len)) {
834 0 : return true;
835 : }
836 : }
837 0 : return false;
838 : }
839 :
840 : // If a certificate in the given chain appears to have been issued by one of
841 : // seven roots operated by StartCom and WoSign that are not trusted to issue new
842 : // certificates, verify that the end-entity has a notBefore date before 21
843 : // October 2016. If the value of notBefore is after this time, the chain is not
844 : // valid.
845 : // (NB: While there are seven distinct roots being checked for, two of them
846 : // share distinguished names, resulting in six distinct distinguished names to
847 : // actually look for.)
848 : static Result
849 0 : CheckForStartComOrWoSign(const UniqueCERTCertList& certChain)
850 : {
851 0 : if (CERT_LIST_EMPTY(certChain)) {
852 0 : return Result::FATAL_ERROR_LIBRARY_FAILURE;
853 : }
854 0 : const CERTCertListNode* endEntityNode = CERT_LIST_HEAD(certChain);
855 0 : if (!endEntityNode || !endEntityNode->cert) {
856 0 : return Result::FATAL_ERROR_LIBRARY_FAILURE;
857 : }
858 : PRTime notBefore;
859 : PRTime notAfter;
860 0 : if (CERT_GetCertTimes(endEntityNode->cert, ¬Before, ¬After)
861 : != SECSuccess) {
862 0 : return Result::FATAL_ERROR_LIBRARY_FAILURE;
863 : }
864 : // PRTime is microseconds since the epoch, whereas JS time is milliseconds.
865 : // (new Date("2016-10-21T00:00:00Z")).getTime() * 1000
866 : static const PRTime OCTOBER_21_2016 = 1477008000000000;
867 0 : if (notBefore <= OCTOBER_21_2016) {
868 0 : return Success;
869 : }
870 :
871 0 : for (const CERTCertListNode* node = CERT_LIST_HEAD(certChain);
872 0 : !CERT_LIST_END(node, certChain); node = CERT_LIST_NEXT(node)) {
873 0 : if (!node || !node->cert) {
874 0 : return Result::FATAL_ERROR_LIBRARY_FAILURE;
875 : }
876 0 : if (CertIsStartComOrWoSign(node->cert)) {
877 0 : return Result::ERROR_REVOKED_CERTIFICATE;
878 : }
879 : }
880 0 : return Success;
881 : }
882 :
883 : // python DottedOIDToCode.py sGlobalSignEVPolicyBytes 1.3.6.1.4.1.4146.1.1
884 : static const uint8_t sGlobalSignEVPolicyBytes[] = {
885 : 0x2b, 0x06, 0x01, 0x04, 0x01, 0xa0, 0x32, 0x01, 0x01
886 : };
887 :
888 : static const CertPolicyId sGlobalSignEVPolicy = {
889 : sizeof(sGlobalSignEVPolicyBytes),
890 : // It's unfortunate, but there isn't a nice way to do this.
891 : // Just make sure these bytes match sGlobalSignEVPolicyBytes.
892 : { 0x2b, 0x06, 0x01, 0x04, 0x01, 0xa0, 0x32, 0x01, 0x01 }
893 : };
894 :
895 : static const unsigned char sGlobalSignRootCAR2SubjectBytes[] = {
896 : 0x30, 0x4c, 0x31, 0x20, 0x30, 0x1e, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x17,
897 : 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x52, 0x6f,
898 : 0x6f, 0x74, 0x20, 0x43, 0x41, 0x20, 0x2d, 0x20, 0x52, 0x32, 0x31, 0x13, 0x30,
899 : 0x11, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0a, 0x47, 0x6c, 0x6f, 0x62, 0x61,
900 : 0x6c, 0x53, 0x69, 0x67, 0x6e, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04,
901 : 0x03, 0x13, 0x0a, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x53, 0x69, 0x67, 0x6e,
902 : };
903 :
904 : static const unsigned char sGlobalSignRootCAR2SPKIBytes[] = {
905 : 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
906 : 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82,
907 : 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xa6, 0xcf, 0x24, 0x0e, 0xbe, 0x2e,
908 : 0x6f, 0x28, 0x99, 0x45, 0x42, 0xc4, 0xab, 0x3e, 0x21, 0x54, 0x9b, 0x0b, 0xd3,
909 : 0x7f, 0x84, 0x70, 0xfa, 0x12, 0xb3, 0xcb, 0xbf, 0x87, 0x5f, 0xc6, 0x7f, 0x86,
910 : 0xd3, 0xb2, 0x30, 0x5c, 0xd6, 0xfd, 0xad, 0xf1, 0x7b, 0xdc, 0xe5, 0xf8, 0x60,
911 : 0x96, 0x09, 0x92, 0x10, 0xf5, 0xd0, 0x53, 0xde, 0xfb, 0x7b, 0x7e, 0x73, 0x88,
912 : 0xac, 0x52, 0x88, 0x7b, 0x4a, 0xa6, 0xca, 0x49, 0xa6, 0x5e, 0xa8, 0xa7, 0x8c,
913 : 0x5a, 0x11, 0xbc, 0x7a, 0x82, 0xeb, 0xbe, 0x8c, 0xe9, 0xb3, 0xac, 0x96, 0x25,
914 : 0x07, 0x97, 0x4a, 0x99, 0x2a, 0x07, 0x2f, 0xb4, 0x1e, 0x77, 0xbf, 0x8a, 0x0f,
915 : 0xb5, 0x02, 0x7c, 0x1b, 0x96, 0xb8, 0xc5, 0xb9, 0x3a, 0x2c, 0xbc, 0xd6, 0x12,
916 : 0xb9, 0xeb, 0x59, 0x7d, 0xe2, 0xd0, 0x06, 0x86, 0x5f, 0x5e, 0x49, 0x6a, 0xb5,
917 : 0x39, 0x5e, 0x88, 0x34, 0xec, 0xbc, 0x78, 0x0c, 0x08, 0x98, 0x84, 0x6c, 0xa8,
918 : 0xcd, 0x4b, 0xb4, 0xa0, 0x7d, 0x0c, 0x79, 0x4d, 0xf0, 0xb8, 0x2d, 0xcb, 0x21,
919 : 0xca, 0xd5, 0x6c, 0x5b, 0x7d, 0xe1, 0xa0, 0x29, 0x84, 0xa1, 0xf9, 0xd3, 0x94,
920 : 0x49, 0xcb, 0x24, 0x62, 0x91, 0x20, 0xbc, 0xdd, 0x0b, 0xd5, 0xd9, 0xcc, 0xf9,
921 : 0xea, 0x27, 0x0a, 0x2b, 0x73, 0x91, 0xc6, 0x9d, 0x1b, 0xac, 0xc8, 0xcb, 0xe8,
922 : 0xe0, 0xa0, 0xf4, 0x2f, 0x90, 0x8b, 0x4d, 0xfb, 0xb0, 0x36, 0x1b, 0xf6, 0x19,
923 : 0x7a, 0x85, 0xe0, 0x6d, 0xf2, 0x61, 0x13, 0x88, 0x5c, 0x9f, 0xe0, 0x93, 0x0a,
924 : 0x51, 0x97, 0x8a, 0x5a, 0xce, 0xaf, 0xab, 0xd5, 0xf7, 0xaa, 0x09, 0xaa, 0x60,
925 : 0xbd, 0xdc, 0xd9, 0x5f, 0xdf, 0x72, 0xa9, 0x60, 0x13, 0x5e, 0x00, 0x01, 0xc9,
926 : 0x4a, 0xfa, 0x3f, 0xa4, 0xea, 0x07, 0x03, 0x21, 0x02, 0x8e, 0x82, 0xca, 0x03,
927 : 0xc2, 0x9b, 0x8f, 0x02, 0x03, 0x01, 0x00, 0x01,
928 : };
929 :
930 : static const unsigned char sGlobalSignExtendedValidationCASHA256G2SubjectBytes[] = {
931 : 0x30, 0x62, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
932 : 0x42, 0x45, 0x31, 0x19, 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x10,
933 : 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x6e, 0x76,
934 : 0x2d, 0x73, 0x61, 0x31, 0x38, 0x30, 0x36, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13,
935 : 0x2f, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x45,
936 : 0x78, 0x74, 0x65, 0x6e, 0x64, 0x65, 0x64, 0x20, 0x56, 0x61, 0x6c, 0x69, 0x64,
937 : 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x43, 0x41, 0x20, 0x2d, 0x20, 0x53, 0x48,
938 : 0x41, 0x32, 0x35, 0x36, 0x20, 0x2d, 0x20, 0x47, 0x32,
939 : };
940 :
941 : static const unsigned char sGlobalSignExtendedValidationCASHA256G2SPKIBytes[] = {
942 : 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
943 : 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82,
944 : 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xa3, 0xea, 0xa1, 0xd2, 0xc3, 0x49,
945 : 0xe5, 0xf7, 0x1c, 0x5d, 0xaf, 0xc3, 0x92, 0x42, 0xaf, 0x8a, 0x3c, 0xdc, 0xef,
946 : 0x4c, 0xe6, 0x2f, 0x5f, 0x0c, 0x2b, 0x9f, 0x8a, 0x50, 0x30, 0x66, 0xef, 0x4e,
947 : 0xc8, 0x4f, 0x21, 0x4a, 0xf6, 0xe7, 0xf2, 0x4e, 0x1b, 0x8c, 0x53, 0x57, 0xb0,
948 : 0x9e, 0xc8, 0x5b, 0xf7, 0xb8, 0x46, 0x55, 0xb3, 0x1a, 0xed, 0xc2, 0x6a, 0xfe,
949 : 0xf4, 0x1b, 0xec, 0x48, 0x46, 0x0e, 0x8f, 0xe0, 0xfb, 0xe0, 0x91, 0x19, 0xdf,
950 : 0x99, 0x18, 0x6f, 0x2e, 0x51, 0xaf, 0xda, 0xf6, 0x9a, 0xca, 0x64, 0x6f, 0x99,
951 : 0x54, 0x10, 0x74, 0xea, 0x3c, 0xc8, 0xaa, 0x80, 0x4d, 0x43, 0x37, 0xfb, 0xc8,
952 : 0xa4, 0x7f, 0x05, 0x9d, 0x37, 0x92, 0xbd, 0x98, 0x00, 0x35, 0x5a, 0xaf, 0xbb,
953 : 0x5b, 0x74, 0x15, 0x0e, 0xbc, 0xbc, 0xc6, 0xe9, 0xb7, 0x86, 0xe7, 0xee, 0xae,
954 : 0x4d, 0x4b, 0x04, 0x4c, 0x2b, 0xa0, 0xb4, 0x65, 0x48, 0xb8, 0xc3, 0x3a, 0xcd,
955 : 0x75, 0xbb, 0x37, 0xc9, 0x4a, 0xc0, 0x01, 0x11, 0xd9, 0xbf, 0x3f, 0x15, 0x86,
956 : 0x60, 0x19, 0x6b, 0x34, 0x20, 0x46, 0xf5, 0x86, 0x66, 0x0f, 0x24, 0xf4, 0xcc,
957 : 0x62, 0x9f, 0x9f, 0x9e, 0x1d, 0xfd, 0x10, 0xa4, 0x99, 0x5e, 0xf0, 0x41, 0xeb,
958 : 0xb0, 0x94, 0xff, 0x2c, 0xb3, 0x36, 0xd6, 0xeb, 0x1d, 0xa7, 0x17, 0x5f, 0xdf,
959 : 0xce, 0x6a, 0x77, 0xc7, 0x9a, 0xc4, 0x32, 0x63, 0xa7, 0x06, 0xad, 0xf3, 0x12,
960 : 0x1b, 0x9d, 0x30, 0x72, 0x59, 0x0b, 0xeb, 0x72, 0xeb, 0x2a, 0xd2, 0x77, 0x7b,
961 : 0x91, 0x77, 0xdb, 0x00, 0xfc, 0xd8, 0x6f, 0xf5, 0x2f, 0xd8, 0x7a, 0xc5, 0x0c,
962 : 0x3a, 0xa0, 0x7b, 0x5e, 0x90, 0xf3, 0x9d, 0x84, 0x59, 0xc8, 0x01, 0xd9, 0x91,
963 : 0x37, 0x56, 0xe5, 0x3a, 0x53, 0x93, 0xad, 0x60, 0x49, 0x27, 0x25, 0xd9, 0xe1,
964 : 0xda, 0x82, 0xd7, 0x02, 0x03, 0x01, 0x00, 0x01,
965 : };
966 :
967 : template<size_t T, size_t R>
968 : static bool
969 0 : CertMatchesStaticData(const CERTCertificate* cert,
970 : const unsigned char (&subject)[T],
971 : const unsigned char (&spki)[R]) {
972 0 : MOZ_ASSERT(cert);
973 0 : if (!cert) {
974 0 : return false;
975 : }
976 0 : return cert->derSubject.len == T &&
977 0 : mozilla::PodEqual(cert->derSubject.data, subject, T) &&
978 0 : cert->derPublicKey.len == R &&
979 0 : mozilla::PodEqual(cert->derPublicKey.data, spki, R);
980 : }
981 :
982 : Result
983 0 : NSSCertDBTrustDomain::IsChainValid(const DERArray& certArray, Time time,
984 : const CertPolicyId& requiredPolicy)
985 : {
986 0 : MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
987 : ("NSSCertDBTrustDomain: IsChainValid"));
988 :
989 0 : UniqueCERTCertList certList;
990 : SECStatus srv = ConstructCERTCertListFromReversedDERArray(certArray,
991 0 : certList);
992 0 : if (srv != SECSuccess) {
993 0 : return MapPRErrorCodeToResult(PR_GetError());
994 : }
995 0 : if (CERT_LIST_EMPTY(certList)) {
996 0 : return Result::FATAL_ERROR_LIBRARY_FAILURE;
997 : }
998 :
999 0 : Result rv = CheckForStartComOrWoSign(certList);
1000 0 : if (rv != Success) {
1001 0 : return rv;
1002 : }
1003 :
1004 : // If the certificate appears to have been issued by a CNNIC root, only allow
1005 : // it if it is on the whitelist.
1006 0 : CERTCertListNode* rootNode = CERT_LIST_TAIL(certList);
1007 0 : if (!rootNode) {
1008 0 : return Result::FATAL_ERROR_LIBRARY_FAILURE;
1009 : }
1010 0 : CERTCertificate* root = rootNode->cert;
1011 0 : if (!root) {
1012 0 : return Result::FATAL_ERROR_LIBRARY_FAILURE;
1013 : }
1014 0 : if ((root->derSubject.len == sizeof(CNNIC_ROOT_CA_SUBJECT_DATA) - 1 &&
1015 0 : memcmp(root->derSubject.data, CNNIC_ROOT_CA_SUBJECT_DATA,
1016 0 : root->derSubject.len) == 0) ||
1017 0 : (root->derSubject.len == sizeof(CNNIC_EV_ROOT_CA_SUBJECT_DATA) - 1 &&
1018 0 : memcmp(root->derSubject.data, CNNIC_EV_ROOT_CA_SUBJECT_DATA,
1019 0 : root->derSubject.len) == 0)) {
1020 0 : CERTCertListNode* certNode = CERT_LIST_HEAD(certList);
1021 0 : if (!certNode) {
1022 0 : return Result::FATAL_ERROR_LIBRARY_FAILURE;
1023 : }
1024 0 : CERTCertificate* cert = certNode->cert;
1025 0 : if (!cert) {
1026 0 : return Result::FATAL_ERROR_LIBRARY_FAILURE;
1027 : }
1028 0 : Digest digest;
1029 0 : nsresult nsrv = digest.DigestBuf(SEC_OID_SHA256, cert->derCert.data,
1030 0 : cert->derCert.len);
1031 0 : if (NS_FAILED(nsrv)) {
1032 0 : return Result::FATAL_ERROR_LIBRARY_FAILURE;
1033 : }
1034 : const uint8_t* certHash(
1035 0 : BitwiseCast<uint8_t*, unsigned char*>(digest.get().data));
1036 0 : size_t certHashLen = digest.get().len;
1037 : size_t unused;
1038 0 : if (!mozilla::BinarySearchIf(WhitelistedCNNICHashes, 0,
1039 : ArrayLength(WhitelistedCNNICHashes),
1040 0 : WhitelistedCNNICHashBinarySearchComparator(
1041 : certHash, certHashLen),
1042 : &unused)) {
1043 0 : return Result::ERROR_REVOKED_CERTIFICATE;
1044 : }
1045 : }
1046 :
1047 0 : bool isBuiltInRoot = false;
1048 0 : rv = IsCertBuiltInRoot(root, isBuiltInRoot);
1049 0 : if (rv != Success) {
1050 0 : return rv;
1051 : }
1052 : bool skipPinningChecksBecauseOfMITMMode =
1053 0 : (!isBuiltInRoot && mPinningMode == CertVerifier::pinningAllowUserCAMITM);
1054 : // If mHostname isn't set, we're not verifying in the context of a TLS
1055 : // handshake, so don't verify HPKP in those cases.
1056 0 : if (mHostname && (mPinningMode != CertVerifier::pinningDisabled) &&
1057 0 : !skipPinningChecksBecauseOfMITMMode) {
1058 : bool enforceTestMode =
1059 0 : (mPinningMode == CertVerifier::pinningEnforceTestMode);
1060 : bool chainHasValidPins;
1061 0 : nsresult nsrv = PublicKeyPinningService::ChainHasValidPins(
1062 : certList, mHostname, time, enforceTestMode, mOriginAttributes,
1063 0 : chainHasValidPins, mPinningTelemetryInfo);
1064 0 : if (NS_FAILED(nsrv)) {
1065 0 : return Result::FATAL_ERROR_LIBRARY_FAILURE;
1066 : }
1067 0 : if (!chainHasValidPins) {
1068 0 : return Result::ERROR_KEY_PINNING_FAILURE;
1069 : }
1070 : }
1071 :
1072 : // See bug 1349762. If the root is "GlobalSign Root CA - R2", don't consider
1073 : // the end-entity valid for EV unless the
1074 : // "GlobalSign Extended Validation CA - SHA256 - G2" intermediate is in the
1075 : // chain as well. It should be possible to remove this workaround after
1076 : // January 2019 as per bug 1349727 comment 17.
1077 0 : if (requiredPolicy == sGlobalSignEVPolicy &&
1078 0 : CertMatchesStaticData(root, sGlobalSignRootCAR2SubjectBytes,
1079 : sGlobalSignRootCAR2SPKIBytes)) {
1080 0 : bool foundRequiredIntermediate = false;
1081 0 : for (CERTCertListNode* node = CERT_LIST_HEAD(certList);
1082 0 : !CERT_LIST_END(node, certList); node = CERT_LIST_NEXT(node)) {
1083 0 : if (CertMatchesStaticData(
1084 0 : node->cert,
1085 : sGlobalSignExtendedValidationCASHA256G2SubjectBytes,
1086 : sGlobalSignExtendedValidationCASHA256G2SPKIBytes)) {
1087 0 : foundRequiredIntermediate = true;
1088 0 : break;
1089 : }
1090 : }
1091 0 : if (!foundRequiredIntermediate) {
1092 0 : return Result::ERROR_POLICY_VALIDATION_FAILED;
1093 : }
1094 : }
1095 :
1096 0 : mBuiltChain = Move(certList);
1097 :
1098 0 : return Success;
1099 : }
1100 :
1101 : Result
1102 0 : NSSCertDBTrustDomain::CheckSignatureDigestAlgorithm(DigestAlgorithm aAlg,
1103 : EndEntityOrCA endEntityOrCA,
1104 : Time notBefore)
1105 : {
1106 : // (new Date("2016-01-01T00:00:00Z")).getTime() / 1000
1107 0 : static const Time JANUARY_FIRST_2016 = TimeFromEpochInSeconds(1451606400);
1108 :
1109 0 : MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
1110 : ("NSSCertDBTrustDomain: CheckSignatureDigestAlgorithm"));
1111 0 : if (aAlg == DigestAlgorithm::sha1) {
1112 0 : switch (mSHA1Mode) {
1113 : case CertVerifier::SHA1Mode::Forbidden:
1114 0 : MOZ_LOG(gCertVerifierLog, LogLevel::Debug, ("SHA-1 certificate rejected"));
1115 0 : return Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED;
1116 : case CertVerifier::SHA1Mode::ImportedRootOrBefore2016:
1117 0 : if (JANUARY_FIRST_2016 <= notBefore) {
1118 0 : MOZ_LOG(gCertVerifierLog, LogLevel::Debug, ("Post-2015 SHA-1 certificate rejected"));
1119 0 : return Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED;
1120 : }
1121 0 : break;
1122 : case CertVerifier::SHA1Mode::Allowed:
1123 : // Enforcing that the resulting chain uses an imported root is only
1124 : // possible at a higher level. This is done in CertVerifier::VerifyCert.
1125 : case CertVerifier::SHA1Mode::ImportedRoot:
1126 : default:
1127 0 : break;
1128 : // MSVC warns unless we explicitly handle this now-unused option.
1129 : case CertVerifier::SHA1Mode::UsedToBeBefore2016ButNowIsForbidden:
1130 0 : MOZ_ASSERT_UNREACHABLE("unexpected SHA1Mode type");
1131 : return Result::FATAL_ERROR_LIBRARY_FAILURE;
1132 : }
1133 : }
1134 :
1135 0 : return Success;
1136 : }
1137 :
1138 : Result
1139 0 : NSSCertDBTrustDomain::CheckRSAPublicKeyModulusSizeInBits(
1140 : EndEntityOrCA /*endEntityOrCA*/, unsigned int modulusSizeInBits)
1141 : {
1142 0 : if (modulusSizeInBits < mMinRSABits) {
1143 0 : return Result::ERROR_INADEQUATE_KEY_SIZE;
1144 : }
1145 0 : return Success;
1146 : }
1147 :
1148 : Result
1149 0 : NSSCertDBTrustDomain::VerifyRSAPKCS1SignedDigest(
1150 : const SignedDigest& signedDigest,
1151 : Input subjectPublicKeyInfo)
1152 : {
1153 0 : return VerifyRSAPKCS1SignedDigestNSS(signedDigest, subjectPublicKeyInfo,
1154 0 : mPinArg);
1155 : }
1156 :
1157 : Result
1158 0 : NSSCertDBTrustDomain::CheckECDSACurveIsAcceptable(
1159 : EndEntityOrCA /*endEntityOrCA*/, NamedCurve curve)
1160 : {
1161 0 : switch (curve) {
1162 : case NamedCurve::secp256r1: // fall through
1163 : case NamedCurve::secp384r1: // fall through
1164 : case NamedCurve::secp521r1:
1165 0 : return Success;
1166 : }
1167 :
1168 0 : return Result::ERROR_UNSUPPORTED_ELLIPTIC_CURVE;
1169 : }
1170 :
1171 : Result
1172 0 : NSSCertDBTrustDomain::VerifyECDSASignedDigest(const SignedDigest& signedDigest,
1173 : Input subjectPublicKeyInfo)
1174 : {
1175 0 : return VerifyECDSASignedDigestNSS(signedDigest, subjectPublicKeyInfo,
1176 0 : mPinArg);
1177 : }
1178 :
1179 : Result
1180 0 : NSSCertDBTrustDomain::CheckValidityIsAcceptable(Time notBefore, Time notAfter,
1181 : EndEntityOrCA endEntityOrCA,
1182 : KeyPurposeId keyPurpose)
1183 : {
1184 0 : if (endEntityOrCA != EndEntityOrCA::MustBeEndEntity) {
1185 0 : return Success;
1186 : }
1187 0 : if (keyPurpose == KeyPurposeId::id_kp_OCSPSigning) {
1188 0 : return Success;
1189 : }
1190 :
1191 : Duration DURATION_27_MONTHS_PLUS_SLOP((2 * 365 + 3 * 31 + 7) *
1192 0 : Time::ONE_DAY_IN_SECONDS);
1193 0 : Duration maxValidityDuration(UINT64_MAX);
1194 0 : Duration validityDuration(notBefore, notAfter);
1195 :
1196 0 : switch (mValidityCheckingMode) {
1197 : case ValidityCheckingMode::CheckingOff:
1198 0 : return Success;
1199 : case ValidityCheckingMode::CheckForEV:
1200 : // The EV Guidelines say the maximum is 27 months, but we use a slightly
1201 : // higher limit here to (hopefully) minimize compatibility breakage.
1202 0 : maxValidityDuration = DURATION_27_MONTHS_PLUS_SLOP;
1203 0 : break;
1204 : default:
1205 0 : MOZ_ASSERT_UNREACHABLE("We're not handling every ValidityCheckingMode type");
1206 : }
1207 :
1208 0 : if (validityDuration > maxValidityDuration) {
1209 0 : return Result::ERROR_VALIDITY_TOO_LONG;
1210 : }
1211 :
1212 0 : return Success;
1213 : }
1214 :
1215 : Result
1216 0 : NSSCertDBTrustDomain::NetscapeStepUpMatchesServerAuth(Time notBefore,
1217 : /*out*/ bool& matches)
1218 : {
1219 : // (new Date("2015-08-23T00:00:00Z")).getTime() / 1000
1220 0 : static const Time AUGUST_23_2015 = TimeFromEpochInSeconds(1440288000);
1221 : // (new Date("2016-08-23T00:00:00Z")).getTime() / 1000
1222 0 : static const Time AUGUST_23_2016 = TimeFromEpochInSeconds(1471910400);
1223 :
1224 0 : switch (mNetscapeStepUpPolicy) {
1225 : case NetscapeStepUpPolicy::AlwaysMatch:
1226 0 : matches = true;
1227 0 : return Success;
1228 : case NetscapeStepUpPolicy::MatchBefore23August2016:
1229 0 : matches = notBefore < AUGUST_23_2016;
1230 0 : return Success;
1231 : case NetscapeStepUpPolicy::MatchBefore23August2015:
1232 0 : matches = notBefore < AUGUST_23_2015;
1233 0 : return Success;
1234 : case NetscapeStepUpPolicy::NeverMatch:
1235 0 : matches = false;
1236 0 : return Success;
1237 : default:
1238 0 : MOZ_ASSERT_UNREACHABLE("unhandled NetscapeStepUpPolicy type");
1239 : }
1240 : return Result::FATAL_ERROR_LIBRARY_FAILURE;
1241 : }
1242 :
1243 : void
1244 0 : NSSCertDBTrustDomain::ResetAccumulatedState()
1245 : {
1246 0 : mOCSPStaplingStatus = CertVerifier::OCSP_STAPLING_NEVER_CHECKED;
1247 0 : mSCTListFromOCSPStapling = nullptr;
1248 0 : mSCTListFromCertificate = nullptr;
1249 0 : }
1250 :
1251 : static Input
1252 0 : SECItemToInput(const UniqueSECItem& item)
1253 : {
1254 0 : Input result;
1255 0 : if (item) {
1256 0 : MOZ_ASSERT(item->type == siBuffer);
1257 0 : Result rv = result.Init(item->data, item->len);
1258 : // As used here, |item| originally comes from an Input,
1259 : // so there should be no issues converting it back.
1260 0 : MOZ_ASSERT(rv == Success);
1261 : Unused << rv; // suppresses warnings in release builds
1262 : }
1263 0 : return result;
1264 : }
1265 :
1266 : Input
1267 0 : NSSCertDBTrustDomain::GetSCTListFromCertificate() const
1268 : {
1269 0 : return SECItemToInput(mSCTListFromCertificate);
1270 : }
1271 :
1272 : Input
1273 0 : NSSCertDBTrustDomain::GetSCTListFromOCSPStapling() const
1274 : {
1275 0 : return SECItemToInput(mSCTListFromOCSPStapling);
1276 : }
1277 :
1278 : void
1279 0 : NSSCertDBTrustDomain::NoteAuxiliaryExtension(AuxiliaryExtension extension,
1280 : Input extensionData)
1281 : {
1282 0 : UniqueSECItem* out = nullptr;
1283 0 : switch (extension) {
1284 : case AuxiliaryExtension::EmbeddedSCTList:
1285 0 : out = &mSCTListFromCertificate;
1286 0 : break;
1287 : case AuxiliaryExtension::SCTListFromOCSPResponse:
1288 0 : out = &mSCTListFromOCSPStapling;
1289 0 : break;
1290 : default:
1291 0 : MOZ_ASSERT_UNREACHABLE("unhandled AuxiliaryExtension");
1292 : }
1293 0 : if (out) {
1294 0 : SECItem extensionDataItem = UnsafeMapInputToSECItem(extensionData);
1295 0 : out->reset(SECITEM_DupItem(&extensionDataItem));
1296 : }
1297 0 : }
1298 :
1299 : SECStatus
1300 1 : InitializeNSS(const char* dir, bool readOnly, bool loadPKCS11Modules)
1301 : {
1302 : // The NSS_INIT_NOROOTINIT flag turns off the loading of the root certs
1303 : // module by NSS_Initialize because we will load it in InstallLoadableRoots
1304 : // later. It also allows us to work around a bug in the system NSS in
1305 : // Ubuntu 8.04, which loads any nonexistent "<configdir>/libnssckbi.so" as
1306 : // "/usr/lib/nss/libnssckbi.so".
1307 1 : uint32_t flags = NSS_INIT_NOROOTINIT | NSS_INIT_OPTIMIZESPACE;
1308 1 : if (readOnly) {
1309 0 : flags |= NSS_INIT_READONLY;
1310 : }
1311 1 : if (!loadPKCS11Modules) {
1312 0 : flags |= NSS_INIT_NOMODDB;
1313 : }
1314 1 : MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
1315 : ("InitializeNSS(%s, %d, %d)", dir, readOnly, loadPKCS11Modules));
1316 1 : return ::NSS_Initialize(dir, "", "", SECMOD_DB, flags);
1317 : }
1318 :
1319 : void
1320 2 : DisableMD5()
1321 : {
1322 : NSS_SetAlgorithmPolicy(SEC_OID_MD5,
1323 2 : 0, NSS_USE_ALG_IN_CERT_SIGNATURE | NSS_USE_ALG_IN_CMS_SIGNATURE);
1324 : NSS_SetAlgorithmPolicy(SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION,
1325 2 : 0, NSS_USE_ALG_IN_CERT_SIGNATURE | NSS_USE_ALG_IN_CMS_SIGNATURE);
1326 : NSS_SetAlgorithmPolicy(SEC_OID_PKCS5_PBE_WITH_MD5_AND_DES_CBC,
1327 2 : 0, NSS_USE_ALG_IN_CERT_SIGNATURE | NSS_USE_ALG_IN_CMS_SIGNATURE);
1328 2 : }
1329 :
1330 : bool
1331 1 : LoadLoadableRoots(const nsCString& dir, const nsCString& modNameUTF8)
1332 : {
1333 : UniquePRLibraryName fullLibraryPath(
1334 2 : PR_GetLibraryName(dir.IsEmpty() ? nullptr : dir.get(), "nssckbi"));
1335 1 : if (!fullLibraryPath) {
1336 0 : return false;
1337 : }
1338 :
1339 : // Escape the \ and " characters.
1340 2 : nsAutoCString escapedFullLibraryPath(fullLibraryPath.get());
1341 1 : escapedFullLibraryPath.ReplaceSubstring("\\", "\\\\");
1342 1 : escapedFullLibraryPath.ReplaceSubstring("\"", "\\\"");
1343 1 : if (escapedFullLibraryPath.IsEmpty()) {
1344 0 : return false;
1345 : }
1346 :
1347 : // If a module exists with the same name, make a best effort attempt to delete
1348 : // it. Note that it isn't possible to delete the internal module, so checking
1349 : // the return value would be detrimental in that case.
1350 : int unusedModType;
1351 1 : Unused << SECMOD_DeleteModule(modNameUTF8.get(), &unusedModType);
1352 :
1353 2 : nsAutoCString pkcs11ModuleSpec;
1354 1 : pkcs11ModuleSpec.AppendPrintf("name=\"%s\" library=\"%s\"", modNameUTF8.get(),
1355 1 : escapedFullLibraryPath.get());
1356 1 : if (pkcs11ModuleSpec.IsEmpty()) {
1357 0 : return false;
1358 : }
1359 :
1360 : UniqueSECMODModule rootsModule(
1361 1 : SECMOD_LoadUserModule(const_cast<char*>(pkcs11ModuleSpec.get()), nullptr,
1362 2 : false));
1363 1 : if (!rootsModule) {
1364 0 : return false;
1365 : }
1366 :
1367 1 : if (!rootsModule->loaded) {
1368 0 : return false;
1369 : }
1370 :
1371 1 : return true;
1372 : }
1373 :
1374 : void
1375 0 : UnloadLoadableRoots(const char* modNameUTF8)
1376 : {
1377 0 : MOZ_ASSERT(modNameUTF8);
1378 0 : UniqueSECMODModule rootsModule(SECMOD_FindModule(modNameUTF8));
1379 :
1380 0 : if (rootsModule) {
1381 0 : SECMOD_UnloadUserModule(rootsModule.get());
1382 : }
1383 0 : }
1384 :
1385 : nsresult
1386 0 : DefaultServerNicknameForCert(const CERTCertificate* cert,
1387 : /*out*/ nsCString& nickname)
1388 : {
1389 0 : MOZ_ASSERT(cert);
1390 0 : NS_ENSURE_ARG_POINTER(cert);
1391 :
1392 0 : UniquePORTString baseName(CERT_GetCommonName(&cert->subject));
1393 0 : if (!baseName) {
1394 0 : baseName = UniquePORTString(CERT_GetOrgUnitName(&cert->subject));
1395 : }
1396 0 : if (!baseName) {
1397 0 : baseName = UniquePORTString(CERT_GetOrgName(&cert->subject));
1398 : }
1399 0 : if (!baseName) {
1400 0 : baseName = UniquePORTString(CERT_GetLocalityName(&cert->subject));
1401 : }
1402 0 : if (!baseName) {
1403 0 : baseName = UniquePORTString(CERT_GetStateName(&cert->subject));
1404 : }
1405 0 : if (!baseName) {
1406 0 : baseName = UniquePORTString(CERT_GetCountryName(&cert->subject));
1407 : }
1408 0 : if (!baseName) {
1409 0 : return NS_ERROR_FAILURE;
1410 : }
1411 :
1412 : // This function is only used in contexts where a failure to find a suitable
1413 : // nickname does not block the overall task from succeeding.
1414 : // As such, we use an arbitrary limit to prevent this nickname searching
1415 : // process from taking forever.
1416 : static const uint32_t ARBITRARY_LIMIT = 500;
1417 0 : for (uint32_t count = 1; count < ARBITRARY_LIMIT; count++) {
1418 0 : nickname = baseName.get();
1419 0 : if (count != 1) {
1420 0 : nickname.AppendPrintf(" #%u", count);
1421 : }
1422 0 : if (nickname.IsEmpty()) {
1423 0 : return NS_ERROR_FAILURE;
1424 : }
1425 :
1426 0 : bool conflict = SEC_CertNicknameConflict(nickname.get(), &cert->derSubject,
1427 0 : cert->dbhandle);
1428 0 : if (!conflict) {
1429 0 : return NS_OK;
1430 : }
1431 : }
1432 :
1433 0 : return NS_ERROR_FAILURE;
1434 : }
1435 :
1436 : /**
1437 : * Given a list of certificates representing a verified certificate path from an
1438 : * end-entity certificate to a trust anchor, imports the intermediate
1439 : * certificates into the permanent certificate database. This is an attempt to
1440 : * cope with misconfigured servers that don't include the appropriate
1441 : * intermediate certificates in the TLS handshake.
1442 : *
1443 : * @param certList the verified certificate list
1444 : */
1445 : void
1446 0 : SaveIntermediateCerts(const UniqueCERTCertList& certList)
1447 : {
1448 0 : if (!certList) {
1449 0 : return;
1450 : }
1451 :
1452 0 : UniquePK11SlotInfo slot(PK11_GetInternalKeySlot());
1453 0 : if (!slot) {
1454 0 : return;
1455 : }
1456 :
1457 0 : bool isEndEntity = true;
1458 0 : for (CERTCertListNode* node = CERT_LIST_HEAD(certList);
1459 0 : !CERT_LIST_END(node, certList);
1460 0 : node = CERT_LIST_NEXT(node)) {
1461 0 : if (isEndEntity) {
1462 : // Skip the end-entity; we only want to store intermediates
1463 0 : isEndEntity = false;
1464 0 : continue;
1465 : }
1466 :
1467 0 : if (node->cert->slot) {
1468 : // This cert was found on a token; no need to remember it in the permanent
1469 : // database.
1470 0 : continue;
1471 : }
1472 :
1473 0 : if (node->cert->isperm) {
1474 : // We don't need to remember certs already stored in perm db.
1475 0 : continue;
1476 : }
1477 :
1478 : // No need to save the trust anchor - it's either already a permanent
1479 : // certificate or it's the Microsoft Family Safety root or an enterprise
1480 : // root temporarily imported via the child mode or enterprise root features.
1481 : // We don't want to import these because they're intended to be temporary
1482 : // (and because importing them happens to reset their trust settings, which
1483 : // breaks these features).
1484 0 : if (node == CERT_LIST_TAIL(certList)) {
1485 0 : continue;
1486 : }
1487 :
1488 0 : nsAutoCString nickname;
1489 0 : nsresult rv = DefaultServerNicknameForCert(node->cert, nickname);
1490 0 : if (NS_FAILED(rv)) {
1491 0 : continue;
1492 : }
1493 :
1494 : // As mentioned in the documentation of this function, we're importing only
1495 : // to cope with misconfigured servers. As such, we ignore the return value
1496 : // below, since it doesn't really matter if the import fails.
1497 0 : Unused << PK11_ImportCert(slot.get(), node->cert, CK_INVALID_HANDLE,
1498 : nickname.get(), false);
1499 : }
1500 : }
1501 :
1502 : } } // namespace mozilla::psm
|