Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/security/certverifier/NSSCertDBTrustDomain.cpp
Line
Count
Source (jump to first uncovered line)
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 "OCSPVerificationTrustDomain.h"
14
#include "PublicKeyPinningService.h"
15
#include "cert.h"
16
#include "certdb.h"
17
#include "mozilla/Assertions.h"
18
#include "mozilla/Casting.h"
19
#include "mozilla/Move.h"
20
#include "mozilla/PodOperations.h"
21
#include "mozilla/TimeStamp.h"
22
#include "mozilla/Unused.h"
23
#include "nsCRTGlue.h"
24
#include "nsNSSCertHelper.h"
25
#include "nsNSSCertValidity.h"
26
#include "nsNSSCertificate.h"
27
#include "nsServiceManagerUtils.h"
28
#include "nsThreadUtils.h"
29
#include "nss.h"
30
#include "pk11pub.h"
31
#include "pkix/Result.h"
32
#include "pkix/pkix.h"
33
#include "pkix/pkixnss.h"
34
#include "prerror.h"
35
#include "secerr.h"
36
37
#include "TrustOverrideUtils.h"
38
#include "TrustOverride-StartComAndWoSignData.inc"
39
#include "TrustOverride-GlobalSignData.inc"
40
#include "TrustOverride-SymantecData.inc"
41
#include "TrustOverride-AppleGoogleDigiCertData.inc"
42
43
using namespace mozilla;
44
using namespace mozilla::pkix;
45
46
extern LazyLogModule gCertVerifierLog;
47
48
static const uint64_t ServerFailureDelaySeconds = 5 * 60;
49
50
namespace mozilla { namespace psm {
51
52
NSSCertDBTrustDomain::NSSCertDBTrustDomain(SECTrustType certDBTrustType,
53
                                           OCSPFetching ocspFetching,
54
                                           OCSPCache& ocspCache,
55
             /*optional but shouldn't be*/ void* pinArg,
56
                                           TimeDuration ocspTimeoutSoft,
57
                                           TimeDuration ocspTimeoutHard,
58
                                           uint32_t certShortLifetimeInDays,
59
                                           CertVerifier::PinningMode pinningMode,
60
                                           unsigned int minRSABits,
61
                                           ValidityCheckingMode validityCheckingMode,
62
                                           CertVerifier::SHA1Mode sha1Mode,
63
                                           NetscapeStepUpPolicy netscapeStepUpPolicy,
64
                                           DistrustedCAPolicy distrustedCAPolicy,
65
                                           const OriginAttributes& originAttributes,
66
                                           UniqueCERTCertList& builtChain,
67
                              /*optional*/ PinningTelemetryInfo* pinningTelemetryInfo,
68
                              /*optional*/ const char* hostname)
69
  : mCertDBTrustType(certDBTrustType)
70
  , mOCSPFetching(ocspFetching)
71
  , mOCSPCache(ocspCache)
72
  , mPinArg(pinArg)
73
  , mOCSPTimeoutSoft(ocspTimeoutSoft)
74
  , mOCSPTimeoutHard(ocspTimeoutHard)
75
  , mCertShortLifetimeInDays(certShortLifetimeInDays)
76
  , mPinningMode(pinningMode)
77
  , mMinRSABits(minRSABits)
78
  , mValidityCheckingMode(validityCheckingMode)
79
  , mSHA1Mode(sha1Mode)
80
  , mNetscapeStepUpPolicy(netscapeStepUpPolicy)
81
  , mDistrustedCAPolicy(distrustedCAPolicy)
82
  , mSawDistrustedCAByPolicyError(false)
83
  , mOriginAttributes(originAttributes)
84
  , mBuiltChain(builtChain)
85
  , mPinningTelemetryInfo(pinningTelemetryInfo)
86
  , mHostname(hostname)
87
  , mCertBlocklist(do_GetService(NS_CERTBLOCKLIST_CONTRACTID))
88
  , mOCSPStaplingStatus(CertVerifier::OCSP_STAPLING_NEVER_CHECKED)
89
  , mSCTListFromCertificate()
90
  , mSCTListFromOCSPStapling()
91
0
{
92
0
}
93
94
// If useRoots is true, we only use root certificates in the candidate list.
95
// If useRoots is false, we only use non-root certificates in the list.
96
static Result
97
FindIssuerInner(const UniqueCERTCertList& candidates, bool useRoots,
98
                Input encodedIssuerName, TrustDomain::IssuerChecker& checker,
99
                /*out*/ bool& keepGoing)
100
0
{
101
0
  keepGoing = true;
102
0
  for (CERTCertListNode* n = CERT_LIST_HEAD(candidates);
103
0
       !CERT_LIST_END(n, candidates); n = CERT_LIST_NEXT(n)) {
104
0
    bool candidateIsRoot = !!n->cert->isRoot;
105
0
    if (candidateIsRoot != useRoots) {
106
0
      continue;
107
0
    }
108
0
    Input certDER;
109
0
    Result rv = certDER.Init(n->cert->derCert.data, n->cert->derCert.len);
110
0
    if (rv != Success) {
111
0
      continue; // probably too big
112
0
    }
113
0
114
0
    const SECItem encodedIssuerNameItem = {
115
0
      siBuffer,
116
0
      const_cast<unsigned char*>(encodedIssuerName.UnsafeGetData()),
117
0
      encodedIssuerName.GetLength()
118
0
    };
119
0
    ScopedAutoSECItem nameConstraints;
120
0
    SECStatus srv = CERT_GetImposedNameConstraints(&encodedIssuerNameItem,
121
0
                                                   &nameConstraints);
122
0
    if (srv != SECSuccess) {
123
0
      if (PR_GetError() != SEC_ERROR_EXTENSION_NOT_FOUND) {
124
0
        return Result::FATAL_ERROR_LIBRARY_FAILURE;
125
0
      }
126
0
127
0
      // If no imposed name constraints were found, continue without them
128
0
      rv = checker.Check(certDER, nullptr, keepGoing);
129
0
    } else {
130
0
      // Otherwise apply the constraints
131
0
      Input nameConstraintsInput;
132
0
      if (nameConstraintsInput.Init(nameConstraints.data, nameConstraints.len)
133
0
            != Success) {
134
0
        return Result::FATAL_ERROR_LIBRARY_FAILURE;
135
0
      }
136
0
      rv = checker.Check(certDER, &nameConstraintsInput, keepGoing);
137
0
    }
138
0
    if (rv != Success) {
139
0
      return rv;
140
0
    }
141
0
    if (!keepGoing) {
142
0
      break;
143
0
    }
144
0
  }
145
0
146
0
  return Success;
147
0
}
148
149
Result
150
NSSCertDBTrustDomain::FindIssuer(Input encodedIssuerName,
151
                                 IssuerChecker& checker, Time)
152
0
{
153
0
  // TODO: NSS seems to be ambiguous between "no potential issuers found" and
154
0
  // "there was an error trying to retrieve the potential issuers."
155
0
  SECItem encodedIssuerNameItem = UnsafeMapInputToSECItem(encodedIssuerName);
156
0
  UniqueCERTCertList
157
0
    candidates(CERT_CreateSubjectCertList(nullptr, CERT_GetDefaultCertDB(),
158
0
                                          &encodedIssuerNameItem, 0,
159
0
                                          false));
160
0
  if (candidates) {
161
0
    // First, try all the root certs; then try all the non-root certs.
162
0
    bool keepGoing;
163
0
    Result rv = FindIssuerInner(candidates, true, encodedIssuerName, checker,
164
0
                                keepGoing);
165
0
    if (rv != Success) {
166
0
      return rv;
167
0
    }
168
0
    if (keepGoing) {
169
0
      rv = FindIssuerInner(candidates, false, encodedIssuerName, checker,
170
0
                           keepGoing);
171
0
      if (rv != Success) {
172
0
        return rv;
173
0
      }
174
0
    }
175
0
  }
176
0
177
0
  return Success;
178
0
}
179
180
Result
181
NSSCertDBTrustDomain::GetCertTrust(EndEntityOrCA endEntityOrCA,
182
                                   const CertPolicyId& policy,
183
                                   Input candidateCertDER,
184
                                   /*out*/ TrustLevel& trustLevel)
185
0
{
186
0
  // XXX: This would be cleaner and more efficient if we could get the trust
187
0
  // information without constructing a CERTCertificate here, but NSS doesn't
188
0
  // expose it in any other easy-to-use fashion. The use of
189
0
  // CERT_NewTempCertificate to get a CERTCertificate shouldn't be a
190
0
  // performance problem because NSS will just find the existing
191
0
  // CERTCertificate in its in-memory cache and return it.
192
0
  SECItem candidateCertDERSECItem = UnsafeMapInputToSECItem(candidateCertDER);
193
0
  UniqueCERTCertificate candidateCert(
194
0
    CERT_NewTempCertificate(CERT_GetDefaultCertDB(), &candidateCertDERSECItem,
195
0
                            nullptr, false, true));
196
0
  if (!candidateCert) {
197
0
    return MapPRErrorCodeToResult(PR_GetError());
198
0
  }
199
0
200
0
  // Check the certificate against the OneCRL cert blocklist
201
0
  if (!mCertBlocklist) {
202
0
    return Result::FATAL_ERROR_LIBRARY_FAILURE;
203
0
  }
204
0
205
0
  // The certificate blocklist currently only applies to TLS server
206
0
  // certificates.
207
0
  if (mCertDBTrustType == trustSSL) {
208
0
    bool isCertRevoked;
209
0
    nsresult nsrv = mCertBlocklist->IsCertRevoked(
210
0
                      candidateCert->derIssuer.data,
211
0
                      candidateCert->derIssuer.len,
212
0
                      candidateCert->serialNumber.data,
213
0
                      candidateCert->serialNumber.len,
214
0
                      candidateCert->derSubject.data,
215
0
                      candidateCert->derSubject.len,
216
0
                      candidateCert->derPublicKey.data,
217
0
                      candidateCert->derPublicKey.len,
218
0
                      &isCertRevoked);
219
0
    if (NS_FAILED(nsrv)) {
220
0
      return Result::FATAL_ERROR_LIBRARY_FAILURE;
221
0
    }
222
0
223
0
    if (isCertRevoked) {
224
0
      MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
225
0
             ("NSSCertDBTrustDomain: certificate is in blocklist"));
226
0
      return Result::ERROR_REVOKED_CERTIFICATE;
227
0
    }
228
0
  }
229
0
230
0
  // XXX: CERT_GetCertTrust seems to be abusing SECStatus as a boolean, where
231
0
  // SECSuccess means that there is a trust record and SECFailure means there
232
0
  // is not a trust record. I looked at NSS's internal uses of
233
0
  // CERT_GetCertTrust, and all that code uses the result as a boolean meaning
234
0
  // "We have a trust record."
235
0
  CERTCertTrust trust;
236
0
  if (CERT_GetCertTrust(candidateCert.get(), &trust) == SECSuccess) {
237
0
    uint32_t flags = SEC_GET_TRUST_FLAGS(&trust, mCertDBTrustType);
238
0
239
0
    // For DISTRUST, we use the CERTDB_TRUSTED or CERTDB_TRUSTED_CA bit,
240
0
    // because we can have active distrust for either type of cert. Note that
241
0
    // CERTDB_TERMINAL_RECORD means "stop trying to inherit trust" so if the
242
0
    // relevant trust bit isn't set then that means the cert must be considered
243
0
    // distrusted.
244
0
    uint32_t relevantTrustBit =
245
0
      endEntityOrCA == EndEntityOrCA::MustBeCA ? CERTDB_TRUSTED_CA
246
0
                                               : CERTDB_TRUSTED;
247
0
    if (((flags & (relevantTrustBit|CERTDB_TERMINAL_RECORD)))
248
0
            == CERTDB_TERMINAL_RECORD) {
249
0
      trustLevel = TrustLevel::ActivelyDistrusted;
250
0
      return Success;
251
0
    }
252
0
253
0
    // For TRUST, we only use the CERTDB_TRUSTED_CA bit, because Gecko hasn't
254
0
    // needed to consider end-entity certs to be their own trust anchors since
255
0
    // Gecko implemented nsICertOverrideService.
256
0
    // Of course, for this to work as expected, we need to make sure we're
257
0
    // inquiring about the trust of a CA and not an end-entity. If an end-entity
258
0
    // has the CERTDB_TRUSTED_CA bit set, Gecko does not consider it to be a
259
0
    // trust anchor; it must inherit its trust.
260
0
    if (flags & CERTDB_TRUSTED_CA && endEntityOrCA == EndEntityOrCA::MustBeCA) {
261
0
      if (policy.IsAnyPolicy()) {
262
0
        trustLevel = TrustLevel::TrustAnchor;
263
0
        return Success;
264
0
      }
265
0
      if (CertIsAuthoritativeForEVPolicy(candidateCert, policy)) {
266
0
        trustLevel = TrustLevel::TrustAnchor;
267
0
        return Success;
268
0
      }
269
0
    }
270
0
  }
271
0
272
0
  trustLevel = TrustLevel::InheritsTrust;
273
0
  return Success;
274
0
}
275
276
Result
277
NSSCertDBTrustDomain::DigestBuf(Input item, DigestAlgorithm digestAlg,
278
                                /*out*/ uint8_t* digestBuf, size_t digestBufLen)
279
0
{
280
0
  return DigestBufNSS(item, digestAlg, digestBuf, digestBufLen);
281
0
}
282
283
TimeDuration
284
NSSCertDBTrustDomain::GetOCSPTimeout() const
285
0
{
286
0
  switch (mOCSPFetching) {
287
0
    case NSSCertDBTrustDomain::FetchOCSPForDVSoftFail:
288
0
      return mOCSPTimeoutSoft;
289
0
    case NSSCertDBTrustDomain::FetchOCSPForEV:
290
0
    case NSSCertDBTrustDomain::FetchOCSPForDVHardFail:
291
0
      return mOCSPTimeoutHard;
292
0
    // The rest of these are error cases. Assert in debug builds, but return
293
0
    // the soft timeout value in release builds.
294
0
    case NSSCertDBTrustDomain::NeverFetchOCSP:
295
0
    case NSSCertDBTrustDomain::LocalOnlyOCSPForEV:
296
0
      MOZ_ASSERT_UNREACHABLE("we should never see this OCSPFetching type here");
297
0
      break;
298
0
  }
299
0
300
0
  MOZ_ASSERT_UNREACHABLE("we're not handling every OCSPFetching type");
301
0
  return mOCSPTimeoutSoft;
302
0
}
303
304
// Copied and modified from CERT_GetOCSPAuthorityInfoAccessLocation and
305
// CERT_GetGeneralNameByType. Returns a non-Result::Success result on error,
306
// Success with result.IsVoid() == true when an OCSP URI was not found, and
307
// Success with result.IsVoid() == false when an OCSP URI was found.
308
static Result
309
GetOCSPAuthorityInfoAccessLocation(const UniquePLArenaPool& arena,
310
                                   Input aiaExtension,
311
                                   /*out*/ nsCString& result)
312
0
{
313
0
  MOZ_ASSERT(arena.get());
314
0
  if (!arena.get()) {
315
0
    return Result::FATAL_ERROR_INVALID_ARGS;
316
0
  }
317
0
318
0
  result.Assign(VoidCString());
319
0
  SECItem aiaExtensionSECItem = UnsafeMapInputToSECItem(aiaExtension);
320
0
  CERTAuthInfoAccess** aia =
321
0
    CERT_DecodeAuthInfoAccessExtension(arena.get(), &aiaExtensionSECItem);
322
0
  if (!aia) {
323
0
    return Result::ERROR_CERT_BAD_ACCESS_LOCATION;
324
0
  }
325
0
  for (size_t i = 0; aia[i]; ++i) {
326
0
    if (SECOID_FindOIDTag(&aia[i]->method) == SEC_OID_PKIX_OCSP) {
327
0
      // NSS chooses the **last** OCSP URL; we choose the **first**
328
0
      CERTGeneralName* current = aia[i]->location;
329
0
      if (!current) {
330
0
        continue;
331
0
      }
332
0
      do {
333
0
        if (current->type == certURI) {
334
0
          const SECItem& location = current->name.other;
335
0
          // (location.len + 1) must be small enough to fit into a uint32_t,
336
0
          // but we limit it to a smaller bound to reduce OOM risk.
337
0
          if (location.len > 1024 || memchr(location.data, 0, location.len)) {
338
0
            // Reject embedded nulls. (NSS doesn't do this)
339
0
            return Result::ERROR_CERT_BAD_ACCESS_LOCATION;
340
0
          }
341
0
          result.Assign(nsDependentCSubstring(
342
0
                          reinterpret_cast<const char*>(location.data),
343
0
                          location.len));
344
0
          return Success;
345
0
        }
346
0
        current = CERT_GetNextGeneralName(current);
347
0
      } while (current != aia[i]->location);
348
0
    }
349
0
  }
350
0
351
0
  return Success;
352
0
}
353
354
Result
355
NSSCertDBTrustDomain::CheckRevocation(EndEntityOrCA endEntityOrCA,
356
                                      const CertID& certID, Time time,
357
                                      Duration validityDuration,
358
                         /*optional*/ const Input* stapledOCSPResponse,
359
                         /*optional*/ const Input* aiaExtension)
360
0
{
361
0
  // Actively distrusted certificates will have already been blocked by
362
0
  // GetCertTrust.
363
0
364
0
  // TODO: need to verify that IsRevoked isn't called for trust anchors AND
365
0
  // that that fact is documented in mozillapkix.
366
0
367
0
  MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
368
0
         ("NSSCertDBTrustDomain: Top of CheckRevocation\n"));
369
0
370
0
  // Bug 991815: The BR allow OCSP for intermediates to be up to one year old.
371
0
  // Since this affects EV there is no reason why DV should be more strict
372
0
  // so all intermediatates are allowed to have OCSP responses up to one year
373
0
  // old.
374
0
  uint16_t maxOCSPLifetimeInDays = 10;
375
0
  if (endEntityOrCA == EndEntityOrCA::MustBeCA) {
376
0
    maxOCSPLifetimeInDays = 365;
377
0
  }
378
0
379
0
  // If we have a stapled OCSP response then the verification of that response
380
0
  // determines the result unless the OCSP response is expired. We make an
381
0
  // exception for expired responses because some servers, nginx in particular,
382
0
  // are known to serve expired responses due to bugs.
383
0
  // We keep track of the result of verifying the stapled response but don't
384
0
  // immediately return failure if the response has expired.
385
0
  //
386
0
  // We only set the OCSP stapling status if we're validating the end-entity
387
0
  // certificate. Non-end-entity certificates would always be
388
0
  // OCSP_STAPLING_NONE unless/until we implement multi-stapling.
389
0
  Result stapledOCSPResponseResult = Success;
390
0
  if (stapledOCSPResponse) {
391
0
    MOZ_ASSERT(endEntityOrCA == EndEntityOrCA::MustBeEndEntity);
392
0
    bool expired;
393
0
    stapledOCSPResponseResult =
394
0
      VerifyAndMaybeCacheEncodedOCSPResponse(certID, time,
395
0
                                             maxOCSPLifetimeInDays,
396
0
                                             *stapledOCSPResponse,
397
0
                                             ResponseWasStapled, expired);
398
0
    if (stapledOCSPResponseResult == Success) {
399
0
      // stapled OCSP response present and good
400
0
      mOCSPStaplingStatus = CertVerifier::OCSP_STAPLING_GOOD;
401
0
      MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
402
0
             ("NSSCertDBTrustDomain: stapled OCSP response: good"));
403
0
      return Success;
404
0
    }
405
0
    if (stapledOCSPResponseResult == Result::ERROR_OCSP_OLD_RESPONSE ||
406
0
        expired) {
407
0
      // stapled OCSP response present but expired
408
0
      mOCSPStaplingStatus = CertVerifier::OCSP_STAPLING_EXPIRED;
409
0
      MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
410
0
             ("NSSCertDBTrustDomain: expired stapled OCSP response"));
411
0
    } else if (stapledOCSPResponseResult ==
412
0
                 Result::ERROR_OCSP_TRY_SERVER_LATER ||
413
0
               stapledOCSPResponseResult ==
414
0
                 Result::ERROR_OCSP_INVALID_SIGNING_CERT) {
415
0
      // Stapled OCSP response present but invalid for a small number of reasons
416
0
      // CAs/servers commonly get wrong. This will be treated similarly to an
417
0
      // expired stapled response.
418
0
      mOCSPStaplingStatus = CertVerifier::OCSP_STAPLING_INVALID;
419
0
      MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
420
0
              ("NSSCertDBTrustDomain: stapled OCSP response: "
421
0
               "failure (whitelisted for compatibility)"));
