bug 1063276 - include the peer cert chain from the TLS handshake when verifying server certificates r=kjacobs

Differential Revision: https://phabricator.services.mozilla.com/D50129

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Dana Keeler 2019-10-24 22:48:40 +00:00
Родитель 0bbded0263
Коммит e064323a59
9 изменённых файлов: 132 добавлений и 64 удалений

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

@ -447,6 +447,7 @@ Result CertVerifier::VerifyCert(
const char* hostname,
/*out*/ UniqueCERTCertList& builtChain,
/*optional*/ const Flags flags,
/*optional*/ const Maybe<nsTArray<nsTArray<uint8_t>>>& extraCertificates,
/*optional*/ const Maybe<nsTArray<uint8_t>>& stapledOCSPResponseArg,
/*optional*/ const Maybe<nsTArray<uint8_t>>& sctsFromTLS,
/*optional*/ const OriginAttributes& originAttributes,
@ -544,7 +545,8 @@ Result CertVerifier::VerifyCert(
MIN_RSA_BITS_WEAK, ValidityCheckingMode::CheckingOff,
SHA1Mode::Allowed, NetscapeStepUpPolicy::NeverMatch,
mDistrustedCAPolicy, originAttributes, mThirdPartyRootInputs,
mThirdPartyIntermediateInputs, builtChain, nullptr, nullptr);
mThirdPartyIntermediateInputs, extraCertificates, builtChain, nullptr,
nullptr);
rv = BuildCertChain(
trustDomain, certDER, time, EndEntityOrCA::MustBeEndEntity,
KeyUsage::digitalSignature, KeyPurposeId::id_kp_clientAuth,
@ -617,8 +619,8 @@ Result CertVerifier::VerifyCert(
MIN_RSA_BITS, ValidityCheckingMode::CheckForEV,
sha1ModeConfigurations[i], mNetscapeStepUpPolicy,
mDistrustedCAPolicy, originAttributes, mThirdPartyRootInputs,
mThirdPartyIntermediateInputs, builtChain, pinningTelemetryInfo,
hostname);
mThirdPartyIntermediateInputs, extraCertificates, builtChain,
pinningTelemetryInfo, hostname);
rv = BuildCertChainForOneKeyUsage(
trustDomain, certDER, time,
KeyUsage::digitalSignature, // (EC)DHE
@ -699,8 +701,8 @@ Result CertVerifier::VerifyCert(
mPinningMode, keySizeOptions[i],
ValidityCheckingMode::CheckingOff, sha1ModeConfigurations[j],
mNetscapeStepUpPolicy, mDistrustedCAPolicy, originAttributes,
mThirdPartyRootInputs, mThirdPartyIntermediateInputs, builtChain,
pinningTelemetryInfo, hostname);
mThirdPartyRootInputs, mThirdPartyIntermediateInputs,
extraCertificates, builtChain, pinningTelemetryInfo, hostname);
rv = BuildCertChainForOneKeyUsage(
trustDomain, certDER, time,
KeyUsage::digitalSignature, //(EC)DHE
@ -769,7 +771,8 @@ Result CertVerifier::VerifyCert(
MIN_RSA_BITS_WEAK, ValidityCheckingMode::CheckingOff,
SHA1Mode::Allowed, mNetscapeStepUpPolicy, mDistrustedCAPolicy,
originAttributes, mThirdPartyRootInputs,
mThirdPartyIntermediateInputs, builtChain, nullptr, nullptr);
mThirdPartyIntermediateInputs, extraCertificates, builtChain, nullptr,
nullptr);
rv = BuildCertChain(trustDomain, certDER, time, EndEntityOrCA::MustBeCA,
KeyUsage::keyCertSign, KeyPurposeId::id_kp_serverAuth,
CertPolicyId::anyPolicy, stapledOCSPResponse);
@ -783,7 +786,8 @@ Result CertVerifier::VerifyCert(
MIN_RSA_BITS_WEAK, ValidityCheckingMode::CheckingOff,
SHA1Mode::Allowed, NetscapeStepUpPolicy::NeverMatch,
mDistrustedCAPolicy, originAttributes, mThirdPartyRootInputs,
mThirdPartyIntermediateInputs, builtChain, nullptr, nullptr);
mThirdPartyIntermediateInputs, extraCertificates, builtChain, nullptr,
nullptr);
rv = BuildCertChain(
trustDomain, certDER, time, EndEntityOrCA::MustBeEndEntity,
KeyUsage::digitalSignature, KeyPurposeId::id_kp_emailProtection,
@ -807,7 +811,8 @@ Result CertVerifier::VerifyCert(
MIN_RSA_BITS_WEAK, ValidityCheckingMode::CheckingOff,
SHA1Mode::Allowed, NetscapeStepUpPolicy::NeverMatch,
mDistrustedCAPolicy, originAttributes, mThirdPartyRootInputs,
mThirdPartyIntermediateInputs, builtChain, nullptr, nullptr);
mThirdPartyIntermediateInputs, extraCertificates, builtChain, nullptr,
nullptr);
rv = BuildCertChain(trustDomain, certDER, time,
EndEntityOrCA::MustBeEndEntity,
KeyUsage::keyEncipherment, // RSA
@ -852,14 +857,15 @@ static bool CertIsSelfSigned(const UniqueCERTCertificate& cert, void* pinarg) {
}
Result CertVerifier::VerifySSLServerCert(
const UniqueCERTCertificate& peerCert,
/*optional*/ const Maybe<nsTArray<uint8_t>>& stapledOCSPResponse,
/*optional*/ const Maybe<nsTArray<uint8_t>>& sctsFromTLS, Time time,
const UniqueCERTCertificate& peerCert, Time time,
/*optional*/ void* pinarg, const nsACString& hostname,
/*out*/ UniqueCERTCertList& builtChain,
/*optional*/ bool saveIntermediatesInPermanentDatabase,
/*optional*/ Flags flags,
/*optional*/ const Maybe<nsTArray<nsTArray<uint8_t>>>& extraCertificates,
/*optional*/ const Maybe<nsTArray<uint8_t>>& stapledOCSPResponse,
/*optional*/ const Maybe<nsTArray<uint8_t>>& sctsFromTLS,
/*optional*/ const OriginAttributes& originAttributes,
/*optional*/ bool saveIntermediatesInPermanentDatabase,
/*optional out*/ SECOidTag* evOidPolicy,
/*optional out*/ OCSPStaplingStatus* ocspStaplingStatus,
/*optional out*/ KeySizeStatus* keySizeStatus,
@ -883,9 +889,9 @@ Result CertVerifier::VerifySSLServerCert(
Result rv =
VerifyCert(peerCert.get(), certificateUsageSSLServer, time, pinarg,
PromiseFlatCString(hostname).get(), builtChain, flags,
stapledOCSPResponse, sctsFromTLS, originAttributes,
evOidPolicy, ocspStaplingStatus, keySizeStatus, sha1ModeResult,
pinningTelemetryInfo, ctInfo);
extraCertificates, stapledOCSPResponse, sctsFromTLS,
originAttributes, evOidPolicy, ocspStaplingStatus,
keySizeStatus, sha1ModeResult, pinningTelemetryInfo, ctInfo);
if (rv != Success) {
if (rv == Result::ERROR_UNKNOWN_ISSUER &&
CertIsSelfSigned(peerCert, pinarg)) {

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

@ -146,10 +146,11 @@ class CertVerifier {
CERTCertificate* cert, SECCertificateUsage usage,
mozilla::pkix::Time time, void* pinArg, const char* hostname,
/*out*/ UniqueCERTCertList& builtChain, Flags flags = 0,
/*optional in*/
const Maybe<nsTArray<nsTArray<uint8_t>>>& extraCertificates = Nothing(),
/*optional in*/ const Maybe<nsTArray<uint8_t>>& stapledOCSPResponseArg =
Maybe<nsTArray<uint8_t>>(),
/*optional in*/ const Maybe<nsTArray<uint8_t>>& sctsFromTLS =
Maybe<nsTArray<uint8_t>>(),
Nothing(),
/*optional in*/ const Maybe<nsTArray<uint8_t>>& sctsFromTLS = Nothing(),
/*optional in*/ const OriginAttributes& originAttributes =
OriginAttributes(),
/*optional out*/ SECOidTag* evOidPolicy = nullptr,
@ -160,16 +161,18 @@ class CertVerifier {
/*optional out*/ CertificateTransparencyInfo* ctInfo = nullptr);
mozilla::pkix::Result VerifySSLServerCert(
const UniqueCERTCertificate& peerCert,
/*optional*/ const Maybe<nsTArray<uint8_t>>& stapledOCSPResponse,
/*optional*/ const Maybe<nsTArray<uint8_t>>& sctsFromTLS,
mozilla::pkix::Time time,
/*optional*/ void* pinarg, const nsACString& hostname,
const UniqueCERTCertificate& peerCert, mozilla::pkix::Time time,
void* pinarg, const nsACString& hostname,
/*out*/ UniqueCERTCertList& builtChain,
/*optional*/ bool saveIntermediatesInPermanentDatabase = false,
/*optional*/ Flags flags = 0,
/*optional*/ const Maybe<nsTArray<nsTArray<uint8_t>>>& extraCertificates =
Nothing(),
/*optional*/ const Maybe<nsTArray<uint8_t>>& stapledOCSPResponse =
Nothing(),
/*optional*/ const Maybe<nsTArray<uint8_t>>& sctsFromTLS = Nothing(),
/*optional*/ const OriginAttributes& originAttributes =
OriginAttributes(),
/*optional*/ bool saveIntermediatesInPermanentDatabase = false,
/*optional out*/ SECOidTag* evOidPolicy = nullptr,
/*optional out*/ OCSPStaplingStatus* ocspStaplingStatus = nullptr,
/*optional out*/ KeySizeStatus* keySizeStatus = nullptr,

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

@ -67,6 +67,7 @@ NSSCertDBTrustDomain::NSSCertDBTrustDomain(
const OriginAttributes& originAttributes,
const Vector<Input>& thirdPartyRootInputs,
const Vector<Input>& thirdPartyIntermediateInputs,
const Maybe<nsTArray<nsTArray<uint8_t>>>& extraCertificates,
/*out*/ UniqueCERTCertList& builtChain,
/*optional*/ PinningTelemetryInfo* pinningTelemetryInfo,
/*optional*/ const char* hostname)
@ -87,6 +88,7 @@ NSSCertDBTrustDomain::NSSCertDBTrustDomain(
mOriginAttributes(originAttributes),
mThirdPartyRootInputs(thirdPartyRootInputs),
mThirdPartyIntermediateInputs(thirdPartyIntermediateInputs),
mExtraCertificates(extraCertificates),
mBuiltChain(builtChain),
mPinningTelemetryInfo(pinningTelemetryInfo),
mHostname(hostname),
@ -306,6 +308,32 @@ Result NSSCertDBTrustDomain::FindIssuer(Input encodedIssuerName,
}
}
if (mExtraCertificates.isSome()) {
for (const auto& extraCert : *mExtraCertificates) {
Input certInput;
Result rv = certInput.Init(extraCert.Elements(), extraCert.Length());
if (rv != Success) {
continue;
}
BackCert cert(certInput, EndEntityOrCA::MustBeCA, nullptr);
rv = cert.Init();
if (rv != Success) {
continue;
}
// Filter out certificates that can't be issuers we're looking for because
// the subject distinguished name doesn't match. This prevents
// mozilla::pkix from accumulating spurious errors during path building.
if (!InputsAreEqual(encodedIssuerName, cert.GetSubject())) {
continue;
}
// We assume that extra certificates (presumably from the TLS handshake)
// are intermediates, since sending trust anchors would be superfluous.
if (!geckoIntermediateCandidates.append(certInput)) {
return Result::FATAL_ERROR_NO_MEMORY;
}
}
}
// Try all root certs first and then all (presumably) intermediates.
if (!geckoRootCandidates.appendAll(geckoIntermediateCandidates)) {
return Result::FATAL_ERROR_NO_MEMORY;
@ -324,10 +352,10 @@ Result NSSCertDBTrustDomain::FindIssuer(Input encodedIssuerName,
// NSS seems not to differentiate between "no potential issuers found" and
// "there was an error trying to retrieve the potential issuers." We assume
// there was no error if CERT_CreateSubjectCertList returns nullptr.
Vector<Input> nssRootCandidates;
Vector<Input> nssIntermediateCandidates;
UniqueCERTCertList candidates(CERT_CreateSubjectCertList(
nullptr, CERT_GetDefaultCertDB(), &encodedIssuerNameItem, 0, false));
Vector<Input> nssRootCandidates;
Vector<Input> nssIntermediateCandidates;
if (candidates) {
for (CERTCertListNode* n = CERT_LIST_HEAD(candidates);
!CERT_LIST_END(n, candidates); n = CERT_LIST_NEXT(n)) {

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

@ -154,6 +154,7 @@ class NSSCertDBTrustDomain : public mozilla::pkix::TrustDomain {
const OriginAttributes& originAttributes,
const Vector<mozilla::pkix::Input>& thirdPartyRootInputs,
const Vector<mozilla::pkix::Input>& thirdPartyIntermediateInputs,
const Maybe<nsTArray<nsTArray<uint8_t>>>& extraCertificates,
/*out*/ UniqueCERTCertList& builtChain,
/*optional*/ PinningTelemetryInfo* pinningTelemetryInfo = nullptr,
/*optional*/ const char* hostname = nullptr);
@ -274,6 +275,7 @@ class NSSCertDBTrustDomain : public mozilla::pkix::TrustDomain {
const Vector<mozilla::pkix::Input>& mThirdPartyRootInputs; // non-owning
const Vector<mozilla::pkix::Input>&
mThirdPartyIntermediateInputs; // non-owning
const Maybe<nsTArray<nsTArray<uint8_t>>>& mExtraCertificates; // non-owning
UniqueCERTCertList& mBuiltChain; // non-owning
PinningTelemetryInfo* mPinningTelemetryInfo;
const char* mHostname; // non-owning - only used for pinning checks

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

@ -173,15 +173,10 @@ CommonSocketControl::IsAcceptableForHost(const nsACString& hostname,
}
psm::CertVerifier::Flags flags = psm::CertVerifier::FLAG_LOCAL_ONLY;
UniqueCERTCertList unusedBuiltChain;
mozilla::pkix::Result result = certVerifier->VerifySSLServerCert(
nssCert,
Maybe<nsTArray<uint8_t>>(), // stapledOCSPResponse
Maybe<nsTArray<uint8_t>>(), // sctsFromTLSExtension
mozilla::pkix::Now(),
nullptr, // pinarg
hostname, unusedBuiltChain,
false, // save intermediates
flags);
mozilla::pkix::Result result =
certVerifier->VerifySSLServerCert(nssCert, mozilla::pkix::Now(),
nullptr, // pinarg
hostname, unusedBuiltChain, flags);
if (result != mozilla::pkix::Success) {
return NS_OK;
}

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

@ -1201,12 +1201,24 @@ Result AuthCertificate(CertVerifier& certVerifier,
PinningTelemetryInfo pinningTelemetryInfo;
CertificateTransparencyInfo certificateTransparencyInfo;
nsTArray<nsTArray<uint8_t>> peerCertsBytes;
for (CERTCertListNode* n = CERT_LIST_HEAD(peerCertChain);
!CERT_LIST_END(n, peerCertChain); n = CERT_LIST_NEXT(n)) {
// Don't include the end-entity certificate.
if (n == CERT_LIST_HEAD(peerCertChain)) {
continue;
}
nsTArray<uint8_t> certBytes;
certBytes.AppendElements(n->cert->derCert.data, n->cert->derCert.len);
peerCertsBytes.AppendElement(std::move(certBytes));
}
Result rv = certVerifier.VerifySSLServerCert(
cert, stapledOCSPResponse, sctsFromTLSExtension, time, infoObject,
infoObject->GetHostName(), builtCertChain, saveIntermediates,
certVerifierFlags, infoObject->GetOriginAttributes(), &evOidPolicy,
&ocspStaplingStatus, &keySizeStatus, &sha1ModeResult,
&pinningTelemetryInfo, &certificateTransparencyInfo);
cert, time, infoObject, infoObject->GetHostName(), builtCertChain,
certVerifierFlags, Some(peerCertsBytes), stapledOCSPResponse,
sctsFromTLSExtension, infoObject->GetOriginAttributes(),
saveIntermediates, &evOidPolicy, &ocspStaplingStatus, &keySizeStatus,
&sha1ModeResult, &pinningTelemetryInfo, &certificateTransparencyInfo);
CollectCertTelemetry(rv, evOidPolicy, ocspStaplingStatus, keySizeStatus,
sha1ModeResult, pinningTelemetryInfo, builtCertChain,
@ -1558,7 +1570,6 @@ SECStatus AuthCertificateHook(void* arg, PRFileDesc* fd, PRBool checkSig,
return SECFailure;
}
// Get the peer certificate chain for error reporting
UniqueCERTCertList peerCertChain(SSL_PeerCertificateChain(fd));
if (!peerCertChain) {
PR_SetError(PR_INVALID_STATE_ERROR, 0);

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

@ -1043,6 +1043,27 @@ static void RebuildVerifiedCertificateInformation(PRFileDesc* fd,
return;
}
Maybe<nsTArray<nsTArray<uint8_t>>> maybePeerCertsBytes;
UniqueCERTCertList peerCertChain(SSL_PeerCertificateChain(fd));
if (!peerCertChain) {
MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
("RebuildVerifiedCertificateInformation: failed to get peer "
"certificate chain"));
} else {
nsTArray<nsTArray<uint8_t>> peerCertsBytes;
for (CERTCertListNode* n = CERT_LIST_HEAD(peerCertChain);
!CERT_LIST_END(n, peerCertChain); n = CERT_LIST_NEXT(n)) {
// Don't include the end-entity certificate.
if (n == CERT_LIST_HEAD(peerCertChain)) {
continue;
}
nsTArray<uint8_t> certBytes;
certBytes.AppendElements(n->cert->derCert.data, n->cert->derCert.len);
peerCertsBytes.AppendElement(std::move(certBytes));
}
maybePeerCertsBytes.emplace(std::move(peerCertsBytes));
}
RefPtr<SharedCertVerifier> certVerifier(GetDefaultCertVerifier());
MOZ_ASSERT(certVerifier,
"Certificate verifier uninitialized in TLS handshake callback?");
@ -1081,9 +1102,10 @@ static void RebuildVerifiedCertificateInformation(PRFileDesc* fd,
UniqueCERTCertList builtChain;
const bool saveIntermediates = false;
mozilla::pkix::Result rv = certVerifier->VerifySSLServerCert(
cert, stapledOCSPResponse, sctsFromTLSExtension, mozilla::pkix::Now(),
infoObject, infoObject->GetHostName(), builtChain, saveIntermediates,
flags, infoObject->GetOriginAttributes(), &evOidPolicy,
cert, mozilla::pkix::Now(), infoObject, infoObject->GetHostName(),
builtChain, flags, maybePeerCertsBytes, stapledOCSPResponse,
sctsFromTLSExtension, infoObject->GetOriginAttributes(),
saveIntermediates, &evOidPolicy,
nullptr, // OCSP stapling telemetry
nullptr, // key size telemetry
nullptr, // SHA-1 telemetry

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

@ -1181,23 +1181,25 @@ nsresult VerifyCertAtTime(nsIX509Cert* aCert,
mozilla::pkix::Result result;
if (!aHostname.IsVoid() && aUsage == certificateUsageSSLServer) {
result = certVerifier->VerifySSLServerCert(
nssCert,
Maybe<nsTArray<uint8_t>>(), // stapledOCSPResponse
Maybe<nsTArray<uint8_t>>(), // sctsFromTLSExtension
aTime,
nullptr, // Assume no context
aHostname, resultChain,
false, // don't save intermediates
aFlags, OriginAttributes(), &evOidPolicy);
result =
certVerifier->VerifySSLServerCert(nssCert, aTime,
nullptr, // Assume no context
aHostname, resultChain, aFlags,
Nothing(), // extraCertificates
Nothing(), // stapledOCSPResponse
Nothing(), // sctsFromTLSExtension
OriginAttributes(),
false, // don't save intermediates
&evOidPolicy);
} else {
const nsCString& flatHostname = PromiseFlatCString(aHostname);
result = certVerifier->VerifyCert(
nssCert.get(), aUsage, aTime,
nullptr, // Assume no context
aHostname.IsVoid() ? nullptr : flatHostname.get(), resultChain, aFlags,
Maybe<nsTArray<uint8_t>>(), // stapledOCSPResponse
Maybe<nsTArray<uint8_t>>(), // sctsFromTLSExtension
Nothing(), // extraCertificates
Nothing(), // stapledOCSPResponse
Nothing(), // sctsFromTLSExtension
OriginAttributes(), &evOidPolicy);
}

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

@ -1051,15 +1051,14 @@ nsresult nsSiteSecurityService::ProcessPKPHeader(
// anyway).
CertVerifier::Flags flags = CertVerifier::FLAG_LOCAL_ONLY |
CertVerifier::FLAG_TLS_IGNORE_STATUS_REQUEST;
if (certVerifier->VerifySSLServerCert(
nssCert,
Maybe<nsTArray<uint8_t>>(), // stapledOCSPResponse
Maybe<nsTArray<uint8_t>>(), // sctsFromTLSExtension
now, nullptr, // pinarg
host, // hostname
certList,
false, // don't store intermediates
flags, aOriginAttributes) != mozilla::pkix::Success) {
if (certVerifier->VerifySSLServerCert(nssCert, now, nullptr, // pinarg
host, certList, flags,
Nothing(), // extraCertificates
Nothing(), // stapledOCSPResponse
Nothing(), // sctsFromTLSExtension
aOriginAttributes,
false // don't store intermediates
) != mozilla::pkix::Success) {
return NS_ERROR_FAILURE;
}