зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1659786 - avoid CERTCertificate in CSTrustDomain and ContentSignatureVerifier r=rmf
Differential Revision: https://phabricator.services.mozilla.com/D87497
This commit is contained in:
Родитель
c5077ba863
Коммит
3ac5dbc513
|
@ -9,15 +9,14 @@
|
|||
#endif
|
||||
#include "CSTrustDomain.h"
|
||||
#include "mozilla/Base64.h"
|
||||
#include "mozilla/Logging.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#ifdef MOZ_NEW_CERT_STORAGE
|
||||
# include "nsDirectoryServiceUtils.h"
|
||||
#endif
|
||||
#include "nsNSSCertificate.h"
|
||||
#include "nsNSSComponent.h"
|
||||
#include "NSSCertDBTrustDomain.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "mozpkix/pkixnss.h"
|
||||
|
||||
using namespace mozilla::pkix;
|
||||
|
@ -28,8 +27,8 @@ namespace psm {
|
|||
static LazyLogModule gTrustDomainPRLog("CSTrustDomain");
|
||||
#define CSTrust_LOG(args) MOZ_LOG(gTrustDomainPRLog, LogLevel::Debug, args)
|
||||
|
||||
CSTrustDomain::CSTrustDomain(UniqueCERTCertList& certChain)
|
||||
: mCertChain(certChain),
|
||||
CSTrustDomain::CSTrustDomain(nsTArray<nsTArray<uint8_t>>& certList)
|
||||
: mCertList(certList),
|
||||
#ifdef MOZ_NEW_CERT_STORAGE
|
||||
mCertBlocklist(do_GetService(NS_CERT_STORAGE_CID)) {
|
||||
}
|
||||
|
@ -47,13 +46,6 @@ Result CSTrustDomain::GetCertTrust(EndEntityOrCA endEntityOrCA,
|
|||
return Result::FATAL_ERROR_INVALID_ARGS;
|
||||
}
|
||||
|
||||
SECItem candidateCertDERSECItem = UnsafeMapInputToSECItem(candidateCertDER);
|
||||
UniqueCERTCertificate candidateCert(CERT_NewTempCertificate(
|
||||
CERT_GetDefaultCertDB(), &candidateCertDERSECItem, nullptr, false, true));
|
||||
if (!candidateCert) {
|
||||
return MapPRErrorCodeToResult(PR_GetError());
|
||||
}
|
||||
|
||||
#ifdef MOZ_NEW_CERT_STORAGE
|
||||
nsTArray<uint8_t> issuerBytes;
|
||||
nsTArray<uint8_t> serialBytes;
|
||||
|
@ -105,7 +97,9 @@ Result CSTrustDomain::GetCertTrust(EndEntityOrCA endEntityOrCA,
|
|||
if (!component) {
|
||||
return Result::FATAL_ERROR_LIBRARY_FAILURE;
|
||||
}
|
||||
nsrv = component->IsCertContentSigningRoot(candidateCert.get(), &isRoot);
|
||||
nsTArray<uint8_t> candidateCert(candidateCertDER.UnsafeGetData(),
|
||||
candidateCertDER.GetLength());
|
||||
nsrv = component->IsCertContentSigningRoot(candidateCert, &isRoot);
|
||||
if (NS_FAILED(nsrv)) {
|
||||
return Result::FATAL_ERROR_LIBRARY_FAILURE;
|
||||
}
|
||||
|
@ -123,28 +117,14 @@ Result CSTrustDomain::GetCertTrust(EndEntityOrCA endEntityOrCA,
|
|||
Result CSTrustDomain::FindIssuer(Input encodedIssuerName,
|
||||
IssuerChecker& checker, Time time) {
|
||||
// Loop over the chain, look for a matching subject
|
||||
for (CERTCertListNode* n = CERT_LIST_HEAD(mCertChain);
|
||||
!CERT_LIST_END(n, mCertChain); n = CERT_LIST_NEXT(n)) {
|
||||
Input certDER;
|
||||
Result rv = certDER.Init(n->cert->derCert.data, n->cert->derCert.len);
|
||||
for (const auto& certBytes : mCertList) {
|
||||
Input certInput;
|
||||
Result rv = certInput.Init(certBytes.Elements(), certBytes.Length());
|
||||
if (rv != Success) {
|
||||
continue; // probably too big
|
||||
}
|
||||
|
||||
// if the subject does not match, try the next certificate
|
||||
Input subjectDER;
|
||||
rv = subjectDER.Init(n->cert->derSubject.data, n->cert->derSubject.len);
|
||||
if (rv != Success) {
|
||||
continue; // just try the next one
|
||||
}
|
||||
if (!InputsAreEqual(subjectDER, encodedIssuerName)) {
|
||||
CSTrust_LOG(("CSTrustDomain: subjects don't match\n"));
|
||||
continue;
|
||||
}
|
||||
|
||||
// If the subject does match, try the next step
|
||||
bool keepGoing;
|
||||
rv = checker.Check(certDER, nullptr /*additionalNameConstraints*/,
|
||||
rv = checker.Check(certInput, nullptr /*additionalNameConstraints*/,
|
||||
keepGoing);
|
||||
if (rv != Success) {
|
||||
return rv;
|
||||
|
|
|
@ -8,15 +8,12 @@
|
|||
#define CSTrustDomain_h
|
||||
|
||||
#include "mozpkix/pkixtypes.h"
|
||||
#include "mozilla/StaticMutex.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
#include "nsDebug.h"
|
||||
#ifdef MOZ_NEW_CERT_STORAGE
|
||||
# include "nsICertStorage.h"
|
||||
#else
|
||||
# include "nsICertBlocklist.h"
|
||||
#endif
|
||||
#include "ScopedNSSTypes.h"
|
||||
#include "nsTArray.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace psm {
|
||||
|
@ -25,7 +22,7 @@ class CSTrustDomain final : public mozilla::pkix::TrustDomain {
|
|||
public:
|
||||
typedef mozilla::pkix::Result Result;
|
||||
|
||||
explicit CSTrustDomain(UniqueCERTCertList& certChain);
|
||||
explicit CSTrustDomain(nsTArray<nsTArray<uint8_t>>& certList);
|
||||
|
||||
virtual Result GetCertTrust(
|
||||
mozilla::pkix::EndEntityOrCA endEntityOrCA,
|
||||
|
@ -76,7 +73,7 @@ class CSTrustDomain final : public mozilla::pkix::TrustDomain {
|
|||
size_t digestBufLen) override;
|
||||
|
||||
private:
|
||||
/*out*/ UniqueCERTCertList& mCertChain;
|
||||
nsTArray<nsTArray<uint8_t>>& mCertList; // non-owning!
|
||||
#ifdef MOZ_NEW_CERT_STORAGE
|
||||
nsCOMPtr<nsICertStorage> mCertBlocklist;
|
||||
#else
|
||||
|
|
|
@ -14,15 +14,14 @@
|
|||
#include "cryptohi.h"
|
||||
#include "keyhi.h"
|
||||
#include "mozilla/Base64.h"
|
||||
#include "mozilla/Unused.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsPromiseFlatString.h"
|
||||
#include "nsProxyRelease.h"
|
||||
#include "nsSecurityHeaderParser.h"
|
||||
#include "nsWhitespaceTokenizer.h"
|
||||
#include "mozpkix/pkix.h"
|
||||
#include "mozpkix/pkixtypes.h"
|
||||
#include "mozpkix/pkixutil.h"
|
||||
#include "secerr.h"
|
||||
#include "ssl.h"
|
||||
|
||||
|
@ -146,7 +145,7 @@ void VerifyContentSignatureTask::CallCallback(nsresult rv) {
|
|||
bool IsNewLine(char16_t c) { return c == '\n' || c == '\r'; }
|
||||
|
||||
nsresult ReadChainIntoCertList(const nsACString& aCertChain,
|
||||
CERTCertList* aCertList) {
|
||||
nsTArray<nsTArray<uint8_t>>& aCertList) {
|
||||
bool inBlock = false;
|
||||
bool certFound = false;
|
||||
|
||||
|
@ -172,22 +171,8 @@ nsresult ReadChainIntoCertList(const nsACString& aCertChain,
|
|||
CSVerifier_LOG(("CSVerifier: decoding the signature failed"));
|
||||
return rv;
|
||||
}
|
||||
SECItem der = {
|
||||
siBuffer,
|
||||
BitwiseCast<unsigned char*, const char*>(derString.get()),
|
||||
derString.Length(),
|
||||
};
|
||||
UniqueCERTCertificate tmpCert(CERT_NewTempCertificate(
|
||||
CERT_GetDefaultCertDB(), &der, nullptr, false, true));
|
||||
if (!tmpCert) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
// if adding tmpCert succeeds, tmpCert will now be owned by aCertList
|
||||
SECStatus res = CERT_AddCertToListTail(aCertList, tmpCert.get());
|
||||
if (res != SECSuccess) {
|
||||
return MapSECStatus(res);
|
||||
}
|
||||
Unused << tmpCert.release();
|
||||
nsTArray<uint8_t> derBytes(derString.Data(), derString.Length());
|
||||
aCertList.AppendElement(std::move(derBytes));
|
||||
} else {
|
||||
blockData.Append(token);
|
||||
}
|
||||
|
@ -218,49 +203,44 @@ static nsresult VerifyContentSignatureInternal(
|
|||
Telemetry::LABELS_CONTENT_SIGNATURE_VERIFICATION_ERRORS& aErrorLabel,
|
||||
/* out */ nsACString& aCertFingerprint,
|
||||
/* out */ uint32_t& aErrorValue) {
|
||||
UniqueCERTCertList certCertList(CERT_NewCertList());
|
||||
if (!certCertList) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
nsresult rv = ReadChainIntoCertList(aCertChain, certCertList.get());
|
||||
nsTArray<nsTArray<uint8_t>> certList;
|
||||
nsresult rv = ReadChainIntoCertList(aCertChain, certList);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
CERTCertListNode* node = CERT_LIST_HEAD(certCertList.get());
|
||||
if (!node || CERT_LIST_END(node, certCertList.get()) || !node->cert) {
|
||||
if (certList.Length() < 1) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
SECItem* certSecItem = &node->cert->derCert;
|
||||
|
||||
Input certDER;
|
||||
// The 0th element should be the end-entity that issued the content
|
||||
// signature.
|
||||
nsTArray<uint8_t>& certBytes(certList.ElementAt(0));
|
||||
Input certInput;
|
||||
mozilla::pkix::Result result =
|
||||
certDER.Init(BitwiseCast<uint8_t*, unsigned char*>(certSecItem->data),
|
||||
certSecItem->len);
|
||||
certInput.Init(certBytes.Elements(), certBytes.Length());
|
||||
if (result != Success) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// Get EE certificate fingerprint for telemetry.
|
||||
unsigned char fingerprint[SHA256_LENGTH] = {0};
|
||||
SECStatus srv = PK11_HashBuf(SEC_OID_SHA256, fingerprint, certSecItem->data,
|
||||
AssertedCast<int32_t>(certSecItem->len));
|
||||
SECStatus srv =
|
||||
PK11_HashBuf(SEC_OID_SHA256, fingerprint, certInput.UnsafeGetData(),
|
||||
certInput.GetLength());
|
||||
if (srv != SECSuccess) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
SECItem fingerprintItem = {siBuffer, fingerprint, SHA256_LENGTH};
|
||||
UniquePORTString tmpFingerprintString(CERT_Hexify(&fingerprintItem, 0));
|
||||
UniquePORTString tmpFingerprintString(
|
||||
CERT_Hexify(&fingerprintItem, false /* don't use colon delimiters */));
|
||||
if (!tmpFingerprintString) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
aCertFingerprint.Assign(tmpFingerprintString.get());
|
||||
|
||||
// Check the signerCert chain is good
|
||||
CSTrustDomain trustDomain(certCertList);
|
||||
CSTrustDomain trustDomain(certList);
|
||||
result = BuildCertChain(
|
||||
trustDomain, certDER, Now(), EndEntityOrCA::MustBeEndEntity,
|
||||
trustDomain, certInput, Now(), EndEntityOrCA::MustBeEndEntity,
|
||||
KeyUsage::noParticularKeyUsageRequired, KeyPurposeId::id_kp_codeSigning,
|
||||
CertPolicyId::anyPolicy, nullptr /*stapledOCSPResponse*/);
|
||||
if (result != Success) {
|
||||
|
@ -300,7 +280,7 @@ static nsresult VerifyContentSignatureInternal(
|
|||
}
|
||||
|
||||
BRNameMatchingPolicy nameMatchingPolicy(BRNameMatchingPolicy::Mode::Enforce);
|
||||
result = CheckCertHostname(certDER, hostnameInput, nameMatchingPolicy);
|
||||
result = CheckCertHostname(certInput, hostnameInput, nameMatchingPolicy);
|
||||
if (result != Success) {
|
||||
// EE cert isnot valid for the given host name.
|
||||
aErrorLabel = Telemetry::LABELS_CONTENT_SIGNATURE_VERIFICATION_ERRORS::err7;
|
||||
|
@ -308,8 +288,28 @@ static nsresult VerifyContentSignatureInternal(
|
|||
return NS_ERROR_INVALID_SIGNATURE;
|
||||
}
|
||||
|
||||
mozilla::UniqueSECKEYPublicKey key(CERT_ExtractPublicKey(node->cert));
|
||||
// in case we were not able to extract a key
|
||||
pkix::BackCert backCert(certInput, EndEntityOrCA::MustBeEndEntity, nullptr);
|
||||
result = backCert.Init();
|
||||
// This should never fail, because we've already built a verified certificate
|
||||
// chain with this certificate.
|
||||
if (result != Success) {
|
||||
aErrorLabel = Telemetry::LABELS_CONTENT_SIGNATURE_VERIFICATION_ERRORS::err8;
|
||||
aErrorValue = 8;
|
||||
CSVerifier_LOG(("CSVerifier: couldn't decode certificate to get spki"));
|
||||
return NS_ERROR_INVALID_SIGNATURE;
|
||||
}
|
||||
Input spkiInput = backCert.GetSubjectPublicKeyInfo();
|
||||
SECItem spkiItem = {siBuffer, const_cast<uint8_t*>(spkiInput.UnsafeGetData()),
|
||||
spkiInput.GetLength()};
|
||||
UniqueCERTSubjectPublicKeyInfo spki(
|
||||
SECKEY_DecodeDERSubjectPublicKeyInfo(&spkiItem));
|
||||
if (!spki) {
|
||||
aErrorLabel = Telemetry::LABELS_CONTENT_SIGNATURE_VERIFICATION_ERRORS::err8;
|
||||
aErrorValue = 8;
|
||||
CSVerifier_LOG(("CSVerifier: couldn't decode spki"));
|
||||
return NS_ERROR_INVALID_SIGNATURE;
|
||||
}
|
||||
mozilla::UniqueSECKEYPublicKey key(SECKEY_ExtractPublicKey(spki.get()));
|
||||
if (!key) {
|
||||
aErrorLabel = Telemetry::LABELS_CONTENT_SIGNATURE_VERIFICATION_ERRORS::err8;
|
||||
aErrorValue = 8;
|
||||
|
|
|
@ -38,10 +38,10 @@ interface nsINSSComponent : nsISupports {
|
|||
[noscript] bool isCertTestBuiltInRoot(in CERTCertificatePtr cert);
|
||||
|
||||
/**
|
||||
* Used to determine if the given CERTCertificate is the content signing root
|
||||
* certificate.
|
||||
* Used to determine if the given certificate (represented as an array of
|
||||
* bytes) is the content signing root certificate.
|
||||
*/
|
||||
[noscript] bool isCertContentSigningRoot(in CERTCertificatePtr cert);
|
||||
[noscript] bool isCertContentSigningRoot(in Array<octet> cert);
|
||||
|
||||
/**
|
||||
* If enabled by the preference "security.enterprise_roots.enabled", returns
|
||||
|
|
|
@ -568,7 +568,8 @@ nsNSSCertificate::GetIssuerName(nsAString& _issuerName) {
|
|||
NS_IMETHODIMP
|
||||
nsNSSCertificate::GetSerialNumber(nsAString& _serialNumber) {
|
||||
_serialNumber.Truncate();
|
||||
UniquePORTString tmpstr(CERT_Hexify(&mCert->serialNumber, 1));
|
||||
UniquePORTString tmpstr(
|
||||
CERT_Hexify(&mCert->serialNumber, true /* use colon delimiters */));
|
||||
if (tmpstr) {
|
||||
_serialNumber = NS_ConvertASCIItoUTF16(tmpstr.get());
|
||||
return NS_OK;
|
||||
|
@ -586,8 +587,8 @@ nsresult nsNSSCertificate::GetCertificateHash(nsAString& aFingerprint,
|
|||
return rv;
|
||||
}
|
||||
|
||||
// CERT_Hexify's second argument is an int that is interpreted as a boolean
|
||||
UniquePORTString fpStr(CERT_Hexify(const_cast<SECItem*>(&digest.get()), 1));
|
||||
UniquePORTString fpStr(CERT_Hexify(const_cast<SECItem*>(&digest.get()),
|
||||
true /* use colon delimiters */));
|
||||
if (!fpStr) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
|
|
@ -2049,8 +2049,8 @@ nsresult nsNSSComponent::InitializeNSS() {
|
|||
mTestBuiltInRootHash);
|
||||
#endif
|
||||
mContentSigningRootHash.Truncate();
|
||||
Preferences::GetString("security.content.signature.root_hash",
|
||||
mContentSigningRootHash);
|
||||
Preferences::GetCString("security.content.signature.root_hash",
|
||||
mContentSigningRootHash);
|
||||
|
||||
mMitmCanaryIssuer.Truncate();
|
||||
Preferences::GetString("security.pki.mitm_canary_issuer",
|
||||
|
@ -2442,8 +2442,8 @@ nsNSSComponent::Observe(nsISupports* aSubject, const char* aTopic,
|
|||
} else if (prefName.EqualsLiteral("security.content.signature.root_hash")) {
|
||||
MutexAutoLock lock(mMutex);
|
||||
mContentSigningRootHash.Truncate();
|
||||
Preferences::GetString("security.content.signature.root_hash",
|
||||
mContentSigningRootHash);
|
||||
Preferences::GetCString("security.content.signature.root_hash",
|
||||
mContentSigningRootHash);
|
||||
} else if (prefName.Equals(kEnterpriseRootModePref) ||
|
||||
prefName.Equals(kFamilySafetyModePref)) {
|
||||
UnloadEnterpriseRoots();
|
||||
|
@ -2566,30 +2566,28 @@ nsNSSComponent::IsCertTestBuiltInRoot(CERTCertificate* cert, bool* result) {
|
|||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsNSSComponent::IsCertContentSigningRoot(CERTCertificate* cert, bool* result) {
|
||||
nsNSSComponent::IsCertContentSigningRoot(const nsTArray<uint8_t>& cert,
|
||||
bool* result) {
|
||||
NS_ENSURE_ARG_POINTER(result);
|
||||
*result = false;
|
||||
|
||||
RefPtr<nsNSSCertificate> nsc = nsNSSCertificate::Create(cert);
|
||||
if (!nsc) {
|
||||
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("creating nsNSSCertificate failed"));
|
||||
return NS_ERROR_FAILURE;
|
||||
if (cert.Length() > std::numeric_limits<uint32_t>::max()) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
nsAutoString certHash;
|
||||
nsresult rv = nsc->GetSha256Fingerprint(certHash);
|
||||
Digest digest;
|
||||
nsresult rv =
|
||||
digest.DigestBuf(SEC_OID_SHA256, cert.Elements(), cert.Length());
|
||||
if (NS_FAILED(rv)) {
|
||||
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("getting cert fingerprint failed"));
|
||||
return rv;
|
||||
}
|
||||
UniquePORTString fingerprintCString(CERT_Hexify(
|
||||
const_cast<SECItem*>(&digest.get()), true /* use colon delimiters */));
|
||||
if (!fingerprintCString) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
MutexAutoLock lock(mMutex);
|
||||
|
||||
if (mContentSigningRootHash.IsEmpty()) {
|
||||
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("mContentSigningRootHash is empty"));
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
*result = mContentSigningRootHash.Equals(certHash);
|
||||
*result = mContentSigningRootHash.Equals(fingerprintCString.get());
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -123,7 +123,7 @@ class nsNSSComponent final : public nsINSSComponent, public nsIObserver {
|
|||
#ifdef DEBUG
|
||||
nsString mTestBuiltInRootHash;
|
||||
#endif
|
||||
nsString mContentSigningRootHash;
|
||||
nsCString mContentSigningRootHash;
|
||||
RefPtr<mozilla::psm::SharedCertVerifier> mDefaultCertVerifier;
|
||||
nsString mMitmCanaryIssuer;
|
||||
bool mMitmDetecionEnabled;
|
||||
|
|
|
@ -377,7 +377,6 @@ add_task(async function run_test() {
|
|||
let badSections = [
|
||||
// data that looks like PEM but isn't
|
||||
"-----BEGIN CERTIFICATE-----\nBSsPRlYp5+gaFMRIczwUzaioRfteCjr94xyz0g==\n",
|
||||
"-----BEGIN CERTIFICATE-----\nBSsPRlYp5+gaFMRIczwUzaioRfteCjr94xyz0g==\n-----END CERTIFICATE-----",
|
||||
// data that will start to parse but won't base64decode
|
||||
"-----BEGIN CERTIFICATE-----\nnon-base64-stuff\n-----END CERTIFICATE-----",
|
||||
// data with garbage outside of PEM sections
|
||||
|
|
Загрузка…
Ссылка в новой задаче