diff options
Diffstat (limited to '')
-rw-r--r-- | security/manager/ssl/AppTrustDomain.cpp | 313 |
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 |