/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// During certificate authentication, we call CertVerifier::VerifySSLServerCert. // This function may make zero or more HTTP requests (e.g. to gather revocation // information). Our fetching logic for these requests processes them on the // socket transport service thread. // // Because the connection for which we are verifying the certificate is // happening on the socket transport thread, if our cert auth hook were to call // VerifySSLServerCert directly, there would be a deadlock: VerifySSLServerCert // would cause an event to be asynchronously posted to the socket transport // thread, and then it would block the socket transport thread waiting to be // notified of the HTTP response. However, the HTTP request would never actually // be processed because the socket transport thread would be blocked and so it // wouldn't be able process HTTP requests. // // Consequently, when we are asked to verify a certificate, we must always call // VerifySSLServerCert on another thread. To accomplish this, our auth cert hook // dispatches a SSLServerCertVerificationJob to a pool of background threads, // and then immediately returns SECWouldBlock to libssl. These jobs are where // VerifySSLServerCert is actually called. // // When our auth cert hook returns SECWouldBlock, libssl will carry on the // handshake while we validate the certificate. This will free up the socket // transport thread so that HTTP requests--including the OCSP requests needed // for cert verification as mentioned above--can be processed. // // Once VerifySSLServerCert returns, the cert verification job dispatches a // SSLServerCertVerificationResult to the socket transport thread; the // SSLServerCertVerificationResult will notify libssl that the certificate // authentication is complete. Once libssl is notified that the authentication // is complete, it will continue the TLS handshake (if it hasn't already // finished) and it will begin allowing us to send/receive data on the // connection. // // Timeline of events (for connections managed by the socket transport service): // // * libssl calls SSLServerCertVerificationJob::Dispatch on the socket // transport thread. // * SSLServerCertVerificationJob::Dispatch queues a job // (instance of SSLServerCertVerificationJob) to its background thread // pool and returns. // * One of the background threads calls CertVerifier::VerifySSLServerCert, // which may enqueue some HTTP request(s) onto the socket transport thread, // and then blocks that background thread waiting for the responses and/or // timeouts or errors for those requests. // * Once those HTTP responses have all come back or failed, the // CertVerifier::VerifySSLServerCert function returns a result indicating // that the validation succeeded or failed. // * If the validation succeeded, then a SSLServerCertVerificationResult // event is posted to the socket transport thread, and the cert // verification thread becomes free to verify other certificates. // * Otherwise, we do cert override processing to see if the validation // error can be convered by override rules. The result of this processing // is similarly dispatched in a SSLServerCertVerificationResult. // * The SSLServerCertVerificationResult event will either wake up the // socket (using SSL_AuthCertificateComplete) if validation succeeded or // there was an error override, or it will set an error flag so that the // next I/O operation on the socket will fail, causing the socket transport // thread to close the connection. // // SSLServerCertVerificationResult must be dispatched to the socket transport // thread because we must only call SSL_* functions on the socket transport // thread since they may do I/O, because many parts of NSSSocketControl and the // PSM NSS I/O layer are not thread-safe, and because we need the event to // interrupt the PR_Poll that may waiting for I/O on the socket for which we // are validating the cert. // // When socket process is enabled, libssl is running on socket process. To // perform certificate authentication with CertVerifier, we have to send all // needed information to parent process and send the result back to socket // process via IPC. The workflow is described below. // 1. In AuthCertificateHookInternal(), we call RemoteProcessCertVerification() // instead of SSLServerCertVerificationJob::Dispatch when we are on socket // process. // 2. In RemoteProcessCertVerification(), PVerifySSLServerCert actors will be // created on IPDL background thread for carrying needed information via IPC. // 3. On parent process, VerifySSLServerCertParent is created and it calls // SSLServerCertVerificationJob::Dispatch for doing certificate verification // on one of CertVerificationThreads. // 4. When validation is done, OnVerifiedSSLServerCertSuccess IPC message is // sent through the IPDL background thread when // CertVerifier::VerifySSLServerCert returns Success. Otherwise, // OnVerifiedSSLServerCertFailure is sent. // 5. After setp 4, PVerifySSLServerCert actors will be released. The // verification result will be dispatched via // SSLServerCertVerificationResult.
// do not use a nsCOMPtr to avoid static initializer/destructor
nsIThreadPool* gCertVerificationThreadPool = nullptr;
// Called when the socket transport thread starts, to initialize the SSL cert // verification thread pool. By tying the thread pool startup/shutdown directly // to the STS thread's lifetime, we ensure that they are *always* available for // SSL connections and that there are no races during startup and especially // shutdown. (Previously, we have had multiple problems with races in PSM // background threads, and the race-prevention/shutdown logic used there is // brittle. Since this service is critical to things like downloading updates, // we take no chances.) Also, by doing things this way, we avoid the need for // locks, since gCertVerificationThreadPool is only ever accessed on the socket // transport thread. void InitializeSSLServerCertVerificationThreads() { // TODO: tuning, make parameters preferences
gCertVerificationThreadPool = new nsThreadPool();
NS_ADDREF(gCertVerificationThreadPool);
// Called when the socket transport thread finishes, to destroy the thread // pool. Since the socket transport service has stopped processing events, it // will not attempt any more SSL I/O operations, so it is clearly safe to shut // down the SSL cert verification infrastructure. Also, the STS will not // dispatch many SSL verification result events at this point, so any pending // cert verifications will (correctly) fail at the point they are dispatched. // // The other shutdown race condition that is possible is a race condition with // shutdown of the nsNSSComponent service. We use the // nsNSSShutdownPreventionLock where needed (not here) to prevent that. void StopSSLServerCertVerificationThreads() { if (gCertVerificationThreadPool) {
gCertVerificationThreadPool->Shutdown();
NS_RELEASE(gCertVerificationThreadPool);
}
}
// A probe value of 1 means "no error".
uint32_t MapOverridableErrorToProbeValue(PRErrorCode errorCode) { switch (errorCode) { case SEC_ERROR_UNKNOWN_ISSUER: return 2; case SEC_ERROR_CA_CERT_INVALID: return 3; case SEC_ERROR_UNTRUSTED_ISSUER: return 4; case SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE: return 5; case SEC_ERROR_UNTRUSTED_CERT: return 6; case SEC_ERROR_INADEQUATE_KEY_USAGE: return 7; case SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED: return 8; case SSL_ERROR_BAD_CERT_DOMAIN: return 9; case SEC_ERROR_EXPIRED_CERTIFICATE: return 10; case mozilla::pkix::MOZILLA_PKIX_ERROR_CA_CERT_USED_AS_END_ENTITY: return 11; case mozilla::pkix::MOZILLA_PKIX_ERROR_V1_CERT_USED_AS_CA: return 12; case mozilla::pkix::MOZILLA_PKIX_ERROR_INADEQUATE_KEY_SIZE: return 13; case mozilla::pkix::MOZILLA_PKIX_ERROR_NOT_YET_VALID_CERTIFICATE: return 14; case mozilla::pkix::MOZILLA_PKIX_ERROR_NOT_YET_VALID_ISSUER_CERTIFICATE: return 15; case SEC_ERROR_INVALID_TIME: return 16; case mozilla::pkix::MOZILLA_PKIX_ERROR_EMPTY_ISSUER_NAME: return 17; case mozilla::pkix::MOZILLA_PKIX_ERROR_ADDITIONAL_POLICY_CONSTRAINT_FAILED: return 18; case mozilla::pkix::MOZILLA_PKIX_ERROR_SELF_SIGNED_CERT: return 19; case mozilla::pkix::MOZILLA_PKIX_ERROR_MITM_DETECTED: return 20; case mozilla::pkix::
MOZILLA_PKIX_ERROR_INSUFFICIENT_CERTIFICATE_TRANSPARENCY: return 21;
}
NS_WARNING( "Unknown certificate error code. Does MapOverridableErrorToProbeValue " "handle everything in CategorizeCertificateError?"); return 0;
}
// Since FATAL_ERROR_FLAG is 0x800, fatal error values are much larger than // non-fatal error values. To conserve space, we remap these so they start at // (decimal) 90 instead of 0x800. Currently there are ~50 non-fatal errors // mozilla::pkix might return, so saving space for 90 should be sufficient // (similarly, there are 4 fatal errors, so saving space for 10 should also // be sufficient).
static_assert(
FATAL_ERROR_FLAG == 0x800, "mozilla::pkix::FATAL_ERROR_FLAG is not what we were expecting"); if (probeValue & FATAL_ERROR_FLAG) {
probeValue ^= FATAL_ERROR_FLAG;
probeValue += 90;
} return probeValue;
}
// If the given PRErrorCode is an overridable certificate error, return which // category (trust, time, domain mismatch) it falls in. If it is not // overridable, return Nothing.
Maybe<nsITransportSecurityInfo::OverridableErrorCategory>
CategorizeCertificateError(PRErrorCode certificateError) { switch (certificateError) { case SEC_ERROR_CA_CERT_INVALID: case SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED: case SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE: case SEC_ERROR_UNKNOWN_ISSUER: case mozilla::pkix::MOZILLA_PKIX_ERROR_ADDITIONAL_POLICY_CONSTRAINT_FAILED: case mozilla::pkix::MOZILLA_PKIX_ERROR_CA_CERT_USED_AS_END_ENTITY: case mozilla::pkix::MOZILLA_PKIX_ERROR_EMPTY_ISSUER_NAME: case mozilla::pkix::MOZILLA_PKIX_ERROR_INADEQUATE_KEY_SIZE: case mozilla::pkix::
MOZILLA_PKIX_ERROR_INSUFFICIENT_CERTIFICATE_TRANSPARENCY: case mozilla::pkix::MOZILLA_PKIX_ERROR_MITM_DETECTED: case mozilla::pkix::MOZILLA_PKIX_ERROR_NOT_YET_VALID_ISSUER_CERTIFICATE: case mozilla::pkix::MOZILLA_PKIX_ERROR_SELF_SIGNED_CERT: case mozilla::pkix::MOZILLA_PKIX_ERROR_V1_CERT_USED_AS_CA: return Some(
nsITransportSecurityInfo::OverridableErrorCategory::ERROR_TRUST);
case SSL_ERROR_BAD_CERT_DOMAIN: return Some(
nsITransportSecurityInfo::OverridableErrorCategory::ERROR_DOMAIN);
case SEC_ERROR_EXPIRED_CERTIFICATE: case SEC_ERROR_INVALID_TIME: case mozilla::pkix::MOZILLA_PKIX_ERROR_NOT_YET_VALID_CERTIFICATE: return Some(
nsITransportSecurityInfo::OverridableErrorCategory::ERROR_TIME);
default: break;
} return Nothing();
}
// Helper function to determine if overrides are allowed for this host. // Overrides are not allowed for known HSTS hosts or hosts with pinning // information. However, IP addresses can never be HSTS hosts and don't have // pinning information. static nsresult OverrideAllowedForHost(
uint64_t aPtrForLog, const nsACString& aHostname, const OriginAttributes& aOriginAttributes, /*out*/ bool& aOverrideAllowed) {
aOverrideAllowed = false;
// If this is an IP address, overrides are allowed, because an IP address is // never an HSTS host. nsISiteSecurityService takes this into account // already, but the real problem here is that calling NS_NewURI with an IPv6 // address fails. We do this to avoid that. A more comprehensive fix would be // to have Necko provide an nsIURI to PSM and to use that here (and // everywhere). However, that would be a wide-spanning change. if (net_IsValidIPv6Addr(aHostname)) {
aOverrideAllowed = true; return NS_OK;
}
// If this is an HTTP Strict Transport Security host or a pinned host and the // certificate is bad, don't allow overrides (RFC 6797 section 12.1). bool strictTransportSecurityEnabled = false; bool isStaticallyPinned = false;
nsCOMPtr<nsISiteSecurityService> sss(do_GetService(NS_SSSERVICE_CONTRACTID)); if (!sss) {
MOZ_LOG(
gPIPNSSLog, LogLevel::Debug,
("[0x%" PRIx64 "] Couldn't get nsISiteSecurityService to check HSTS",
aPtrForLog)); return NS_ERROR_FAILURE;
}
nsCOMPtr<nsIURI> uri;
nsresult rv = NS_NewURI(getter_AddRefs(uri), "https://"_ns + aHostname); if (NS_FAILED(rv)) {
MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
("[0x%" PRIx64 "] Creating new URI failed", aPtrForLog)); return rv;
}
// This function assumes that we will only use the SPDY connection coalescing // feature on connections where we have negotiated SPDY using NPN. If we ever // talk SPDY without having negotiated it with SPDY, this code will give wrong // and perhaps unsafe results. // // Returns SECSuccess on the initial handshake of all connections, on // renegotiations for any connections where we did not negotiate SPDY, or on any // SPDY connection where the server's certificate did not change. // // Prohibit changing the server cert only if we negotiated SPDY, // in order to support SPDY's cross-origin connection pooling. static SECStatus BlockServerCertChangeForSpdy(
NSSSocketControl* socketControl, const UniqueCERTCertificate& serverCert) { if (!socketControl->IsHandshakeCompleted()) { // first handshake on this connection, not a // renegotiation. return SECSuccess;
}
// Filter out sockets that did not neogtiate SPDY via NPN
nsCOMPtr<nsITransportSecurityInfo> securityInfo;
nsresult rv = socketControl->GetSecurityInfo(getter_AddRefs(securityInfo));
MOZ_ASSERT(NS_SUCCEEDED(rv), "GetSecurityInfo() failed during renegotiation"); if (NS_FAILED(rv) || !securityInfo) {
PR_SetError(SEC_ERROR_LIBRARY_FAILURE, 0); return SECFailure;
}
nsAutoCString negotiatedNPN;
rv = securityInfo->GetNegotiatedNPN(negotiatedNPN);
MOZ_ASSERT(NS_SUCCEEDED(rv), "GetNegotiatedNPN() failed during renegotiation");
if (NS_SUCCEEDED(rv) && !StringBeginsWith(negotiatedNPN, "spdy/"_ns)) { return SECSuccess;
} // If GetNegotiatedNPN() failed we will assume spdy for safety's safe if (NS_FAILED(rv)) {
MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
("BlockServerCertChangeForSpdy failed GetNegotiatedNPN() call." " Assuming spdy."));
}
// Check to see if the cert has actually changed
nsCOMPtr<nsIX509Cert> cert(socketControl->GetServerCert()); if (!cert) {
PR_SetError(SEC_ERROR_LIBRARY_FAILURE, 0); return SECFailure;
}
nsTArray<uint8_t> certDER; if (NS_FAILED(cert->GetRawDER(certDER))) {
PR_SetError(SEC_ERROR_LIBRARY_FAILURE, 0); return SECFailure;
} if (certDER.Length() == serverCert->derCert.len &&
memcmp(certDER.Elements(), serverCert->derCert.data, certDER.Length()) ==
0) { return SECSuccess;
}
// Report an error - changed cert is confirmed
MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
("SPDY refused to allow new cert during renegotiation"));
PR_SetError(SSL_ERROR_RENEGOTIATION_NOT_ALLOWED, 0); return SECFailure;
}
void GatherTelemetryForSingleSCT(const ct::VerifiedSCT& verifiedSct) { // See SSL_SCTS_VERIFICATION_STATUS in Histograms.json.
uint32_t verificationStatus = 0; switch (verifiedSct.logState) { case ct::CTLogState::Admissible:
verificationStatus = 1; break; case ct::CTLogState::Retired:
verificationStatus = 5; break;
}
Telemetry::Accumulate(Telemetry::SSL_SCTS_VERIFICATION_STATUS,
verificationStatus);
}
void GatherCertificateTransparencyTelemetry( const nsTArray<uint8_t>& rootCert, const CertificateTransparencyInfo& info) { if (!info.enabled) { // No telemetry is gathered when CT is disabled. return;
}
for (const ct::VerifiedSCT& sct : info.verifyResult.verifiedScts) {
GatherTelemetryForSingleSCT(sct);
}
// See SSL_SCTS_VERIFICATION_STATUS in Histograms.json. for (size_t i = 0; i < info.verifyResult.decodingErrors; ++i) {
Telemetry::Accumulate(Telemetry::SSL_SCTS_VERIFICATION_STATUS, 0);
} for (size_t i = 0; i < info.verifyResult.sctsFromUnknownLogs; ++i) {
Telemetry::Accumulate(Telemetry::SSL_SCTS_VERIFICATION_STATUS, 2);
} for (size_t i = 0; i < info.verifyResult.sctsWithInvalidSignatures; ++i) {
Telemetry::Accumulate(Telemetry::SSL_SCTS_VERIFICATION_STATUS, 3);
} for (size_t i = 0; i < info.verifyResult.sctsWithInvalidTimestamps; ++i) {
Telemetry::Accumulate(Telemetry::SSL_SCTS_VERIFICATION_STATUS, 4);
}
// See SSL_SCTS_ORIGIN in Histograms.json. for (size_t i = 0; i < info.verifyResult.embeddedSCTs; ++i) {
Telemetry::Accumulate(Telemetry::SSL_SCTS_ORIGIN, 1);
} for (size_t i = 0; i < info.verifyResult.sctsFromTLSHandshake; ++i) {
Telemetry::Accumulate(Telemetry::SSL_SCTS_ORIGIN, 2);
} for (size_t i = 0; i < info.verifyResult.sctsFromOCSP; ++i) {
Telemetry::Accumulate(Telemetry::SSL_SCTS_ORIGIN, 3);
}
// Handle the histogram of SCTs counts.
uint32_t sctsCount = static_cast<uint32_t>(info.verifyResult.verifiedScts.size()); // Note that sctsCount can also be 0 in case we've received SCT binary data, // but it failed to parse (e.g. due to unsupported CT protocol version).
Telemetry::Accumulate(Telemetry::SSL_SCTS_PER_CONNECTION, sctsCount);
// Report CT Policy compliance by CA. if (info.policyCompliance.isSome() &&
*info.policyCompliance != ct::CTPolicyCompliance::Compliant) {
AccumulateTelemetryForRootCA(
Telemetry::SSL_CT_POLICY_NON_COMPLIANT_CONNECTIONS_BY_CA_2, rootCert);
}
}
// This function collects telemetry about certs. It will be called on one of // CertVerificationThread. When the socket process is used this will be called // on the parent process. staticvoid CollectCertTelemetry(
mozilla::pkix::Result aCertVerificationResult, EVStatus aEVStatus,
CertVerifier::OCSPStaplingStatus aOcspStaplingStatus,
KeySizeStatus aKeySizeStatus, const PinningTelemetryInfo& aPinningTelemetryInfo, const nsTArray<nsTArray<uint8_t>>& aBuiltCertChain, const CertificateTransparencyInfo& aCertificateTransparencyInfo, const IssuerSources& issuerSources) {
uint32_t evStatus = (aCertVerificationResult != Success) ? 0 // 0 = Failure
: (aEVStatus != EVStatus::EV) ? 1 // 1 = DV
: 2; // 2 = EV
Telemetry::Accumulate(Telemetry::CERT_EV_STATUS, evStatus);
if (aOcspStaplingStatus != CertVerifier::OCSP_STAPLING_NEVER_CHECKED) {
Telemetry::Accumulate(Telemetry::SSL_OCSP_STAPLING, aOcspStaplingStatus);
}
if (aKeySizeStatus != KeySizeStatus::NeverChecked) {
Telemetry::Accumulate(Telemetry::CERT_CHAIN_KEY_SIZE_STATUS, static_cast<uint32_t>(aKeySizeStatus));
}
if (aPinningTelemetryInfo.accumulateForRoot) {
Telemetry::Accumulate(Telemetry::CERT_PINNING_FAILURES_BY_CA_2,
aPinningTelemetryInfo.rootBucket);
}
if (aPinningTelemetryInfo.accumulateResult) {
MOZ_ASSERT(aPinningTelemetryInfo.certPinningResultHistogram.isSome());
Telemetry::Accumulate(
aPinningTelemetryInfo.certPinningResultHistogram.value(),
aPinningTelemetryInfo.certPinningResultBucket);
}
Maybe<nsITransportSecurityInfo::OverridableErrorCategory>
maybeOverridableErrorCategory =
CategorizeCertificateError(aCertVerificationError); // If this isn't an overridable error, return it now. This will stop the // connection and report the given error. if (!maybeOverridableErrorCategory.isSome()) { return aCertVerificationError;
}
aOverridableErrorCategory = *maybeOverridableErrorCategory;
nsresult nrv = gCertVerificationThreadPool->Dispatch(job, NS_DISPATCH_NORMAL); if (NS_FAILED(nrv)) { // We can't call SetCertVerificationResult here to change // mCertVerificationState because SetCertVerificationResult will call // libssl functions that acquire SSL locks that are already being held at // this point. However, we can set an error with PR_SetError and return // SECFailure, and the correct thing will happen (the error will be // propagated and this connection will be terminated).
PRErrorCode error = nrv == NS_ERROR_OUT_OF_MEMORY ? PR_OUT_OF_MEMORY_ERROR
: PR_INVALID_STATE_ERROR;
PR_SetError(error, 0); return SECFailure;
}
NS_IMETHODIMP
SSLServerCertVerificationJob::Run() { // Runs on a cert verification thread and only on parent process.
MOZ_ASSERT(XRE_IsParentProcess());
RefPtr<SharedCertVerifier> certVerifier(GetDefaultCertVerifier()); if (!certVerifier) { // We can't release this off the STS thread because some parts of it // are not threadsafe. Just leak mResultTask.
Unused << mResultTask.forget(); return NS_ERROR_FAILURE;
}
nsresult rv = mResultTask->Dispatch(
std::move(builtChainBytesArray), std::move(mPeerCertChain),
TransportSecurityInfo::ConvertCertificateTransparencyInfoToStatus(
certificateTransparencyInfo),
evStatus, true, 0,
nsITransportSecurityInfo::OverridableErrorCategory::ERROR_UNSET,
isCertChainRootBuiltInRoot, mProviderFlags, madeOCSPRequests); if (NS_FAILED(rv)) { // We can't release this off the STS thread because some parts of it // are not threadsafe. Just leak mResultTask.
Unused << mResultTask.forget();
} return rv;
}
// NB: finalError may be 0 here, in which the connection will continue.
nsresult rv = mResultTask->Dispatch(
std::move(builtChainBytesArray), std::move(mPeerCertChain),
TransportSecurityInfo::ConvertCertificateTransparencyInfoToStatus(
certificateTransparencyInfo),
EVStatus::NotEV, false, finalError, overridableErrorCategory, // If the certificate verifier returned Result::ERROR_BAD_CERT_DOMAIN, // a chain was built, so isCertChainRootBuiltInRoot is valid and // potentially useful. Otherwise, assume no chain was built.
result == Result::ERROR_BAD_CERT_DOMAIN ? isCertChainRootBuiltInRoot
: false,
mProviderFlags, madeOCSPRequests); if (NS_FAILED(rv)) { // We can't release this off the STS thread because some parts of it // are not threadsafe. Just leak mResultTask.
Unused << mResultTask.forget();
} return rv;
}
// Takes information needed for cert verification, does some consistency // checks and calls SSLServerCertVerificationJob::Dispatch.
SECStatus AuthCertificateHookInternal(
CommonSocketControl* socketControl, constvoid* aPtrForLogging, const nsACString& hostName, nsTArray<nsTArray<uint8_t>>&& peerCertChain,
Maybe<nsTArray<uint8_t>>& stapledOCSPResponse,
Maybe<nsTArray<uint8_t>>& sctsFromTLSExtension,
Maybe<DelegatedCredentialInfo>& dcInfo, uint32_t providerFlags,
uint32_t certVerifierFlags) { // Runs on the socket transport thread
// We *must* do certificate verification on a background thread because // we need the socket transport thread to be free for our OCSP requests, // and we *want* to do certificate verification on a background thread // because of the performance benefits of doing so. return SSLServerCertVerificationJob::Dispatch(
addr, socketControl, std::move(peerCertChain), hostName,
socketControl->GetPort(), socketControl->GetOriginAttributes(),
stapledOCSPResponse, sctsFromTLSExtension, dcInfo, providerFlags, Now(),
certVerifierFlags, resultTask);
}
// Extracts whatever information we need out of fd (using SSL_*) and passes it // to AuthCertificateHookInternal. AuthCertificateHookInternal will call // SSLServerCertVerificationJob::Dispatch. SSLServerCertVerificationJob // should never do anything with fd except logging.
SECStatus AuthCertificateHook(void* arg, PRFileDesc* fd, PRBool checkSig,
PRBool isServer) {
MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
("[%p] starting AuthCertificateHook\n", fd));
// Modern libssl always passes PR_TRUE for checkSig, and we have no means of // doing verification without checking signatures.
MOZ_ASSERT(checkSig, "AuthCertificateHook: checkSig unexpectedly false");
// PSM never causes libssl to call this function with PR_TRUE for isServer, // and many things in PSM assume that we are a client.
MOZ_ASSERT(!isServer, "AuthCertificateHook: isServer unexpectedly true");
// SSL_PeerStapledOCSPResponses will never return a non-empty response if // OCSP stapling wasn't enabled because libssl wouldn't have let the server // return a stapled OCSP response. // We don't own these pointers. const SECItemArray* csa = SSL_PeerStapledOCSPResponses(fd);
Maybe<nsTArray<uint8_t>> stapledOCSPResponse; // we currently only support single stapled responses if (csa && csa->len == 1) {
stapledOCSPResponse.emplace();
stapledOCSPResponse->SetCapacity(csa->items[0].len);
stapledOCSPResponse->AppendElements(csa->items[0].data, csa->items[0].len);
}
// Get DC information
Maybe<DelegatedCredentialInfo> dcInfo;
SSLPreliminaryChannelInfo channelPreInfo;
rv = SSL_GetPreliminaryChannelInfo(fd, &channelPreInfo, sizeof(channelPreInfo)); if (rv != SECSuccess) {
PR_SetError(PR_INVALID_STATE_ERROR, 0); return SECFailure;
} if (channelPreInfo.peerDelegCred) {
dcInfo.emplace(DelegatedCredentialInfo(channelPreInfo.signatureScheme,
channelPreInfo.authKeyBits));
}
// If we configured an ECHConfig and NSS returned the public name // for verification, ECH was rejected. Proceed, verifying to the // public name. The result determines how NSS will fail (i.e. with // any provided retry_configs if successful). See draft-ietf-tls-esni-08.
nsCString echConfig;
nsresult nsrv = socketInfo->GetEchConfig(echConfig); bool verifyToEchPublicName =
NS_SUCCEEDED(nsrv) && echConfig.Length() && channelPreInfo.echPublicName;
// Takes information needed for cert verification, does some consistency // checks and calls SSLServerCertVerificationJob::Dispatch. // This function is used for Quic.
SECStatus AuthCertificateHookWithInfo(
CommonSocketControl* socketControl, const nsACString& aHostName, constvoid* aPtrForLogging, nsTArray<nsTArray<uint8_t>>&& peerCertChain,
Maybe<nsTArray<nsTArray<uint8_t>>>& stapledOCSPResponses,
Maybe<nsTArray<uint8_t>>& sctsFromTLSExtension, uint32_t providerFlags) { if (peerCertChain.IsEmpty()) {
PR_SetError(PR_INVALID_STATE_ERROR, 0); return SECFailure;
}
// we currently only support single stapled responses
Maybe<nsTArray<uint8_t>> stapledOCSPResponse; if (stapledOCSPResponses && (stapledOCSPResponses->Length() == 1)) {
stapledOCSPResponse.emplace(stapledOCSPResponses->ElementAt(0).Clone());
}
if (mSucceeded &&
(mBuiltChain.IsEmpty() || mFinalError != 0 ||
mOverridableErrorCategory !=
nsITransportSecurityInfo::OverridableErrorCategory::ERROR_UNSET)) {
MOZ_ASSERT_UNREACHABLE( "if certificate verification succeeded without overridden errors, the " "built chain shouldn't be empty and any error bits should be unset");
mSucceeded = false;
mFinalError = SEC_ERROR_LIBRARY_FAILURE;
} // Note that mSucceeded can be false while mFinalError is 0, in which case // the connection will proceed. if (!mSucceeded && mPeerCertChain.IsEmpty()) {
MOZ_ASSERT_UNREACHABLE( "if certificate verification failed, the peer chain shouldn't be " "empty");
mFinalError = SEC_ERROR_LIBRARY_FAILURE;
}
nsresult rv;
nsCOMPtr<nsIEventTarget> stsTarget =
do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
MOZ_ASSERT(stsTarget, "Failed to get socket transport service event target"); if (!stsTarget) { // This has to be released on STS; just leak it
Unused << mSocketControl.forget(); return NS_ERROR_FAILURE;
}
rv = stsTarget->Dispatch(this, NS_DISPATCH_NORMAL);
MOZ_ASSERT(NS_SUCCEEDED(rv), "Failed to dispatch SSLServerCertVerificationResult"); return rv;
}
if (mSucceeded) {
MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
("SSLServerCertVerificationResult::Run setting NEW cert"));
nsTArray<uint8_t> certBytes(mBuiltChain.ElementAt(0).Clone());
nsCOMPtr<nsIX509Cert> cert(new nsNSSCertificate(std::move(certBytes)));
mSocketControl->SetServerCert(cert, mEVStatus);
mSocketControl->SetSucceededCertChain(std::move(mBuiltChain));
} else {
nsTArray<uint8_t> certBytes(mPeerCertChain.ElementAt(0).Clone());
nsCOMPtr<nsIX509Cert> cert(new nsNSSCertificate(std::move(certBytes))); // Certificate validation failed; store the peer certificate chain on // mSocketControl so it can be used for error reporting.
mSocketControl->SetFailedCertChain(std::move(mPeerCertChain)); if (mOverridableErrorCategory !=
nsITransportSecurityInfo::OverridableErrorCategory::ERROR_UNSET) {
mSocketControl->SetStatusErrorBits(cert, mOverridableErrorCategory);
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.