422
0
    } else {
423
0
      // stapled OCSP response present but invalid for some reason
424
0
      mOCSPStaplingStatus = CertVerifier::OCSP_STAPLING_INVALID;
425
0
      MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
426
0
             ("NSSCertDBTrustDomain: stapled OCSP response: failure"));
427
0
      return stapledOCSPResponseResult;
428
0
    }
429
0
  } else if (endEntityOrCA == EndEntityOrCA::MustBeEndEntity) {
430
0
    // no stapled OCSP response
431
0
    mOCSPStaplingStatus = CertVerifier::OCSP_STAPLING_NONE;
432
0
    MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
433
0
           ("NSSCertDBTrustDomain: no stapled OCSP response"));
434
0
  }
435
0
436
0
  Result cachedResponseResult = Success;
437
0
  Time cachedResponseValidThrough(Time::uninitialized);
438
0
  bool cachedResponsePresent = mOCSPCache.Get(certID, mOriginAttributes,
439
0
                                              cachedResponseResult,
440
0
                                              cachedResponseValidThrough);
441
0
  if (cachedResponsePresent) {
442
0
    if (cachedResponseResult == Success && cachedResponseValidThrough >= time) {
443
0
      MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
444
0
             ("NSSCertDBTrustDomain: cached OCSP response: good"));
445
0
      return Success;
446
0
    }
