Bug 1656692 [wpt PR 24842] - Reland "Origin isolation: a new strategy for window...
[gecko.git] / security / apps / AppTrustDomain.cpp
blob2fae84fa1c28a858d490e0d92a7583076fe16fb2
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "AppTrustDomain.h"
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 "nsIX509CertDB.h"
16 #include "nsNSSCertificate.h"
17 #include "nsNetUtil.h"
18 #include "mozpkix/pkixnss.h"
19 #include "prerror.h"
21 // Generated by gen_cert_header.py, which gets called by the build system.
22 #include "xpcshell.inc"
23 // Add-on signing Certificates
24 #include "addons-public.inc"
25 #include "addons-public-intermediate.inc"
26 #include "addons-stage.inc"
28 using namespace mozilla::pkix;
30 extern mozilla::LazyLogModule gPIPNSSLog;
32 namespace mozilla {
33 namespace psm {
35 AppTrustDomain::AppTrustDomain(UniqueCERTCertList& certChain, void* pinArg)
36 : mCertChain(certChain), mPinArg(pinArg) {}
38 nsresult AppTrustDomain::SetTrustedRoot(AppTrustedRoot trustedRoot) {
39 SECItem trustedDER;
41 // Load the trusted certificate into the in-memory NSS database so that
42 // CERT_CreateSubjectCertList can find it.
44 switch (trustedRoot) {
45 case nsIX509CertDB::AppXPCShellRoot:
46 trustedDER.data = const_cast<uint8_t*>(xpcshellRoot);
47 trustedDER.len = mozilla::ArrayLength(xpcshellRoot);
48 break;
50 case nsIX509CertDB::AddonsPublicRoot:
51 trustedDER.data = const_cast<uint8_t*>(addonsPublicRoot);
52 trustedDER.len = mozilla::ArrayLength(addonsPublicRoot);
53 break;
55 case nsIX509CertDB::AddonsStageRoot:
56 trustedDER.data = const_cast<uint8_t*>(addonsStageRoot);
57 trustedDER.len = mozilla::ArrayLength(addonsStageRoot);
58 break;
60 default:
61 return NS_ERROR_INVALID_ARG;
64 mTrustedRoot.reset(CERT_NewTempCertificate(
65 CERT_GetDefaultCertDB(), &trustedDER, nullptr, false, true));
66 if (!mTrustedRoot) {
67 return mozilla::psm::GetXPCOMFromNSSError(PR_GetError());
70 // If we're verifying add-ons signed by our production root, we want to make
71 // sure a valid intermediate certificate is available for path building.
72 // Merely holding this alive in memory makes it available for NSS to find in
73 // AppTrustDomain::FindIssuer.
74 if (trustedRoot == nsIX509CertDB::AddonsPublicRoot) {
75 SECItem intermediateDER = {
76 siBuffer,
77 const_cast<uint8_t*>(addonsPublicIntermediate),
78 static_cast<unsigned int>(
79 mozilla::ArrayLength(addonsPublicIntermediate)),
81 mAddonsIntermediate.reset(CERT_NewTempCertificate(
82 CERT_GetDefaultCertDB(), &intermediateDER, nullptr, false, true));
83 if (!mAddonsIntermediate) {
84 return mozilla::psm::GetXPCOMFromNSSError(PR_GetError());
88 return NS_OK;
91 Result AppTrustDomain::FindIssuer(Input encodedIssuerName,
92 IssuerChecker& checker, Time)
95 MOZ_ASSERT(mTrustedRoot);
96 if (!mTrustedRoot) {
97 return Result::FATAL_ERROR_INVALID_STATE;
100 // TODO(bug 1035418): If/when mozilla::pkix relaxes the restriction that
101 // FindIssuer must only pass certificates with a matching subject name to
102 // checker.Check, we can stop using CERT_CreateSubjectCertList and instead
103 // use logic like this:
105 // 1. First, try the trusted trust anchor.
106 // 2. Secondly, iterate through the certificates that were stored in the CMS
107 // message, passing each one to checker.Check.
108 SECItem encodedIssuerNameSECItem = UnsafeMapInputToSECItem(encodedIssuerName);
109 UniqueCERTCertList candidates(CERT_CreateSubjectCertList(
110 nullptr, CERT_GetDefaultCertDB(), &encodedIssuerNameSECItem, 0, false));
111 if (candidates) {
112 for (CERTCertListNode* n = CERT_LIST_HEAD(candidates);
113 !CERT_LIST_END(n, candidates); n = CERT_LIST_NEXT(n)) {
114 Input certDER;
115 Result rv = certDER.Init(n->cert->derCert.data, n->cert->derCert.len);
116 if (rv != Success) {
117 continue; // probably too big
120 bool keepGoing;
121 rv = checker.Check(certDER, nullptr /*additionalNameConstraints*/,
122 keepGoing);
123 if (rv != Success) {
124 return rv;
126 if (!keepGoing) {
127 break;
132 return Success;
135 Result AppTrustDomain::GetCertTrust(EndEntityOrCA endEntityOrCA,
136 const CertPolicyId& policy,
137 Input candidateCertDER,
138 /*out*/ TrustLevel& trustLevel) {
139 MOZ_ASSERT(policy.IsAnyPolicy());
140 MOZ_ASSERT(mTrustedRoot);
141 if (!policy.IsAnyPolicy()) {
142 return Result::FATAL_ERROR_INVALID_ARGS;
144 if (!mTrustedRoot) {
145 return Result::FATAL_ERROR_INVALID_STATE;
148 // Handle active distrust of the certificate.
150 // XXX: This would be cleaner and more efficient if we could get the trust
151 // information without constructing a CERTCertificate here, but NSS doesn't
152 // expose it in any other easy-to-use fashion.
153 SECItem candidateCertDERSECItem = UnsafeMapInputToSECItem(candidateCertDER);
154 UniqueCERTCertificate candidateCert(CERT_NewTempCertificate(
155 CERT_GetDefaultCertDB(), &candidateCertDERSECItem, nullptr, false, true));
156 if (!candidateCert) {
157 return MapPRErrorCodeToResult(PR_GetError());
160 CERTCertTrust trust;
161 if (CERT_GetCertTrust(candidateCert.get(), &trust) == SECSuccess) {
162 uint32_t flags = SEC_GET_TRUST_FLAGS(&trust, trustObjectSigning);
164 // For DISTRUST, we use the CERTDB_TRUSTED or CERTDB_TRUSTED_CA bit,
165 // because we can have active distrust for either type of cert. Note that
166 // CERTDB_TERMINAL_RECORD means "stop trying to inherit trust" so if the
167 // relevant trust bit isn't set then that means the cert must be considered
168 // distrusted.
169 uint32_t relevantTrustBit = endEntityOrCA == EndEntityOrCA::MustBeCA
170 ? CERTDB_TRUSTED_CA
171 : CERTDB_TRUSTED;
172 if (((flags & (relevantTrustBit | CERTDB_TERMINAL_RECORD))) ==
173 CERTDB_TERMINAL_RECORD) {
174 trustLevel = TrustLevel::ActivelyDistrusted;
175 return Success;
179 // mTrustedRoot is the only trust anchor for this validation.
180 if (CERT_CompareCerts(mTrustedRoot.get(), candidateCert.get())) {
181 trustLevel = TrustLevel::TrustAnchor;
182 return Success;
185 trustLevel = TrustLevel::InheritsTrust;
186 return Success;
189 Result AppTrustDomain::DigestBuf(Input item, DigestAlgorithm digestAlg,
190 /*out*/ uint8_t* digestBuf,
191 size_t digestBufLen) {
192 return DigestBufNSS(item, digestAlg, digestBuf, digestBufLen);
195 Result AppTrustDomain::CheckRevocation(EndEntityOrCA, const CertID&, Time, Time,
196 Duration,
197 /*optional*/ const Input*,
198 /*optional*/ const Input*) {
199 // We don't currently do revocation checking. If we need to distrust an Apps
200 // certificate, we will use the active distrust mechanism.
201 return Success;
204 Result AppTrustDomain::IsChainValid(const DERArray& certChain, Time time,
205 const CertPolicyId& requiredPolicy) {
206 MOZ_ASSERT(requiredPolicy.IsAnyPolicy());
207 SECStatus srv =
208 ConstructCERTCertListFromReversedDERArray(certChain, mCertChain);
209 if (srv != SECSuccess) {
210 return MapPRErrorCodeToResult(PR_GetError());
212 return Success;
215 Result AppTrustDomain::CheckSignatureDigestAlgorithm(DigestAlgorithm,
216 EndEntityOrCA, Time) {
217 // TODO: We should restrict signatures to SHA-256 or better.
218 return Success;
221 Result AppTrustDomain::CheckRSAPublicKeyModulusSizeInBits(
222 EndEntityOrCA /*endEntityOrCA*/, unsigned int modulusSizeInBits) {
223 if (modulusSizeInBits < 2048u) {
224 return Result::ERROR_INADEQUATE_KEY_SIZE;
226 return Success;
229 Result AppTrustDomain::VerifyRSAPKCS1SignedDigest(
230 const SignedDigest& signedDigest, Input subjectPublicKeyInfo) {
231 // TODO: We should restrict signatures to SHA-256 or better.
232 return VerifyRSAPKCS1SignedDigestNSS(signedDigest, subjectPublicKeyInfo,
233 mPinArg);
236 Result AppTrustDomain::CheckECDSACurveIsAcceptable(
237 EndEntityOrCA /*endEntityOrCA*/, NamedCurve curve) {
238 switch (curve) {
239 case NamedCurve::secp256r1: // fall through
240 case NamedCurve::secp384r1: // fall through
241 case NamedCurve::secp521r1:
242 return Success;
245 return Result::ERROR_UNSUPPORTED_ELLIPTIC_CURVE;
248 Result AppTrustDomain::VerifyECDSASignedDigest(const SignedDigest& signedDigest,
249 Input subjectPublicKeyInfo) {
250 return VerifyECDSASignedDigestNSS(signedDigest, subjectPublicKeyInfo,
251 mPinArg);
254 Result AppTrustDomain::CheckValidityIsAcceptable(
255 Time /*notBefore*/, Time /*notAfter*/, EndEntityOrCA /*endEntityOrCA*/,
256 KeyPurposeId /*keyPurpose*/) {
257 return Success;
260 Result AppTrustDomain::NetscapeStepUpMatchesServerAuth(Time /*notBefore*/,
261 /*out*/ bool& matches) {
262 matches = false;
263 return Success;
266 void AppTrustDomain::NoteAuxiliaryExtension(AuxiliaryExtension /*extension*/,
267 Input /*extensionData*/) {}
269 } // namespace psm
270 } // namespace mozilla