gecko-dev/security/apps/AppTrustDomain.cpp

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

302 строки
9.6 KiB
C++
Исходник Обычный вид История

/* -*- 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"
#ifdef MOZ_NEW_CERT_STORAGE
# include "cert_storage/src/cert_storage.h"
#endif
#include "certdb.h"
#include "mozilla/ArrayUtils.h"
#include "mozilla/Casting.h"
#include "mozilla/Preferences.h"
#include "nsComponentManagerUtils.h"
#include "nsDirectoryServiceUtils.h"
#include "nsIX509CertDB.h"
#include "nsNSSCertificate.h"
#include "nsNetUtil.h"
#include "mozpkix/pkixnss.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"
bug 985201 - rename insanity::pkix to mozilla::pkix r=cviecco r=briansmith --HG-- rename : security/insanity/include/insanity/ScopedPtr.h => security/pkix/include/pkix/ScopedPtr.h rename : security/insanity/include/insanity/bind.h => security/pkix/include/pkix/bind.h rename : security/insanity/include/insanity/nullptr.h => security/pkix/include/pkix/nullptr.h rename : security/insanity/include/insanity/pkix.h => security/pkix/include/pkix/pkix.h rename : security/insanity/include/insanity/pkixtypes.h => security/pkix/include/pkix/pkixtypes.h rename : security/insanity/lib/pkixbind.cpp => security/pkix/lib/pkixbind.cpp rename : security/insanity/lib/pkixbuild.cpp => security/pkix/lib/pkixbuild.cpp rename : security/insanity/lib/pkixcheck.cpp => security/pkix/lib/pkixcheck.cpp rename : security/insanity/lib/pkixcheck.h => security/pkix/lib/pkixcheck.h rename : security/insanity/lib/pkixder.cpp => security/pkix/lib/pkixder.cpp rename : security/insanity/lib/pkixder.h => security/pkix/lib/pkixder.h rename : security/insanity/lib/pkixkey.cpp => security/pkix/lib/pkixkey.cpp rename : security/insanity/lib/pkixocsp.cpp => security/pkix/lib/pkixocsp.cpp rename : security/insanity/lib/pkixutil.h => security/pkix/lib/pkixutil.h rename : security/insanity/moz.build => security/pkix/moz.build rename : security/insanity/test/lib/moz.build => security/pkix/test/lib/moz.build rename : security/insanity/test/lib/pkixtestutil.cpp => security/pkix/test/lib/pkixtestutil.cpp rename : security/insanity/test/lib/pkixtestutil.h => security/pkix/test/lib/pkixtestutil.h
2014-03-21 01:29:21 +04:00
using namespace mozilla::pkix;
extern mozilla::LazyLogModule gPIPNSSLog;
namespace mozilla {
namespace psm {
AppTrustDomain::AppTrustDomain(nsTArray<Span<const uint8_t>>&& collectedCerts)
: mIntermediates(std::move(collectedCerts)),
#ifdef MOZ_NEW_CERT_STORAGE
mCertBlocklist(do_GetService(NS_CERT_STORAGE_CID)) {
}
#else
mCertBlocklist(do_GetService(NS_CERTBLOCKLIST_CONTRACTID)) {
}
#endif
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;
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;
}
#ifdef MOZ_NEW_CERT_STORAGE
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);
#else
nsAutoCString encIssuer;
nsAutoCString encSerial;
nsAutoCString encSubject;
nsAutoCString encPubKey;
Result result =
BuildRevocationCheckStrings(candidateCertDER, endEntityOrCA, encIssuer,
encSerial, encSubject, encPubKey);
#endif
if (result != Success) {
return result;
}
#ifdef MOZ_NEW_CERT_STORAGE
int16_t revocationState;
nsresult nsrv = mCertBlocklist->GetRevocationState(
issuerBytes, serialBytes, subjectBytes, pubKeyBytes, &revocationState);
#else
bool isCertRevoked;
nsresult nsrv = mCertBlocklist->IsCertRevoked(
encIssuer, encSerial, encSubject, encPubKey, &isCertRevoked);
#endif
if (NS_FAILED(nsrv)) {
return Result::FATAL_ERROR_LIBRARY_FAILURE;
}
#ifdef MOZ_NEW_CERT_STORAGE
if (revocationState == nsICertStorage::STATE_ENFORCE) {
#else
if (isCertRevoked) {
#endif
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,
EndEntityOrCA, Time) {
// TODO: We should restrict signatures to SHA-256 or better.
return Success;
}
Result AppTrustDomain::CheckRSAPublicKeyModulusSizeInBits(
EndEntityOrCA /*endEntityOrCA*/, unsigned int modulusSizeInBits) {
if (modulusSizeInBits < 2048u) {
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,
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::VerifyECDSASignedDigest(const SignedDigest& signedDigest,
Input subjectPublicKeyInfo) {
return VerifyECDSASignedDigestNSS(signedDigest, 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