447
0
    // If we have a cached revoked response, use it.
448
0
    if (cachedResponseResult == Result::ERROR_REVOKED_CERTIFICATE) {
449
0
      MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
450
0
             ("NSSCertDBTrustDomain: cached OCSP response: revoked"));
451
0
      return Result::ERROR_REVOKED_CERTIFICATE;
452
0
    }
453
0
    // The cached response may indicate an unknown certificate or it may be
454
0
    // expired. Don't return with either of these statuses yet - we may be
455
0
    // able to fetch a more recent one.
456
0
    MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
457
0
           ("NSSCertDBTrustDomain: cached OCSP response: error %d",
458
0
            static_cast<int>(cachedResponseResult)));
459
0
    // When a good cached response has expired, it is more convenient
460
0
    // to convert that to an error code and just deal with
461
0
    // cachedResponseResult from here on out.
462
0
    if (cachedResponseResult == Success && cachedResponseValidThrough < time) {
463
0
      cachedResponseResult = Result::ERROR_OCSP_OLD_RESPONSE;
464
0
    }
465
0
    // We may have a cached indication of server failure. Ignore it if
466
0
    // it has expired.
467
0
    if (cachedResponseResult != Success &&
468
0
        cachedResponseResult != Result::ERROR_OCSP_UNKNOWN_CERT &&
469
0
        cachedResponseResult != Result::ERROR_OCSP_OLD_RESPONSE &&
470
0
        cachedResponseValidThrough < time) {
471
0
      cachedResponseResult = Success;
472
0
      cachedResponsePresent = false;
473
0
    }
474
0
  } else {
475
0
    MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
476
0
           ("NSSCertDBTrustDomain: no cached OCSP response"));
477
0
  }
478
0
  // At this point, if and only if cachedErrorResult is Success, there was no
479
0
  // cached response.
480
0
  MOZ_ASSERT((!cachedResponsePresent && cachedResponseResult == Success) ||
481
0
             (cachedResponsePresent && cachedResponseResult != Success));
482
0
483
0
  // If we have a fresh OneCRL Blocklist we can skip OCSP for CA certs
484
0
  bool blocklistIsFresh;
485
0
  nsresult nsrv = mCertBlocklist->IsBlocklistFresh(&blocklistIsFresh);
486
0
  if (NS_FAILED(nsrv)) {
487
0
    return Result::FATAL_ERROR_LIBRARY_FAILURE;
488
0
  }
489
0
490
0
  // TODO: We still need to handle the fallback for invalid stapled responses.
491
0
  // But, if/when we disable OCSP fetching by default, it would be ambiguous
492
0
  // whether security.OCSP.enable==0 means "I want the default" or "I really
493
0
  // never want you to ever fetch OCSP."
494
0
  // Additionally, this doesn't properly handle OCSP-must-staple when OCSP
495
0
  // fetching is disabled.
496
0
  Duration shortLifetime(mCertShortLifetimeInDays * Time::ONE_DAY_IN_SECONDS);
