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:
R. Martinho Fernandes
2021-08-14 02:11:30 +00:00
parent bef4208b9e
commit 33c1fa451a
14 changed files with 267 additions and 195 deletions

View File

@@ -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, &notBefore, &notAfter) != SECSuccess) {
if (CERT_GetCertTimes(endEntityCert.get(), &notBefore, &notAfter) !=
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,

View File

@@ -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);

View File

@@ -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,98 +1109,121 @@ nsresult isDistrustedCertificateChain(const UniqueCERTCertList& certList,
return NS_OK;
}
// 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;
SECStatus runnableRV = SECFailure;
// Set isDistrusted to false if there is no distrust for the root.
if (!certRoot->distrust) {
isDistrusted = false;
return NS_OK;
}
RefPtr<Runnable> isDistrustedChainTask =
NS_NewRunnableFunction("isDistrustedCertificateChain", [&]() {
// Allocate objects and retreive the root and end-entity certificates.
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;
}
// Create a pointer to refer to the selected distrust struct.
SECItem* distrustPtr = nullptr;
if (certDBTrustType == trustSSL) {
distrustPtr = &certRoot->distrust->serverDistrustAfter;
}
if (certDBTrustType == trustEmail) {
distrustPtr = &certRoot->distrust->emailDistrustAfter;
}
// Set isDistrusted to false if there is no distrust for the root.
if (!certRoot->distrust) {
isDistrusted = false;
runnableRV = SECSuccess;
return;
}
// Get validity for the current end-entity certificate
// and get the distrust field for the root certificate.
PRTime certRootDistrustAfter;
PRTime certLeafNotBefore;
// Create a pointer to refer to the selected distrust struct.
SECItem* distrustPtr = nullptr;
if (certDBTrustType == trustSSL) {
distrustPtr = &certRoot->distrust->serverDistrustAfter;
}
if (certDBTrustType == trustEmail) {
distrustPtr = &certRoot->distrust->emailDistrustAfter;
}
SECStatus rv = GetCertDistrustAfterValue(distrustPtr, certRootDistrustAfter);
if (rv != SECSuccess) {
// Get validity for the current end-entity certificate
// and get the distrust field for the root certificate.
PRTime certRootDistrustAfter;
PRTime certLeafNotBefore;
runnableRV =
GetCertDistrustAfterValue(distrustPtr, certRootDistrustAfter);
if (runnableRV != SECSuccess) {
return;
}
runnableRV = GetCertNotBeforeValue(certLeaf.get(), certLeafNotBefore);
if (runnableRV != SECSuccess) {
return;
}
// Compare the validity of the end-entity certificate with
// the distrust value of the root.
if (certLeafNotBefore <= certRootDistrustAfter) {
isDistrusted = false;
}
runnableRV = SECSuccess;
});
nsCOMPtr<nsIEventTarget> socketThread(
do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID));
if (!socketThread) {
return NS_ERROR_FAILURE;
}
rv = GetCertNotBeforeValue(certLeaf, certLeafNotBefore);
if (rv != SECSuccess) {
nsresult rv =
SyncRunnable::DispatchToThread(socketThread, isDistrustedChainTask);
if (NS_FAILED(rv) || runnableRV != SECSuccess) {
return NS_ERROR_FAILURE;
}
// Compare the validity of the end-entity certificate with
// the distrust value of the root.
if (certLeafNotBefore <= certRootDistrustAfter) {
isDistrusted = false;
}
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())};

View File

@@ -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;

View File

@@ -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,10 +129,9 @@ 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,
const DataAndLength (&allowlist)[T],
/* out */ bool& isDistrusted) {
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
// affected Symantec roots

View File

@@ -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";
}

View File

@@ -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;
}

View File

@@ -10,6 +10,7 @@
#include "CTLog.h"
#include "CTVerifyResult.h"
#include "certt.h"
#include "nsTArray.h"
#include "mozpkix/Result.h"
namespace mozilla {
@@ -30,9 +31,10 @@ 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,
const CTLogOperatorList& operators,
CTLogOperatorList& dependentOperators);
pkix::Result GetDependentOperators(
const nsTArray<nsTArray<uint8_t>>& builtChain,
const CTLogOperatorList& operators,
CTLogOperatorList& dependentOperators);
};
} // namespace ct

View File

@@ -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

View File

@@ -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);

View File

@@ -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){};

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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) {