summaryrefslogtreecommitdiffstats
path: root/security/manager/ssl/AppTrustDomain.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--security/manager/ssl/AppTrustDomain.cpp313
1 files changed, 313 insertions, 0 deletions
diff --git a/security/manager/ssl/AppTrustDomain.cpp b/security/manager/ssl/AppTrustDomain.cpp
new file mode 100644
index 0000000000..3959d3bc86
--- /dev/null
+++ b/security/manager/ssl/AppTrustDomain.cpp
@@ -0,0 +1,313 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "AppTrustDomain.h"
+
+#include "MainThreadUtils.h"
+#include "cert_storage/src/cert_storage.h"
+#include "certdb.h"
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/Casting.h"
+#include "mozilla/Preferences.h"
+#include "mozpkix/pkixnss.h"
+#include "nsComponentManagerUtils.h"
+#include "nsDirectoryServiceUtils.h"
+#include "nsIContentSignatureVerifier.h"
+#include "nsIX509CertDB.h"
+#include "nsNSSCertificate.h"
+#include "nsNetUtil.h"
+#include "prerror.h"
+
+// Generated by gen_cert_header.py, which gets called by the build system.
+#include "xpcshell.inc"
+// Add-on signing Certificates
+#include "addons-public.inc"
+#include "addons-public-intermediate.inc"
+#include "addons-stage.inc"
+// Content signature root certificates
+#include "content-signature-dev.inc"
+#include "content-signature-local.inc"
+#include "content-signature-prod.inc"
+#include "content-signature-stage.inc"
+
+using namespace mozilla::pkix;
+
+extern mozilla::LazyLogModule gPIPNSSLog;
+
+namespace mozilla {
+namespace psm {
+
+AppTrustDomain::AppTrustDomain(nsTArray<Span<const uint8_t>>&& collectedCerts)
+ : mIntermediates(std::move(collectedCerts)),
+ mCertBlocklist(do_GetService(NS_CERT_STORAGE_CID)) {}
+
+nsresult AppTrustDomain::SetTrustedRoot(AppTrustedRoot trustedRoot) {
+ switch (trustedRoot) {
+ case nsIX509CertDB::AppXPCShellRoot:
+ mTrustedRoot = {xpcshellRoot};
+ break;
+
+ case nsIX509CertDB::AddonsPublicRoot:
+ mTrustedRoot = {addonsPublicRoot};
+ break;
+
+ case nsIX509CertDB::AddonsStageRoot:
+ mTrustedRoot = {addonsStageRoot};
+ break;
+
+ case nsIContentSignatureVerifier::ContentSignatureLocalRoot:
+ mTrustedRoot = {contentSignatureLocalRoot};
+ break;
+
+ case nsIContentSignatureVerifier::ContentSignatureProdRoot:
+ mTrustedRoot = {contentSignatureProdRoot};
+ break;
+
+ case nsIContentSignatureVerifier::ContentSignatureStageRoot:
+ mTrustedRoot = {contentSignatureStageRoot};
+ break;
+
+ case nsIContentSignatureVerifier::ContentSignatureDevRoot:
+ mTrustedRoot = {contentSignatureDevRoot};
+ break;
+
+ default:
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ // If we're verifying add-ons signed by our production root, we want to make
+ // sure a valid intermediate certificate is available for path building.
+ if (trustedRoot == nsIX509CertDB::AddonsPublicRoot) {
+ mAddonsIntermediate = {addonsPublicIntermediate};
+ }
+
+ return NS_OK;
+}
+
+Result AppTrustDomain::FindIssuer(Input encodedIssuerName,
+ IssuerChecker& checker, Time) {
+ MOZ_ASSERT(!mTrustedRoot.IsEmpty());
+ if (mTrustedRoot.IsEmpty()) {
+ return Result::FATAL_ERROR_INVALID_STATE;
+ }
+
+ nsTArray<Input> candidates;
+ Input rootInput;
+ Result rv = rootInput.Init(mTrustedRoot.Elements(), mTrustedRoot.Length());
+ // This should never fail, since the possible roots are all hard-coded and
+ // they should never be too long.
+ if (rv != Success) {
+ return rv;
+ }
+ candidates.AppendElement(std::move(rootInput));
+ if (!mAddonsIntermediate.IsEmpty()) {
+ Input intermediateInput;
+ rv = intermediateInput.Init(mAddonsIntermediate.Elements(),
+ mAddonsIntermediate.Length());
+ // Again, this should never fail for the same reason as above.
+ if (rv != Success) {
+ return rv;
+ }
+ candidates.AppendElement(std::move(intermediateInput));
+ }
+ for (const auto& intermediate : mIntermediates) {
+ Input intermediateInput;
+ rv = intermediateInput.Init(intermediate.Elements(), intermediate.Length());
+ // This is untrusted input, so skip any intermediates that are too large.
+ if (rv != Success) {
+ continue;
+ }
+ candidates.AppendElement(std::move(intermediateInput));
+ }
+
+ for (const auto& candidate : candidates) {
+ bool keepGoing;
+ rv = checker.Check(candidate, nullptr /*additionalNameConstraints*/,
+ keepGoing);
+ if (rv != Success) {
+ return rv;
+ }
+ if (!keepGoing) {
+ return Success;
+ }
+ }
+
+ // If the above did not succeed in building a verified certificate chain,
+ // fall back to searching for candidates in NSS. This is important in case an
+ // intermediate involved in add-on signing expires before it is replaced. See
+ // bug 1548973.
+ SECItem encodedIssuerNameSECItem = UnsafeMapInputToSECItem(encodedIssuerName);
+ UniqueCERTCertList nssCandidates(CERT_CreateSubjectCertList(
+ nullptr, CERT_GetDefaultCertDB(), &encodedIssuerNameSECItem, 0, false));
+ if (nssCandidates) {
+ for (CERTCertListNode* n = CERT_LIST_HEAD(nssCandidates);
+ !CERT_LIST_END(n, nssCandidates); n = CERT_LIST_NEXT(n)) {
+ Input certDER;
+ Result rv = certDER.Init(n->cert->derCert.data, n->cert->derCert.len);
+ if (rv != Success) {
+ continue; // probably too big
+ }
+
+ bool keepGoing;
+ rv = checker.Check(certDER, nullptr /*additionalNameConstraints*/,
+ keepGoing);
+ if (rv != Success) {
+ return rv;
+ }
+ if (!keepGoing) {
+ break;
+ }
+ }
+ }
+
+ return Success;
+}
+
+Result AppTrustDomain::GetCertTrust(EndEntityOrCA endEntityOrCA,
+ const CertPolicyId& policy,
+ Input candidateCertDER,
+ /*out*/ TrustLevel& trustLevel) {
+ MOZ_ASSERT(policy.IsAnyPolicy());
+ MOZ_ASSERT(!mTrustedRoot.IsEmpty());
+ if (!policy.IsAnyPolicy()) {
+ return Result::FATAL_ERROR_INVALID_ARGS;
+ }
+ if (mTrustedRoot.IsEmpty()) {
+ return Result::FATAL_ERROR_INVALID_STATE;
+ }
+
+ nsTArray<uint8_t> issuerBytes;
+ nsTArray<uint8_t> serialBytes;
+ nsTArray<uint8_t> subjectBytes;
+ nsTArray<uint8_t> pubKeyBytes;
+
+ Result result =
+ BuildRevocationCheckArrays(candidateCertDER, endEntityOrCA, issuerBytes,
+ serialBytes, subjectBytes, pubKeyBytes);
+ if (result != Success) {
+ return result;
+ }
+
+ int16_t revocationState;
+ nsresult nsrv = mCertBlocklist->GetRevocationState(
+ issuerBytes, serialBytes, subjectBytes, pubKeyBytes, &revocationState);
+ if (NS_FAILED(nsrv)) {
+ return Result::FATAL_ERROR_LIBRARY_FAILURE;
+ }
+
+ if (revocationState == nsICertStorage::STATE_ENFORCE) {
+ return Result::ERROR_REVOKED_CERTIFICATE;
+ }
+
+ // mTrustedRoot is the only trust anchor for this validation.
+ Span<const uint8_t> candidateCertDERSpan = {candidateCertDER.UnsafeGetData(),
+ candidateCertDER.GetLength()};
+ if (mTrustedRoot == candidateCertDERSpan) {
+ trustLevel = TrustLevel::TrustAnchor;
+ return Success;
+ }
+
+ trustLevel = TrustLevel::InheritsTrust;
+ return Success;
+}
+
+Result AppTrustDomain::DigestBuf(Input item, DigestAlgorithm digestAlg,
+ /*out*/ uint8_t* digestBuf,
+ size_t digestBufLen) {
+ return DigestBufNSS(item, digestAlg, digestBuf, digestBufLen);
+}
+
+Result AppTrustDomain::CheckRevocation(EndEntityOrCA, const CertID&, Time,
+ Duration,
+ /*optional*/ const Input*,
+ /*optional*/ const Input*,
+ /*optional*/ const Input*) {
+ // We don't currently do revocation checking. If we need to distrust an Apps
+ // certificate, we will use the active distrust mechanism.
+ return Success;
+}
+
+Result AppTrustDomain::IsChainValid(const DERArray& certChain, Time time,
+ const CertPolicyId& requiredPolicy) {
+ MOZ_ASSERT(requiredPolicy.IsAnyPolicy());
+ return Success;
+}
+
+Result AppTrustDomain::CheckSignatureDigestAlgorithm(DigestAlgorithm digestAlg,
+ EndEntityOrCA, Time) {
+ switch (digestAlg) {
+ case DigestAlgorithm::sha256: // fall through
+ case DigestAlgorithm::sha384: // fall through
+ case DigestAlgorithm::sha512:
+ return Success;
+ case DigestAlgorithm::sha1:
+ return Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED;
+ }
+ return Result::FATAL_ERROR_LIBRARY_FAILURE;
+}
+
+Result AppTrustDomain::CheckRSAPublicKeyModulusSizeInBits(
+ EndEntityOrCA /*endEntityOrCA*/, unsigned int modulusSizeInBits) {
+ if (modulusSizeInBits < 2048u) {
+ return Result::ERROR_INADEQUATE_KEY_SIZE;
+ }
+ return Success;
+}
+
+Result AppTrustDomain::VerifyRSAPKCS1SignedData(Input data,
+ DigestAlgorithm digestAlgorithm,
+ Input signature,
+ Input subjectPublicKeyInfo) {
+ // TODO: We should restrict signatures to SHA-256 or better.
+ return VerifyRSAPKCS1SignedDataNSS(data, digestAlgorithm, signature,
+ subjectPublicKeyInfo, nullptr);
+}
+
+Result AppTrustDomain::VerifyRSAPSSSignedData(Input data,
+ DigestAlgorithm digestAlgorithm,
+ Input signature,
+ Input subjectPublicKeyInfo) {
+ return VerifyRSAPSSSignedDataNSS(data, digestAlgorithm, signature,
+ subjectPublicKeyInfo, nullptr);
+}
+
+Result AppTrustDomain::CheckECDSACurveIsAcceptable(
+ EndEntityOrCA /*endEntityOrCA*/, NamedCurve curve) {
+ switch (curve) {
+ case NamedCurve::secp256r1: // fall through
+ case NamedCurve::secp384r1: // fall through
+ case NamedCurve::secp521r1:
+ return Success;
+ }
+
+ return Result::ERROR_UNSUPPORTED_ELLIPTIC_CURVE;
+}
+
+Result AppTrustDomain::VerifyECDSASignedData(Input data,
+ DigestAlgorithm digestAlgorithm,
+ Input signature,
+ Input subjectPublicKeyInfo) {
+ return VerifyECDSASignedDataNSS(data, digestAlgorithm, signature,
+ subjectPublicKeyInfo, nullptr);
+}
+
+Result AppTrustDomain::CheckValidityIsAcceptable(
+ Time /*notBefore*/, Time /*notAfter*/, EndEntityOrCA /*endEntityOrCA*/,
+ KeyPurposeId /*keyPurpose*/) {
+ return Success;
+}
+
+Result AppTrustDomain::NetscapeStepUpMatchesServerAuth(Time /*notBefore*/,
+ /*out*/ bool& matches) {
+ matches = false;
+ return Success;
+}
+
+void AppTrustDomain::NoteAuxiliaryExtension(AuxiliaryExtension /*extension*/,
+ Input /*extensionData*/) {}
+
+} // namespace psm
+} // namespace mozilla