497
0
  if ((mOCSPFetching == NeverFetchOCSP) ||
498
0
      (validityDuration < shortLifetime) ||
499
0
      (endEntityOrCA == EndEntityOrCA::MustBeCA &&
500
0
       (mOCSPFetching == FetchOCSPForDVHardFail ||
501
0
        mOCSPFetching == FetchOCSPForDVSoftFail ||
502
0
        blocklistIsFresh))) {
503
0
    // We're not going to be doing any fetching, so if there was a cached
504
0
    // "unknown" response, say so.
505
0
    if (cachedResponseResult == Result::ERROR_OCSP_UNKNOWN_CERT) {
506
0
      return Result::ERROR_OCSP_UNKNOWN_CERT;
507
0
    }
508
0
    // If we're doing hard-fail, we want to know if we have a cached response
509
0
    // that has expired.
510
0
    if (mOCSPFetching == FetchOCSPForDVHardFail &&
511
0
        cachedResponseResult == Result::ERROR_OCSP_OLD_RESPONSE) {
512
0
      return Result::ERROR_OCSP_OLD_RESPONSE;
513
0
    }
514
0
515
0
    return Success;
516
0
  }
517
0
518
0
  if (mOCSPFetching == LocalOnlyOCSPForEV) {
519
0
    if (cachedResponseResult != Success) {
520
0
      return cachedResponseResult;
521
0
    }
522
0
    return Result::ERROR_OCSP_UNKNOWN_CERT;
523
0
  }
524
0
525
0
  UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
526
0
  if (!arena) {
527
0
    return Result::FATAL_ERROR_NO_MEMORY;
528
0
  }
529
0
530
0
  Result rv;
531
0
  nsCString aiaLocation(VoidCString());
532
0
533
0
  if (aiaExtension) {
534
0
    rv = GetOCSPAuthorityInfoAccessLocation(arena, *aiaExtension, aiaLocation);
535
0
    if (rv != Success) {
536
0
      return rv;
537
0
    }
538
0
  }
539
0
540
0
  if (aiaLocation.IsVoid()) {
541
0
    if (mOCSPFetching == FetchOCSPForEV ||
542
0
        cachedResponseResult == Result::ERROR_OCSP_UNKNOWN_CERT) {
543
0
      return Result::ERROR_OCSP_UNKNOWN_CERT;
544
0
    }
545
0
    if (cachedResponseResult == Result::ERROR_OCSP_OLD_RESPONSE) {
546
0
      return Result::ERROR_OCSP_OLD_RESPONSE;
547
0
    }
548
0
    if (stapledOCSPResponseResult != Success) {
549
0
      return stapledOCSPResponseResult;
550
0
    }
551
0
552
0
    // Nothing to do if we don't have an OCSP responder URI for the cert; just
553
0
    // assume it is good. Note that this is the confusing, but intended,
554
0
    // interpretation of "strict" revocation checking in the face of a
555
0
    // certificate that lacks an OCSP responder URI.
556
0
    return Success;
557
0
  }
558
0
559
0
  // Only request a response if we didn't have a cached indication of failure
560
0
  // (don't keep requesting responses from a failing server).
561
0
  bool attemptedRequest;
562
0
  Vector<uint8_t> ocspResponse;
563
0
  Input response;
564
0
  if (cachedResponseResult == Success ||
565
0
      cachedResponseResult == Result::ERROR_OCSP_UNKNOWN_CERT ||
566
0
      cachedResponseResult == Result::ERROR_OCSP_OLD_RESPONSE) {
567
0
    uint8_t ocspRequestBytes[OCSP_REQUEST_MAX_LENGTH];
568
0
    size_t ocspRequestLength;
569
0
    rv = CreateEncodedOCSPRequest(*this, certID, ocspRequestBytes,
570
0
                                  ocspRequestLength);
571
0
    if (rv != Success) {
572
0
      return rv;
573
0
    }
574
0
    Vector<uint8_t> ocspRequest;
575
0
    if (!ocspRequest.append(ocspRequestBytes, ocspRequestLength)) {
576
0
      return Result::FATAL_ERROR_NO_MEMORY;
577
0
    }
578
0
    Result tempRV = DoOCSPRequest(aiaLocation, mOriginAttributes,
579
0
                                  std::move(ocspRequest), GetOCSPTimeout(),
580
0
                                  ocspResponse);
581
0
    MOZ_ASSERT((tempRV != Success) || ocspResponse.length() > 0);
582
0
    if (tempRV != Success) {
583
0
      rv = tempRV;
584
0
    } else if (response.Init(ocspResponse.begin(), ocspResponse.length())
585
0
                 != Success) {
586
0
      rv = Result::ERROR_OCSP_MALFORMED_RESPONSE; // too big
587
0
    }
588
0
    attemptedRequest = true;
589
0
  } else {
590
0
    rv = cachedResponseResult;
591
0
    attemptedRequest = false;
592
0
  }
593
0
594
0
  if (response.GetLength() == 0) {
595
0
    Result error = rv;
596
0
    if (attemptedRequest) {
597
0
      Time timeout(time);
598
0
      if (timeout.AddSeconds(ServerFailureDelaySeconds) != Success) {
599
0
        return Result::FATAL_ERROR_LIBRARY_FAILURE; // integer overflow
600
0
      }
601
0
      rv = mOCSPCache.Put(certID, mOriginAttributes, error, time, timeout);
602
0
      if (rv != Success) {
603
0
        return rv;
604
0
      }
605
0
    }
606
0
    if (mOCSPFetching != FetchOCSPForDVSoftFail) {
607
0
      MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
608
0
             ("NSSCertDBTrustDomain: returning SECFailure after "
609
0
              "OCSP request failure"));
610
0
      return error;
611
0
    }
612
0
    if (cachedResponseResult == Result::ERROR_OCSP_UNKNOWN_CERT) {
613
0
      MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
614
0
             ("NSSCertDBTrustDomain: returning SECFailure from cached "
615
0
              "response after OCSP request failure"));
616
0
      return cachedResponseResult;
617
0
    }
618
0
    if (stapledOCSPResponseResult != Success) {
619
0
      MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
620
0
             ("NSSCertDBTrustDomain: returning SECFailure from expired/invalid "
621
0
              "stapled response after OCSP request failure"));
622
0
      return stapledOCSPResponseResult;
623
0
    }
624
0
625
0
    MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
626
0
           ("NSSCertDBTrustDomain: returning SECSuccess after "
627
0
            "OCSP request failure"));
628
0
    return Success; // Soft fail -> success :(
629
0
  }
630
0
631
0
  // If the response from the network has expired but indicates a revoked
632
0
  // or unknown certificate, PR_GetError() will return the appropriate error.
633
0
  // We actually ignore expired here.
634
0
  bool expired;
635
0
  rv = VerifyAndMaybeCacheEncodedOCSPResponse(certID, time,
636
0
                                              maxOCSPLifetimeInDays,
637
0
                                              response, ResponseIsFromNetwork,
638
0
                                              expired);
639
0
  if (rv == Success || mOCSPFetching != FetchOCSPForDVSoftFail) {
640
0
    MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
641
0
      ("NSSCertDBTrustDomain: returning after VerifyEncodedOCSPResponse"));
642
0
    return rv;
643
0
  }
644
0
645
0
  if (rv == Result::ERROR_OCSP_UNKNOWN_CERT ||
646
0
      rv == Result::ERROR_REVOKED_CERTIFICATE) {
647
0
    return rv;
648
0
  }
649
0
  if (stapledOCSPResponseResult != Success) {
650
0
    MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
651
0
           ("NSSCertDBTrustDomain: returning SECFailure from expired/invalid "
652
0
            "stapled response after OCSP request verification failure"));
653
0
    return stapledOCSPResponseResult;
654
0
  }
655
0
656
0
  MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
657
0
         ("NSSCertDBTrustDomain: end of CheckRevocation"));
658
0
659
0
  return Success; // Soft fail -> success :(
660
0
}
661
662
Result
663
NSSCertDBTrustDomain::VerifyAndMaybeCacheEncodedOCSPResponse(
664
  const CertID& certID, Time time, uint16_t maxLifetimeInDays,
665
  Input encodedResponse, EncodedResponseSource responseSource,
666
  /*out*/ bool& expired)
