Bug 1713602 - Use NSS only on the socket thread in NSSCertDBTrustDomain::IsChainValid r=keeler
Differential Revision: https://phabricator.services.mozilla.com/D116879
This commit is contained in:
@@ -131,20 +131,18 @@ CertVerifier::CertVerifier(OcspDownloadConfig odc, OcspStrictConfig osc,
|
||||
|
||||
CertVerifier::~CertVerifier() = default;
|
||||
|
||||
Result IsCertChainRootBuiltInRoot(const UniqueCERTCertList& chain,
|
||||
Result IsCertChainRootBuiltInRoot(const nsTArray<nsTArray<uint8_t>>& chain,
|
||||
bool& result) {
|
||||
if (!chain || CERT_LIST_EMPTY(chain)) {
|
||||
if (chain.IsEmpty()) {
|
||||
return Result::FATAL_ERROR_LIBRARY_FAILURE;
|
||||
}
|
||||
CERTCertListNode* rootNode = CERT_LIST_TAIL(chain);
|
||||
if (!rootNode) {
|
||||
return Result::FATAL_ERROR_LIBRARY_FAILURE;
|
||||
const nsTArray<uint8_t>& rootBytes = chain.LastElement();
|
||||
Input rootInput;
|
||||
Result rv = rootInput.Init(rootBytes.Elements(), rootBytes.Length());
|
||||
if (rv != Result::Success) {
|
||||
return rv;
|
||||
}
|
||||
CERTCertificate* root = rootNode->cert;
|
||||
if (!root) {
|
||||
return Result::FATAL_ERROR_LIBRARY_FAILURE;
|
||||
}
|
||||
return IsCertBuiltInRoot(root, result);
|
||||
return IsCertBuiltInRoot(rootInput, result);
|
||||
}
|
||||
|
||||
Result IsDelegatedCredentialAcceptable(const DelegatedCredentialInfo& dcInfo) {
|
||||
@@ -167,18 +165,26 @@ Result IsDelegatedCredentialAcceptable(const DelegatedCredentialInfo& dcInfo) {
|
||||
// has been added to the NSS trust store, because it has been approved
|
||||
// for inclusion according to the Mozilla CA policy, and might be accepted
|
||||
// by Mozilla applications as an issuer for certificates seen on the public web.
|
||||
Result IsCertBuiltInRoot(CERTCertificate* cert, bool& result) {
|
||||
Result IsCertBuiltInRoot(Input certInput, bool& result) {
|
||||
if (NS_FAILED(BlockUntilLoadableCertsLoaded())) {
|
||||
return Result::FATAL_ERROR_LIBRARY_FAILURE;
|
||||
}
|
||||
|
||||
CERTCertDBHandle* certDB(CERT_GetDefaultCertDB());
|
||||
SECItem certDER(UnsafeMapInputToSECItem(certInput));
|
||||
UniqueCERTCertificate cert(
|
||||
CERT_NewTempCertificate(certDB, &certDER, nullptr, false, true));
|
||||
if (!cert) {
|
||||
return Result::FATAL_ERROR_LIBRARY_FAILURE;
|
||||
}
|
||||
|
||||
result = false;
|
||||
#ifdef DEBUG
|
||||
nsCOMPtr<nsINSSComponent> component(do_GetService(PSM_COMPONENT_CONTRACTID));
|
||||
if (!component) {
|
||||
return Result::FATAL_ERROR_LIBRARY_FAILURE;
|
||||
}
|
||||
nsresult rv = component->IsCertTestBuiltInRoot(cert, &result);
|
||||
nsresult rv = component->IsCertTestBuiltInRoot(cert.get(), &result);
|
||||
if (NS_FAILED(rv)) {
|
||||
return Result::FATAL_ERROR_LIBRARY_FAILURE;
|
||||
}
|
||||
@@ -205,7 +211,8 @@ Result IsCertBuiltInRoot(CERTCertificate* cert, bool& result) {
|
||||
// If the certificate has attribute CKA_NSS_MOZILLA_CA_POLICY set to true,
|
||||
// then we treat it as a "builtin root".
|
||||
if (PK11_IsPresent(slot) && PK11_HasRootCerts(slot)) {
|
||||
CK_OBJECT_HANDLE handle = PK11_FindCertInSlot(slot, cert, nullptr);
|
||||
CK_OBJECT_HANDLE handle =
|
||||
PK11_FindCertInSlot(slot, cert.get(), nullptr);
|
||||
if (handle != CK_INVALID_HANDLE &&
|
||||
PK11_HasAttributeSet(slot, handle, CKA_NSS_MOZILLA_CA_POLICY,
|
||||
false)) {
|
||||
@@ -281,8 +288,8 @@ void CertVerifier::LoadKnownCTLogs() {
|
||||
}
|
||||
|
||||
Result CertVerifier::VerifyCertificateTransparencyPolicy(
|
||||
NSSCertDBTrustDomain& trustDomain, const UniqueCERTCertList& builtChain,
|
||||
Input sctsFromTLS, Time time,
|
||||
NSSCertDBTrustDomain& trustDomain,
|
||||
const nsTArray<nsTArray<uint8_t>>& builtChain, Input sctsFromTLS, Time time,
|
||||
/*optional out*/ CertificateTransparencyInfo* ctInfo) {
|
||||
if (ctInfo) {
|
||||
ctInfo->Reset();
|
||||
@@ -294,7 +301,7 @@ Result CertVerifier::VerifyCertificateTransparencyPolicy(
|
||||
ctInfo->enabled = true;
|
||||
}
|
||||
|
||||
if (!builtChain || CERT_LIST_EMPTY(builtChain)) {
|
||||
if (builtChain.IsEmpty()) {
|
||||
return Result::FATAL_ERROR_INVALID_ARGS;
|
||||
}
|
||||
|
||||
@@ -316,12 +323,7 @@ Result CertVerifier::VerifyCertificateTransparencyPolicy(
|
||||
static_cast<size_t>(sctsFromTLS.GetLength())));
|
||||
}
|
||||
|
||||
CERTCertListNode* endEntityNode = CERT_LIST_HEAD(builtChain);
|
||||
if (!endEntityNode || CERT_LIST_END(endEntityNode, builtChain)) {
|
||||
return Result::FATAL_ERROR_INVALID_ARGS;
|
||||
}
|
||||
CERTCertListNode* issuerNode = CERT_LIST_NEXT(endEntityNode);
|
||||
if (!issuerNode || CERT_LIST_END(issuerNode, builtChain)) {
|
||||
if (builtChain.Length() == 1) {
|
||||
// Issuer certificate is required for SCT verification.
|
||||
// If we've arrived here, we probably have a "trust chain" with only one
|
||||
// certificate (i.e. a self-signed end-entity that has been set as a trust
|
||||
@@ -338,34 +340,41 @@ Result CertVerifier::VerifyCertificateTransparencyPolicy(
|
||||
return Success;
|
||||
}
|
||||
|
||||
CERTCertificate* endEntity = endEntityNode->cert;
|
||||
CERTCertificate* issuer = issuerNode->cert;
|
||||
if (!endEntity || !issuer) {
|
||||
return Result::FATAL_ERROR_INVALID_ARGS;
|
||||
const nsTArray<uint8_t>& endEntityBytes = builtChain.ElementAt(0);
|
||||
Input endEntityInput;
|
||||
Result rv =
|
||||
endEntityInput.Init(endEntityBytes.Elements(), endEntityBytes.Length());
|
||||
if (rv != Success) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (endEntity->subjectName) {
|
||||
const nsTArray<uint8_t>& issuerBytes = builtChain.ElementAt(1);
|
||||
Input issuerInput;
|
||||
rv = issuerInput.Init(issuerBytes.Elements(), issuerBytes.Length());
|
||||
if (rv != Success) {
|
||||
return rv;
|
||||
}
|
||||
BackCert issuerBackCert(issuerInput, EndEntityOrCA::MustBeCA, nullptr);
|
||||
rv = issuerBackCert.Init();
|
||||
if (rv != Success) {
|
||||
return rv;
|
||||
}
|
||||
Input issuerPublicKeyInput = issuerBackCert.GetSubjectPublicKeyInfo();
|
||||
|
||||
SECItem endEntityDERItem = UnsafeMapInputToSECItem(endEntityInput);
|
||||
UniqueCERTCertificate endEntityCert(CERT_NewTempCertificate(
|
||||
CERT_GetDefaultCertDB(), &endEntityDERItem, nullptr, false, true));
|
||||
if (!endEntityCert) {
|
||||
return Result::FATAL_ERROR_LIBRARY_FAILURE;
|
||||
}
|
||||
if (endEntityCert->subjectName) {
|
||||
MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
|
||||
("Verifying CT Policy compliance of subject %s\n",
|
||||
endEntity->subjectName));
|
||||
}
|
||||
|
||||
Input endEntityDER;
|
||||
Result rv =
|
||||
endEntityDER.Init(endEntity->derCert.data, endEntity->derCert.len);
|
||||
if (rv != Success) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
Input issuerPublicKeyDER;
|
||||
rv = issuerPublicKeyDER.Init(issuer->derPublicKey.data,
|
||||
issuer->derPublicKey.len);
|
||||
if (rv != Success) {
|
||||
return rv;
|
||||
endEntityCert->subjectName));
|
||||
}
|
||||
|
||||
CTVerifyResult result;
|
||||
rv = mCTVerifier->Verify(endEntityDER, issuerPublicKeyDER, embeddedSCTs,
|
||||
rv = mCTVerifier->Verify(endEntityInput, issuerPublicKeyInput, embeddedSCTs,
|
||||
sctsFromOCSP, sctsFromTLS, time, result);
|
||||
if (rv != Success) {
|
||||
MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
|
||||
@@ -414,7 +423,8 @@ Result CertVerifier::VerifyCertificateTransparencyPolicy(
|
||||
|
||||
PRTime notBefore;
|
||||
PRTime notAfter;
|
||||
if (CERT_GetCertTimes(endEntity, ¬Before, ¬After) != SECSuccess) {
|
||||
if (CERT_GetCertTimes(endEntityCert.get(), ¬Before, ¬After) !=
|
||||
SECSuccess) {
|
||||
return Result::FATAL_ERROR_LIBRARY_FAILURE;
|
||||
}
|
||||
size_t lifetimeInMonths;
|
||||
@@ -427,7 +437,7 @@ Result CertVerifier::VerifyCertificateTransparencyPolicy(
|
||||
GetCTLogOperatorsFromVerifiedSCTList(result.verifiedScts, allOperators);
|
||||
|
||||
CTLogOperatorList dependentOperators;
|
||||
rv = mCTDiversityPolicy->GetDependentOperators(builtChain.get(), allOperators,
|
||||
rv = mCTDiversityPolicy->GetDependentOperators(builtChain, allOperators,
|
||||
dependentOperators);
|
||||
if (rv != Success) {
|
||||
return rv;
|
||||
@@ -466,7 +476,7 @@ bool CertVerifier::SHA1ModeMoreRestrictiveThanGivenMode(SHA1Mode mode) {
|
||||
Result CertVerifier::VerifyCert(
|
||||
CERTCertificate* cert, SECCertificateUsage usage, Time time, void* pinArg,
|
||||
const char* hostname,
|
||||
/*out*/ UniqueCERTCertList& builtChain,
|
||||
/*out*/ nsTArray<nsTArray<uint8_t>>& builtChain,
|
||||
/*optional*/ const Flags flags,
|
||||
/*optional*/ const Maybe<nsTArray<nsTArray<uint8_t>>>& extraCertificates,
|
||||
/*optional*/ const Maybe<nsTArray<uint8_t>>& stapledOCSPResponseArg,
|
||||
@@ -888,7 +898,7 @@ static bool CertIsSelfSigned(const UniqueCERTCertificate& cert, void* pinarg) {
|
||||
Result CertVerifier::VerifySSLServerCert(
|
||||
const UniqueCERTCertificate& peerCert, Time time,
|
||||
/*optional*/ void* pinarg, const nsACString& hostname,
|
||||
/*out*/ UniqueCERTCertList& builtChain,
|
||||
/*out*/ nsTArray<nsTArray<uint8_t>>& builtChain,
|
||||
/*optional*/ Flags flags,
|
||||
/*optional*/ const Maybe<nsTArray<nsTArray<uint8_t>>>& extraCertificates,
|
||||
/*optional*/ const Maybe<nsTArray<uint8_t>>& stapledOCSPResponse,
|
||||
|
||||
@@ -159,7 +159,7 @@ class CertVerifier {
|
||||
mozilla::pkix::Result VerifyCert(
|
||||
CERTCertificate* cert, SECCertificateUsage usage,
|
||||
mozilla::pkix::Time time, void* pinArg, const char* hostname,
|
||||
/*out*/ UniqueCERTCertList& builtChain, Flags flags = 0,
|
||||
/*out*/ nsTArray<nsTArray<uint8_t>>& builtChain, Flags flags = 0,
|
||||
/*optional in*/
|
||||
const Maybe<nsTArray<nsTArray<uint8_t>>>& extraCertificates = Nothing(),
|
||||
/*optional in*/ const Maybe<nsTArray<uint8_t>>& stapledOCSPResponseArg =
|
||||
@@ -177,7 +177,7 @@ class CertVerifier {
|
||||
mozilla::pkix::Result VerifySSLServerCert(
|
||||
const UniqueCERTCertificate& peerCert, mozilla::pkix::Time time,
|
||||
void* pinarg, const nsACString& hostname,
|
||||
/*out*/ UniqueCERTCertList& builtChain,
|
||||
/*out*/ nsTArray<nsTArray<uint8_t>>& builtChain,
|
||||
/*optional*/ Flags flags = 0,
|
||||
/*optional*/ const Maybe<nsTArray<nsTArray<uint8_t>>>& extraCertificates =
|
||||
Nothing(),
|
||||
@@ -256,7 +256,8 @@ class CertVerifier {
|
||||
|
||||
void LoadKnownCTLogs();
|
||||
mozilla::pkix::Result VerifyCertificateTransparencyPolicy(
|
||||
NSSCertDBTrustDomain& trustDomain, const UniqueCERTCertList& builtChain,
|
||||
NSSCertDBTrustDomain& trustDomain,
|
||||
const nsTArray<nsTArray<uint8_t>>& builtChain,
|
||||
mozilla::pkix::Input sctsFromTLS, mozilla::pkix::Time time,
|
||||
/*optional out*/ CertificateTransparencyInfo* ctInfo);
|
||||
|
||||
@@ -267,7 +268,7 @@ class CertVerifier {
|
||||
bool SHA1ModeMoreRestrictiveThanGivenMode(SHA1Mode mode);
|
||||
};
|
||||
|
||||
mozilla::pkix::Result IsCertBuiltInRoot(CERTCertificate* cert, bool& result);
|
||||
mozilla::pkix::Result IsCertBuiltInRoot(pkix::Input certInput, bool& result);
|
||||
mozilla::pkix::Result CertListContainsExpectedKeys(const CERTCertList* certList,
|
||||
const char* hostname,
|
||||
mozilla::pkix::Time time);
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "mozilla/Casting.h"
|
||||
#include "mozilla/PodOperations.h"
|
||||
#include "mozilla/Services.h"
|
||||
#include "mozilla/SyncRunnable.h"
|
||||
#include "mozilla/TimeStamp.h"
|
||||
#include "mozilla/Unused.h"
|
||||
#include "mozpkix/Result.h"
|
||||
@@ -30,6 +31,7 @@
|
||||
#include "mozpkix/pkixutil.h"
|
||||
#include "nsCRTGlue.h"
|
||||
#include "nsIObserverService.h"
|
||||
#include "nsNetCID.h"
|
||||
#include "nsNSSCertHelper.h"
|
||||
#include "nsNSSCertificate.h"
|
||||
#include "nsNSSCertificateDB.h"
|
||||
@@ -73,7 +75,7 @@ NSSCertDBTrustDomain::NSSCertDBTrustDomain(
|
||||
const Vector<Input>& thirdPartyRootInputs,
|
||||
const Vector<Input>& thirdPartyIntermediateInputs,
|
||||
const Maybe<nsTArray<nsTArray<uint8_t>>>& extraCertificates,
|
||||
/*out*/ UniqueCERTCertList& builtChain,
|
||||
/*out*/ nsTArray<nsTArray<uint8_t>>& builtChain,
|
||||
/*optional*/ PinningTelemetryInfo* pinningTelemetryInfo,
|
||||
/*optional*/ const char* hostname)
|
||||
: mCertDBTrustType(certDBTrustType),
|
||||
@@ -1091,9 +1093,13 @@ SECStatus GetCertNotBeforeValue(const CERTCertificate* cert,
|
||||
return DER_DecodeTimeChoice(&distrustTime, &cert->validity.notBefore);
|
||||
}
|
||||
|
||||
nsresult isDistrustedCertificateChain(const UniqueCERTCertList& certList,
|
||||
const SECTrustType certDBTrustType,
|
||||
bool& isDistrusted) {
|
||||
nsresult isDistrustedCertificateChain(
|
||||
const nsTArray<nsTArray<uint8_t>>& certArray,
|
||||
const SECTrustType certDBTrustType, bool& isDistrusted) {
|
||||
if (certArray.Length() == 0) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// Set the default result to be distrusted.
|
||||
isDistrusted = true;
|
||||
|
||||
@@ -1103,14 +1109,38 @@ nsresult isDistrustedCertificateChain(const UniqueCERTCertList& certList,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
SECStatus runnableRV = SECFailure;
|
||||
|
||||
RefPtr<Runnable> isDistrustedChainTask =
|
||||
NS_NewRunnableFunction("isDistrustedCertificateChain", [&]() {
|
||||
// Allocate objects and retreive the root and end-entity certificates.
|
||||
const CERTCertificate* certRoot = CERT_LIST_TAIL(certList)->cert;
|
||||
const CERTCertificate* certLeaf = CERT_LIST_HEAD(certList)->cert;
|
||||
CERTCertDBHandle* certDB(CERT_GetDefaultCertDB());
|
||||
const nsTArray<uint8_t>& certRootDER = certArray.LastElement();
|
||||
SECItem certRootDERItem = {
|
||||
siBuffer, const_cast<unsigned char*>(certRootDER.Elements()),
|
||||
AssertedCast<unsigned int>(certRootDER.Length())};
|
||||
UniqueCERTCertificate certRoot(CERT_NewTempCertificate(
|
||||
certDB, &certRootDERItem, nullptr, false, true));
|
||||
if (!certRoot) {
|
||||
runnableRV = SECFailure;
|
||||
return;
|
||||
}
|
||||
const nsTArray<uint8_t>& certLeafDER = certArray.ElementAt(0);
|
||||
SECItem certLeafDERItem = {
|
||||
siBuffer, const_cast<unsigned char*>(certLeafDER.Elements()),
|
||||
AssertedCast<unsigned int>(certLeafDER.Length())};
|
||||
UniqueCERTCertificate certLeaf(CERT_NewTempCertificate(
|
||||
certDB, &certLeafDERItem, nullptr, false, true));
|
||||
if (!certLeaf) {
|
||||
runnableRV = SECFailure;
|
||||
return;
|
||||
}
|
||||
|
||||
// Set isDistrusted to false if there is no distrust for the root.
|
||||
if (!certRoot->distrust) {
|
||||
isDistrusted = false;
|
||||
return NS_OK;
|
||||
runnableRV = SECSuccess;
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a pointer to refer to the selected distrust struct.
|
||||
@@ -1127,14 +1157,15 @@ nsresult isDistrustedCertificateChain(const UniqueCERTCertList& certList,
|
||||
PRTime certRootDistrustAfter;
|
||||
PRTime certLeafNotBefore;
|
||||
|
||||
SECStatus rv = GetCertDistrustAfterValue(distrustPtr, certRootDistrustAfter);
|
||||
if (rv != SECSuccess) {
|
||||
return NS_ERROR_FAILURE;
|
||||
runnableRV =
|
||||
GetCertDistrustAfterValue(distrustPtr, certRootDistrustAfter);
|
||||
if (runnableRV != SECSuccess) {
|
||||
return;
|
||||
}
|
||||
|
||||
rv = GetCertNotBeforeValue(certLeaf, certLeafNotBefore);
|
||||
if (rv != SECSuccess) {
|
||||
return NS_ERROR_FAILURE;
|
||||
runnableRV = GetCertNotBeforeValue(certLeaf.get(), certLeafNotBefore);
|
||||
if (runnableRV != SECSuccess) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Compare the validity of the end-entity certificate with
|
||||
@@ -1143,58 +1174,56 @@ nsresult isDistrustedCertificateChain(const UniqueCERTCertList& certList,
|
||||
isDistrusted = false;
|
||||
}
|
||||
|
||||
runnableRV = SECSuccess;
|
||||
});
|
||||
nsCOMPtr<nsIEventTarget> socketThread(
|
||||
do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID));
|
||||
if (!socketThread) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
nsresult rv =
|
||||
SyncRunnable::DispatchToThread(socketThread, isDistrustedChainTask);
|
||||
if (NS_FAILED(rv) || runnableRV != SECSuccess) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
Result NSSCertDBTrustDomain::IsChainValid(const DERArray& certArray, Time time,
|
||||
Result NSSCertDBTrustDomain::IsChainValid(const DERArray& reversedDERArray,
|
||||
Time time,
|
||||
const CertPolicyId& requiredPolicy) {
|
||||
MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
|
||||
("NSSCertDBTrustDomain: IsChainValid"));
|
||||
|
||||
UniqueCERTCertList certList;
|
||||
SECStatus srv =
|
||||
ConstructCERTCertListFromReversedDERArray(certArray, certList);
|
||||
if (srv != SECSuccess) {
|
||||
return MapPRErrorCodeToResult(PR_GetError());
|
||||
}
|
||||
if (CERT_LIST_EMPTY(certList)) {
|
||||
size_t numCerts = reversedDERArray.GetLength();
|
||||
if (numCerts < 1) {
|
||||
return Result::FATAL_ERROR_LIBRARY_FAILURE;
|
||||
}
|
||||
nsTArray<nsTArray<uint8_t>> certArray;
|
||||
for (size_t i = numCerts; i > 0; --i) {
|
||||
const Input* derInput = reversedDERArray.GetDER(i - 1);
|
||||
certArray.EmplaceBack(derInput->UnsafeGetData(), derInput->GetLength());
|
||||
}
|
||||
|
||||
// Modernization in-progress: Keep certList as a CERTCertList for storage into
|
||||
// the mBuiltChain variable at the end.
|
||||
nsTArray<RefPtr<nsIX509Cert>> nssCertList;
|
||||
nsresult nsrv = nsNSSCertificateDB::ConstructCertArrayFromUniqueCertList(
|
||||
certList, nssCertList);
|
||||
|
||||
if (NS_FAILED(nsrv)) {
|
||||
return Result::FATAL_ERROR_LIBRARY_FAILURE;
|
||||
}
|
||||
nsCOMPtr<nsIX509Cert> rootCert;
|
||||
nsrv = nsNSSCertificate::GetRootCertificate(nssCertList, rootCert);
|
||||
if (NS_FAILED(nsrv)) {
|
||||
return Result::FATAL_ERROR_LIBRARY_FAILURE;
|
||||
}
|
||||
UniqueCERTCertificate root(rootCert->GetCert());
|
||||
if (!root) {
|
||||
return Result::FATAL_ERROR_LIBRARY_FAILURE;
|
||||
}
|
||||
bool isBuiltInRoot = false;
|
||||
nsrv = rootCert->GetIsBuiltInRoot(&isBuiltInRoot);
|
||||
if (NS_FAILED(nsrv)) {
|
||||
return Result::FATAL_ERROR_LIBRARY_FAILURE;
|
||||
|
||||
const nsTArray<uint8_t>& rootBytes = certArray.LastElement();
|
||||
Input rootInput;
|
||||
Result rv = rootInput.Init(rootBytes.Elements(), rootBytes.Length());
|
||||
if (rv != Success) {
|
||||
return rv;
|
||||
}
|
||||
rv = IsCertBuiltInRoot(rootInput, isBuiltInRoot);
|
||||
if (rv != Result::Success) {
|
||||
return rv;
|
||||
}
|
||||
nsresult nsrv;
|
||||
// If mHostname isn't set, we're not verifying in the context of a TLS
|
||||
// handshake, so don't verify key pinning in those cases.
|
||||
if (mHostname) {
|
||||
nsTArray<Span<const uint8_t>> derCertSpanList;
|
||||
size_t numCerts = certArray.GetLength();
|
||||
for (size_t i = numCerts; i > 0; --i) {
|
||||
const Input* der = certArray.GetDER(i - 1);
|
||||
if (!der) {
|
||||
return Result::FATAL_ERROR_LIBRARY_FAILURE;
|
||||
}
|
||||
derCertSpanList.EmplaceBack(der->UnsafeGetData(), der->GetLength());
|
||||
for (const auto& certDER : certArray) {
|
||||
derCertSpanList.EmplaceBack(certDER.Elements(), certDER.Length());
|
||||
}
|
||||
|
||||
bool chainHasValidPins;
|
||||
@@ -1214,7 +1243,7 @@ Result NSSCertDBTrustDomain::IsChainValid(const DERArray& certArray, Time time,
|
||||
if (isBuiltInRoot) {
|
||||
bool isDistrusted;
|
||||
nsrv =
|
||||
isDistrustedCertificateChain(certList, mCertDBTrustType, isDistrusted);
|
||||
isDistrustedCertificateChain(certArray, mCertDBTrustType, isDistrusted);
|
||||
if (NS_FAILED(nsrv)) {
|
||||
return Result::FATAL_ERROR_LIBRARY_FAILURE;
|
||||
}
|
||||
@@ -1230,16 +1259,24 @@ Result NSSCertDBTrustDomain::IsChainValid(const DERArray& certArray, Time time,
|
||||
// This algorithm only applies if we are verifying in the context of a TLS
|
||||
// handshake. To determine this, we check mHostname: If it isn't set, this is
|
||||
// not TLS, so don't run the algorithm.
|
||||
nsTArray<uint8_t> rootCertDER(root.get()->derCert.data,
|
||||
root.get()->derCert.len);
|
||||
const nsTArray<uint8_t>& rootCertDER = certArray.LastElement();
|
||||
if (mHostname && CertDNIsInList(rootCertDER, RootSymantecDNs)) {
|
||||
nsTArray<nsTArray<uint8_t>> intCerts;
|
||||
|
||||
nsrv = nsNSSCertificate::GetIntermediatesAsDER(nssCertList, intCerts);
|
||||
if (NS_FAILED(nsrv)) {
|
||||
if (numCerts <= 1) {
|
||||
// This chain is supposed to be complete, so this is an error.
|
||||
return Result::ERROR_ADDITIONAL_POLICY_CONSTRAINT_FAILED;
|
||||
}
|
||||
nsTArray<Input> intCerts;
|
||||
|
||||
for (size_t i = 1; i < certArray.Length() - 1; ++i) {
|
||||
const nsTArray<uint8_t>& certBytes = certArray.ElementAt(i);
|
||||
Input certInput;
|
||||
rv = certInput.Init(certBytes.Elements(), certBytes.Length());
|
||||
if (rv != Success) {
|
||||
return Result::FATAL_ERROR_LIBRARY_FAILURE;
|
||||
}
|
||||
|
||||
intCerts.EmplaceBack(certInput);
|
||||
}
|
||||
|
||||
bool isDistrusted = false;
|
||||
nsrv = CheckForSymantecDistrust(intCerts, RootAppleAndGoogleSPKIs,
|
||||
@@ -1253,7 +1290,7 @@ Result NSSCertDBTrustDomain::IsChainValid(const DERArray& certArray, Time time,
|
||||
}
|
||||
}
|
||||
|
||||
mBuiltChain = std::move(certList);
|
||||
mBuiltChain = std::move(certArray);
|
||||
|
||||
return Success;
|
||||
}
|
||||
@@ -1699,7 +1736,6 @@ void SaveIntermediateCerts(const nsTArray<nsTArray<uint8_t>>& certList) {
|
||||
if (index == 1 || index == certList.Length()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
SECItem certDERItem = {siBuffer,
|
||||
const_cast<unsigned char*>(certDER.Elements()),
|
||||
AssertedCast<unsigned int>(certDER.Length())};
|
||||
|
||||
@@ -136,7 +136,7 @@ class NSSCertDBTrustDomain : public mozilla::pkix::TrustDomain {
|
||||
const Vector<mozilla::pkix::Input>& thirdPartyRootInputs,
|
||||
const Vector<mozilla::pkix::Input>& thirdPartyIntermediateInputs,
|
||||
const Maybe<nsTArray<nsTArray<uint8_t>>>& extraCertificates,
|
||||
/*out*/ UniqueCERTCertList& builtChain,
|
||||
/*out*/ nsTArray<nsTArray<uint8_t>>& builtChain,
|
||||
/*optional*/ PinningTelemetryInfo* pinningTelemetryInfo = nullptr,
|
||||
/*optional*/ const char* hostname = nullptr);
|
||||
|
||||
@@ -258,7 +258,7 @@ class NSSCertDBTrustDomain : public mozilla::pkix::TrustDomain {
|
||||
const Vector<mozilla::pkix::Input>&
|
||||
mThirdPartyIntermediateInputs; // non-owning
|
||||
const Maybe<nsTArray<nsTArray<uint8_t>>>& mExtraCertificates; // non-owning
|
||||
UniqueCERTCertList& mBuiltChain; // non-owning
|
||||
nsTArray<nsTArray<uint8_t>>& mBuiltChain; // non-owning
|
||||
PinningTelemetryInfo* mPinningTelemetryInfo;
|
||||
const char* mHostname; // non-owning - only used for pinning checks
|
||||
nsCOMPtr<nsICertStorage> mCertStorage;
|
||||
|
||||
@@ -55,19 +55,13 @@ static bool CertDNIsInList(const nsTArray<uint8_t>& aCert,
|
||||
}
|
||||
|
||||
template <size_t T>
|
||||
static bool CertSPKIIsInList(const nsTArray<uint8_t>& aCert,
|
||||
static bool CertSPKIIsInList(Input aCertInput,
|
||||
const DataAndLength (&aSpkiList)[T]) {
|
||||
Input certInput;
|
||||
mozilla::pkix::Result rv = certInput.Init(aCert.Elements(), aCert.Length());
|
||||
if (rv != Success) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// we don't use the certificate for path building, so this parameter doesn't
|
||||
// matter
|
||||
EndEntityOrCA notUsedForPaths = EndEntityOrCA::MustBeEndEntity;
|
||||
BackCert cert(certInput, notUsedForPaths, nullptr);
|
||||
rv = cert.Init();
|
||||
BackCert cert(aCertInput, notUsedForPaths, nullptr);
|
||||
mozilla::pkix::Result rv = cert.Init();
|
||||
if (rv != Success) {
|
||||
return false;
|
||||
}
|
||||
@@ -135,8 +129,7 @@ static bool CertMatchesStaticData(const nsTArray<uint8_t>& aCert,
|
||||
// "not distrusted." Otherwise, due to the precondition holding, the chain is
|
||||
// "distrusted."
|
||||
template <size_t T>
|
||||
static nsresult CheckForSymantecDistrust(
|
||||
const nsTArray<nsTArray<uint8_t>>& intCerts,
|
||||
static nsresult CheckForSymantecDistrust(const nsTArray<Input>& intCerts,
|
||||
const DataAndLength (&allowlist)[T],
|
||||
/* out */ bool& isDistrusted) {
|
||||
// PRECONDITION: The rootCert is already verified as being one of the
|
||||
|
||||
@@ -215,12 +215,18 @@ TEST_F(psm_TrustOverrideTest, CheckCertDNIsInList) {
|
||||
}
|
||||
|
||||
TEST_F(psm_TrustOverrideTest, CheckCertSPKIIsInList) {
|
||||
nsTArray<uint8_t> caArray(kOverrideCaDer, sizeof(kOverrideCaDer));
|
||||
nsTArray<uint8_t> intermediateArray(kOverrideCaIntermediateDer,
|
||||
sizeof(kOverrideCaIntermediateDer));
|
||||
mozilla::pkix::Input caInput;
|
||||
mozilla::pkix::Result rv =
|
||||
caInput.Init(kOverrideCaDer, sizeof(kOverrideCaDer));
|
||||
ASSERT_TRUE(rv == Success);
|
||||
|
||||
EXPECT_TRUE(CertSPKIIsInList(caArray, OverrideCaSPKIs))
|
||||
mozilla::pkix::Input intermediateInput;
|
||||
rv = intermediateInput.Init(kOverrideCaIntermediateDer,
|
||||
sizeof(kOverrideCaIntermediateDer));
|
||||
ASSERT_TRUE(rv == Success);
|
||||
|
||||
EXPECT_TRUE(CertSPKIIsInList(caInput, OverrideCaSPKIs))
|
||||
<< "CA should be in the SPKI list";
|
||||
EXPECT_FALSE(CertSPKIIsInList(intermediateArray, OverrideCaSPKIs))
|
||||
EXPECT_FALSE(CertSPKIIsInList(intermediateInput, OverrideCaSPKIs))
|
||||
<< "Int should not be in the SPKI list";
|
||||
}
|
||||
|
||||
@@ -30,8 +30,8 @@ void GetCTLogOperatorsFromVerifiedSCTList(const VerifiedSCTList& list,
|
||||
}
|
||||
|
||||
Result CTDiversityPolicy::GetDependentOperators(
|
||||
const CERTCertList* builtChain, const CTLogOperatorList& operators,
|
||||
CTLogOperatorList& dependentOperators) {
|
||||
const nsTArray<nsTArray<uint8_t>>& builtChain,
|
||||
const CTLogOperatorList& operators, CTLogOperatorList& dependentOperators) {
|
||||
return Success;
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "CTLog.h"
|
||||
#include "CTVerifyResult.h"
|
||||
#include "certt.h"
|
||||
#include "nsTArray.h"
|
||||
#include "mozpkix/Result.h"
|
||||
|
||||
namespace mozilla {
|
||||
@@ -30,7 +31,8 @@ class CTDiversityPolicy {
|
||||
// issuing the certificate (as defined by the CT Policy).
|
||||
//
|
||||
// NOTE: TBD, PENDING FINALIZATION OF MOZILLA CT POLICY.
|
||||
pkix::Result GetDependentOperators(const CERTCertList* builtChain,
|
||||
pkix::Result GetDependentOperators(
|
||||
const nsTArray<nsTArray<uint8_t>>& builtChain,
|
||||
const CTLogOperatorList& operators,
|
||||
CTLogOperatorList& dependentOperators);
|
||||
};
|
||||
|
||||
@@ -543,7 +543,12 @@ void GatherEKUTelemetry(const UniqueCERTCertList& certList) {
|
||||
return;
|
||||
}
|
||||
bool isBuiltIn = false;
|
||||
Result rv = IsCertBuiltInRoot(rootCert, isBuiltIn);
|
||||
Input rootInput;
|
||||
Result rv = rootInput.Init(rootCert->derCert.data, rootCert->derCert.len);
|
||||
if (rv != Result::Success) {
|
||||
return;
|
||||
}
|
||||
rv = IsCertBuiltInRoot(rootInput, isBuiltIn);
|
||||
if (rv != Success || !isBuiltIn) {
|
||||
return;
|
||||
}
|
||||
@@ -759,8 +764,28 @@ static void CollectCertTelemetry(
|
||||
CertVerifier::OCSPStaplingStatus aOcspStaplingStatus,
|
||||
KeySizeStatus aKeySizeStatus, SHA1ModeResult aSha1ModeResult,
|
||||
const PinningTelemetryInfo& aPinningTelemetryInfo,
|
||||
const UniqueCERTCertList& aBuiltCertChain,
|
||||
const nsTArray<nsTArray<uint8_t>>& aBuiltCertChain,
|
||||
const CertificateTransparencyInfo& aCertificateTransparencyInfo) {
|
||||
UniqueCERTCertList builtCertChainList(CERT_NewCertList());
|
||||
if (!builtCertChainList) {
|
||||
return;
|
||||
}
|
||||
CERTCertDBHandle* certDB(CERT_GetDefaultCertDB());
|
||||
for (const auto& certBytes : aBuiltCertChain) {
|
||||
SECItem certDERItem = {siBuffer, const_cast<uint8_t*>(certBytes.Elements()),
|
||||
AssertedCast<unsigned int>(certBytes.Length())};
|
||||
UniqueCERTCertificate cert(
|
||||
CERT_NewTempCertificate(certDB, &certDERItem, nullptr, false, true));
|
||||
if (!cert) {
|
||||
return;
|
||||
}
|
||||
if (CERT_AddCertToListTail(builtCertChainList.get(), cert.get()) !=
|
||||
SECSuccess) {
|
||||
return;
|
||||
}
|
||||
Unused << cert.release(); // cert is now owned by certList.
|
||||
}
|
||||
|
||||
uint32_t evStatus = (aCertVerificationResult != Success) ? 0 // 0 = Failure
|
||||
: (aEVStatus != EVStatus::EV) ? 1 // 1 = DV
|
||||
: 2; // 2 = EV
|
||||
@@ -793,8 +818,8 @@ static void CollectCertTelemetry(
|
||||
}
|
||||
|
||||
if (aCertVerificationResult == Success) {
|
||||
GatherSuccessfulValidationTelemetry(aBuiltCertChain);
|
||||
GatherCertificateTransparencyTelemetry(aBuiltCertChain,
|
||||
GatherSuccessfulValidationTelemetry(builtCertChainList);
|
||||
GatherCertificateTransparencyTelemetry(builtCertChainList,
|
||||
aEVStatus == EVStatus::EV,
|
||||
aCertificateTransparencyInfo);
|
||||
}
|
||||
@@ -839,7 +864,7 @@ Result AuthCertificate(
|
||||
const Maybe<nsTArray<uint8_t>>& sctsFromTLSExtension,
|
||||
const Maybe<DelegatedCredentialInfo>& dcInfo, uint32_t providerFlags,
|
||||
Time time, uint32_t certVerifierFlags,
|
||||
/*out*/ UniqueCERTCertList& builtCertChain,
|
||||
/*out*/ nsTArray<nsTArray<uint8_t>>& builtCertChain,
|
||||
/*out*/ EVStatus& evStatus,
|
||||
/*out*/ CertificateTransparencyInfo& certificateTransparencyInfo,
|
||||
/*out*/ bool& aIsCertChainRootBuiltInRoot) {
|
||||
@@ -991,6 +1016,18 @@ PRErrorCode AuthCertificateParseResults(
|
||||
|
||||
} // unnamed namespace
|
||||
|
||||
static nsTArray<nsTArray<uint8_t>> CreateCertBytesArray(
|
||||
const UniqueCERTCertList& aCertChain) {
|
||||
nsTArray<nsTArray<uint8_t>> certsBytes;
|
||||
for (CERTCertListNode* n = CERT_LIST_HEAD(aCertChain);
|
||||
!CERT_LIST_END(n, aCertChain); n = CERT_LIST_NEXT(n)) {
|
||||
nsTArray<uint8_t> certBytes;
|
||||
certBytes.AppendElements(n->cert->derCert.data, n->cert->derCert.len);
|
||||
certsBytes.AppendElement(std::move(certBytes));
|
||||
}
|
||||
return certsBytes;
|
||||
}
|
||||
|
||||
/*static*/
|
||||
SECStatus SSLServerCertVerificationJob::Dispatch(
|
||||
uint64_t addrForLogging, void* aPinArg,
|
||||
@@ -1053,26 +1090,23 @@ SSLServerCertVerificationJob::Run() {
|
||||
}
|
||||
|
||||
TimeStamp jobStartTime = TimeStamp::Now();
|
||||
UniqueCERTCertList builtCertChain;
|
||||
EVStatus evStatus;
|
||||
CertificateTransparencyInfo certificateTransparencyInfo;
|
||||
bool isCertChainRootBuiltInRoot = false;
|
||||
nsTArray<nsTArray<uint8_t>> certBytesArray;
|
||||
Result rv = AuthCertificate(
|
||||
*certVerifier, mPinArg, mCert, mPeerCertChain, mHostName,
|
||||
mOriginAttributes, mStapledOCSPResponse, mSCTsFromTLSExtension, mDCInfo,
|
||||
mProviderFlags, mTime, mCertVerifierFlags, builtCertChain, evStatus,
|
||||
mProviderFlags, mTime, mCertVerifierFlags, certBytesArray, evStatus,
|
||||
certificateTransparencyInfo, isCertChainRootBuiltInRoot);
|
||||
|
||||
RefPtr<nsNSSCertificate> nsc = nsNSSCertificate::Create(mCert.get());
|
||||
nsTArray<nsTArray<uint8_t>> certBytesArray;
|
||||
if (rv == Success) {
|
||||
Telemetry::AccumulateTimeDelta(
|
||||
Telemetry::SSL_SUCCESFUL_CERT_VALIDATION_TIME_MOZILLAPKIX, jobStartTime,
|
||||
TimeStamp::Now());
|
||||
Telemetry::Accumulate(Telemetry::SSL_CERT_ERROR_OVERRIDES, 1);
|
||||
|
||||
certBytesArray =
|
||||
TransportSecurityInfo::CreateCertBytesArray(builtCertChain);
|
||||
mResultTask->Dispatch(
|
||||
nsc, std::move(certBytesArray), std::move(mPeerCertChain),
|
||||
TransportSecurityInfo::ConvertCertificateTransparencyInfoToStatus(
|
||||
@@ -1202,7 +1236,7 @@ SECStatus AuthCertificateHook(void* arg, PRFileDesc* fd, PRBool checkSig,
|
||||
}
|
||||
|
||||
nsTArray<nsTArray<uint8_t>> peerCertsBytes =
|
||||
TransportSecurityInfo::CreateCertBytesArray(peerCertChain);
|
||||
CreateCertBytesArray(peerCertChain);
|
||||
|
||||
// SSL_PeerStapledOCSPResponses will never return a non-empty response if
|
||||
// OCSP stapling wasn't enabled because libssl wouldn't have let the server
|
||||
|
||||
@@ -1153,19 +1153,6 @@ uint16_t TransportSecurityInfo::ConvertCertificateTransparencyInfoToStatus(
|
||||
return nsITransportSecurityInfo::CERTIFICATE_TRANSPARENCY_NOT_APPLICABLE;
|
||||
}
|
||||
|
||||
// static
|
||||
nsTArray<nsTArray<uint8_t>> TransportSecurityInfo::CreateCertBytesArray(
|
||||
const UniqueCERTCertList& aCertChain) {
|
||||
nsTArray<nsTArray<uint8_t>> certsBytes;
|
||||
for (CERTCertListNode* n = CERT_LIST_HEAD(aCertChain);
|
||||
!CERT_LIST_END(n, aCertChain); n = CERT_LIST_NEXT(n)) {
|
||||
nsTArray<uint8_t> certBytes;
|
||||
certBytes.AppendElements(n->cert->derCert.data, n->cert->derCert.len);
|
||||
certsBytes.AppendElement(std::move(certBytes));
|
||||
}
|
||||
return certsBytes;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
TransportSecurityInfo::GetIsDomainMismatch(bool* aIsDomainMismatch) {
|
||||
NS_ENSURE_ARG_POINTER(aIsDomainMismatch);
|
||||
|
||||
@@ -91,9 +91,6 @@ class TransportSecurityInfo : public nsITransportSecurityInfo,
|
||||
static uint16_t ConvertCertificateTransparencyInfoToStatus(
|
||||
const mozilla::psm::CertificateTransparencyInfo& info);
|
||||
|
||||
static nsTArray<nsTArray<uint8_t>> CreateCertBytesArray(
|
||||
const UniqueCERTCertList& aCertChain);
|
||||
|
||||
// Use errorCode == 0 to indicate success;
|
||||
virtual void SetCertVerificationResult(PRErrorCode errorCode){};
|
||||
|
||||
|
||||
@@ -1098,11 +1098,11 @@ static void RebuildVerifiedCertificateInformation(PRFileDesc* fd,
|
||||
|
||||
EVStatus evStatus;
|
||||
CertificateTransparencyInfo certificateTransparencyInfo;
|
||||
UniqueCERTCertList builtChain;
|
||||
nsTArray<nsTArray<uint8_t>> certBytesArray;
|
||||
bool isBuiltCertChainRootBuiltInRoot = false;
|
||||
mozilla::pkix::Result rv = certVerifier->VerifySSLServerCert(
|
||||
cert, mozilla::pkix::Now(), infoObject, infoObject->GetHostName(),
|
||||
builtChain, flags, maybePeerCertsBytes, stapledOCSPResponse,
|
||||
certBytesArray, flags, maybePeerCertsBytes, stapledOCSPResponse,
|
||||
sctsFromTLSExtension, Nothing(), infoObject->GetOriginAttributes(),
|
||||
&evStatus,
|
||||
nullptr, // OCSP stapling telemetry
|
||||
@@ -1132,8 +1132,6 @@ static void RebuildVerifiedCertificateInformation(PRFileDesc* fd,
|
||||
TransportSecurityInfo::ConvertCertificateTransparencyInfoToStatus(
|
||||
certificateTransparencyInfo);
|
||||
infoObject->SetCertificateTransparencyStatus(status);
|
||||
nsTArray<nsTArray<uint8_t>> certBytesArray =
|
||||
TransportSecurityInfo::CreateCertBytesArray(builtChain);
|
||||
infoObject->SetSucceededCertChain(std::move(certBytesArray));
|
||||
infoObject->SetIsBuiltCertChainRootBuiltInRoot(
|
||||
isBuiltCertChainRootBuiltInRoot);
|
||||
|
||||
@@ -151,7 +151,12 @@ NS_IMETHODIMP
|
||||
nsNSSCertificate::GetIsBuiltInRoot(bool* aIsBuiltInRoot) {
|
||||
NS_ENSURE_ARG(aIsBuiltInRoot);
|
||||
|
||||
pkix::Result rv = IsCertBuiltInRoot(mCert.get(), *aIsBuiltInRoot);
|
||||
pkix::Input certInput;
|
||||
pkix::Result rv = certInput.Init(mCert->derCert.data, mCert->derCert.len);
|
||||
if (rv != pkix::Result::Success) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
rv = IsCertBuiltInRoot(certInput, *aIsBuiltInRoot);
|
||||
if (rv != pkix::Result::Success) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
@@ -1262,7 +1262,7 @@ nsresult VerifyCertAtTime(nsIX509Cert* aCert,
|
||||
RefPtr<SharedCertVerifier> certVerifier(GetDefaultCertVerifier());
|
||||
NS_ENSURE_TRUE(certVerifier, NS_ERROR_FAILURE);
|
||||
|
||||
UniqueCERTCertList resultChain;
|
||||
nsTArray<nsTArray<uint8_t>> resultChain;
|
||||
EVStatus evStatus;
|
||||
mozilla::pkix::Result result;
|
||||
|
||||
@@ -1289,11 +1289,14 @@ nsresult VerifyCertAtTime(nsIX509Cert* aCert,
|
||||
}
|
||||
|
||||
if (result == mozilla::pkix::Success) {
|
||||
nsresult rv = nsNSSCertificateDB::ConstructCertArrayFromUniqueCertList(
|
||||
resultChain, aVerifiedChain);
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
for (const auto& certDER : resultChain) {
|
||||
RefPtr<nsIX509Cert> cert = nsNSSCertificate::ConstructFromDER(
|
||||
const_cast<char*>(reinterpret_cast<const char*>(certDER.Elements())),
|
||||
static_cast<int>(certDER.Length()));
|
||||
if (!cert) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
aVerifiedChain.AppendElement(cert);
|
||||
}
|
||||
|
||||
if (evStatus == EVStatus::EV) {
|
||||
|
||||
Reference in New Issue
Block a user