Bug 1713605 - Avoid NSS usage in CertVerifier::VerifySSLServerCert r=keeler

Differential Revision: https://phabricator.services.mozilla.com/D118030
This commit is contained in:
R. Martinho Fernandes 2021-09-14 18:11:05 +00:00
Родитель 45b533e45e
Коммит 4fa1dee9c4
5 изменённых файлов: 57 добавлений и 56 удалений

Просмотреть файл

@ -483,8 +483,8 @@ bool CertVerifier::SHA1ModeMoreRestrictiveThanGivenMode(SHA1Mode mode) {
}
Result CertVerifier::VerifyCert(
CERTCertificate* cert, SECCertificateUsage usage, Time time, void* pinArg,
const char* hostname,
const nsTArray<uint8_t>& certBytes, SECCertificateUsage usage, Time time,
void* pinArg, const char* hostname,
/*out*/ nsTArray<nsTArray<uint8_t>>& builtChain,
/*optional*/ const Flags flags,
/*optional*/ const Maybe<nsTArray<nsTArray<uint8_t>>>& extraCertificates,
@ -499,7 +499,6 @@ Result CertVerifier::VerifyCert(
/*optional out*/ CertificateTransparencyInfo* ctInfo) {
MOZ_LOG(gCertVerifierLog, LogLevel::Debug, ("Top of VerifyCert\n"));
MOZ_ASSERT(cert);
MOZ_ASSERT(usage == certificateUsageSSLServer || !(flags & FLAG_MUST_BE_EV));
MOZ_ASSERT(usage == certificateUsageSSLServer || !keySizeStatus);
MOZ_ASSERT(usage == certificateUsageSSLServer || !sha1ModeResult);
@ -535,13 +534,12 @@ Result CertVerifier::VerifyCert(
*sha1ModeResult = SHA1ModeResult::NeverChecked;
}
if (!cert ||
(usage != certificateUsageSSLServer && (flags & FLAG_MUST_BE_EV))) {
if (usage != certificateUsageSSLServer && (flags & FLAG_MUST_BE_EV)) {
return Result::FATAL_ERROR_INVALID_ARGS;
}
Input certDER;
Result rv = certDER.Init(cert->derCert.data, cert->derCert.len);
Result rv = certDER.Init(certBytes.Elements(), certBytes.Length());
if (rv != Success) {
return rv;
}
@ -630,7 +628,6 @@ Result CertVerifier::VerifyCert(
: NSSCertDBTrustDomain::FetchOCSPForEV;
CertPolicyId evPolicy;
nsTArray<uint8_t> certBytes(cert->derCert.data, cert->derCert.len);
bool foundEVPolicy = GetFirstEVPolicy(certBytes, evPolicy);
rv = Result::ERROR_UNKNOWN_ERROR;
for (size_t i = 0;
@ -878,20 +875,7 @@ Result CertVerifier::VerifyCert(
return Success;
}
static bool CertIsSelfSigned(const UniqueCERTCertificate& cert, void* pinarg) {
Input certInput;
Result rv = certInput.Init(cert->derCert.data, cert->derCert.len);
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 backCert(certInput, notUsedForPaths, nullptr);
rv = backCert.Init();
if (rv != Success) {
return false;
}
static bool CertIsSelfSigned(const BackCert& backCert, void* pinarg) {
if (!InputsAreEqual(backCert.GetIssuer(), backCert.GetSubject())) {
return false;
}
@ -899,13 +883,13 @@ static bool CertIsSelfSigned(const UniqueCERTCertificate& cert, void* pinarg) {
nsTArray<nsTArray<uint8_t>> emptyCertList;
// CSTrustDomain is only used for the signature verification callbacks
mozilla::psm::CSTrustDomain trustDomain(emptyCertList);
rv = VerifySignedData(trustDomain, backCert.GetSignedData(),
backCert.GetSubjectPublicKeyInfo());
Result rv = VerifySignedData(trustDomain, backCert.GetSignedData(),
backCert.GetSubjectPublicKeyInfo());
return rv == Success;
}
Result CertVerifier::VerifySSLServerCert(
const UniqueCERTCertificate& peerCert, Time time,
const nsTArray<uint8_t>& peerCertBytes, Time time,
/*optional*/ void* pinarg, const nsACString& hostname,
/*out*/ nsTArray<nsTArray<uint8_t>>& builtChain,
/*optional*/ Flags flags,
@ -921,7 +905,6 @@ Result CertVerifier::VerifySSLServerCert(
/*optional out*/ PinningTelemetryInfo* pinningTelemetryInfo,
/*optional out*/ CertificateTransparencyInfo* ctInfo,
/*optional out*/ bool* isBuiltCertChainRootBuiltInRoot) {
MOZ_ASSERT(peerCert);
// XXX: MOZ_ASSERT(pinarg);
MOZ_ASSERT(!hostname.IsEmpty());
@ -939,15 +922,27 @@ Result CertVerifier::VerifySSLServerCert(
// CreateCertErrorRunnable assumes that CheckCertHostname is only called
// if VerifyCert succeeded.
Input peerCertInput;
Result rv =
VerifyCert(peerCert.get(), certificateUsageSSLServer, time, pinarg,
PromiseFlatCString(hostname).get(), builtChain, flags,
extraCertificates, stapledOCSPResponse, sctsFromTLS,
originAttributes, evStatus, ocspStaplingStatus, keySizeStatus,
sha1ModeResult, pinningTelemetryInfo, ctInfo);
peerCertInput.Init(peerCertBytes.Elements(), peerCertBytes.Length());
if (rv != Success) {
return rv;
}
rv = VerifyCert(peerCertBytes, certificateUsageSSLServer, time, pinarg,
PromiseFlatCString(hostname).get(), builtChain, flags,
extraCertificates, stapledOCSPResponse, sctsFromTLS,
originAttributes, evStatus, ocspStaplingStatus, keySizeStatus,
sha1ModeResult, pinningTelemetryInfo, ctInfo);
if (rv != Success) {
// we don't use the certificate for path building, so this parameter doesn't
// matter
EndEntityOrCA notUsedForPaths = EndEntityOrCA::MustBeEndEntity;
BackCert peerBackCert(peerCertInput, notUsedForPaths, nullptr);
if (peerBackCert.Init() != Success) {
return rv;
}
if (rv == Result::ERROR_UNKNOWN_ISSUER &&
CertIsSelfSigned(peerCert, pinarg)) {
CertIsSelfSigned(peerBackCert, pinarg)) {
// In this case we didn't find any issuer for the certificate and the
// certificate is self-signed.
return Result::ERROR_SELF_SIGNED_CERT;
@ -965,7 +960,13 @@ Result CertVerifier::VerifySSLServerCert(
}
// IssuerMatchesMitmCanary succeeds if the issuer matches the canary and
// the feature is enabled.
nsresult rv = component->IssuerMatchesMitmCanary(peerCert->issuerName);
Input issuerNameInput = peerBackCert.GetIssuer();
SECItem issuerNameItem = UnsafeMapInputToSECItem(issuerNameInput);
UniquePORTString issuerName(CERT_DerNameToAscii(&issuerNameItem));
if (!issuerName) {
return Result::ERROR_BAD_DER;
}
nsresult rv = component->IssuerMatchesMitmCanary(issuerName.get());
if (NS_SUCCEEDED(rv)) {
return Result::ERROR_MITM_DETECTED;
}
@ -980,12 +981,6 @@ Result CertVerifier::VerifySSLServerCert(
}
}
Input peerCertInput;
rv = peerCertInput.Init(peerCert->derCert.data, peerCert->derCert.len);
if (rv != Success) {
return rv;
}
Input stapledOCSPResponseInput;
Input* responseInputPtr = nullptr;
if (stapledOCSPResponse) {

Просмотреть файл

@ -157,7 +157,7 @@ class CertVerifier {
// *evOidPolicy == SEC_OID_UNKNOWN means the cert is NOT EV
// Only one usage per verification is supported.
mozilla::pkix::Result VerifyCert(
CERTCertificate* cert, SECCertificateUsage usage,
const nsTArray<uint8_t>& certBytes, SECCertificateUsage usage,
mozilla::pkix::Time time, void* pinArg, const char* hostname,
/*out*/ nsTArray<nsTArray<uint8_t>>& builtChain, Flags flags = 0,
/*optional in*/
@ -175,8 +175,8 @@ class CertVerifier {
/*optional out*/ CertificateTransparencyInfo* ctInfo = nullptr);
mozilla::pkix::Result VerifySSLServerCert(
const UniqueCERTCertificate& peerCert, mozilla::pkix::Time time,
void* pinarg, const nsACString& hostname,
const nsTArray<uint8_t>& peerCert, mozilla::pkix::Time time, void* pinarg,
const nsACString& hostname,
/*out*/ nsTArray<nsTArray<uint8_t>>& builtChain,
/*optional*/ Flags flags = 0,
/*optional*/ const Maybe<nsTArray<nsTArray<uint8_t>>>& extraCertificates =

Просмотреть файл

@ -857,7 +857,7 @@ static void AuthCertificateSetResults(
// Note: Takes ownership of |peerCertChain| if SECSuccess is not returned.
Result AuthCertificate(
CertVerifier& certVerifier, void* aPinArg,
const UniqueCERTCertificate& cert,
const nsTArray<uint8_t>& certBytes,
const nsTArray<nsTArray<uint8_t>>& peerCertChain,
const nsACString& aHostName, const OriginAttributes& aOriginAttributes,
const Maybe<nsTArray<uint8_t>>& stapledOCSPResponse,
@ -868,8 +868,6 @@ Result AuthCertificate(
/*out*/ EVStatus& evStatus,
/*out*/ CertificateTransparencyInfo& certificateTransparencyInfo,
/*out*/ bool& aIsCertChainRootBuiltInRoot) {
MOZ_ASSERT(cert);
CertVerifier::OCSPStaplingStatus ocspStaplingStatus =
CertVerifier::OCSP_STAPLING_NEVER_CHECKED;
KeySizeStatus keySizeStatus = KeySizeStatus::NeverChecked;
@ -886,7 +884,7 @@ Result AuthCertificate(
}
Result rv = certVerifier.VerifySSLServerCert(
cert, time, aPinArg, aHostName, builtCertChain, certVerifierFlags,
certBytes, time, aPinArg, aHostName, builtCertChain, certVerifierFlags,
Some(std::move(peerCertsBytes)), stapledOCSPResponse,
sctsFromTLSExtension, dcInfo, aOriginAttributes, &evStatus,
&ocspStaplingStatus, &keySizeStatus, &sha1ModeResult,
@ -1093,11 +1091,12 @@ SSLServerCertVerificationJob::Run() {
EVStatus evStatus;
CertificateTransparencyInfo certificateTransparencyInfo;
bool isCertChainRootBuiltInRoot = false;
nsTArray<nsTArray<uint8_t>> certBytesArray;
nsTArray<nsTArray<uint8_t>> builtChainBytesArray;
nsTArray<uint8_t> certBytes(mCert->derCert.data, mCert->derCert.len);
Result rv = AuthCertificate(
*certVerifier, mPinArg, mCert, mPeerCertChain, mHostName,
*certVerifier, mPinArg, certBytes, mPeerCertChain, mHostName,
mOriginAttributes, mStapledOCSPResponse, mSCTsFromTLSExtension, mDCInfo,
mProviderFlags, mTime, mCertVerifierFlags, certBytesArray, evStatus,
mProviderFlags, mTime, mCertVerifierFlags, builtChainBytesArray, evStatus,
certificateTransparencyInfo, isCertChainRootBuiltInRoot);
RefPtr<nsNSSCertificate> nsc = nsNSSCertificate::Create(mCert.get());
@ -1108,7 +1107,7 @@ SSLServerCertVerificationJob::Run() {
Telemetry::Accumulate(Telemetry::SSL_CERT_ERROR_OVERRIDES, 1);
mResultTask->Dispatch(
nsc, std::move(certBytesArray), std::move(mPeerCertChain),
nsc, std::move(builtChainBytesArray), std::move(mPeerCertChain),
TransportSecurityInfo::ConvertCertificateTransparencyInfoToStatus(
certificateTransparencyInfo),
evStatus, true, 0, 0, isCertChainRootBuiltInRoot, mProviderFlags);
@ -1127,7 +1126,7 @@ SSLServerCertVerificationJob::Run() {
// NB: finalError may be 0 here, in which the connection will continue.
mResultTask->Dispatch(
nsc, std::move(certBytesArray), std::move(mPeerCertChain),
nsc, std::move(builtChainBytesArray), std::move(mPeerCertChain),
nsITransportSecurityInfo::CERTIFICATE_TRANSPARENCY_NOT_APPLICABLE,
EVStatus::NotEV, false, finalError, collectedErrors, false,
mProviderFlags);

Просмотреть файл

@ -1094,11 +1094,12 @@ static void RebuildVerifiedCertificateInformation(PRFileDesc* fd,
EVStatus evStatus;
CertificateTransparencyInfo certificateTransparencyInfo;
nsTArray<nsTArray<uint8_t>> certBytesArray;
nsTArray<nsTArray<uint8_t>> builtChainCertBytes;
nsTArray<uint8_t> certBytes(cert->derCert.data, cert->derCert.len);
bool isBuiltCertChainRootBuiltInRoot = false;
mozilla::pkix::Result rv = certVerifier->VerifySSLServerCert(
cert, mozilla::pkix::Now(), infoObject, infoObject->GetHostName(),
certBytesArray, flags, maybePeerCertsBytes, stapledOCSPResponse,
certBytes, mozilla::pkix::Now(), infoObject, infoObject->GetHostName(),
builtChainCertBytes, flags, maybePeerCertsBytes, stapledOCSPResponse,
sctsFromTLSExtension, Nothing(), infoObject->GetOriginAttributes(),
&evStatus,
nullptr, // OCSP stapling telemetry
@ -1128,7 +1129,7 @@ static void RebuildVerifiedCertificateInformation(PRFileDesc* fd,
TransportSecurityInfo::ConvertCertificateTransparencyInfoToStatus(
certificateTransparencyInfo);
infoObject->SetCertificateTransparencyStatus(status);
infoObject->SetSucceededCertChain(std::move(certBytesArray));
infoObject->SetSucceededCertChain(std::move(builtChainCertBytes));
infoObject->SetIsBuiltCertChainRootBuiltInRoot(
isBuiltCertChainRootBuiltInRoot);
}

Просмотреть файл

@ -1266,9 +1266,15 @@ nsresult VerifyCertAtTime(nsIX509Cert* aCert,
EVStatus evStatus;
mozilla::pkix::Result result;
nsTArray<uint8_t> certBytes;
nsresult nsrv = aCert->GetRawDER(certBytes);
if (NS_FAILED(nsrv)) {
return nsrv;
}
if (!aHostname.IsVoid() && aUsage == certificateUsageSSLServer) {
result =
certVerifier->VerifySSLServerCert(nssCert, aTime,
certVerifier->VerifySSLServerCert(certBytes, aTime,
nullptr, // Assume no context
aHostname, resultChain, aFlags,
Nothing(), // extraCertificates
@ -1279,7 +1285,7 @@ nsresult VerifyCertAtTime(nsIX509Cert* aCert,
} else {
const nsCString& flatHostname = PromiseFlatCString(aHostname);
result = certVerifier->VerifyCert(
nssCert.get(), aUsage, aTime,
certBytes, aUsage, aTime,
nullptr, // Assume no context
aHostname.IsVoid() ? nullptr : flatHostname.get(), resultChain, aFlags,
Nothing(), // extraCertificates