667
0
{
668
0
  Time thisUpdate(Time::uninitialized);
669
0
  Time validThrough(Time::uninitialized);
670
0
671
0
  // We use a try and fallback approach which first mandates good signature
672
0
  // digest algorithms, then falls back to SHA-1 if this fails. If a delegated
673
0
  // OCSP response signing certificate was issued with a SHA-1 signature,
674
0
  // verification initially fails. We cache the failure and then re-use that
675
0
  // result even when doing fallback (i.e. when weak signature digest algorithms
676
0
  // should succeed). To address this we use an OCSPVerificationTrustDomain
677
0
  // here, rather than using *this, to ensure verification succeeds for all
678
0
  // allowed signature digest algorithms.
679
0
  OCSPVerificationTrustDomain trustDomain(*this);
680
0
  Result rv = VerifyEncodedOCSPResponse(trustDomain, certID, time,
681
0
                                        maxLifetimeInDays, encodedResponse,
682
0
                                        expired, &thisUpdate, &validThrough);
683
0
  // If a response was stapled and expired, we don't want to cache it. Return
684
0
  // early to simplify the logic here.
685
0
  if (responseSource == ResponseWasStapled && expired) {
686
0
    MOZ_ASSERT(rv != Success);
687
0
    return rv;
688
0
  }
689
0
  // validThrough is only trustworthy if the response successfully verifies
690
0
  // or it indicates a revoked or unknown certificate.
691
0
  // If this isn't the case, store an indication of failure (to prevent
692
0
  // repeatedly requesting a response from a failing server).
693
0
  if (rv != Success && rv != Result::ERROR_REVOKED_CERTIFICATE &&
694
0
      rv != Result::ERROR_OCSP_UNKNOWN_CERT) {
695
0
    validThrough = time;
696
0
    if (validThrough.AddSeconds(ServerFailureDelaySeconds) != Success) {
697
0
      return Result::FATAL_ERROR_LIBRARY_FAILURE; // integer overflow
698
0
    }
699
0
  }
700
0
  if (responseSource == ResponseIsFromNetwork ||
701
0
      rv == Success ||
702
0
      rv == Result::ERROR_REVOKED_CERTIFICATE ||
703
0
      rv == Result::ERROR_OCSP_UNKNOWN_CERT) {
704
0
    MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
705
0
           ("NSSCertDBTrustDomain: caching OCSP response"));
706
0
    Result putRV = mOCSPCache.Put(certID, mOriginAttributes, rv, thisUpdate,
707
0
                                  validThrough);
708
0
    if (putRV != Success) {
709
0
      return putRV;
710
0
    }
711
0
  }
712
0
713
0
  return rv;
714
0
}
715
716
// If a certificate in the given chain appears to have been issued by one of
717
// seven roots operated by StartCom and WoSign that are not trusted to issue new
718
// certificates, verify that the end-entity has a notBefore date before 21
719
// October 2016. If the value of notBefore is after this time, the chain is not
720
// valid.
721
// (NB: While there are seven distinct roots being checked for, two of them
722
// share distinguished names, resulting in six distinct distinguished names to
723
// actually look for.)
724
static Result
725
CheckForStartComOrWoSign(const UniqueCERTCertList& certChain)
726
0
{
727
0
  if (CERT_LIST_EMPTY(certChain)) {
728
0
    return Result::FATAL_ERROR_LIBRARY_FAILURE;
729
0
  }
730
0
  const CERTCertListNode* endEntityNode = CERT_LIST_HEAD(certChain);
731
0
  if (!endEntityNode || !endEntityNode->cert) {
732
0
    return Result::FATAL_ERROR_LIBRARY_FAILURE;
733
0
  }
734
0
  PRTime notBefore;
735
0
  PRTime notAfter;
736
0
  if (CERT_GetCertTimes(endEntityNode->cert, &notBefore, &notAfter)
737
0
        != SECSuccess) {
738
0
    return Result::FATAL_ERROR_LIBRARY_FAILURE;
739
0
  }
740
0
  // PRTime is microseconds since the epoch, whereas JS time is milliseconds.
741
0
  // (new Date("2016-10-21T00:00:00Z")).getTime() * 1000
742
0
  static const PRTime OCTOBER_21_2016 = 1477008000000000;
743
0
  if (notBefore <= OCTOBER_21_2016) {
744
0
    return Success;
745
0
  }
746
0
747
0
  for (const CERTCertListNode* node = CERT_LIST_HEAD(certChain);
748
0
       !CERT_LIST_END(node, certChain); node = CERT_LIST_NEXT(node)) {
749
0
    if (!node || !node->cert) {
750
0
      return Result::FATAL_ERROR_LIBRARY_FAILURE;
751
0
    }
752
0
    if (CertDNIsInList(node->cert, StartComAndWoSignDNs)) {
753
0
      return Result::ERROR_REVOKED_CERTIFICATE;
754
0
    }
755
0
  }
756
0
  return Success;
757
0
}
758
759
Result
760
NSSCertDBTrustDomain::IsChainValid(const DERArray& certArray, Time time,
761
                                   const CertPolicyId& requiredPolicy)
762
0
{
763
0
  MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
764
0
         ("NSSCertDBTrustDomain: IsChainValid"));
765
0
766
0
  UniqueCERTCertList certList;
767
0
  SECStatus srv = ConstructCERTCertListFromReversedDERArray(certArray,
768
0
                                                            certList);
769
0
  if (srv != SECSuccess) {
770
0
    return MapPRErrorCodeToResult(PR_GetError());
771
0
  }
772
0
  if (CERT_LIST_EMPTY(certList)) {
773
0
    return Result::FATAL_ERROR_LIBRARY_FAILURE;
774
0
  }
775
0
776
0
  Result rv = CheckForStartComOrWoSign(certList);
777
0
  if (rv != Success) {
778
0
    return rv;
779
0
  }
780
0
781
0
  // Modernization in-progress: Keep certList as a CERTCertList for storage into
782
0
  // the mBuiltChain variable at the end, but let's use nsNSSCertList for the
783
0
  // validity calculations.
784
0
  UniqueCERTCertList certListCopy = nsNSSCertList::DupCertList(certList);
785
0
786
0
  // This adopts the list
787
0
  RefPtr<nsNSSCertList> nssCertList = new nsNSSCertList(std::move(certListCopy));
788
0
  if (!nssCertList) {
789
0
    return Result::FATAL_ERROR_LIBRARY_FAILURE;
790
0
  }
791
0
792
0
  nsCOMPtr<nsIX509Cert> rootCert;
793
0
  nsresult nsrv = nssCertList->GetRootCertificate(rootCert);
794
0
  if (NS_FAILED(nsrv)) {
795
0
    return Result::FATAL_ERROR_LIBRARY_FAILURE;
796
0
  }
797
0
  UniqueCERTCertificate root(rootCert->GetCert());
798
0
  if (!root) {
799
0
    return Result::FATAL_ERROR_LIBRARY_FAILURE;
800
0
  }
801
0
  bool isBuiltInRoot = false;
802
0
  nsrv = rootCert->GetIsBuiltInRoot(&isBuiltInRoot);
803
0
  if (NS_FAILED(nsrv)) {
804
0
    return Result::FATAL_ERROR_LIBRARY_FAILURE;
805
0
  }
806
0
  bool skipPinningChecksBecauseOfMITMMode =
807
0
    (!isBuiltInRoot && mPinningMode == CertVerifier::pinningAllowUserCAMITM);
808
0
  // If mHostname isn't set, we're not verifying in the context of a TLS
809
0
  // handshake, so don't verify HPKP in those cases.
810
0
  if (mHostname && (mPinningMode != CertVerifier::pinningDisabled) &&
811
0
      !skipPinningChecksBecauseOfMITMMode) {
812
0
    bool enforceTestMode =
813
0
      (mPinningMode == CertVerifier::pinningEnforceTestMode);
814
0
    bool chainHasValidPins;
815
0
    nsrv = PublicKeyPinningService::ChainHasValidPins(
816
0
      nssCertList, mHostname, time, enforceTestMode, mOriginAttributes,
817
0
      chainHasValidPins, mPinningTelemetryInfo);
818
0
    if (NS_FAILED(nsrv)) {
819
0
      return Result::FATAL_ERROR_LIBRARY_FAILURE;
820
0
    }
821
0
    if (!chainHasValidPins) {
822
0
      return Result::ERROR_KEY_PINNING_FAILURE;
823
0
    }
824
0
  }
825
0
826
0
  // See bug 1349762. If the root is "GlobalSign Root CA - R2", don't consider
827
0
  // the end-entity valid for EV unless the
828
0
  // "GlobalSign Extended Validation CA - SHA256 - G2" intermediate is in the
829
0
  // chain as well. It should be possible to remove this workaround after
830
0
  // January 2019 as per bug 1349727 comment 17.
