Coverage Report

Created: 2018-09-25 14:53

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