Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/security/apps/AppTrustDomain.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 "AppTrustDomain.h"
8
9
#include "MainThreadUtils.h"
10
#include "certdb.h"
11
#include "mozilla/ArrayUtils.h"
12
#include "mozilla/Casting.h"
13
#include "mozilla/Preferences.h"
14
#include "nsComponentManagerUtils.h"
15
#include "nsIFile.h"
16
#include "nsIFileStreams.h"
17
#include "nsIX509CertDB.h"
18
#include "nsNSSCertificate.h"
19
#include "nsNetUtil.h"
20
#include "pkix/pkixnss.h"
21
#include "prerror.h"
22
23
// Generated by gen_cert_header.py, which gets called by the build system.
24
#include "xpcshell.inc"
25
// Add-on signing Certificates
26
#include "addons-public.inc"
27
#include "addons-stage.inc"
28
// Privileged Package Certificates
29
#include "privileged-package-root.inc"
30
31
using namespace mozilla::pkix;
32
33
extern mozilla::LazyLogModule gPIPNSSLog;
34
35
static char kDevImportedDER[] =
36
  "network.http.signed-packages.developer-root";
37
38
namespace mozilla { namespace psm {
39
40
StaticMutex AppTrustDomain::sMutex;
41
UniquePtr<unsigned char[]> AppTrustDomain::sDevImportedDERData;
42
unsigned int AppTrustDomain::sDevImportedDERLen = 0;
43
44
AppTrustDomain::AppTrustDomain(UniqueCERTCertList& certChain, void* pinArg)
45
  : mCertChain(certChain)
46
  , mPinArg(pinArg)
47
0
{
48
0
}
49
50
nsresult
51
AppTrustDomain::SetTrustedRoot(AppTrustedRoot trustedRoot)
52
0
{
53
0
  SECItem trustedDER;
54
0
55
0
  // Load the trusted certificate into the in-memory NSS database so that
56
0
  // CERT_CreateSubjectCertList can find it.
57
0
58
0
  switch (trustedRoot)
59
0
  {
60
0
    case nsIX509CertDB::AppXPCShellRoot:
61
0
      trustedDER.data = const_cast<uint8_t*>(xpcshellRoot);
62
0
      trustedDER.len = mozilla::ArrayLength(xpcshellRoot);
63
0
      break;
64
0
65
0
    case nsIX509CertDB::AddonsPublicRoot:
66
0
      trustedDER.data = const_cast<uint8_t*>(addonsPublicRoot);
67
0
      trustedDER.len = mozilla::ArrayLength(addonsPublicRoot);
68
0
      break;
69
0
70
0
    case nsIX509CertDB::AddonsStageRoot:
71
0
      trustedDER.data = const_cast<uint8_t*>(addonsStageRoot);
72
0
      trustedDER.len = mozilla::ArrayLength(addonsStageRoot);
73
0
      break;
74
0
75
0
    case nsIX509CertDB::PrivilegedPackageRoot:
76
0
      trustedDER.data = const_cast<uint8_t*>(privilegedPackageRoot);
77
0
      trustedDER.len = mozilla::ArrayLength(privilegedPackageRoot);
78
0
      break;
79
0
80
0
    case nsIX509CertDB::DeveloperImportedRoot: {
81
0
      StaticMutexAutoLock lock(sMutex);
82
0
      if (!sDevImportedDERData) {
83
0
        MOZ_ASSERT(!NS_IsMainThread());
84
0
        nsCOMPtr<nsIFile> file(do_CreateInstance("@mozilla.org/file/local;1"));
85
0
        if (!file) {
86
0
          return NS_ERROR_FAILURE;
87
0
        }
88
0
        nsAutoCString path;
89
0
        Preferences::GetCString(kDevImportedDER, path);
90
0
        nsresult rv = file->InitWithNativePath(path);
91
0
        if (NS_FAILED(rv)) {
92
0
          return rv;
93
0
        }
94
0
95
0
        nsCOMPtr<nsIInputStream> inputStream;
96
0
        rv = NS_NewLocalFileInputStream(getter_AddRefs(inputStream), file, -1,
97
0
                                        -1, nsIFileInputStream::CLOSE_ON_EOF);
98
0
        if (NS_FAILED(rv)) {
99
0
          return rv;
100
0
        }
101
0
102
0
        uint64_t length;
103
0
        rv = inputStream->Available(&length);
104
0
        if (NS_FAILED(rv)) {
105
0
          return rv;
106
0
        }
107
0
108
0
        auto data = MakeUnique<char[]>(length);
109
0
        rv = inputStream->Read(data.get(), length, &sDevImportedDERLen);
110
0
        if (NS_FAILED(rv)) {
111
0
          return rv;
112
0
        }
113
0
114
0
        MOZ_ASSERT(length == sDevImportedDERLen);
115
0
        sDevImportedDERData.reset(
116
0
          BitwiseCast<unsigned char*, char*>(data.release()));
117
0
      }
118
0
119
0
      trustedDER.data = sDevImportedDERData.get();
120
0
      trustedDER.len = sDevImportedDERLen;
121
0
      break;
122
0
    }
123
0
124
0
    default:
125
0
      return NS_ERROR_INVALID_ARG;
126
0
  }
127
0
128
0
  mTrustedRoot.reset(CERT_NewTempCertificate(CERT_GetDefaultCertDB(),
129
0
                                             &trustedDER, nullptr, false, true));
130
0
  if (!mTrustedRoot) {
131
0
    return mozilla::psm::GetXPCOMFromNSSError(PR_GetError());
132
0
  }
133
0
134
0
  return NS_OK;
135
0
}
136
137
Result
138
AppTrustDomain::FindIssuer(Input encodedIssuerName, IssuerChecker& checker,
139
                           Time)
140
141
0
{
142
0
  MOZ_ASSERT(mTrustedRoot);
143
0
  if (!mTrustedRoot) {
144
0
    return Result::FATAL_ERROR_INVALID_STATE;
145
0
  }
146
0
147
0
  // TODO(bug 1035418): If/when mozilla::pkix relaxes the restriction that
148
0
  // FindIssuer must only pass certificates with a matching subject name to
149
0
  // checker.Check, we can stop using CERT_CreateSubjectCertList and instead
150
0
  // use logic like this:
151
0
  //
152
0
  // 1. First, try the trusted trust anchor.
153
0
  // 2. Secondly, iterate through the certificates that were stored in the CMS
154
0
  //    message, passing each one to checker.Check.
155
0
  SECItem encodedIssuerNameSECItem =
156
0
    UnsafeMapInputToSECItem(encodedIssuerName);
157
0
  UniqueCERTCertList
158
0
    candidates(CERT_CreateSubjectCertList(nullptr, CERT_GetDefaultCertDB(),
159
0
                                          &encodedIssuerNameSECItem, 0,
160
0
                                          false));
161
0
  if (candidates) {
162
0
    for (CERTCertListNode* n = CERT_LIST_HEAD(candidates);
163
0
         !CERT_LIST_END(n, candidates); n = CERT_LIST_NEXT(n)) {
164
0
      Input certDER;
165
0
      Result rv = certDER.Init(n->cert->derCert.data, n->cert->derCert.len);
166
0
      if (rv != Success) {
167
0
        continue; // probably too big
168
0
      }
169
0
170
0
      bool keepGoing;
171
0
      rv = checker.Check(certDER, nullptr/*additionalNameConstraints*/,
172
0
                         keepGoing);
173
0
      if (rv != Success) {
174
0
        return rv;
175
0
      }
176
0
      if (!keepGoing) {
177
0
        break;
178
0
      }
179
0
    }
180
0
  }
181
0
182
0
  return Success;
183
0
}
184
185
Result
186
AppTrustDomain::GetCertTrust(EndEntityOrCA endEntityOrCA,
187
                             const CertPolicyId& policy,
188
                             Input candidateCertDER,
189
                     /*out*/ TrustLevel& trustLevel)
190
0
{
191
0
  MOZ_ASSERT(policy.IsAnyPolicy());
192
0
  MOZ_ASSERT(mTrustedRoot);
193
0
  if (!policy.IsAnyPolicy()) {
194
0
    return Result::FATAL_ERROR_INVALID_ARGS;
195
0
  }
196
0
  if (!mTrustedRoot) {
197
0
    return Result::FATAL_ERROR_INVALID_STATE;
198
0
  }
199
0
200
0
  // Handle active distrust of the certificate.
201
0
202
0
  // XXX: This would be cleaner and more efficient if we could get the trust
203
0
  // information without constructing a CERTCertificate here, but NSS doesn't
204
0
  // expose it in any other easy-to-use fashion.
205
0
  SECItem candidateCertDERSECItem =
206
0
    UnsafeMapInputToSECItem(candidateCertDER);
207
0
  UniqueCERTCertificate candidateCert(
208
0
    CERT_NewTempCertificate(CERT_GetDefaultCertDB(), &candidateCertDERSECItem,
209
0
                            nullptr, false, true));
210
0
  if (!candidateCert) {
211
0
    return MapPRErrorCodeToResult(PR_GetError());
212
0
  }
213
0
214
0
  CERTCertTrust trust;
215
0
  if (CERT_GetCertTrust(candidateCert.get(), &trust) == SECSuccess) {
216
0
    uint32_t flags = SEC_GET_TRUST_FLAGS(&trust, trustObjectSigning);
217
0
218
0
    // For DISTRUST, we use the CERTDB_TRUSTED or CERTDB_TRUSTED_CA bit,
219
0
    // because we can have active distrust for either type of cert. Note that
220
0
    // CERTDB_TERMINAL_RECORD means "stop trying to inherit trust" so if the
221
0
    // relevant trust bit isn't set then that means the cert must be considered
222
0
    // distrusted.
223
0
    uint32_t relevantTrustBit = endEntityOrCA == EndEntityOrCA::MustBeCA
224
0
                              ? CERTDB_TRUSTED_CA
225
0
                              : CERTDB_TRUSTED;
226
0
    if (((flags & (relevantTrustBit | CERTDB_TERMINAL_RECORD)))
227
0
            == CERTDB_TERMINAL_RECORD) {
228
0
      trustLevel = TrustLevel::ActivelyDistrusted;
229
0
      return Success;
230
0
    }
231
0
  }
232
0
233
0
  // mTrustedRoot is the only trust anchor for this validation.
234
0
  if (CERT_CompareCerts(mTrustedRoot.get(), candidateCert.get())) {
235
0
    trustLevel = TrustLevel::TrustAnchor;
236
0
    return Success;
237
0
  }
238
0
239
0
  trustLevel = TrustLevel::InheritsTrust;
240
0
  return Success;
241
0
}
242
243
Result
244
AppTrustDomain::DigestBuf(Input item,
245
                          DigestAlgorithm digestAlg,
246
                          /*out*/ uint8_t* digestBuf,
247
                          size_t digestBufLen)
248
0
{
249
0
  return DigestBufNSS(item, digestAlg, digestBuf, digestBufLen);
250
0
}
251
252
Result
253
AppTrustDomain::CheckRevocation(EndEntityOrCA, const CertID&, Time, Duration,
254
                                /*optional*/ const Input*,
255
                                /*optional*/ const Input*)
256
0
{
257
0
  // We don't currently do revocation checking. If we need to distrust an Apps
258
0
  // certificate, we will use the active distrust mechanism.
259
0
  return Success;
260
0
}
261
262
Result
263
AppTrustDomain::IsChainValid(const DERArray& certChain, Time time,
264
                             const CertPolicyId& requiredPolicy)
265
0
{
266
0
  MOZ_ASSERT(requiredPolicy.IsAnyPolicy());
267
0
  SECStatus srv = ConstructCERTCertListFromReversedDERArray(certChain,
268
0
                                                            mCertChain);
269
0
  if (srv != SECSuccess) {
270
0
    return MapPRErrorCodeToResult(PR_GetError());
271
0
  }
272
0
  return Success;
273
0
}
274
275
Result
276
AppTrustDomain::CheckSignatureDigestAlgorithm(DigestAlgorithm,
277
                                              EndEntityOrCA,
278
                                              Time)
279
0
{
280
0
  // TODO: We should restrict signatures to SHA-256 or better.
281
0
  return Success;
282
0
}
283
284
Result
285
AppTrustDomain::CheckRSAPublicKeyModulusSizeInBits(
286
  EndEntityOrCA /*endEntityOrCA*/, unsigned int modulusSizeInBits)
287
0
{
288
0
  if (modulusSizeInBits < 2048u) {
289
0
    return Result::ERROR_INADEQUATE_KEY_SIZE;
290
0
  }
291
0
  return Success;
292
0
}
293
294
Result
295
AppTrustDomain::VerifyRSAPKCS1SignedDigest(const SignedDigest& signedDigest,
296
                                           Input subjectPublicKeyInfo)
297
0
{
298
0
  // TODO: We should restrict signatures to SHA-256 or better.
299
0
  return VerifyRSAPKCS1SignedDigestNSS(signedDigest, subjectPublicKeyInfo,
300
0
                                       mPinArg);
301
0
}
302
303
Result
304
AppTrustDomain::CheckECDSACurveIsAcceptable(EndEntityOrCA /*endEntityOrCA*/,
305
                                            NamedCurve curve)
306
0
{
307
0
  switch (curve) {
308
0
    case NamedCurve::secp256r1: // fall through
309
0
    case NamedCurve::secp384r1: // fall through
310
0
    case NamedCurve::secp521r1:
311
0
      return Success;
312
0
  }
313
0
314
0
  return Result::ERROR_UNSUPPORTED_ELLIPTIC_CURVE;
315
0
}
316
317
Result
318
AppTrustDomain::VerifyECDSASignedDigest(const SignedDigest& signedDigest,
319
                                        Input subjectPublicKeyInfo)
320
0
{
321
0
  return VerifyECDSASignedDigestNSS(signedDigest, subjectPublicKeyInfo,
322
0
                                    mPinArg);
323
0
}
324
325
Result
326
AppTrustDomain::CheckValidityIsAcceptable(Time /*notBefore*/, Time /*notAfter*/,
327
                                          EndEntityOrCA /*endEntityOrCA*/,
328
                                          KeyPurposeId /*keyPurpose*/)
329
0
{
330
0
  return Success;
331
0
}
332
333
Result
334
AppTrustDomain::NetscapeStepUpMatchesServerAuth(Time /*notBefore*/,
335
                                                /*out*/ bool& matches)
336
0
{
337
0
  matches = false;
338
0
  return Success;
339
0
}
340
341
void
342
AppTrustDomain::NoteAuxiliaryExtension(AuxiliaryExtension /*extension*/,
343
                                       Input /*extensionData*/)
344
0
{
345
0
}
346
347
} } // namespace mozilla::psm