summaryrefslogtreecommitdiffstats
path: root/security/apps/AppTrustDomain.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'security/apps/AppTrustDomain.cpp')
-rw-r--r--security/apps/AppTrustDomain.cpp387
1 files changed, 387 insertions, 0 deletions
diff --git a/security/apps/AppTrustDomain.cpp b/security/apps/AppTrustDomain.cpp
new file mode 100644
index 000000000..9131a701b
--- /dev/null
+++ b/security/apps/AppTrustDomain.cpp
@@ -0,0 +1,387 @@
+/* -*- 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 "certdb.h"
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/Casting.h"
+#include "mozilla/Preferences.h"
+#include "nsComponentManagerUtils.h"
+#include "nsIFile.h"
+#include "nsIFileStreams.h"
+#include "nsIX509CertDB.h"
+#include "nsNSSCertificate.h"
+#include "nsNetUtil.h"
+#include "pkix/pkixnss.h"
+#include "prerror.h"
+#include "secerr.h"
+
+// Generated in Makefile.in
+#include "marketplace-prod-public.inc"
+#include "marketplace-prod-reviewers.inc"
+#include "marketplace-dev-public.inc"
+#include "marketplace-dev-reviewers.inc"
+#include "marketplace-stage.inc"
+#include "xpcshell.inc"
+// Trusted Hosted Apps Certificates
+#include "manifest-signing-root.inc"
+#include "manifest-signing-test-root.inc"
+// Add-on signing Certificates
+#include "addons-public.inc"
+#include "addons-stage.inc"
+// Privileged Package Certificates
+#include "privileged-package-root.inc"
+
+using namespace mozilla::pkix;
+
+extern mozilla::LazyLogModule gPIPNSSLog;
+
+static const unsigned int DEFAULT_MIN_RSA_BITS = 2048;
+static char kDevImportedDER[] =
+ "network.http.signed-packages.developer-root";
+
+namespace mozilla { namespace psm {
+
+StaticMutex AppTrustDomain::sMutex;
+UniquePtr<unsigned char[]> AppTrustDomain::sDevImportedDERData;
+unsigned int AppTrustDomain::sDevImportedDERLen = 0;
+
+AppTrustDomain::AppTrustDomain(UniqueCERTCertList& certChain, void* pinArg)
+ : mCertChain(certChain)
+ , mPinArg(pinArg)
+ , mMinRSABits(DEFAULT_MIN_RSA_BITS)
+{
+}
+
+SECStatus
+AppTrustDomain::SetTrustedRoot(AppTrustedRoot trustedRoot)
+{
+ SECItem trustedDER;
+
+ // Load the trusted certificate into the in-memory NSS database so that
+ // CERT_CreateSubjectCertList can find it.
+
+ switch (trustedRoot)
+ {
+ case nsIX509CertDB::AppMarketplaceProdPublicRoot:
+ trustedDER.data = const_cast<uint8_t*>(marketplaceProdPublicRoot);
+ trustedDER.len = mozilla::ArrayLength(marketplaceProdPublicRoot);
+ break;
+
+ case nsIX509CertDB::AppMarketplaceProdReviewersRoot:
+ trustedDER.data = const_cast<uint8_t*>(marketplaceProdReviewersRoot);
+ trustedDER.len = mozilla::ArrayLength(marketplaceProdReviewersRoot);
+ break;
+
+ case nsIX509CertDB::AppMarketplaceDevPublicRoot:
+ trustedDER.data = const_cast<uint8_t*>(marketplaceDevPublicRoot);
+ trustedDER.len = mozilla::ArrayLength(marketplaceDevPublicRoot);
+ break;
+
+ case nsIX509CertDB::AppMarketplaceDevReviewersRoot:
+ trustedDER.data = const_cast<uint8_t*>(marketplaceDevReviewersRoot);
+ trustedDER.len = mozilla::ArrayLength(marketplaceDevReviewersRoot);
+ break;
+
+ case nsIX509CertDB::AppMarketplaceStageRoot:
+ trustedDER.data = const_cast<uint8_t*>(marketplaceStageRoot);
+ trustedDER.len = mozilla::ArrayLength(marketplaceStageRoot);
+ // The staging root was generated with a 1024-bit key.
+ mMinRSABits = 1024u;
+ break;
+
+ case nsIX509CertDB::AppXPCShellRoot:
+ trustedDER.data = const_cast<uint8_t*>(xpcshellRoot);
+ trustedDER.len = mozilla::ArrayLength(xpcshellRoot);
+ break;
+
+ case nsIX509CertDB::AddonsPublicRoot:
+ trustedDER.data = const_cast<uint8_t*>(addonsPublicRoot);
+ trustedDER.len = mozilla::ArrayLength(addonsPublicRoot);
+ break;
+
+ case nsIX509CertDB::AddonsStageRoot:
+ trustedDER.data = const_cast<uint8_t*>(addonsStageRoot);
+ trustedDER.len = mozilla::ArrayLength(addonsStageRoot);
+ break;
+
+ case nsIX509CertDB::PrivilegedPackageRoot:
+ trustedDER.data = const_cast<uint8_t*>(privilegedPackageRoot);
+ trustedDER.len = mozilla::ArrayLength(privilegedPackageRoot);
+ break;
+
+ case nsIX509CertDB::DeveloperImportedRoot: {
+ StaticMutexAutoLock lock(sMutex);
+ if (!sDevImportedDERData) {
+ MOZ_ASSERT(!NS_IsMainThread());
+ nsCOMPtr<nsIFile> file(do_CreateInstance("@mozilla.org/file/local;1"));
+ if (!file) {
+ PR_SetError(SEC_ERROR_IO, 0);
+ return SECFailure;
+ }
+ nsresult rv = file->InitWithNativePath(
+ Preferences::GetCString(kDevImportedDER));
+ if (NS_FAILED(rv)) {
+ PR_SetError(SEC_ERROR_IO, 0);
+ return SECFailure;
+ }
+
+ nsCOMPtr<nsIInputStream> inputStream;
+ NS_NewLocalFileInputStream(getter_AddRefs(inputStream), file, -1, -1,
+ nsIFileInputStream::CLOSE_ON_EOF);
+ if (!inputStream) {
+ PR_SetError(SEC_ERROR_IO, 0);
+ return SECFailure;
+ }
+
+ uint64_t length;
+ rv = inputStream->Available(&length);
+ if (NS_FAILED(rv)) {
+ PR_SetError(SEC_ERROR_IO, 0);
+ return SECFailure;
+ }
+
+ auto data = MakeUnique<char[]>(length);
+ rv = inputStream->Read(data.get(), length, &sDevImportedDERLen);
+ if (NS_FAILED(rv)) {
+ PR_SetError(SEC_ERROR_IO, 0);
+ return SECFailure;
+ }
+
+ MOZ_ASSERT(length == sDevImportedDERLen);
+ sDevImportedDERData.reset(
+ BitwiseCast<unsigned char*, char*>(data.release()));
+ }
+
+ trustedDER.data = sDevImportedDERData.get();
+ trustedDER.len = sDevImportedDERLen;
+ break;
+ }
+
+ default:
+ PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
+ return SECFailure;
+ }
+
+ mTrustedRoot.reset(CERT_NewTempCertificate(CERT_GetDefaultCertDB(),
+ &trustedDER, nullptr, false, true));
+ if (!mTrustedRoot) {
+ return SECFailure;
+ }
+
+ return SECSuccess;
+}
+
+Result
+AppTrustDomain::FindIssuer(Input encodedIssuerName, IssuerChecker& checker,
+ Time)
+
+{
+ MOZ_ASSERT(mTrustedRoot);
+ if (!mTrustedRoot) {
+ return Result::FATAL_ERROR_INVALID_STATE;
+ }
+
+ // TODO(bug 1035418): If/when mozilla::pkix relaxes the restriction that
+ // FindIssuer must only pass certificates with a matching subject name to
+ // checker.Check, we can stop using CERT_CreateSubjectCertList and instead
+ // use logic like this:
+ //
+ // 1. First, try the trusted trust anchor.
+ // 2. Secondly, iterate through the certificates that were stored in the CMS
+ // message, passing each one to checker.Check.
+ SECItem encodedIssuerNameSECItem =
+ UnsafeMapInputToSECItem(encodedIssuerName);
+ UniqueCERTCertList
+ candidates(CERT_CreateSubjectCertList(nullptr, CERT_GetDefaultCertDB(),
+ &encodedIssuerNameSECItem, 0,
+ false));
+ if (candidates) {
+ for (CERTCertListNode* n = CERT_LIST_HEAD(candidates);
+ !CERT_LIST_END(n, candidates); 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);
+ if (!policy.IsAnyPolicy()) {
+ return Result::FATAL_ERROR_INVALID_ARGS;
+ }
+ if (!mTrustedRoot) {
+ return Result::FATAL_ERROR_INVALID_STATE;
+ }
+
+ // Handle active distrust of the certificate.
+
+ // XXX: This would be cleaner and more efficient if we could get the trust
+ // information without constructing a CERTCertificate here, but NSS doesn't
+ // expose it in any other easy-to-use fashion.
+ SECItem candidateCertDERSECItem =
+ UnsafeMapInputToSECItem(candidateCertDER);
+ UniqueCERTCertificate candidateCert(
+ CERT_NewTempCertificate(CERT_GetDefaultCertDB(), &candidateCertDERSECItem,
+ nullptr, false, true));
+ if (!candidateCert) {
+ return MapPRErrorCodeToResult(PR_GetError());
+ }
+
+ CERTCertTrust trust;
+ if (CERT_GetCertTrust(candidateCert.get(), &trust) == SECSuccess) {
+ uint32_t flags = SEC_GET_TRUST_FLAGS(&trust, trustObjectSigning);
+
+ // For DISTRUST, we use the CERTDB_TRUSTED or CERTDB_TRUSTED_CA bit,
+ // because we can have active distrust for either type of cert. Note that
+ // CERTDB_TERMINAL_RECORD means "stop trying to inherit trust" so if the
+ // relevant trust bit isn't set then that means the cert must be considered
+ // distrusted.
+ uint32_t relevantTrustBit = endEntityOrCA == EndEntityOrCA::MustBeCA
+ ? CERTDB_TRUSTED_CA
+ : CERTDB_TRUSTED;
+ if (((flags & (relevantTrustBit | CERTDB_TERMINAL_RECORD)))
+ == CERTDB_TERMINAL_RECORD) {
+ trustLevel = TrustLevel::ActivelyDistrusted;
+ return Success;
+ }
+ }
+
+ // mTrustedRoot is the only trust anchor for this validation.
+ if (CERT_CompareCerts(mTrustedRoot.get(), candidateCert.get())) {
+ 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*)
+{
+ // 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)
+{
+ SECStatus srv = ConstructCERTCertListFromReversedDERArray(certChain,
+ mCertChain);
+ if (srv != SECSuccess) {
+ return MapPRErrorCodeToResult(PR_GetError());
+ }
+ return Success;
+}
+
+Result
+AppTrustDomain::CheckSignatureDigestAlgorithm(DigestAlgorithm,
+ EndEntityOrCA,
+ Time)
+{
+ // TODO: We should restrict signatures to SHA-256 or better.
+ return Success;
+}
+
+Result
+AppTrustDomain::CheckRSAPublicKeyModulusSizeInBits(
+ EndEntityOrCA /*endEntityOrCA*/, unsigned int modulusSizeInBits)
+{
+ if (modulusSizeInBits < mMinRSABits) {
+ return Result::ERROR_INADEQUATE_KEY_SIZE;
+ }
+ return Success;
+}
+
+Result
+AppTrustDomain::VerifyRSAPKCS1SignedDigest(const SignedDigest& signedDigest,
+ Input subjectPublicKeyInfo)
+{
+ // TODO: We should restrict signatures to SHA-256 or better.
+ return VerifyRSAPKCS1SignedDigestNSS(signedDigest, subjectPublicKeyInfo,
+ mPinArg);
+}
+
+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::VerifyECDSASignedDigest(const SignedDigest& signedDigest,
+ Input subjectPublicKeyInfo)
+{
+ return VerifyECDSASignedDigestNSS(signedDigest, subjectPublicKeyInfo,
+ mPinArg);
+}
+
+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 mozilla::psm