831
0
  if (requiredPolicy == sGlobalSignEVPolicy &&
832
0
      CertMatchesStaticData(root.get(), sGlobalSignRootCAR2SubjectBytes,
833
0
                            sGlobalSignRootCAR2SPKIBytes)) {
834
0
835
0
    rootCert = nullptr; // Clear the state for Segment...
836
0
    nsCOMPtr<nsIX509CertList> intCerts;
837
0
    nsCOMPtr<nsIX509Cert> eeCert;
838
0
839
0
    nsrv = nssCertList->SegmentCertificateChain(rootCert, intCerts, eeCert);
840
0
    if (NS_FAILED(nsrv)) {
841
0
      // This chain is supposed to be complete, so this is an error. There
842
0
      // are no intermediates, so return before searching just as if the
843
0
      // search failed.
844
0
      return Result::ERROR_ADDITIONAL_POLICY_CONSTRAINT_FAILED;
845
0
    }
846
0
847
0
    bool foundRequiredIntermediate = false;
848
0
    RefPtr<nsNSSCertList> intCertList = intCerts->GetCertList();
849
0
    nsrv = intCertList->ForEachCertificateInChain(
850
0
      [&foundRequiredIntermediate] (nsCOMPtr<nsIX509Cert> aCert, bool aHasMore,
851
0
                                    /* out */ bool& aContinue) {
852
0
        // We need an owning handle when calling nsIX509Cert::GetCert().
853
0
        UniqueCERTCertificate nssCert(aCert->GetCert());
854
0
        if (CertMatchesStaticData(
855
0
            nssCert.get(),
856
0
            sGlobalSignExtendedValidationCASHA256G2SubjectBytes,
857
0
            sGlobalSignExtendedValidationCASHA256G2SPKIBytes)) {
858
0
          foundRequiredIntermediate = true;
859
0
          aContinue = false;
860
0
        }
861
0
        return NS_OK;
862
0
    });
863
0
864
0
    if (NS_FAILED(nsrv)) {
865
0
      return Result::FATAL_ERROR_LIBRARY_FAILURE;
866
0
    }
867
0
868
0
    if (!foundRequiredIntermediate) {
869
0
      return Result::ERROR_ADDITIONAL_POLICY_CONSTRAINT_FAILED;
870
0
    }
871
0
  }
872
0
873
0
  // See bug 1434300. If the root is a Symantec root, see if we distrust this
874
0
  // path. Since we already have the root available, we can check that cheaply
875
0
  // here before proceeding with the rest of the algorithm.
876
0
877
0
  // This algorithm only applies if we are verifying in the context of a TLS
878
0
  // handshake. To determine this, we check mHostname: If it isn't set, this is
879
0
  // not TLS, so don't run the algorithm.
880
0
  if (mHostname && CertDNIsInList(root.get(), RootSymantecDNs) &&
881
0
      ((mDistrustedCAPolicy & DistrustedCAPolicy::DistrustSymantecRoots) ||
882
0
       (mDistrustedCAPolicy & DistrustedCAPolicy::DistrustSymantecRootsRegardlessOfDate))) {
883
0
884
0
    rootCert = nullptr; // Clear the state for Segment...
885
0
    nsCOMPtr<nsIX509CertList> intCerts;
886
0
    nsCOMPtr<nsIX509Cert> eeCert;
887
0
888
0
    nsrv = nssCertList->SegmentCertificateChain(rootCert, intCerts, eeCert);
889
0
    if (NS_FAILED(nsrv)) {
890
0
      // This chain is supposed to be complete, so this is an error.
891
0
      return Result::FATAL_ERROR_LIBRARY_FAILURE;
892
0
    }
893
0
894
0
    // PRTime is microseconds since the epoch, whereas JS time is milliseconds.
895
0
    // (new Date("2016-06-01T00:00:00Z")).getTime() * 1000
896
0
    static const PRTime JUNE_1_2016 = 1464739200000000;
897
0
898
0
    PRTime permitAfterDate = JUNE_1_2016;
899
0
    if (mDistrustedCAPolicy & DistrustedCAPolicy::DistrustSymantecRootsRegardlessOfDate) {
900
0
      permitAfterDate = 0; // 0 indicates there is no permitAfterDate
901
0
    }
902
0
903
0
    bool isDistrusted = false;
904
0
    nsrv = CheckForSymantecDistrust(intCerts, eeCert, permitAfterDate,
905
0
                                    RootAppleAndGoogleSPKIs, isDistrusted);
906
0
    if (NS_FAILED(nsrv)) {
907
0
      return Result::FATAL_ERROR_LIBRARY_FAILURE;
908
0
    }
909
0
    if (isDistrusted) {
910
0
      mSawDistrustedCAByPolicyError = true;
911
0
      return Result::ERROR_ADDITIONAL_POLICY_CONSTRAINT_FAILED;
912
0
    }
913
0
  }
914
0
915
0
  mBuiltChain = std::move(certList);
916
0
917
0
  return Success;
918
0
}
919
920
Result
921
NSSCertDBTrustDomain::CheckSignatureDigestAlgorithm(DigestAlgorithm aAlg,
922
                                                    EndEntityOrCA endEntityOrCA,
923
                                                    Time notBefore)
924
0
{
925
0
  // (new Date("2016-01-01T00:00:00Z")).getTime() / 1000
926
0
  static const Time JANUARY_FIRST_2016 = TimeFromEpochInSeconds(1451606400);
927
0
928
0
  MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
929
0
          ("NSSCertDBTrustDomain: CheckSignatureDigestAlgorithm"));
930
0
  if (aAlg == DigestAlgorithm::sha1) {
931
0
    switch (mSHA1Mode) {
932
0
      case CertVerifier::SHA1Mode::Forbidden:
933
0
        MOZ_LOG(gCertVerifierLog, LogLevel::Debug, ("SHA-1 certificate rejected"));
934
0
        return Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED;
935
0
      case CertVerifier::SHA1Mode::ImportedRootOrBefore2016:
936
0
        if (JANUARY_FIRST_2016 <= notBefore) {
937
0
          MOZ_LOG(gCertVerifierLog, LogLevel::Debug, ("Post-2015 SHA-1 certificate rejected"));
938
0
          return Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED;
939
0
        }
940
0
        break;
941
0
      case CertVerifier::SHA1Mode::Allowed:
942
0
      // Enforcing that the resulting chain uses an imported root is only
943
0
      // possible at a higher level. This is done in CertVerifier::VerifyCert.
944
0
      case CertVerifier::SHA1Mode::ImportedRoot:
945
0
      default:
946
0
        break;
947
0
      // MSVC warns unless we explicitly handle this now-unused option.
948
0
      case CertVerifier::SHA1Mode::UsedToBeBefore2016ButNowIsForbidden:
949
0
        MOZ_ASSERT_UNREACHABLE("unexpected SHA1Mode type");
950
0
        return Result::FATAL_ERROR_LIBRARY_FAILURE;
951
0
    }
952
0
  }
953
0
954
0
  return Success;
955
0
}
956
957
Result
958
NSSCertDBTrustDomain::CheckRSAPublicKeyModulusSizeInBits(
959
  EndEntityOrCA /*endEntityOrCA*/, unsigned int modulusSizeInBits)
960
0
{
961
0
  if (modulusSizeInBits < mMinRSABits) {
962
0
    return Result::ERROR_INADEQUATE_KEY_SIZE;
963
0
  }
964
0
  return Success;
965
0
}
966
967
Result
968
NSSCertDBTrustDomain::VerifyRSAPKCS1SignedDigest(
969
  const SignedDigest& signedDigest,
970
  Input subjectPublicKeyInfo)
971
0
{
972
0
  return VerifyRSAPKCS1SignedDigestNSS(signedDigest, subjectPublicKeyInfo,
973
0
                                       mPinArg);
974
0
}
975
976
Result
977
NSSCertDBTrustDomain::CheckECDSACurveIsAcceptable(
978
  EndEntityOrCA /*endEntityOrCA*/, NamedCurve curve)
979
0
{
980
0
  switch (curve) {
981
0
    case NamedCurve::secp256r1: // fall through
982
0
    case NamedCurve::secp384r1: // fall through
983
0
    case NamedCurve::secp521r1:
984
0
      return Success;
985
0
  }
986
0
987
0
  return Result::ERROR_UNSUPPORTED_ELLIPTIC_CURVE;
988
0
}
989
990
Result
991
NSSCertDBTrustDomain::VerifyECDSASignedDigest(const SignedDigest& signedDigest,
992
                                              Input subjectPublicKeyInfo)
993
0
{
994
0
  return VerifyECDSASignedDigestNSS(signedDigest, subjectPublicKeyInfo,
995
0
                                    mPinArg);
996
0
}
997
998
Result
999
NSSCertDBTrustDomain::CheckValidityIsAcceptable(Time notBefore, Time notAfter,
1000
                                                EndEntityOrCA endEntityOrCA,
1001
                                                KeyPurposeId keyPurpose)
