/* -*- 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/. */
// #define DEBUG_SSL_VERBOSE //Enable this define to get minimal // reports when doing SSL read/write
// #define DUMP_BUFFER //Enable this define along with // DEBUG_SSL_VERBOSE to dump SSL // read/write buffer to a log. // Uses PR_LOG except on Mac where // we always write out to our own // file.
namespace {
// The NSSSocketInfo tls flags are meant to be opaque to most calling // applications but provide a mechanism for direct TLS manipulation when // experimenting with new features in the scope of a single socket. They do not // create a persistent ABI. // // Use of these flags creates a new 'sharedSSLState' so existing states for // intolerance are not carried to sockets that use these flags (and intolerance // they discover does not impact other normal sockets not using the flags.) // // Their current definitions are: // // bits 0-2 (mask 0x07) specify the max tls version // 0 means no override 1->4 are 1.0, 1.1, 1.2, 1.3, 4->7 unused // bits 3-5 (mask 0x38) specify the tls fallback limit // 0 means no override, values 1->4 match prefs // bit 6 (mask 0x40) was used to specify compat mode. Temporarily reserved.
if (socketInfo->IsCanceled()) {
PRErrorCode err = socketInfo->GetErrorCode();
PR_SetError(err, 0); if (op == reading || op == writing) { // We must do TLS intolerance checks for reads and writes, for timeouts // in particular.
(void)checkHandshake(-1, op == reading, fd, socketInfo);
}
// If we get here, it is probably because cert verification failed and this // is the first I/O attempt since that failure. return nullptr;
}
// returns true if we should retry the handshake bool nsSSLIOLayerHelpers::rememberIntolerantAtVersion( const nsACString& hostName, uint16_t port, uint16_t minVersion,
uint16_t intolerant, PRErrorCode intoleranceReason) { if (intolerant <= minVersion || fallbackLimitReached(hostName, intolerant)) { // We can't fall back any further. Assume that intolerance isn't the issue.
forgetIntolerance(hostName, port); returnfalse;
}
nsCString key;
getSiteKey(hostName, port, key);
MutexAutoLock lock(mutex);
IntoleranceEntry entry; if (mTLSIntoleranceInfo.Get(key, &entry)) {
entry.AssertInvariant(); if (intolerant <= entry.tolerant) { // We already know the server is tolerant at an equal or higher version. returnfalse;
} if ((entry.intolerant != 0 && intolerant >= entry.intolerant)) { // We already know that the server is intolerant at a lower version. returntrue;
}
} else {
entry.tolerant = 0;
}
MutexAutoLock lock(mutex); if (!mTLSIntoleranceInfo.Get(key, &entry)) { return;
}
}
entry.AssertInvariant();
if (entry.intolerant != 0) { // We've tried connecting at a higher range but failed, so try at the // version we haven't tried yet, unless we have reached the minimum. if (range.min < entry.intolerant) {
range.max = entry.intolerant - 1;
}
}
}
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("[%p] Shutting down socket", fd));
// Take the owning reference from the layer. See the corresponding comment in // nsSSLIOLayerAddToSocket where this gets set.
RefPtr<NSSSocketControl> socketInfo(
already_AddRefed((NSSSocketControl*)fd->secret));
fd->secret = nullptr; if (!socketInfo) { return PR_FAILURE;
}
return socketInfo->CloseSocketAndDestroy();
}
#ifdefined(DEBUG_SSL_VERBOSE) && defined(DUMP_BUFFER) // Dumps a (potentially binary) buffer using SSM_DEBUG. (We could have used // the version in ssltrace.c, but that's specifically tailored to SSLTRACE.) # define DUMPBUF_LINESIZE 24 staticvoid nsDumpBuffer(unsignedchar* buf, int len) { char hexbuf[DUMPBUF_LINESIZE * 3 + 1]; char chrbuf[DUMPBUF_LINESIZE + 1]; staticconstchar* hex = "0123456789abcdef"; int i = 0; int l = 0; char ch; char* c; char* h; if (len == 0) return;
hexbuf[DUMPBUF_LINESIZE * 3] = '\0';
chrbuf[DUMPBUF_LINESIZE] = '\0';
(void)memset(hexbuf, 0x20, DUMPBUF_LINESIZE * 3);
(void)memset(chrbuf, 0x20, DUMPBUF_LINESIZE);
h = hexbuf;
c = chrbuf;
while (i < len) {
ch = buf[i];
if (l == DUMPBUF_LINESIZE) {
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("%s%s\n", hexbuf, chrbuf));
(void)memset(hexbuf, 0x20, DUMPBUF_LINESIZE * 3);
(void)memset(chrbuf, 0x20, DUMPBUF_LINESIZE);
h = hexbuf;
c = chrbuf;
l = 0;
}
// Convert a character to hex.
*h++ = hex[(ch >> 4) & 0xf];
*h++ = hex[ch & 0xf];
h++;
// Put the character (if it's printable) into the character buffer. if ((ch >= 0x20) && (ch <= 0x7e)) {
*c++ = ch;
} else {
*c++ = '.';
}
i++;
l++;
}
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("%s%s\n", hexbuf, chrbuf));
}
uint32_t tlsIntoleranceTelemetryBucket(PRErrorCode err) { // returns a numeric code for where we track various errors in telemetry // only errors that cause version fallback are tracked, // so this is also used to determine which errors can cause version fallback switch (err) { case SSL_ERROR_BAD_MAC_ALERT: return 1; case SSL_ERROR_BAD_MAC_READ: return 2; case SSL_ERROR_HANDSHAKE_FAILURE_ALERT: return 3; case SSL_ERROR_HANDSHAKE_UNEXPECTED_ALERT: return 4; case SSL_ERROR_ILLEGAL_PARAMETER_ALERT: return 6; case SSL_ERROR_NO_CYPHER_OVERLAP: return 7; case SSL_ERROR_UNSUPPORTED_VERSION: return 10; case SSL_ERROR_PROTOCOL_VERSION_ALERT: return 11; case SSL_ERROR_BAD_HANDSHAKE_HASH_VALUE: return 13; case SSL_ERROR_DECODE_ERROR_ALERT: return 14; case PR_CONNECT_RESET_ERROR: return 16; case PR_END_OF_FILE_ERROR: return 17; case SSL_ERROR_INTERNAL_ERROR_ALERT: return 18; default: return 0;
}
}
bool retryDueToTLSIntolerance(PRErrorCode err, NSSSocketControl* socketInfo) { // This function is supposed to decide which error codes should // be used to conclude server is TLS intolerant. // Note this only happens during the initial SSL handshake.
if (StaticPrefs::security_tls_ech_disable_grease_on_fallback() &&
socketInfo->GetEchExtensionStatus() == EchExtensionStatus::kGREASE) { // Don't record any intolerances if we used ECH GREASE but force a retry. returntrue;
}
if (!socketInfo->IsPreliminaryHandshakeDone() &&
!socketInfo->HasTls13HandshakeSecrets() && socketInfo->SentMlkemShare()) {
nsAutoCString errorName; constchar* prErrorName = PR_ErrorToName(err); if (prErrorName) {
errorName.AppendASCII(prErrorName);
}
mozilla::glean::tls::xyber_intolerance_reason.Get(errorName).Add(1); // Don't record version intolerance if we sent mlkem768x25519, just force a // retry. returntrue;
}
SSLVersionRange range = socketInfo->GetTLSVersionRange();
// NSS will return SSL_ERROR_RX_MALFORMED_SERVER_HELLO if anti-downgrade // detected the downgrade. if (err == SSL_ERROR_INAPPROPRIATE_FALLBACK_ALERT ||
err == SSL_ERROR_RX_MALFORMED_SERVER_HELLO) { // This is a clear signal that we've fallen back too many versions. Treat // this as a hard failure, but forget any intolerance so that later attempts // don't use this version (i.e., range.max) and trigger the error again.
// First, track the original cause of the version fallback. This uses the // same buckets as the telemetry below, except that bucket 0 will include // all cases where there wasn't an original reason.
PRErrorCode originalReason = socketInfo->GetTLSIntoleranceReason();
Telemetry::Accumulate(Telemetry::SSL_VERSION_FALLBACK_INAPPROPRIATE,
tlsIntoleranceTelemetryBucket(originalReason));
socketInfo->ForgetTLSIntolerance();
returnfalse;
}
// When not using a proxy we'll see a connection reset error. // When using a proxy, we'll see an end of file error.
// Don't allow STARTTLS connections to fall back on connection resets or // EOF. if ((err == PR_CONNECT_RESET_ERROR || err == PR_END_OF_FILE_ERROR) &&
socketInfo->GetForSTARTTLS()) { returnfalse;
}
Telemetry::HistogramID pre;
Telemetry::HistogramID post; switch (range.max) { case SSL_LIBRARY_VERSION_TLS_1_3:
pre = Telemetry::SSL_TLS13_INTOLERANCE_REASON_PRE;
post = Telemetry::SSL_TLS13_INTOLERANCE_REASON_POST; break; case SSL_LIBRARY_VERSION_TLS_1_2:
pre = Telemetry::SSL_TLS12_INTOLERANCE_REASON_PRE;
post = Telemetry::SSL_TLS12_INTOLERANCE_REASON_POST; break; case SSL_LIBRARY_VERSION_TLS_1_1:
pre = Telemetry::SSL_TLS11_INTOLERANCE_REASON_PRE;
post = Telemetry::SSL_TLS11_INTOLERANCE_REASON_POST; break; case SSL_LIBRARY_VERSION_TLS_1_0:
pre = Telemetry::SSL_TLS10_INTOLERANCE_REASON_PRE;
post = Telemetry::SSL_TLS10_INTOLERANCE_REASON_POST; break; default:
MOZ_CRASH("impossible TLS version"); returnfalse;
}
// The difference between _PRE and _POST represents how often we avoided // TLS intolerance fallback due to remembered tolerance.
Telemetry::Accumulate(pre, reason);
if (!socketInfo->RememberTLSIntolerant(err)) { returnfalse;
}
Telemetry::Accumulate(post, reason);
returntrue;
}
// Ensure that we haven't added too many errors to fit.
static_assert((SSL_ERROR_END_OF_LIST - SSL_ERROR_BASE) <= 256, "too many SSL errors");
static_assert((SEC_ERROR_END_OF_LIST - SEC_ERROR_BASE) <= 256, "too many SEC errors");
static_assert((PR_MAX_ERROR - PR_NSPR_ERROR_BASE) <= 128, "too many NSPR errors");
static_assert((mozilla::pkix::ERROR_BASE - mozilla::pkix::END_OF_LIST) < 31, "too many moz::pkix errors");
// As bucket is 0 we are reporting the results of a sucessful connection // and so TransportSecurityInfo should be populated. However, this isn't // happening in all cases, see Bug 1789458. if (success) {
uint8_t TLSPrivacyResult = 0;
TLSPrivacyResult |= usedTLS13 << 0;
TLSPrivacyResult |= !madeOCSPRequest << 1;
TLSPrivacyResult |= usedPrivateDNS << 2;
TLSPrivacyResult |= usedECH << 3;
// Check the status of the handshake. This is where PSM checks for TLS // intolerance and potentially sets up TLS intolerance fallback by noting the // intolerance, setting the NSPR error to PR_CONNECT_RESET_ERROR, and returning // -1 as the bytes transferred so that necko retries the connection. // Otherwise, PSM returns the bytes transferred unchanged.
int32_t checkHandshake(int32_t bytesTransferred, bool wasReading,
PRFileDesc* ssl_layer_fd, NSSSocketControl* socketInfo) { const PRErrorCode originalError = PR_GetError();
// If the connection would block, return early. if (bytesTransferred < 0 && originalError == PR_WOULD_BLOCK_ERROR) {
PR_SetError(PR_WOULD_BLOCK_ERROR, 0); return bytesTransferred;
}
// We only need to do TLS intolerance checking for the first transfer. bool handleHandshakeResultNow = socketInfo->IsHandshakePending(); if (!handleHandshakeResultNow) { // If we've encountered an error since the handshake, ensure the socket // control is cancelled, so that getSocketInfoIfRunning will correctly // cause us to fail if another part of Gecko (erroneously) calls an I/O // function (PR_Send/PR_Recv/etc.) again on this socket. if (bytesTransferred < 0) { if (!socketInfo->IsCanceled()) {
socketInfo->SetCanceled(originalError);
}
PR_SetError(originalError, 0);
} return bytesTransferred;
}
// TLS intolerant servers only cause the first transfer to fail, so let's // set the HandshakePending attribute to false so that we don't try this logic // again in a subsequent transfer.
socketInfo->SetHandshakeNotPending(); // Report the result once for each handshake. Note that this does not // get handshakes which are cancelled before any reads or writes // happen.
reportHandshakeResult(bytesTransferred, wasReading, originalError,
socketInfo);
// If there was no error, return early. The case where we read 0 bytes is not // considered an error by NSS, but PSM interprets this as TLS intolerance, so // we turn it into an error. Writes of 0 bytes are an error, because PR_Write // is never supposed to return 0. if (bytesTransferred > 0) { return bytesTransferred;
}
// There was some sort of error. Determine what it was and if we want to // retry the connection due to TLS intolerance.
PRErrorCode errorToUse = originalError; // Turn zero-length reads into errors and handle zero-length write errors. if (bytesTransferred == 0) { if (wasReading) {
errorToUse = PR_END_OF_FILE_ERROR;
} else {
errorToUse = SEC_ERROR_LIBRARY_FAILURE;
}
bytesTransferred = -1;
} bool wantRetry = retryDueToTLSIntolerance(errorToUse, socketInfo); // Set the error on the socket control and cancel it. if (!socketInfo->IsCanceled()) {
socketInfo->SetCanceled(errorToUse);
}
if (wantRetry) {
MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
("[%p] checkHandshake: will retry with lower max TLS version",
ssl_layer_fd)); // Setting the error PR_CONNECT_RESET_ERROR causes necko to retry the // connection.
PR_SetError(PR_CONNECT_RESET_ERROR, 0);
} else {
PR_SetError(originalError, 0);
}
return bytesTransferred;
}
} // namespace
static int16_t nsSSLIOLayerPoll(PRFileDesc* fd, int16_t in_flags,
int16_t* out_flags) { if (!out_flags) {
NS_WARNING("nsSSLIOLayerPoll called with null out_flags"); return 0;
}
if (!socketInfo) { // If we get here, it is probably because certificate validation failed // and this is the first I/O operation after the failure.
MOZ_LOG(
gPIPNSSLog, LogLevel::Debug,
("[%p] polling SSL socket right after certificate verification failed " "or NSS shutdown or SDR logout %d\n",
fd, (int)in_flags));
MOZ_ASSERT(in_flags & PR_POLL_EXCEPT, "Caller did not poll for EXCEPT (canceled)"); // Since this poll method cannot return errors, we want the caller to call // PR_Send/PR_Recv right away to get the error, so we tell that we are // ready for whatever I/O they are asking for. (See getSocketInfoIfRunning).
*out_flags = in_flags | PR_POLL_EXCEPT; // see also bug 480619 return in_flags;
}
// We want the handshake to continue during certificate validation, so we // don't need to do anything special here. libssl automatically blocks when // it reaches any point that would be unsafe to send/receive something before // cert validation is complete.
int16_t result = fd->lower->methods->poll(fd->lower, in_flags, out_flags);
MOZ_LOG(gPIPNSSLog, LogLevel::Verbose,
("[%p] poll SSL socket returned %d\n", (void*)fd, (int)result)); return result;
}
// PSMAvailable and PSMAvailable64 are reachable, but they're unimplemented in // PSM, so we set an error and return -1. static int32_t PSMAvailable(PRFileDesc*) {
PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); return -1;
}
if (socketInfo->IsShortWritePending() && amount > 0) { // We got "SSL short write" last time, try to flush the pending byte. #ifdef DEBUG
socketInfo->CheckShortWrittenBuffer(static_cast<constunsignedchar*>(buf),
amount); #endif
// NSS indicates that it can't write all requested data (due to network // congestion, for example) by returning either one less than the amount // of data requested or 16383, if the requested amount is greater than // 16384. We refer to this as a "short write". If we simply returned // the amount that NSS did write, the layer above us would then call // PSMSend with a very small amount of data (often 1). This is inefficient // and can lead to alternating between sending large packets and very small // packets. To prevent this, we alert the layer calling us that the operation // would block and that it should be retried later, with the same data. // When it does, we tell NSS to write the remaining byte it didn't write // in the previous call. We then return the total number of bytes written, // which is the number that caused the short write plus the additional byte // we just wrote out.
// The 16384 value is based on libssl's maximum buffer size: // MAX_FRAGMENT_LENGTH - 1 // // It's in a private header, though, filed bug 1394822 to expose it. staticconst int32_t kShortWrite16k = 16383;
if ((amount > 1 && bytesWritten == (amount - 1)) ||
(amount > kShortWrite16k && bytesWritten == kShortWrite16k)) { // This is indication of an "SSL short write", block to force retry.
socketInfo->SetShortWritePending(
bytesWritten + 1, // The amount to return after the flush
*(static_cast<constunsignedchar*>(buf) + bytesWritten));
MOZ_LOG(
gPIPNSSLog, LogLevel::Verbose,
("[%p] indicated SSL short write for %d bytes (written just %d bytes)",
fd, amount, bytesWritten));
} elseif (socketInfo->IsShortWritePending() && bytesWritten == 1) { // We have now flushed all pending data in the SSL socket // after the indicated short write. Tell the upper layer // it has sent all its data now.
MOZ_LOG(gPIPNSSLog, LogLevel::Verbose,
("[%p] finished SSL short write", fd));
void nsSSLIOLayerHelpers::GlobalInit() {
MOZ_ASSERT(NS_IsMainThread(), "Not on main thread");
gPublicSSLIOLayerHelpers = new nsSSLIOLayerHelpers(PublicOrPrivate::Public);
gPublicSSLIOLayerHelpers->Init();
gPrivateSSLIOLayerHelpers = new nsSSLIOLayerHelpers(PublicOrPrivate::Private);
gPrivateSSLIOLayerHelpers->Init();
}
/*static*/ void nsSSLIOLayerHelpers::GlobalCleanup() {
MOZ_ASSERT(NS_IsMainThread(), "Not on main thread");
if (gPrivateSSLIOLayerHelpers) {
gPrivateSSLIOLayerHelpers = nullptr;
}
if (gPublicSSLIOLayerHelpers) {
gPublicSSLIOLayerHelpers = nullptr;
}
}
// non main thread helpers will need to use defaults if (NS_IsMainThread()) {
initInsecureFallbackSites();
Preferences::AddStrongObserver(this, "security.tls.version.fallback-limit"); if (isPublic()) { // Changes to the allowlist on the public side will update the pref. // Don't propagate the changes to the private side.
Preferences::AddStrongObserver(this, "security.tls.insecure_fallback_hosts");
} else {
nsCOMPtr<nsIObserverService> obsSvc =
mozilla::services::GetObserverService(); if (obsSvc) {
obsSvc->AddObserver(this, "last-pb-context-exited", false);
}
}
} else {
MOZ_ASSERT(mTlsFlags, "Only per socket version can ignore prefs");
}
return NS_OK;
}
void nsSSLIOLayerHelpers::loadVersionFallbackLimit() { // see nsNSSComponent::SetEnabledTLSVersions for pref handling rules
uint32_t limit = StaticPrefs::security_tls_version_fallback_limit();
// set fallback limit if it is set in the tls flags
uint32_t tlsFlagsFallbackLimit = getTLSProviderFlagFallbackLimit(mTlsFlags);
if (tlsFlagsFallbackLimit) {
limit = tlsFlagsFallbackLimit;
MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
("loadVersionFallbackLimit overriden by tlsFlags %d\n", limit));
}
static PRFileDesc* nsSSLIOLayerImportFD(PRFileDesc* fd,
NSSSocketControl* infoObject, constchar* host, bool haveHTTPSProxy) { // Memory allocated here is released when fd is closed, regardless of the // success of this function.
PRFileDesc* sslSock = SSL_ImportFD(nullptr, fd); if (!sslSock) { return nullptr;
} if (SSL_SetPKCS11PinArg(sslSock, infoObject) != SECSuccess) { return nullptr;
} if (SSL_HandshakeCallback(sslSock, HandshakeCallback, infoObject) !=
SECSuccess) { return nullptr;
} if (SSL_SecretCallback(sslSock, SecretCallback, infoObject) != SECSuccess) { return nullptr;
} if (SSL_SetCanFalseStartCallback(sslSock, CanFalseStartCallback,
infoObject) != SECSuccess) { return nullptr;
}
// Disable this hook if we connect anonymously. See bug 466080.
uint32_t flags = infoObject->GetProviderFlags();
SSLGetClientAuthData clientAuthDataHook = SSLGetClientAuthDataHook; // Provide the client cert to HTTPS proxy no matter if it is anonymous. if (flags & nsISocketProvider::ANONYMOUS_CONNECT && !haveHTTPSProxy &&
!(flags & nsISocketProvider::ANONYMOUS_CONNECT_ALLOW_CLIENT_CERT)) {
clientAuthDataHook = nullptr;
} if (SSL_GetClientAuthDataHook(sslSock, clientAuthDataHook, infoObject) !=
SECSuccess) { return nullptr;
}
if (SSL_AuthCertificateHook(sslSock, AuthCertificateHook, infoObject) !=
SECSuccess) { return nullptr;
} if (SSL_SetURL(sslSock, host) != SECSuccess) { return nullptr;
}
return sslSock;
}
// Please change getSignatureName in nsNSSCallbacks.cpp when changing the list // here. See NOTE at SSL_SignatureSchemePrefSet call site. staticconst SSLSignatureScheme sEnabledSignatureSchemes[] = {
ssl_sig_ecdsa_secp256r1_sha256,
ssl_sig_ecdsa_secp384r1_sha384,
ssl_sig_ecdsa_secp521r1_sha512,
ssl_sig_rsa_pss_sha256,
ssl_sig_rsa_pss_sha384,
ssl_sig_rsa_pss_sha512,
ssl_sig_rsa_pkcs1_sha256,
ssl_sig_rsa_pkcs1_sha384,
ssl_sig_rsa_pkcs1_sha512, #if !defined(EARLY_BETA_OR_EARLIER)
ssl_sig_ecdsa_sha1, #endif
ssl_sig_rsa_pkcs1_sha1,
};
switch (alg) { case zlib:
decoder.AssignLiteral("zlib"); break; case brotli:
decoder.AssignLiteral("brotli"); break; case zstd:
decoder.AssignLiteral("zstd"); break;
}
if (rv != SECSuccess) {
mozilla::glean::cert_compression::failures.Get(decoder).Add(1); return;
} // Glam requires us to send 0 in case of success.
mozilla::glean::cert_compression::failures.Get(decoder).Add(0);
}
if ((infoObject->GetProviderFlags() & nsISocketProvider::BE_CONSERVATIVE) &&
(range.max > SSL_LIBRARY_VERSION_TLS_1_2)) {
MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
("[%p] nsSSLIOLayerSetOptions: range.max limited to 1.2 due to " "BE_CONSERVATIVE flag\n",
fd));
range.max = SSL_LIBRARY_VERSION_TLS_1_2;
}
uint16_t maxEnabledVersion = range.max;
infoObject->AdjustForTLSIntolerance(range);
MOZ_LOG(
gPIPNSSLog, LogLevel::Debug,
("[%p] nsSSLIOLayerSetOptions: using TLS version range (0x%04x,0x%04x)\n",
fd, static_cast<unsignedint>(range.min), static_cast<unsignedint>(range.max)));
// If the user has set their minimum version to something higher than what // we've now set the maximum to, this will result in an inconsistent version // range unless we fix it up. This will override their preference, but we only // do this for sites critical to the operation of the browser (e.g. update // servers) and telemetry experiments. if (range.min > range.max) {
range.min = range.max;
}
if (SSL_VersionRangeSet(fd, &range) != SECSuccess) { return NS_ERROR_FAILURE;
}
infoObject->SetTLSVersionRange(range);
// when adjustForTLSIntolerance tweaks the maximum version downward, // we tell the server using this SCSV so they can detect a downgrade attack if (range.max < maxEnabledVersion) {
MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
("[%p] nsSSLIOLayerSetOptions: enabling TLS_FALLBACK_SCSV\n", fd)); // Some servers will choke if we send the fallback SCSV with TLS 1.2. if (range.max < SSL_LIBRARY_VERSION_TLS_1_2) { if (SECSuccess != SSL_OptionSet(fd, SSL_ENABLE_FALLBACK_SCSV, true)) { return NS_ERROR_FAILURE;
}
} // tell NSS the max enabled version to make anti-downgrade effective if (SECSuccess != SSL_SetDowngradeCheckVersion(fd, maxEnabledVersion)) { return NS_ERROR_FAILURE;
}
}
// Enable ECH GREASE if suitable. Has no impact if 'real' ECH is being used. if (range.max >= SSL_LIBRARY_VERSION_TLS_1_3 &&
!(infoObject->GetProviderFlags() & (nsISocketProvider::BE_CONSERVATIVE |
nsISocketProvider::DONT_TRY_ECH)) &&
StaticPrefs::security_tls_ech_grease_probability()) { if ((RandomUint64().valueOr(0) % 100) >=
100 - StaticPrefs::security_tls_ech_grease_probability()) {
MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
("[%p] nsSSLIOLayerSetOptions: enabling TLS ECH Grease\n", fd)); if (SECSuccess != SSL_EnableTls13GreaseEch(fd, PR_TRUE)) { return NS_ERROR_FAILURE;
} // ECH Padding can be between 1 and 255 if (SECSuccess !=
SSL_SetTls13GreaseEchSize(
fd, std::clamp(StaticPrefs::security_tls_ech_grease_size(), 1U,
255U))) { return NS_ERROR_FAILURE;
}
infoObject->UpdateEchExtensionStatus(EchExtensionStatus::kGREASE);
}
}
// Include a modest set of named groups in supported_groups and determine how // many key shares to send. Please change getKeaGroupName in // nsNSSCallbacks.cpp when changing the lists here. unsignedint additional_shares =
StaticPrefs::security_tls_client_hello_send_p256_keyshare(); if (StaticPrefs::security_tls_enable_kyber() &&
range.max >= SSL_LIBRARY_VERSION_TLS_1_3 &&
!(infoObject->GetProviderFlags() &
(nsISocketProvider::BE_CONSERVATIVE | nsISocketProvider::IS_RETRY))) { const SSLNamedGroup namedGroups[] = {
ssl_grp_kem_mlkem768x25519, ssl_grp_ec_curve25519, ssl_grp_ec_secp256r1,
ssl_grp_ec_secp384r1, ssl_grp_ec_secp521r1, ssl_grp_ffdhe_2048,
ssl_grp_ffdhe_3072}; if (SECSuccess !=
SSL_NamedGroupConfig(fd, namedGroups, std::size(namedGroups))) { return NS_ERROR_FAILURE;
}
additional_shares += 1;
infoObject->WillSendMlkemShare();
} else { const SSLNamedGroup namedGroups[] = {
ssl_grp_ec_curve25519, ssl_grp_ec_secp256r1, ssl_grp_ec_secp384r1,
ssl_grp_ec_secp521r1, ssl_grp_ffdhe_2048, ssl_grp_ffdhe_3072}; // Skip the |ssl_grp_kem_mlkem768x25519| entry. if (SECSuccess !=
SSL_NamedGroupConfig(fd, namedGroups, std::size(namedGroups))) { return NS_ERROR_FAILURE;
}
}
// If additional_shares == 2, send mlkem768x25519, x25519, and p256. // If additional_shares == 1, send {mlkem768x25519, x25519} or {x25519, p256}. // If additional_shares == 0, send x25519. if (SECSuccess != SSL_SendAdditionalKeyShares(fd, additional_shares)) { return NS_ERROR_FAILURE;
}
// NOTE: Should this list ever include ssl_sig_rsa_pss_pss_sha* (or should // it become possible to enable this scheme via a pref), it is required // to test that a Delegated Credential containing a small-modulus RSA-PSS SPKI // is properly rejected. NSS will not advertise PKCS1 or RSAE schemes (which // the |ssl_sig_rsa_pss_*| defines alias, meaning we will not currently accept // any RSA DC. if (SECSuccess !=
SSL_SignatureSchemePrefSet(fd, sEnabledSignatureSchemes,
std::size(sEnabledSignatureSchemes))) { return NS_ERROR_FAILURE;
}
// On ARM, prefer (TLS_CHACHA20_POLY1305_SHA256) over AES when hardware // support for AES isn't available. However, it may be disabled. If enabled, // it will either be element [0] or [1]*. If [0], we're done. If [1], swap // it with [0] (TLS_AES_128_GCM_SHA256). // *(assuming the compile-time order remains unchanged) if (enabledCiphers > 1) { if (ciphers[0] != TLS_CHACHA20_POLY1305_SHA256 &&
ciphers[1] == TLS_CHACHA20_POLY1305_SHA256) {
std::swap(ciphers[0], ciphers[1]);
// Set the Peer ID so that SSL proxy connections work properly and to // separate anonymous and/or private browsing connections.
nsAutoCString peerId;
infoObject->GetPeerId(peerId); if (SECSuccess != SSL_SetSockPeerID(fd, peerId.get())) { return NS_ERROR_FAILURE;
}
// A plaintext observer shim is inserted so we can observe some protocol // details without modifying nss
PRFileDesc* plaintextLayer =
PR_CreateIOLayerStub(nsSSLIOLayerHelpers::nsSSLPlaintextLayerIdentity,
&nsSSLIOLayerHelpers::nsSSLPlaintextLayerMethods); if (!plaintextLayer) { return NS_ERROR_FAILURE;
}
plaintextLayer->secret = (PRFilePrivate*)infoObject.get(); if (PR_PushIOLayer(fd, PR_TOP_IO_LAYER, plaintextLayer) != PR_SUCCESS) {
plaintextLayer->dtor(plaintextLayer); return NS_ERROR_FAILURE;
} auto plaintextLayerCleanup = MakeScopeExit([&fd] { // Note that PR_*IOLayer operations may modify the stack of fds, so a // previously-valid pointer may no longer point to what we think it points // to after calling PR_PopIOLayer. We must operate on the pointer returned // by PR_PopIOLayer.
PRFileDesc* plaintextLayer =
PR_PopIOLayer(fd, nsSSLIOLayerHelpers::nsSSLPlaintextLayerIdentity); if (plaintextLayer) {
plaintextLayer->dtor(plaintextLayer);
}
});
// Now, layer ourselves on top of the SSL socket...
PRFileDesc* layer =
PR_CreateIOLayerStub(nsSSLIOLayerHelpers::nsSSLIOLayerIdentity,
&nsSSLIOLayerHelpers::nsSSLIOLayerMethods); if (!layer) { return NS_ERROR_FAILURE;
} // Give the layer an owning reference to the NSSSocketControl. // This is the simplest way to prevent the layer from outliving the // NSSSocketControl (otherwise, the layer could potentially use it in // nsSSLIOLayerClose after it has been released). // nsSSLIOLayerClose takes the owning reference when the underlying fd gets // closed. If the fd never gets closed (as in, leaks), the NSSSocketControl // will also leak.
layer->secret = (PRFilePrivate*)do_AddRef(infoObject).take();
if (PR_PushIOLayer(sslSock, PR_GetLayersIdentity(sslSock), layer) !=
PR_SUCCESS) {
layer->dtor(layer); return NS_ERROR_FAILURE;
} auto layerCleanup = MakeScopeExit([&fd] {
PRFileDesc* layer =
PR_PopIOLayer(fd, nsSSLIOLayerHelpers::nsSSLIOLayerIdentity); if (layer) {
layer->dtor(layer);
}
});
// We are going use a clear connection first // if (forSTARTTLS || haveProxy) {
infoObject->SetHandshakeNotPending();
}
rv = infoObject->SetResumptionTokenFromExternalCache(sslSock); if (NS_FAILED(rv)) { return rv;
} if (SSL_SetResumptionTokenCallback(sslSock, &StoreResumptionToken,
infoObject) != SECSuccess) { return NS_ERROR_FAILURE;
}
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("[%p] Socket set up", (void*)sslSock));
// This function is provided to the IPC client certs module so it can cause the // parent process to find certificates and keys and send identifying // information about them over IPC. void DoFindObjects(FindObjectsCallback cb, void* ctx) {
net::SocketProcessChild* socketChild =
net::SocketProcessChild::GetSingleton(); if (!socketChild) { return;
}
// This function is provided to the IPC client certs module so it can cause the // parent process to sign the given data using the key corresponding to the // given certificate, using the given parameters.
--> --------------------
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.