1002
0
{
1003
0
  if (endEntityOrCA != EndEntityOrCA::MustBeEndEntity) {
1004
0
    return Success;
1005
0
  }
1006
0
  if (keyPurpose == KeyPurposeId::id_kp_OCSPSigning) {
1007
0
    return Success;
1008
0
  }
1009
0
1010
0
  Duration DURATION_27_MONTHS_PLUS_SLOP((2 * 365 + 3 * 31 + 7) *
1011
0
                                        Time::ONE_DAY_IN_SECONDS);
1012
0
  Duration maxValidityDuration(UINT64_MAX);
1013
0
  Duration validityDuration(notBefore, notAfter);
1014
0
1015
0
  switch (mValidityCheckingMode) {
1016
0
    case ValidityCheckingMode::CheckingOff:
1017
0
      return Success;
1018
0
    case ValidityCheckingMode::CheckForEV:
1019
0
      // The EV Guidelines say the maximum is 27 months, but we use a slightly
1020
0
      // higher limit here to (hopefully) minimize compatibility breakage.
1021
0
      maxValidityDuration = DURATION_27_MONTHS_PLUS_SLOP;
1022
0
      break;
1023
0
    default:
1024
0
      MOZ_ASSERT_UNREACHABLE("We're not handling every ValidityCheckingMode type");
1025
0
  }
1026
0
1027
0
  if (validityDuration > maxValidityDuration) {
1028
0
    return Result::ERROR_VALIDITY_TOO_LONG;
1029
0
  }
1030
0
1031
0
  return Success;
1032
0
}
1033
1034
Result
1035
NSSCertDBTrustDomain::NetscapeStepUpMatchesServerAuth(Time notBefore,
1036
                                                      /*out*/ bool& matches)
1037
0
{
1038
0
  // (new Date("2015-08-23T00:00:00Z")).getTime() / 1000
1039
0
  static const Time AUGUST_23_2015 = TimeFromEpochInSeconds(1440288000);
1040
0
  // (new Date("2016-08-23T00:00:00Z")).getTime() / 1000
1041
0
  static const Time AUGUST_23_2016 = TimeFromEpochInSeconds(1471910400);
1042
0
1043
0
  switch (mNetscapeStepUpPolicy) {
1044
0
    case NetscapeStepUpPolicy::AlwaysMatch:
1045
0
      matches = true;
1046
0
      return Success;
1047
0
    case NetscapeStepUpPolicy::MatchBefore23August2016:
1048
0
      matches = notBefore < AUGUST_23_2016;
1049
0
      return Success;
1050
0
    case NetscapeStepUpPolicy::MatchBefore23August2015:
1051
0
      matches = notBefore < AUGUST_23_2015;
1052
0
      return Success;
1053
0
    case NetscapeStepUpPolicy::NeverMatch:
1054
0
      matches = false;
1055
0
      return Success;
1056
0
    default:
1057
0
      MOZ_ASSERT_UNREACHABLE("unhandled NetscapeStepUpPolicy type");
1058
0
  }
1059
0
  return Result::FATAL_ERROR_LIBRARY_FAILURE;
1060
0
}
1061
1062
void
1063
NSSCertDBTrustDomain::ResetAccumulatedState()
1064
0
{
1065
0
  mOCSPStaplingStatus = CertVerifier::OCSP_STAPLING_NEVER_CHECKED;
1066
0
  mSCTListFromOCSPStapling = nullptr;
1067
0
  mSCTListFromCertificate = nullptr;
1068
0
  mSawDistrustedCAByPolicyError = false;
1069
0
}
1070
1071
static Input
1072
SECItemToInput(const UniqueSECItem& item)
1073
0
{
1074
0
  Input result;
1075
0
  if (item) {
1076
0
    MOZ_ASSERT(item->type == siBuffer);
1077
0
    Result rv = result.Init(item->data, item->len);
1078
0
    // As used here, |item| originally comes from an Input,
1079
0
    // so there should be no issues converting it back.
1080
0
    MOZ_ASSERT(rv == Success);
1081
0
    Unused << rv; // suppresses warnings in release builds
1082
0
  }
1083
0
  return result;
1084
0
}
1085
1086
Input
1087
NSSCertDBTrustDomain::GetSCTListFromCertificate() const
1088
0
{
1089
0
  return SECItemToInput(mSCTListFromCertificate);
1090
0
}
1091
1092
Input
1093
NSSCertDBTrustDomain::GetSCTListFromOCSPStapling() const
1094
0
{
1095
0
  return SECItemToInput(mSCTListFromOCSPStapling);
1096
0
}
1097
1098
bool
1099
NSSCertDBTrustDomain::GetIsErrorDueToDistrustedCAPolicy() const
1100
0
{
1101
0
  return mSawDistrustedCAByPolicyError;
1102
0
}
1103
1104
void
1105
NSSCertDBTrustDomain::NoteAuxiliaryExtension(AuxiliaryExtension extension,
1106
                                             Input extensionData)
1107
0
{
1108
0
  UniqueSECItem* out = nullptr;
1109
0
  switch (extension) {
1110
0
    case AuxiliaryExtension::EmbeddedSCTList:
1111
0
      out = &mSCTListFromCertificate;
1112
0
      break;
1113
0
    case AuxiliaryExtension::SCTListFromOCSPResponse:
1114
0
      out = &mSCTListFromOCSPStapling;
1115
0
      break;
1116
0
    default:
1117
0
      MOZ_ASSERT_UNREACHABLE("unhandled AuxiliaryExtension");
1118
0
  }
1119
0
  if (out) {
1120
0
    SECItem extensionDataItem = UnsafeMapInputToSECItem(extensionData);
1121
0
    out->reset(SECITEM_DupItem(&extensionDataItem));
1122
0
  }
1123
0
}
1124
1125
SECStatus
1126
InitializeNSS(const nsACString& dir, bool readOnly, bool loadPKCS11Modules)
1127
0
{
1128
0
  MOZ_ASSERT(NS_IsMainThread());
1129
0
1130
0
  // The NSS_INIT_NOROOTINIT flag turns off the loading of the root certs
1131
0
  // module by NSS_Initialize because we will load it in InstallLoadableRoots
1132
0
  // later.  It also allows us to work around a bug in the system NSS in
1133
0
  // Ubuntu 8.04, which loads any nonexistent "<configdir>/libnssckbi.so" as
1134
0
  // "/usr/lib/nss/libnssckbi.so".
1135
0
  uint32_t flags = NSS_INIT_NOROOTINIT | NSS_INIT_OPTIMIZESPACE;
1136
0
  if (readOnly) {
1137
0
    flags |= NSS_INIT_READONLY;
1138
0
  }
1139
0
  if (!loadPKCS11Modules) {
1140
0
    flags |= NSS_INIT_NOMODDB;
1141
0
  }
1142
0
  nsAutoCString dbTypeAndDirectory("sql:");
1143
0
  dbTypeAndDirectory.Append(dir);
1144
0
  MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
1145
0
          ("InitializeNSS(%s, %d, %d)", dbTypeAndDirectory.get(), readOnly,
1146
0
           loadPKCS11Modules));
1147
0
  SECStatus srv = NSS_Initialize(dbTypeAndDirectory.get(), "", "",
1148
0
                                 SECMOD_DB, flags);
1149
0
  if (srv != SECSuccess) {
1150
0
    return srv;
1151
0
  }
1152
0
1153
0
  if (!readOnly) {
1154
0
    UniquePK11SlotInfo slot(PK11_GetInternalKeySlot());
1155
0
    if (!slot) {
1156
0
      return SECFailure;
1157
0
    }
1158
0
    // If the key DB doesn't have a password set, PK11_NeedUserInit will return
1159
0
    // true. For the SQL DB, we need to set a password or we won't be able to
1160
0
    // import any certificates or change trust settings.
1161
0
    if (PK11_NeedUserInit(slot.get())) {
1162
0
      srv = PK11_InitPin(slot.get(), nullptr, nullptr);
1163
0
      MOZ_ASSERT(srv == SECSuccess);
1164
0
      Unused << srv;
1165
0
    }
1166
0
  }
1167
0
1168
0
  return SECSuccess;
1169
0
}
1170
1171
void
1172
DisableMD5()
1173
0
{
1174
0
  NSS_SetAlgorithmPolicy(SEC_OID_MD5,
1175
0
    0, NSS_USE_ALG_IN_CERT_SIGNATURE | NSS_USE_ALG_IN_CMS_SIGNATURE);
1176
0
  NSS_SetAlgorithmPolicy(SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION,
1177
0
    0, NSS_USE_ALG_IN_CERT_SIGNATURE | NSS_USE_ALG_IN_CMS_SIGNATURE);
1178
0
  NSS_SetAlgorithmPolicy(SEC_OID_PKCS5_PBE_WITH_MD5_AND_DES_CBC,
1179
0
    0, NSS_USE_ALG_IN_CERT_SIGNATURE | NSS_USE_ALG_IN_CMS_SIGNATURE);
1180
0
}
1181
1182
bool
1183
LoadLoadableRoots(const nsCString& dir)
1184
0
{
1185
0
  // If a module exists with the same name, make a best effort attempt to delete
1186
0
  // it. Note that it isn't possible to delete the internal module, so checking
1187
0
  // the return value would be detrimental in that case.
1188
0
  int unusedModType;
1189
0
  Unused << SECMOD_DeleteModule(kRootModuleName, &unusedModType);
1190
0
  // Some NSS command-line utilities will load a roots module under the name
1191
0
  // "Root Certs" if there happens to be a `MOZ_DLL_PREFIX "nssckbi" MOZ_DLL_SUFFIX`
1192
0
  // file in the directory being operated on. In some cases this can cause us to
1193
0
  // fail to load our roots module. In these cases, deleting the "Root Certs"
1194
0
  // module allows us to load the correct one. See bug 1406396.
1195
0
  Unused << SECMOD_DeleteModule("Root Certs", &unusedModType);
1196
0
1197
0
  nsAutoCString fullLibraryPath;
1198
0
  if (!dir.IsEmpty()) {
1199
0
    fullLibraryPath.Assign(dir);
1200
0
    fullLibraryPath.AppendLiteral(FILE_PATH_SEPARATOR);
1201
0
  }
1202
0
  fullLibraryPath.Append(MOZ_DLL_PREFIX "nssckbi" MOZ_DLL_SUFFIX);
1203
0
  // Escape the \ and " characters.
1204
0
  fullLibraryPath.ReplaceSubstring("\\", "\\\\");
1205
0
  fullLibraryPath.ReplaceSubstring("\"", "\\\"");
1206
0
1207
0
  nsAutoCString pkcs11ModuleSpec("name=\"");
1208
0
  pkcs11ModuleSpec.Append(kRootModuleName);
1209
0
  pkcs11ModuleSpec.AppendLiteral("\" library=\"");
1210
0
  pkcs11ModuleSpec.Append(fullLibraryPath);
1211
0
  pkcs11ModuleSpec.AppendLiteral("\"");
1212
0
1213
0
  UniqueSECMODModule rootsModule(
1214
0
    SECMOD_LoadUserModule(const_cast<char*>(pkcs11ModuleSpec.get()), nullptr,
1215
0
                          false));
1216
0
  if (!rootsModule) {
1217
0
    return false;
1218
0
  }
1219
0
1220
0
  if (!rootsModule->loaded) {
1221
0
    return false;
1222
0
  }
1223
0
1224
0
  return true;
1225
0
}
1226
1227
void
1228
UnloadLoadableRoots()
1229
0
{
1230
0
  UniqueSECMODModule rootsModule(SECMOD_FindModule(kRootModuleName));
1231
0
1232
0
  if (rootsModule) {
1233
0
    SECMOD_UnloadUserModule(rootsModule.get());
1234
0
  }
1235
0
}
1236
1237
nsresult
1238
DefaultServerNicknameForCert(const CERTCertificate* cert,
1239
                     /*out*/ nsCString& nickname)
1240
0
{
1241
0
  MOZ_ASSERT(cert);
1242
0
  NS_ENSURE_ARG_POINTER(cert);
1243
0
1244
0
  UniquePORTString baseName(CERT_GetCommonName(&cert->subject));
1245
0
  if (!baseName) {
1246
0
    baseName = UniquePORTString(CERT_GetOrgUnitName(&cert->subject));
1247
0
  }
1248
0
  if (!baseName) {
1249
0
    baseName = UniquePORTString(CERT_GetOrgName(&cert->subject));
1250
0
  }
1251
0
  if (!baseName) {
1252
0
    baseName = UniquePORTString(CERT_GetLocalityName(&cert->subject));
1253
0
  }
1254
0
  if (!baseName) {
1255
0
    baseName = UniquePORTString(CERT_GetStateName(&cert->subject));
1256
0
  }
1257
0
  if (!baseName) {
1258
0
    baseName = UniquePORTString(CERT_GetCountryName(&cert->subject));
1259
0
  }
1260
0
  if (!baseName) {
1261
0
    return NS_ERROR_FAILURE;
1262
0
  }
1263
0
1264
0
  // This function is only used in contexts where a failure to find a suitable
1265
0
  // nickname does not block the overall task from succeeding.
1266
0
  // As such, we use an arbitrary limit to prevent this nickname searching
1267
0
  // process from taking forever.
1268
0
  static const uint32_t ARBITRARY_LIMIT = 500;
1269
0
  for (uint32_t count = 1; count < ARBITRARY_LIMIT; count++) {
1270
0
    nickname = baseName.get();
1271
0
    if (count != 1) {
1272
0
      nickname.AppendPrintf(" #%u", count);
1273
0
    }
1274
0
    if (nickname.IsEmpty()) {
1275
0
      return NS_ERROR_FAILURE;
1276
0
    }
1277
0
1278
0
    bool conflict = SEC_CertNicknameConflict(nickname.get(), &cert->derSubject,
1279
0
                                             cert->dbhandle);
1280
0
    if (!conflict) {
1281
0
      return NS_OK;
1282
0
    }
1283
0
  }
1284
0
1285
0
  return NS_ERROR_FAILURE;
1286
0
}
1287
1288
/**
1289
 * Given a list of certificates representing a verified certificate path from an
1290
 * end-entity certificate to a trust anchor, imports the intermediate
1291
 * certificates into the permanent certificate database. This is an attempt to
1292
 * cope with misconfigured servers that don't include the appropriate
1293
 * intermediate certificates in the TLS handshake.
1294
 *
1295
 * @param certList the verified certificate list
1296
 */
1297
void
1298
SaveIntermediateCerts(const UniqueCERTCertList& certList)
1299
0
{
1300
0
  if (!certList) {
1301
0
    return;
1302
0
  }
1303
0
1304
0
  UniquePK11SlotInfo slot(PK11_GetInternalKeySlot());
1305
0
  if (!slot) {
1306
0
    return;
1307
0
  }
1308
0
1309
0
  bool isEndEntity = true;
1310
0
  for (CERTCertListNode* node = CERT_LIST_HEAD(certList);
1311
0
        !CERT_LIST_END(node, certList);
1312
0
        node = CERT_LIST_NEXT(node)) {
1313
0
    if (isEndEntity) {
1314
0
      // Skip the end-entity; we only want to store intermediates
1315
0
      isEndEntity = false;
1316
0
      continue;
1317
0
    }
1318
0
1319
0
    if (node->cert->slot) {
1320
0
      // This cert was found on a token; no need to remember it in the permanent
1321
0
      // database.
1322
0
      continue;
1323
0
    }
1324
0
1325
0
    if (node->cert->isperm) {
1326
0
      // We don't need to remember certs already stored in perm db.
1327
0
      continue;
1328
0
    }
1329
0
1330
0
    // No need to save the trust anchor - it's either already a permanent
1331
0
    // certificate or it's the Microsoft Family Safety root or an enterprise
1332
0
    // root temporarily imported via the child mode or enterprise root features.
1333
0
    // We don't want to import these because they're intended to be temporary
1334
0
    // (and because importing them happens to reset their trust settings, which
1335
0
    // breaks these features).
1336
0
    if (node == CERT_LIST_TAIL(certList)) {
1337
0
      continue;
1338
0
    }
1339
0
1340
0
    nsAutoCString nickname;
1341
0
    nsresult rv = DefaultServerNicknameForCert(node->cert, nickname);
1342
0
    if (NS_FAILED(rv)) {
1343
0
      continue;
1344
0
    }
1345
0
1346
0
    // As mentioned in the documentation of this function, we're importing only
1347
0
    // to cope with misconfigured servers. As such, we ignore the return value
1348
0
    // below, since it doesn't really matter if the import fails.
1349
0
    Unused << PK11_ImportCert(slot.get(), node->cert, CK_INVALID_HANDLE,
1350
0
                              nickname.get(), false);
1351
0
  }
1352
0
}
1353
1354
} } // namespace mozilla::psm