#590: TLS 1.3 support (consolidated) with locale workaround

This commit is contained in:
Cameron Kaiser 2020-02-17 20:51:23 -08:00
parent 91d1dfb59b
commit fae264c819
24 changed files with 453 additions and 112 deletions

View File

@ -90,6 +90,9 @@ var security = {
case nsISSLStatus.TLS_VERSION_1_2: case nsISSLStatus.TLS_VERSION_1_2:
retval.version = "TLS 1.2" retval.version = "TLS 1.2"
break; break;
case nsISSLStatus.TLS_VERSION_1_3:
retval.version = "TLS 1.3"
break;
} }
return retval; return retval;

View File

@ -122,6 +122,7 @@ DEFAULT_GMAKE_FLAGS += NSPR_LIB_DIR=$(NSPR_LIB_DIR)
DEFAULT_GMAKE_FLAGS += MOZILLA_CLIENT=1 DEFAULT_GMAKE_FLAGS += MOZILLA_CLIENT=1
DEFAULT_GMAKE_FLAGS += NO_MDUPDATE=1 DEFAULT_GMAKE_FLAGS += NO_MDUPDATE=1
DEFAULT_GMAKE_FLAGS += NSS_ENABLE_ECC=1 DEFAULT_GMAKE_FLAGS += NSS_ENABLE_ECC=1
DEFAULT_GMAKE_FLAGS += NSS_ENABLE_TLS_1_3=1
ifeq ($(OS_ARCH)_$(GNU_CC),WINNT_1) ifeq ($(OS_ARCH)_$(GNU_CC),WINNT_1)
DEFAULT_GMAKE_FLAGS += OS_DLLFLAGS='-static-libgcc' NSPR31_LIB_PREFIX=lib DEFAULT_GMAKE_FLAGS += OS_DLLFLAGS='-static-libgcc' NSPR31_LIB_PREFIX=lib
endif endif

View File

@ -529,7 +529,7 @@ var NetworkHelper = {
* If state == broken: * If state == broken:
* - errorMessage: full error message from nsITransportSecurityInfo. * - errorMessage: full error message from nsITransportSecurityInfo.
* If state == secure: * If state == secure:
* - protocolVersion: one of TLSv1, TLSv1.1, TLSv1.2. * - protocolVersion: one of TLSv1, TLSv1.1, TLSv1.2, TLSv1.3.
* - cipherSuite: the cipher suite used in this connection. * - cipherSuite: the cipher suite used in this connection.
* - cert: information about certificate used in this connection. * - cert: information about certificate used in this connection.
* See parseCertificateInfo for the contents. * See parseCertificateInfo for the contents.
@ -712,7 +712,7 @@ var NetworkHelper = {
* @param Number version * @param Number version
* One of nsISSLStatus version constants. * One of nsISSLStatus version constants.
* @return string * @return string
* One of TLSv1, TLSv1.1, TLSv1.2 if @param version is valid, * One of TLSv1, TLSv1.1, TLSv1.2, TLSv1.3 if @param version is valid,
* Unknown otherwise. * Unknown otherwise.
*/ */
formatSecurityProtocol: function NH_formatSecurityProtocol(version) { formatSecurityProtocol: function NH_formatSecurityProtocol(version) {
@ -723,6 +723,8 @@ var NetworkHelper = {
return "TLSv1.1"; return "TLSv1.1";
case Ci.nsISSLStatus.TLS_VERSION_1_2: case Ci.nsISSLStatus.TLS_VERSION_1_2:
return "TLSv1.2"; return "TLSv1.2";
case Ci.nsISSLStatus.TLS_VERSION_1_3:
return "TLSv1.3";
default: default:
DevToolsUtils.reportException("NetworkHelper.formatSecurityProtocol", DevToolsUtils.reportException("NetworkHelper.formatSecurityProtocol",
"protocolVersion " + version + " is unknown."); "protocolVersion " + version + " is unknown.");

View File

@ -1116,6 +1116,8 @@ NS_IMETHODIMP nsWebBrowserPersist::OnStatus(
case NS_NET_STATUS_END_FTP_TRANSACTION: case NS_NET_STATUS_END_FTP_TRANSACTION:
case NS_NET_STATUS_CONNECTING_TO: case NS_NET_STATUS_CONNECTING_TO:
case NS_NET_STATUS_CONNECTED_TO: case NS_NET_STATUS_CONNECTED_TO:
case NS_NET_STATUS_TLS_HANDSHAKE_STARTING:
case NS_NET_STATUS_TLS_HANDSHAKE_ENDED:
case NS_NET_STATUS_SENDING_TO: case NS_NET_STATUS_SENDING_TO:
case NS_NET_STATUS_RECEIVING_FROM: case NS_NET_STATUS_RECEIVING_FROM:
case NS_NET_STATUS_WAITING_FOR: case NS_NET_STATUS_WAITING_FOR:

View File

@ -454,6 +454,15 @@ TransportLayerDtls::SetVerificationDigest(const std::string digest_algorithm,
return NS_OK; return NS_OK;
} }
// These are the named groups that we will allow.
static const SSLNamedGroup NamedGroupPreferences[] = {
ssl_grp_ec_curve25519,
ssl_grp_ec_secp256r1,
ssl_grp_ec_secp384r1,
ssl_grp_ffdhe_2048,
ssl_grp_ffdhe_3072
};
// TODO: make sure this is called from STS. Otherwise // TODO: make sure this is called from STS. Otherwise
// we have thread safety issues // we have thread safety issues
bool TransportLayerDtls::Setup() { bool TransportLayerDtls::Setup() {
@ -588,6 +597,13 @@ bool TransportLayerDtls::Setup() {
return false; return false;
} }
rv = SSL_NamedGroupConfig(ssl_fd, NamedGroupPreferences,
mozilla::ArrayLength(NamedGroupPreferences));
if (rv != SECSuccess) {
MOZ_MTLOG(ML_ERROR, "Couldn't set named groups");
return false;
}
// Certificate validation // Certificate validation
rv = SSL_AuthCertificateHook(ssl_fd, AuthCertificateHook, rv = SSL_AuthCertificateHook(ssl_fd, AuthCertificateHook,
reinterpret_cast<void *>(this)); reinterpret_cast<void *>(this));
@ -675,6 +691,7 @@ static const uint32_t EnabledCiphers[] = {
static const uint32_t DisabledCiphers[] = { static const uint32_t DisabledCiphers[] = {
// ALL SHA384 ciphers are disabled per bug 1310061. // ALL SHA384 ciphers are disabled per bug 1310061.
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
TLS_RSA_WITH_AES_256_GCM_SHA384,
TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA, TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA,
TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
@ -702,22 +719,14 @@ static const uint32_t DisabledCiphers[] = {
TLS_RSA_WITH_AES_256_CBC_SHA256, TLS_RSA_WITH_AES_256_CBC_SHA256,
TLS_RSA_WITH_CAMELLIA_256_CBC_SHA, TLS_RSA_WITH_CAMELLIA_256_CBC_SHA,
TLS_RSA_WITH_SEED_CBC_SHA, TLS_RSA_WITH_SEED_CBC_SHA,
SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA,
TLS_RSA_WITH_3DES_EDE_CBC_SHA, TLS_RSA_WITH_3DES_EDE_CBC_SHA,
TLS_RSA_WITH_RC4_128_SHA, TLS_RSA_WITH_RC4_128_SHA,
TLS_RSA_WITH_RC4_128_MD5, TLS_RSA_WITH_RC4_128_MD5,
TLS_DHE_RSA_WITH_DES_CBC_SHA, TLS_DHE_RSA_WITH_DES_CBC_SHA,
TLS_DHE_DSS_WITH_DES_CBC_SHA, TLS_DHE_DSS_WITH_DES_CBC_SHA,
SSL_RSA_FIPS_WITH_DES_CBC_SHA,
TLS_RSA_WITH_DES_CBC_SHA, TLS_RSA_WITH_DES_CBC_SHA,
TLS_RSA_EXPORT1024_WITH_RC4_56_SHA,
TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA,
TLS_RSA_EXPORT_WITH_RC4_40_MD5,
TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5,
TLS_ECDHE_ECDSA_WITH_NULL_SHA, TLS_ECDHE_ECDSA_WITH_NULL_SHA,
TLS_ECDHE_RSA_WITH_NULL_SHA, TLS_ECDHE_RSA_WITH_NULL_SHA,
TLS_ECDH_ECDSA_WITH_NULL_SHA, TLS_ECDH_ECDSA_WITH_NULL_SHA,

View File

@ -893,13 +893,15 @@ typedef struct
#define ERROR(key, val) {key, #key} #define ERROR(key, val) {key, #key}
ErrorEntry socketTransportStatuses[] = { ErrorEntry socketTransportStatuses[] = {
ERROR(NS_NET_STATUS_RESOLVING_HOST, FAILURE(3)), ERROR(NS_NET_STATUS_RESOLVING_HOST, FAILURE(3)),
ERROR(NS_NET_STATUS_RESOLVED_HOST, FAILURE(11)), ERROR(NS_NET_STATUS_RESOLVED_HOST, FAILURE(11)),
ERROR(NS_NET_STATUS_CONNECTING_TO, FAILURE(7)), ERROR(NS_NET_STATUS_CONNECTING_TO, FAILURE(7)),
ERROR(NS_NET_STATUS_CONNECTED_TO, FAILURE(4)), ERROR(NS_NET_STATUS_CONNECTED_TO, FAILURE(4)),
ERROR(NS_NET_STATUS_SENDING_TO, FAILURE(5)), ERROR(NS_NET_STATUS_TLS_HANDSHAKE_STARTING, FAILURE(12)),
ERROR(NS_NET_STATUS_WAITING_FOR, FAILURE(10)), ERROR(NS_NET_STATUS_TLS_HANDSHAKE_ENDED, FAILURE(13)),
ERROR(NS_NET_STATUS_RECEIVING_FROM, FAILURE(6)), ERROR(NS_NET_STATUS_SENDING_TO, FAILURE(5)),
ERROR(NS_NET_STATUS_WAITING_FOR, FAILURE(10)),
ERROR(NS_NET_STATUS_RECEIVING_FROM, FAILURE(6)),
}; };
#undef ERROR #undef ERROR

View File

@ -100,6 +100,7 @@ interface nsITLSClientStatus : nsISupports
const short TLS_VERSION_1 = 0x0301; const short TLS_VERSION_1 = 0x0301;
const short TLS_VERSION_1_1 = 0x0302; const short TLS_VERSION_1_1 = 0x0302;
const short TLS_VERSION_1_2 = 0x0303; const short TLS_VERSION_1_2 = 0x0303;
const short TLS_VERSION_1_3 = 0x0304;
const short TLS_VERSION_UNKNOWN = -1; const short TLS_VERSION_UNKNOWN = -1;
/** /**

View File

@ -3,10 +3,11 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
pref("security.tls.version.min", 1); pref("security.tls.version.min", 1);
pref("security.tls.version.max", 3); pref("security.tls.version.max", 4);
pref("security.tls.version.fallback-limit", 3); pref("security.tls.version.fallback-limit", 3);
pref("security.tls.insecure_fallback_hosts", ""); pref("security.tls.insecure_fallback_hosts", "");
pref("security.tls.unrestricted_rc4_fallback", false); pref("security.tls.unrestricted_rc4_fallback", false);
pref("security.tls.enable_0rtt_data", true);
pref("security.ssl.treat_unsafe_negotiation_as_broken", false); pref("security.ssl.treat_unsafe_negotiation_as_broken", false);
pref("security.ssl.require_safe_negotiation", false); pref("security.ssl.require_safe_negotiation", false);

View File

@ -17,6 +17,8 @@
9=Wrote %1$S 9=Wrote %1$S
10=Waiting for %1$S… 10=Waiting for %1$S…
11=Looked up %1$S… 11=Looked up %1$S…
12=Performing a TLS handshake to %1$S…
13=The TLS handshake finished for %1$S…
27=Beginning FTP transaction… 27=Beginning FTP transaction…
28=Finished FTP transaction 28=Finished FTP transaction

View File

@ -2025,6 +2025,8 @@ nsFtpState::OnTransportStatus(nsITransport *transport, nsresult status,
case NS_NET_STATUS_RESOLVED_HOST: case NS_NET_STATUS_RESOLVED_HOST:
case NS_NET_STATUS_CONNECTING_TO: case NS_NET_STATUS_CONNECTING_TO:
case NS_NET_STATUS_CONNECTED_TO: case NS_NET_STATUS_CONNECTED_TO:
case NS_NET_STATUS_TLS_HANDSHAKE_STARTING:
case NS_NET_STATUS_TLS_HANDSHAKE_ENDED:
break; break;
default: default:
return NS_OK; return NS_OK;

View File

@ -2240,6 +2240,8 @@ Http2Session::OnTransportStatus(nsITransport* aTransport,
case NS_NET_STATUS_RESOLVED_HOST: case NS_NET_STATUS_RESOLVED_HOST:
case NS_NET_STATUS_CONNECTING_TO: case NS_NET_STATUS_CONNECTING_TO:
case NS_NET_STATUS_CONNECTED_TO: case NS_NET_STATUS_CONNECTED_TO:
case NS_NET_STATUS_TLS_HANDSHAKE_STARTING:
case NS_NET_STATUS_TLS_HANDSHAKE_ENDED:
{ {
Http2Stream *target = mStreamIDHash.Get(1); Http2Stream *target = mStreamIDHash.Get(1);
nsAHttpTransaction *transaction = target ? target->Transaction() : nullptr; nsAHttpTransaction *transaction = target ? target->Transaction() : nullptr;
@ -2306,7 +2308,13 @@ Http2Session::ReadSegmentsAgain(nsAHttpSegmentReader *reader,
if (!stream) { if (!stream) {
LOG3(("Http2Session %p could not identify a stream to write; suspending.", LOG3(("Http2Session %p could not identify a stream to write; suspending.",
this)); this));
uint32_t availBeforeFlush = mOutputQueueUsed - mOutputQueueSent;
FlushOutputQueue(); FlushOutputQueue();
uint32_t availAfterFlush = mOutputQueueUsed - mOutputQueueSent;
if (availBeforeFlush != availAfterFlush) {
LOG3(("Http2Session %p ResumeRecv After early flush in ReadSegments", this));
ResumeRecv();
}
SetWriteCallbacks(); SetWriteCallbacks();
return NS_BASE_STREAM_WOULD_BLOCK; return NS_BASE_STREAM_WOULD_BLOCK;
} }

View File

@ -204,6 +204,22 @@ public:
virtual void DisableSpdy() { } virtual void DisableSpdy() { }
virtual void ReuseConnectionOnRestartOK(bool) { } virtual void ReuseConnectionOnRestartOK(bool) { }
// Returns true if early-data is possible.
virtual bool Do0RTT() {
return false;
}
// This function will be called when a tls handshake has been finished and
// we know whether early-data that was sent has been accepted or not, e.g.
// do we need to restart a transaction. This will be called only if Do0RTT
// returns true.
// If aRestart parameter is true we need to restart the transaction,
// otherwise the erly-data has been accepted and we can continue the
// transaction.
// The function will return success or failure of the transaction restart.
virtual nsresult Finish0RTT(bool aRestart) {
return NS_ERROR_NOT_IMPLEMENTED;
}
}; };
NS_DEFINE_STATIC_IID_ACCESSOR(nsAHttpTransaction, NS_AHTTPTRANSACTION_IID) NS_DEFINE_STATIC_IID_ACCESSOR(nsAHttpTransaction, NS_AHTTPTRANSACTION_IID)

View File

@ -79,6 +79,9 @@ nsHttpConnection::nsHttpConnection()
, mResponseTimeoutEnabled(false) , mResponseTimeoutEnabled(false)
, mTCPKeepaliveConfig(kTCPKeepaliveDisabled) , mTCPKeepaliveConfig(kTCPKeepaliveDisabled)
, mForceSendPending(false) , mForceSendPending(false)
, m0RTTChecked(false)
, mWaitingFor0RTTResponse(false)
, mContentBytesWritten0RTT(0)
{ {
LOG(("Creating nsHttpConnection @%p\n", this)); LOG(("Creating nsHttpConnection @%p\n", this));
@ -96,18 +99,22 @@ nsHttpConnection::~nsHttpConnection()
if (!mEverUsedSpdy) { if (!mEverUsedSpdy) {
LOG(("nsHttpConnection %p performed %d HTTP/1.x transactions\n", LOG(("nsHttpConnection %p performed %d HTTP/1.x transactions\n",
this, mHttp1xTransactionCount)); this, mHttp1xTransactionCount));
/*
Telemetry::Accumulate(Telemetry::HTTP_REQUEST_PER_CONN, Telemetry::Accumulate(Telemetry::HTTP_REQUEST_PER_CONN,
mHttp1xTransactionCount); mHttp1xTransactionCount);
*/
} }
if (mTotalBytesRead) { if (mTotalBytesRead) {
uint32_t totalKBRead = static_cast<uint32_t>(mTotalBytesRead >> 10); uint32_t totalKBRead = static_cast<uint32_t>(mTotalBytesRead >> 10);
LOG(("nsHttpConnection %p read %dkb on connection spdy=%d\n", LOG(("nsHttpConnection %p read %dkb on connection spdy=%d\n",
this, totalKBRead, mEverUsedSpdy)); this, totalKBRead, mEverUsedSpdy));
/*
Telemetry::Accumulate(mEverUsedSpdy ? Telemetry::Accumulate(mEverUsedSpdy ?
Telemetry::SPDY_KBREAD_PER_CONN : Telemetry::SPDY_KBREAD_PER_CONN :
Telemetry::HTTP_KBREAD_PER_CONN, Telemetry::HTTP_KBREAD_PER_CONN,
totalKBRead); totalKBRead);
*/
} }
if (mForceSendTimer) { if (mForceSendTimer) {
mForceSendTimer->Cancel(); mForceSendTimer->Cancel();
@ -268,8 +275,12 @@ nsHttpConnection::StartSpdy(uint8_t spdyVersion)
} }
bool bool
nsHttpConnection::EnsureNPNComplete() nsHttpConnection::EnsureNPNComplete(nsresult &aOut0RTTWriteHandshakeValue,
uint32_t &aOut0RTTBytesWritten)
{ {
aOut0RTTWriteHandshakeValue = NS_OK;
aOut0RTTBytesWritten = 0;
// If for some reason the components to check on NPN aren't available, // If for some reason the components to check on NPN aren't available,
// this function will just return true to continue on and disable SPDY // this function will just return true to continue on and disable SPDY
@ -294,16 +305,81 @@ nsHttpConnection::EnsureNPNComplete()
goto npnComplete; goto npnComplete;
} }
if (!m0RTTChecked) {
// We reuse m0RTTChecked. We want to send this status only once.
mTransaction->OnTransportStatus(mSocketTransport,
NS_NET_STATUS_TLS_HANDSHAKE_STARTING,
0);
}
ssl = do_QueryInterface(securityInfo, &rv); ssl = do_QueryInterface(securityInfo, &rv);
if (NS_FAILED(rv)) if (NS_FAILED(rv))
goto npnComplete; goto npnComplete;
rv = ssl->GetNegotiatedNPN(negotiatedNPN); rv = ssl->GetNegotiatedNPN(negotiatedNPN);
if (!m0RTTChecked && (rv == NS_ERROR_NOT_CONNECTED) &&
!mConnInfo->UsingProxy()) {
// There is no ALPN info (yet!). We need to consider doing 0RTT. We
// will do so if there is ALPN information from a previous session
// (AlpnEarlySelection), we are using HTTP/1, and the request data can
// be safely retried.
m0RTTChecked = true;
nsAutoCString earlyNegotiatedNPN;
nsresult rvEarlyAlpn = ssl->GetAlpnEarlySelection(earlyNegotiatedNPN);
if (NS_FAILED(rvEarlyAlpn)) {
// if ssl->DriveHandshake() has never been called the value
// for AlpnEarlySelection is still not set. So call it here and
// check again.
LOG(("nsHttpConnection::EnsureNPNComplete %p - "
"early selected alpn not available, we will try one more time.",
this));
// Let's do DriveHandshake again.
rv = ssl->DriveHandshake();
if (NS_FAILED(rv) && rv != NS_BASE_STREAM_WOULD_BLOCK) {
goto npnComplete;
}
// Check NegotiatedNPN first.
rv = ssl->GetNegotiatedNPN(negotiatedNPN);
if (rv == NS_ERROR_NOT_CONNECTED) {
rvEarlyAlpn = ssl->GetAlpnEarlySelection(earlyNegotiatedNPN);
}
}
if (NS_FAILED(rvEarlyAlpn)) {
LOG(("nsHttpConnection::EnsureNPNComplete %p - "
"early selected alpn not available", this));
} else {
LOG(("nsHttpConnection::EnsureNPNComplete %p -"
"early selected alpn: %s", this, earlyNegotiatedNPN.get()));
uint32_t infoIndex;
const SpdyInformation *info = gHttpHandler->SpdyInfo();
// We are doing 0RTT only with Http/1 right now!
if (NS_FAILED(info->GetNPNIndex(earlyNegotiatedNPN, &infoIndex))) {
// Check if early-data is allowed for this transaction.
if (mTransaction->Do0RTT()) {
LOG(("nsHttpConnection::EnsureNPNComplete [this=%p] - We "
"can do 0RTT!", this));
mWaitingFor0RTTResponse = true;
}
}
}
}
if (rv == NS_ERROR_NOT_CONNECTED) { if (rv == NS_ERROR_NOT_CONNECTED) {
// By writing 0 bytes to the socket the SSL handshake machine is if (mWaitingFor0RTTResponse) {
// pushed forward. aOut0RTTWriteHandshakeValue = mTransaction->ReadSegments(this,
uint32_t count = 0; nsIOService::gDefaultSegmentSize, &aOut0RTTBytesWritten);
rv = mSocketOut->Write("", 0, &count); if (NS_FAILED(aOut0RTTWriteHandshakeValue) &&
aOut0RTTWriteHandshakeValue != NS_BASE_STREAM_WOULD_BLOCK) {
goto npnComplete;
}
LOG(("nsHttpConnection::EnsureNPNComplete [this=%p] - written %d "
"bytes during 0RTT", this, aOut0RTTBytesWritten));
mContentBytesWritten0RTT += aOut0RTTBytesWritten;
}
rv = ssl->DriveHandshake();
if (NS_FAILED(rv) && rv != NS_BASE_STREAM_WOULD_BLOCK) { if (NS_FAILED(rv) && rv != NS_BASE_STREAM_WOULD_BLOCK) {
goto npnComplete; goto npnComplete;
} }
@ -316,18 +392,49 @@ nsHttpConnection::EnsureNPNComplete()
this, mConnInfo->HashKey().get(), negotiatedNPN.get(), this, mConnInfo->HashKey().get(), negotiatedNPN.get(),
mTLSFilter ? " [Double Tunnel]" : "")); mTLSFilter ? " [Double Tunnel]" : ""));
uint32_t infoIndex; bool ealyDataAccepted = false;
const SpdyInformation *info = gHttpHandler->SpdyInfo(); if (mWaitingFor0RTTResponse) {
if (NS_SUCCEEDED(info->GetNPNIndex(negotiatedNPN, &infoIndex))) { mWaitingFor0RTTResponse = false;
StartSpdy(info->Version[infoIndex]); // Check if early data has been accepted.
rv = ssl->GetEarlyDataAccepted(&ealyDataAccepted);
LOG(("nsHttpConnection::EnsureNPNComplete [this=%p] - early data "
"that was sent during 0RTT %s been accepted.",
this, ealyDataAccepted ? "has" : "has not"));
if (NS_FAILED(rv) ||
NS_FAILED(mTransaction->Finish0RTT(!ealyDataAccepted))) {
mTransaction->Close(NS_ERROR_NET_RESET);
goto npnComplete;
}
}
if (!ealyDataAccepted) {
uint32_t infoIndex;
const SpdyInformation *info = gHttpHandler->SpdyInfo();
if (NS_SUCCEEDED(info->GetNPNIndex(negotiatedNPN, &infoIndex))) {
StartSpdy(info->Version[infoIndex]);
}
} else {
LOG(("nsHttpConnection::EnsureNPNComplete [this=%p] - %d bytes "
"has been sent during 0RTT.", this, mContentBytesWritten0RTT));
mContentBytesWritten = mContentBytesWritten0RTT;
} }
Telemetry::Accumulate(Telemetry::SPDY_NPN_CONNECT, UsingSpdy()); //Telemetry::Accumulate(Telemetry::SPDY_NPN_CONNECT, UsingSpdy());
} }
npnComplete: npnComplete:
LOG(("nsHttpConnection::EnsureNPNComplete setting complete to true")); LOG(("nsHttpConnection::EnsureNPNComplete setting complete to true"));
mNPNComplete = true; mNPNComplete = true;
mTransaction->OnTransportStatus(mSocketTransport,
NS_NET_STATUS_TLS_HANDSHAKE_ENDED,
0);
if (mWaitingFor0RTTResponse) {
mWaitingFor0RTTResponse = false;
if (NS_FAILED(mTransaction->Finish0RTT(true))) {
mTransaction->Close(NS_ERROR_NET_RESET);
}
mContentBytesWritten0RTT = 0;
}
return true; return true;
} }
@ -1572,7 +1679,9 @@ nsHttpConnection::OnSocketWritable()
// request differently for http/1, http/2, spdy, etc.. and that is // request differently for http/1, http/2, spdy, etc.. and that is
// negotiated with NPN/ALPN in the SSL handshake. // negotiated with NPN/ALPN in the SSL handshake.
if (mConnInfo->UsingHttpsProxy() && !EnsureNPNComplete()) { if (mConnInfo->UsingHttpsProxy() &&
!EnsureNPNComplete(rv, transactionBytes)) {
MOZ_ASSERT(!transactionBytes);
mSocketOutCondition = NS_BASE_STREAM_WOULD_BLOCK; mSocketOutCondition = NS_BASE_STREAM_WOULD_BLOCK;
} else if (mProxyConnectStream) { } else if (mProxyConnectStream) {
// If we're need an HTTP/1 CONNECT tunnel through a proxy // If we're need an HTTP/1 CONNECT tunnel through a proxy
@ -1581,8 +1690,11 @@ nsHttpConnection::OnSocketWritable()
rv = mProxyConnectStream->ReadSegments(ReadFromStream, this, rv = mProxyConnectStream->ReadSegments(ReadFromStream, this,
nsIOService::gDefaultSegmentSize, nsIOService::gDefaultSegmentSize,
&transactionBytes); &transactionBytes);
} else if (!EnsureNPNComplete()) { } else if (!EnsureNPNComplete(rv, transactionBytes)) {
mSocketOutCondition = NS_BASE_STREAM_WOULD_BLOCK; if (NS_SUCCEEDED(rv) && !transactionBytes &&
NS_SUCCEEDED(mSocketOutCondition)) {
mSocketOutCondition = NS_BASE_STREAM_WOULD_BLOCK;
}
} else { } else {
// for non spdy sessions let the connection manager know // for non spdy sessions let the connection manager know
@ -1630,7 +1742,7 @@ nsHttpConnection::OnSocketWritable()
} else if (!transactionBytes) { } else if (!transactionBytes) {
rv = NS_OK; rv = NS_OK;
if (mTransaction) { // in case the ReadSegments stack called CloseTransaction() if (mTransaction && !mWaitingFor0RTTResponse) { // in case the ReadSegments stack called CloseTransaction()
// //
// at this point we've written out the entire transaction, and now we // at this point we've written out the entire transaction, and now we
// must wait for the server's response. we manufacture a status message // must wait for the server's response. we manufacture a status message

View File

@ -241,7 +241,8 @@ private:
// Makes certain the SSL handshake is complete and NPN negotiation // Makes certain the SSL handshake is complete and NPN negotiation
// has had a chance to happen // has had a chance to happen
bool EnsureNPNComplete(); bool EnsureNPNComplete(nsresult &aOut0RTTWriteHandshakeValue,
uint32_t &aOut0RTTBytesWritten);
void SetupSSL(); void SetupSSL();
// Start the Spdy transaction handler when NPN indicates spdy/* // Start the Spdy transaction handler when NPN indicates spdy/*
@ -350,6 +351,17 @@ private:
uint32_t mTCPKeepaliveConfig; uint32_t mTCPKeepaliveConfig;
nsCOMPtr<nsITimer> mTCPKeepaliveTransitionTimer; nsCOMPtr<nsITimer> mTCPKeepaliveTransitionTimer;
// Helper variable for 0RTT handshake;
bool m0RTTChecked; // Possible 0RTT has been
// checked.
bool mWaitingFor0RTTResponse; // We have are
// sending 0RTT
// data and we
// are waiting
// for the end of
// the handsake.
int64_t mContentBytesWritten0RTT;
private: private:
// For ForceSend() // For ForceSend()
static void ForceSendIO(nsITimer *aTimer, void *aClosure); static void ForceSendIO(nsITimer *aTimer, void *aClosure);

View File

@ -142,6 +142,8 @@ nsHttpTransaction::nsHttpTransaction()
, mAppId(NECKO_NO_APP_ID) , mAppId(NECKO_NO_APP_ID)
, mIsInBrowser(false) , mIsInBrowser(false)
, mClassOfService(0) , mClassOfService(0)
, m0RTTInProgress(false)
, mTransportStatus(NS_OK)
{ {
LOG(("Creating nsHttpTransaction @%p\n", this)); LOG(("Creating nsHttpTransaction @%p\n", this));
gHttpHandler->GetMaxPipelineObjectSize(&mMaxPipelineObjectSize); gHttpHandler->GetMaxPipelineObjectSize(&mMaxPipelineObjectSize);
@ -532,6 +534,50 @@ nsHttpTransaction::OnTransportStatus(nsITransport* transport,
} }
} }
// A transaction can given to multiple HalfOpen sockets (this is a bug in
// nsHttpConnectionMgr). We are going to fix it here as a work around to be
// able to uplift it.
switch(status) {
case NS_NET_STATUS_RESOLVING_HOST:
if (mTransportStatus != NS_OK) {
LOG(("nsHttpTransaction::OnSocketStatus - ignore socket events "
"from backup transport"));
return;
}
break;
case NS_NET_STATUS_RESOLVED_HOST:
if (mTransportStatus != NS_NET_STATUS_RESOLVING_HOST &&
mTransportStatus != NS_OK) {
LOG(("nsHttpTransaction::OnSocketStatus - ignore socket events "
"from backup transport"));
return;
}
break;
case NS_NET_STATUS_CONNECTING_TO:
if (mTransportStatus != NS_NET_STATUS_RESOLVING_HOST &&
mTransportStatus != NS_NET_STATUS_RESOLVED_HOST &&
mTransportStatus != NS_OK) {
LOG(("nsHttpTransaction::OnSocketStatus - ignore socket events "
"from backup transport"));
return;
}
break;
case NS_NET_STATUS_CONNECTED_TO:
if (mTransportStatus != NS_NET_STATUS_RESOLVING_HOST &&
mTransportStatus != NS_NET_STATUS_RESOLVED_HOST &&
mTransportStatus != NS_NET_STATUS_CONNECTING_TO &&
mTransportStatus != NS_OK) {
LOG(("nsHttpTransaction::OnSocketStatus - ignore socket events "
"from backup transport"));
return;
}
break;
default:
LOG(("nsHttpTransaction::OnSocketStatus - a new event"));
}
mTransportStatus = status;
// If the timing is enabled, and we are not using a persistent connection // If the timing is enabled, and we are not using a persistent connection
// then the requestStart timestamp will be null, so we mark the timestamps // then the requestStart timestamp will be null, so we mark the timestamps
// for domainLookupStart/End and connectStart/End // for domainLookupStart/End and connectStart/End
@ -545,7 +591,9 @@ nsHttpTransaction::OnTransportStatus(nsITransport* transport,
} else if (status == NS_NET_STATUS_CONNECTING_TO) { } else if (status == NS_NET_STATUS_CONNECTING_TO) {
SetConnectStart(TimeStamp::Now()); SetConnectStart(TimeStamp::Now());
} else if (status == NS_NET_STATUS_CONNECTED_TO) { } else if (status == NS_NET_STATUS_CONNECTED_TO) {
SetConnectEnd(TimeStamp::Now()); SetConnectEnd(TimeStamp::Now(), true);
} else if (status == NS_NET_STATUS_TLS_HANDSHAKE_ENDED) {
SetConnectEnd(TimeStamp::Now(), false);
} }
} }
@ -690,7 +738,7 @@ nsHttpTransaction::ReadSegments(nsAHttpSegmentReader *reader,
return mStatus; return mStatus;
} }
if (!mConnected) { if (!mConnected && !m0RTTInProgress) {
mConnected = true; mConnected = true;
mConnection->GetSecurityInfo(getter_AddRefs(mSecurityInfo)); mConnection->GetSecurityInfo(getter_AddRefs(mSecurityInfo));
} }
@ -1274,6 +1322,8 @@ nsHttpTransaction::Restart()
} }
} }
mTransportStatus = NS_OK;
return gHttpHandler->InitiateTransaction(this, mPriority); return gHttpHandler->InitiateTransaction(this, mPriority);
} }
@ -2307,5 +2357,38 @@ nsHttpTransaction::GetNetworkAddresses(NetAddr &self, NetAddr &peer)
peer = mPeerAddr; peer = mPeerAddr;
} }
bool
nsHttpTransaction::Do0RTT()
{
if (mRequestHead->IsSafeMethod() &&
!mConnection->IsProxyConnectInProgress()) {
m0RTTInProgress = true;
}
return m0RTTInProgress;
}
nsresult
nsHttpTransaction::Finish0RTT(bool aRestart)
{
LOG(("nsHttpTransaction::Finish0RTT %p %d\n", this, aRestart));
MOZ_ASSERT(m0RTTInProgress);
m0RTTInProgress = false;
if (aRestart) {
// Reset request headers to be sent again.
nsCOMPtr<nsISeekableStream> seekable =
do_QueryInterface(mRequestStream);
if (seekable) {
seekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
} else {
return NS_ERROR_FAILURE;
}
} else if (!mConnected) {
// this is code that was skipped in ::ReadSegments while in 0RTT
mConnected = true;
mConnection->GetSecurityInfo(getter_AddRefs(mSecurityInfo));
}
return NS_OK;
}
} // namespace net } // namespace net
} // namespace mozilla } // namespace mozilla

View File

@ -166,6 +166,8 @@ public:
int64_t GetTransferSize() { return mTransferSize; } int64_t GetTransferSize() { return mTransferSize; }
bool Do0RTT() override;
nsresult Finish0RTT(bool aRestart) override;
private: private:
friend class DeleteHttpTransaction; friend class DeleteHttpTransaction;
virtual ~nsHttpTransaction(); virtual ~nsHttpTransaction();
@ -453,6 +455,10 @@ public:
private: private:
RefPtr<ASpdySession> mTunnelProvider; RefPtr<ASpdySession> mTunnelProvider;
bool m0RTTInProgress;
nsresult mTransportStatus;
public: public:
void GetNetworkAddresses(NetAddr &self, NetAddr &peer); void GetNetworkAddresses(NetAddr &self, NetAddr &peer);

View File

@ -44,6 +44,22 @@ interface nsISSLSocketControl : nsISupports {
*/ */
readonly attribute ACString negotiatedNPN; readonly attribute ACString negotiatedNPN;
/* For 0RTT we need to know the alpn protocol selected for the last tls
* session. This function will return a value if applicable or an error
* NS_ERROR_NOT_AVAILABLE.
*/
ACString getAlpnEarlySelection();
/* If 0RTT handshake was applied and some data has been sent, as soon as
* the handshake finishes this attribute will be set to appropriate value.
*/
readonly attribute bool earlyDataAccepted;
/* When 0RTT is performed, PR_Write will not drive the handshake forward.
* It must be forced by calling this function.
*/
void driveHandshake();
/* Determine if a potential SSL connection to hostname:port with /* Determine if a potential SSL connection to hostname:port with
* a desired NPN negotiated protocol of npnProtocol can use the socket * a desired NPN negotiated protocol of npnProtocol can use the socket
* associated with this object instead of making a new one. * associated with this object instead of making a new one.
@ -83,6 +99,7 @@ interface nsISSLSocketControl : nsISupports {
const short TLS_VERSION_1 = 0x0301; const short TLS_VERSION_1 = 0x0301;
const short TLS_VERSION_1_1 = 0x0302; const short TLS_VERSION_1_1 = 0x0302;
const short TLS_VERSION_1_2 = 0x0303; const short TLS_VERSION_1_2 = 0x0303;
const short TLS_VERSION_1_3 = 0x0304;
const short SSL_VERSION_UNKNOWN = -1; const short SSL_VERSION_UNKNOWN = -1;
[infallible] readonly attribute short SSLVersionUsed; [infallible] readonly attribute short SSLVersionUsed;

View File

@ -20,6 +20,7 @@ interface nsISSLStatus : nsISupports {
const short TLS_VERSION_1 = 1; const short TLS_VERSION_1 = 1;
const short TLS_VERSION_1_1 = 2; const short TLS_VERSION_1_1 = 2;
const short TLS_VERSION_1_2 = 3; const short TLS_VERSION_1_2 = 3;
const short TLS_VERSION_1_3 = 4;
readonly attribute unsigned short protocolVersion; readonly attribute unsigned short protocolVersion;
readonly attribute boolean isDomainMismatch; readonly attribute boolean isDomainMismatch;

View File

@ -891,6 +891,7 @@ PreliminaryHandshakeDone(PRFileDesc* fd)
SSLChannelInfo channelInfo; SSLChannelInfo channelInfo;
if (SSL_GetChannelInfo(fd, &channelInfo, sizeof(channelInfo)) == SECSuccess) { if (SSL_GetChannelInfo(fd, &channelInfo, sizeof(channelInfo)) == SECSuccess) {
infoObject->SetSSLVersionUsed(channelInfo.protocolVersion); infoObject->SetSSLVersionUsed(channelInfo.protocolVersion);
infoObject->SetEarlyDataAccepted(channelInfo.earlyDataAccepted);
SSLCipherSuiteInfo cipherInfo; SSLCipherSuiteInfo cipherInfo;
if (SSL_GetCipherSuiteInfo(channelInfo.cipherSuite, &cipherInfo, if (SSL_GetCipherSuiteInfo(channelInfo.cipherSuite, &cipherInfo,
@ -905,7 +906,7 @@ PreliminaryHandshakeDone(PRFileDesc* fd)
status->mHaveCipherSuiteAndProtocol = true; status->mHaveCipherSuiteAndProtocol = true;
status->mCipherSuite = channelInfo.cipherSuite; status->mCipherSuite = channelInfo.cipherSuite;
status->mProtocolVersion = channelInfo.protocolVersion & 0xFF; status->mProtocolVersion = channelInfo.protocolVersion & 0xFF;
infoObject->SetKEAUsed(cipherInfo.keaType); infoObject->SetKEAUsed(channelInfo.keaType);
infoObject->SetKEAKeyBits(channelInfo.keaKeyBits); infoObject->SetKEAKeyBits(channelInfo.keaKeyBits);
infoObject->SetMACAlgorithmUsed(cipherInfo.macAlgorithm); infoObject->SetMACAlgorithmUsed(cipherInfo.macAlgorithm);
} }
@ -966,7 +967,7 @@ CanFalseStartCallback(PRFileDesc* fd, void* client_data, PRBool *canFalseStart)
sizeof (cipherInfo)) != SECSuccess) { sizeof (cipherInfo)) != SECSuccess) {
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("CanFalseStartCallback [%p] failed - " MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("CanFalseStartCallback [%p] failed - "
" KEA %d\n", fd, " KEA %d\n", fd,
static_cast<int32_t>(cipherInfo.keaType))); static_cast<int32_t>(channelInfo.keaType)));
return SECSuccess; return SECSuccess;
} }
@ -982,10 +983,10 @@ CanFalseStartCallback(PRFileDesc* fd, void* client_data, PRBool *canFalseStart)
} }
// See bug 952863 for why ECDHE is allowed, but DHE (and RSA) are not. // See bug 952863 for why ECDHE is allowed, but DHE (and RSA) are not.
if (cipherInfo.keaType != ssl_kea_ecdh) { if (channelInfo.keaType != ssl_kea_ecdh) {
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("CanFalseStartCallback [%p] failed - " MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("CanFalseStartCallback [%p] failed - "
"unsupported KEA %d\n", fd, "unsupported KEA %d\n", fd,
static_cast<int32_t>(cipherInfo.keaType))); static_cast<int32_t>(channelInfo.keaType)));
reasonsForNotFalseStarting |= KEA_NOT_SUPPORTED; reasonsForNotFalseStarting |= KEA_NOT_SUPPORTED;
} }
@ -1032,6 +1033,7 @@ CanFalseStartCallback(PRFileDesc* fd, void* client_data, PRBool *canFalseStart)
} }
#if(0) // TenFourFox issue 334 #if(0) // TenFourFox issue 334
#error this no longer has correct constants and should not be reenabled
static void static void
AccumulateNonECCKeySize(Telemetry::ID probe, uint32_t bits) AccumulateNonECCKeySize(Telemetry::ID probe, uint32_t bits)
{ {
@ -1156,7 +1158,7 @@ void HandshakeCallback(PRFileDesc* fd, void* client_data) {
if (rv == SECSuccess) { if (rv == SECSuccess) {
#if(0) // TenFourFox issue 334 #if(0) // TenFourFox issue 334
// Get the protocol version for telemetry // Get the protocol version for telemetry
// 1=tls1, 2=tls1.1, 3=tls1.2 // 1=tls1, 2=tls1.1, 3=tls1.2, 4=tls1.3
unsigned int versionEnum = channelInfo.protocolVersion & 0xFF; unsigned int versionEnum = channelInfo.protocolVersion & 0xFF;
MOZ_ASSERT(versionEnum > 0); MOZ_ASSERT(versionEnum > 0);
Telemetry::Accumulate(Telemetry::SSL_HANDSHAKE_VERSION, versionEnum); Telemetry::Accumulate(Telemetry::SSL_HANDSHAKE_VERSION, versionEnum);
@ -1179,16 +1181,16 @@ void HandshakeCallback(PRFileDesc* fd, void* client_data) {
infoObject->IsFullHandshake() infoObject->IsFullHandshake()
? Telemetry::SSL_KEY_EXCHANGE_ALGORITHM_FULL ? Telemetry::SSL_KEY_EXCHANGE_ALGORITHM_FULL
: Telemetry::SSL_KEY_EXCHANGE_ALGORITHM_RESUMED, : Telemetry::SSL_KEY_EXCHANGE_ALGORITHM_RESUMED,
cipherInfo.keaType); channelInfo.keaType);
#endif #endif
DebugOnly<int16_t> KEAUsed; DebugOnly<int16_t> KEAUsed;
MOZ_ASSERT(NS_SUCCEEDED(infoObject->GetKEAUsed(&KEAUsed)) && MOZ_ASSERT(NS_SUCCEEDED(infoObject->GetKEAUsed(&KEAUsed)) &&
(KEAUsed == cipherInfo.keaType)); (KEAUsed == channelInfo.keaType));
#if(0) #if(0)
if (infoObject->IsFullHandshake()) { if (infoObject->IsFullHandshake()) {
switch (cipherInfo.keaType) { switch (channelInfo.keaType) {
case ssl_kea_rsa: case ssl_kea_rsa:
AccumulateNonECCKeySize(Telemetry::SSL_KEA_RSA_KEY_SIZE_FULL, AccumulateNonECCKeySize(Telemetry::SSL_KEA_RSA_KEY_SIZE_FULL,
channelInfo.keaKeyBits); channelInfo.keaKeyBits);
@ -1210,7 +1212,7 @@ void HandshakeCallback(PRFileDesc* fd, void* client_data) {
cipherInfo.authAlgorithm); cipherInfo.authAlgorithm);
// RSA key exchange doesn't use a signature for auth. // RSA key exchange doesn't use a signature for auth.
if (cipherInfo.keaType != ssl_kea_rsa) { if (channelInfo.keaType != ssl_kea_rsa) {
switch (cipherInfo.authAlgorithm) { switch (cipherInfo.authAlgorithm) {
case ssl_auth_rsa: case ssl_auth_rsa:
AccumulateNonECCKeySize(Telemetry::SSL_AUTH_RSA_KEY_SIZE_FULL, AccumulateNonECCKeySize(Telemetry::SSL_AUTH_RSA_KEY_SIZE_FULL,
@ -1241,11 +1243,16 @@ void HandshakeCallback(PRFileDesc* fd, void* client_data) {
} }
PRBool siteSupportsSafeRenego; PRBool siteSupportsSafeRenego;
rv = SSL_HandshakeNegotiatedExtension(fd, ssl_renegotiation_info_xtn, if (channelInfo.protocolVersion != SSL_LIBRARY_VERSION_TLS_1_3) {
&siteSupportsSafeRenego); rv = SSL_HandshakeNegotiatedExtension(fd, ssl_renegotiation_info_xtn,
MOZ_ASSERT(rv == SECSuccess); &siteSupportsSafeRenego);
if (rv != SECSuccess) { MOZ_ASSERT(rv == SECSuccess);
siteSupportsSafeRenego = false; if (rv != SECSuccess) {
siteSupportsSafeRenego = false;
}
} else {
// TLS 1.3 dropped support for renegotiation.
siteSupportsSafeRenego = true;
} }
bool renegotiationUnsafe = !siteSupportsSafeRenego && bool renegotiationUnsafe = !siteSupportsSafeRenego &&
ioLayerHelpers.treatUnsafeNegotiationAsBroken(); ioLayerHelpers.treatUnsafeNegotiationAsBroken();

View File

@ -646,6 +646,13 @@ static const CipherPref sCipherPrefs[] = {
{ "security.ssl3.dhe_rsa_aes_256_sha", { "security.ssl3.dhe_rsa_aes_256_sha",
TLS_DHE_RSA_WITH_AES_256_CBC_SHA, true }, TLS_DHE_RSA_WITH_AES_256_CBC_SHA, true },
{ "security.tls13.aes_128_gcm_sha256",
TLS_AES_128_GCM_SHA256, true },
{ "security.tls13.chacha20_poly1305_sha256",
TLS_CHACHA20_POLY1305_SHA256, true },
{ "security.tls13.aes_256_gcm_sha384",
TLS_AES_256_GCM_SHA384, true },
{ "security.ssl3.ecdhe_rsa_rc4_128_sha", { "security.ssl3.ecdhe_rsa_rc4_128_sha",
TLS_ECDHE_RSA_WITH_RC4_128_SHA, true, true }, // deprecated (RC4) TLS_ECDHE_RSA_WITH_RC4_128_SHA, true, true }, // deprecated (RC4)
{ "security.ssl3.ecdhe_ecdsa_rc4_128_sha", { "security.ssl3.ecdhe_ecdsa_rc4_128_sha",
@ -730,6 +737,7 @@ static const bool REQUIRE_SAFE_NEGOTIATION_DEFAULT = false;
static const bool FALSE_START_ENABLED_DEFAULT = true; static const bool FALSE_START_ENABLED_DEFAULT = true;
static const bool NPN_ENABLED_DEFAULT = true; static const bool NPN_ENABLED_DEFAULT = true;
static const bool ALPN_ENABLED_DEFAULT = false; static const bool ALPN_ENABLED_DEFAULT = false;
static const bool ENABLED_0RTT_DATA_DEFAULT = false;
static void static void
ConfigureTLSSessionIdentifiers() ConfigureTLSSessionIdentifiers()
@ -899,7 +907,7 @@ nsNSSComponent::setEnabledTLSVersions()
// keep these values in sync with security-prefs.js // keep these values in sync with security-prefs.js
// 1 means TLS 1.0, 2 means TLS 1.1, etc. // 1 means TLS 1.0, 2 means TLS 1.1, etc.
static const uint32_t PSM_DEFAULT_MIN_TLS_VERSION = 1; static const uint32_t PSM_DEFAULT_MIN_TLS_VERSION = 1;
static const uint32_t PSM_DEFAULT_MAX_TLS_VERSION = 3; static const uint32_t PSM_DEFAULT_MAX_TLS_VERSION = 4;
uint32_t minFromPrefs = Preferences::GetUint("security.tls.version.min", uint32_t minFromPrefs = Preferences::GetUint("security.tls.version.min",
PSM_DEFAULT_MIN_TLS_VERSION); PSM_DEFAULT_MIN_TLS_VERSION);
@ -1091,6 +1099,9 @@ nsNSSComponent::InitializeNSS()
SSL_OptionSetDefault(SSL_ENABLE_ALPN, SSL_OptionSetDefault(SSL_ENABLE_ALPN,
Preferences::GetBool("security.ssl.enable_alpn", Preferences::GetBool("security.ssl.enable_alpn",
ALPN_ENABLED_DEFAULT)); ALPN_ENABLED_DEFAULT));
SSL_OptionSetDefault(SSL_ENABLE_0RTT_DATA,
Preferences::GetBool("security.tls.enable_0rtt_data",
ENABLED_0RTT_DATA_DEFAULT));
if (NS_FAILED(InitializeCipherSuite())) { if (NS_FAILED(InitializeCipherSuite())) {
MOZ_LOG(gPIPNSSLog, LogLevel::Error, ("Unable to initialize cipher suite settings\n")); MOZ_LOG(gPIPNSSLog, LogLevel::Error, ("Unable to initialize cipher suite settings\n"));
@ -1306,6 +1317,10 @@ nsNSSComponent::Observe(nsISupports* aSubject, const char* aTopic,
SSL_OptionSetDefault(SSL_ENABLE_ALPN, SSL_OptionSetDefault(SSL_ENABLE_ALPN,
Preferences::GetBool("security.ssl.enable_alpn", Preferences::GetBool("security.ssl.enable_alpn",
ALPN_ENABLED_DEFAULT)); ALPN_ENABLED_DEFAULT));
} else if (prefName.EqualsLiteral("security.tls.enable_0rtt_data")) {
SSL_OptionSetDefault(SSL_ENABLE_0RTT_DATA,
Preferences::GetBool("security.tls.enable_0rtt_data",
ENABLED_0RTT_DATA_DEFAULT));
} else if (prefName.Equals("security.ssl.disable_session_identifiers")) { } else if (prefName.Equals("security.ssl.disable_session_identifiers")) {
ConfigureTLSSessionIdentifiers(); ConfigureTLSSessionIdentifiers();
} else if (prefName.EqualsLiteral("security.OCSP.enabled") || } else if (prefName.EqualsLiteral("security.OCSP.enabled") ||

View File

@ -61,6 +61,8 @@ using namespace mozilla::psm;
namespace { namespace {
#define MAX_ALPN_LENGTH 255
void void
getSiteKey(const nsACString& hostName, uint16_t port, getSiteKey(const nsACString& hostName, uint16_t port,
/*out*/ nsCSubstring& key) /*out*/ nsCSubstring& key)
@ -93,6 +95,7 @@ nsNSSSocketInfo::nsNSSSocketInfo(SharedSSLState& aState, uint32_t providerFlags)
mRememberClientAuthCertificate(false), mRememberClientAuthCertificate(false),
mPreliminaryHandshakeDone(false), mPreliminaryHandshakeDone(false),
mNPNCompleted(false), mNPNCompleted(false),
mEarlyDataAccepted(false),
mFalseStartCallbackCalled(false), mFalseStartCallbackCalled(false),
mFalseStarted(false), mFalseStarted(false),
mIsFullHandshake(false), mIsFullHandshake(false),
@ -316,6 +319,71 @@ nsNSSSocketInfo::GetNegotiatedNPN(nsACString& aNegotiatedNPN)
return NS_OK; return NS_OK;
} }
NS_IMETHODIMP
nsNSSSocketInfo::GetAlpnEarlySelection(nsACString& aAlpnSelected)
{
nsNSSShutDownPreventionLock locker;
if (isAlreadyShutDown() || isPK11LoggedOut()) {
return NS_ERROR_NOT_AVAILABLE;
}
SSLNextProtoState alpnState;
unsigned char chosenAlpn[MAX_ALPN_LENGTH];
unsigned int chosenAlpnLen;
SECStatus rv = SSL_GetNextProto(mFd, &alpnState, chosenAlpn, &chosenAlpnLen,
AssertedCast<unsigned int>(ArrayLength(chosenAlpn)));
if (rv != SECSuccess || alpnState != SSL_NEXT_PROTO_EARLY_VALUE ||
chosenAlpnLen == 0) {
return NS_ERROR_NOT_AVAILABLE;
}
aAlpnSelected.Assign(BitwiseCast<char*, unsigned char*>(chosenAlpn),
chosenAlpnLen);
return NS_OK;
}
NS_IMETHODIMP
nsNSSSocketInfo::GetEarlyDataAccepted(bool* aAccepted)
{
*aAccepted = mEarlyDataAccepted;
return NS_OK;
}
void
nsNSSSocketInfo::SetEarlyDataAccepted(bool aAccepted)
{
mEarlyDataAccepted = aAccepted;
}
NS_IMETHODIMP
nsNSSSocketInfo::DriveHandshake()
{
nsNSSShutDownPreventionLock locker;
if (isAlreadyShutDown() || isPK11LoggedOut()) {
return NS_ERROR_NOT_AVAILABLE;
}
if (!mFd) {
return NS_ERROR_FAILURE;
}
PRErrorCode errorCode = GetErrorCode();
if (errorCode) {
return GetXPCOMFromNSSError(errorCode);
}
SECStatus rv = SSL_ForceHandshake(mFd);
if (rv != SECSuccess) {
errorCode = PR_GetError();
if (errorCode == PR_WOULD_BLOCK_ERROR) {
return NS_BASE_STREAM_WOULD_BLOCK;
}
SetCanceled(errorCode, PlainErrorMessage);
return GetXPCOMFromNSSError(errorCode);
}
return NS_OK;
}
NS_IMETHODIMP NS_IMETHODIMP
nsNSSSocketInfo::IsAcceptableForHost(const nsACString& hostname, bool* _retval) nsNSSSocketInfo::IsAcceptableForHost(const nsACString& hostname, bool* _retval)
{ {
@ -704,7 +772,7 @@ nsSSLIOLayerHelpers::rememberTolerantAtVersion(const nsACString& hostName,
mTLSIntoleranceInfo.Put(key, entry); mTLSIntoleranceInfo.Put(key, entry);
} }
uint16_t void
nsSSLIOLayerHelpers::forgetIntolerance(const nsACString& hostName, nsSSLIOLayerHelpers::forgetIntolerance(const nsACString& hostName,
int16_t port) int16_t port)
{ {
@ -713,12 +781,10 @@ nsSSLIOLayerHelpers::forgetIntolerance(const nsACString& hostName,
MutexAutoLock lock(mutex); MutexAutoLock lock(mutex);
uint16_t tolerant = 0;
IntoleranceEntry entry; IntoleranceEntry entry;
if (mTLSIntoleranceInfo.Get(key, &entry)) { if (mTLSIntoleranceInfo.Get(key, &entry)) {
entry.AssertInvariant(); entry.AssertInvariant();
tolerant = entry.tolerant;
entry.intolerant = 0; entry.intolerant = 0;
entry.intoleranceReason = 0; entry.intoleranceReason = 0;
if (entry.strongCipherStatus != StrongCiphersWorked) { if (entry.strongCipherStatus != StrongCiphersWorked) {
@ -728,8 +794,6 @@ nsSSLIOLayerHelpers::forgetIntolerance(const nsACString& hostName,
entry.AssertInvariant(); entry.AssertInvariant();
mTLSIntoleranceInfo.Put(key, entry); mTLSIntoleranceInfo.Put(key, entry);
} }
return tolerant;
} }
bool bool
@ -753,49 +817,7 @@ nsSSLIOLayerHelpers::rememberIntolerantAtVersion(const nsACString& hostName,
{ {
if (intolerant <= minVersion || fallbackLimitReached(hostName, intolerant)) { if (intolerant <= minVersion || fallbackLimitReached(hostName, intolerant)) {
// We can't fall back any further. Assume that intolerance isn't the issue. // We can't fall back any further. Assume that intolerance isn't the issue.
uint32_t tolerant = forgetIntolerance(hostName, port); forgetIntolerance(hostName, port);
// If we know the server is tolerant at the version, we don't have to
// gather the telemetry.
if (intolerant <= tolerant) {
return false;
}
uint32_t fallbackLimitBucket = 0;
// added if the version has reached the min version.
if (intolerant <= minVersion) {
switch (minVersion) {
case SSL_LIBRARY_VERSION_TLS_1_0:
fallbackLimitBucket += 1;
break;
case SSL_LIBRARY_VERSION_TLS_1_1:
fallbackLimitBucket += 2;
break;
case SSL_LIBRARY_VERSION_TLS_1_2:
fallbackLimitBucket += 3;
break;
}
}
// added if the version has reached the fallback limit.
if (intolerant <= mVersionFallbackLimit) {
switch (mVersionFallbackLimit) {
case SSL_LIBRARY_VERSION_TLS_1_0:
fallbackLimitBucket += 4;
break;
case SSL_LIBRARY_VERSION_TLS_1_1:
fallbackLimitBucket += 8;
break;
case SSL_LIBRARY_VERSION_TLS_1_2:
fallbackLimitBucket += 12;
break;
}
}
#if(0)
if (fallbackLimitBucket) {
Telemetry::Accumulate(Telemetry::SSL_FALLBACK_LIMIT_REACHED,
fallbackLimitBucket);
}
#endif
return false; return false;
} }
@ -1047,6 +1069,7 @@ class SSLErrorRunnable : public SyncRunnableBase
namespace { namespace {
#if(0)
uint32_t tlsIntoleranceTelemetryBucket(PRErrorCode err) uint32_t tlsIntoleranceTelemetryBucket(PRErrorCode err)
{ {
// returns a numeric code for where we track various errors in telemetry // returns a numeric code for where we track various errors in telemetry
@ -1065,9 +1088,11 @@ uint32_t tlsIntoleranceTelemetryBucket(PRErrorCode err)
case SSL_ERROR_DECODE_ERROR_ALERT: return 14; case SSL_ERROR_DECODE_ERROR_ALERT: return 14;
case PR_CONNECT_RESET_ERROR: return 16; case PR_CONNECT_RESET_ERROR: return 16;
case PR_END_OF_FILE_ERROR: return 17; case PR_END_OF_FILE_ERROR: return 17;
case SSL_ERROR_INTERNAL_ERROR_ALERT: return 18;
default: return 0; default: return 0;
} }
} }
#endif
bool bool
retryDueToTLSIntolerance(PRErrorCode err, nsNSSSocketInfo* socketInfo) retryDueToTLSIntolerance(PRErrorCode err, nsNSSSocketInfo* socketInfo)
@ -1143,12 +1168,13 @@ retryDueToTLSIntolerance(PRErrorCode err, nsNSSSocketInfo* socketInfo)
return false; return false;
} }
#if(0)
#error doesn't support TLS 1.3
uint32_t reason = tlsIntoleranceTelemetryBucket(err); uint32_t reason = tlsIntoleranceTelemetryBucket(err);
if (reason == 0) { if (reason == 0) {
return false; return false;
} }
#if(0)
Telemetry::ID pre; Telemetry::ID pre;
Telemetry::ID post; Telemetry::ID post;
switch (range.max) { switch (range.max) {
@ -2564,8 +2590,11 @@ nsSSLIOLayerSetOptions(PRFileDesc* fd, bool forSTARTTLS,
if (range.max < maxEnabledVersion) { if (range.max < maxEnabledVersion) {
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
("[%p] nsSSLIOLayerSetOptions: enabling TLS_FALLBACK_SCSV\n", fd)); ("[%p] nsSSLIOLayerSetOptions: enabling TLS_FALLBACK_SCSV\n", fd));
if (SECSuccess != SSL_OptionSet(fd, SSL_ENABLE_FALLBACK_SCSV, true)) { // Some servers will choke if we send the fallback SCSV with TLS 1.2.
return NS_ERROR_FAILURE; if (range.max < SSL_LIBRARY_VERSION_TLS_1_2) {
if (SECSuccess != SSL_OptionSet(fd, SSL_ENABLE_FALLBACK_SCSV, true)) {
return NS_ERROR_FAILURE;
}
} }
} }

View File

@ -52,6 +52,7 @@ public:
const nsNSSShutDownPreventionLock& proofOfLock); const nsNSSShutDownPreventionLock& proofOfLock);
void SetNegotiatedNPN(const char* value, uint32_t length); void SetNegotiatedNPN(const char* value, uint32_t length);
void SetEarlyDataAccepted(bool aAccepted);
void SetHandshakeCompleted(); void SetHandshakeCompleted();
void NoteTimeUntilReady(); void NoteTimeUntilReady();
@ -139,6 +140,7 @@ private:
nsCString mNegotiatedNPN; nsCString mNegotiatedNPN;
bool mNPNCompleted; bool mNPNCompleted;
bool mEarlyDataAccepted;
bool mFalseStartCallbackCalled; bool mFalseStartCallbackCalled;
bool mFalseStarted; bool mFalseStarted;
bool mIsFullHandshake; bool mIsFullHandshake;
@ -216,9 +218,7 @@ public:
PRErrorCode intoleranceReason); PRErrorCode intoleranceReason);
bool rememberStrongCiphersFailed(const nsACString& hostName, int16_t port, bool rememberStrongCiphersFailed(const nsACString& hostName, int16_t port,
PRErrorCode intoleranceReason); PRErrorCode intoleranceReason);
// returns the known tolerant version void forgetIntolerance(const nsACString& hostname, int16_t port);
// or 0 if there is no known tolerant version
uint16_t forgetIntolerance(const nsACString& hostname, int16_t port);
void adjustForTLSIntolerance(const nsACString& hostname, int16_t port, void adjustForTLSIntolerance(const nsACString& hostname, int16_t port,
/*in/out*/ SSLVersionRange& range, /*in/out*/ SSLVersionRange& range,
/*out*/ StrongCipherStatus& strongCipherStatus); /*out*/ StrongCipherStatus& strongCipherStatus);

View File

@ -1090,7 +1090,15 @@ NS_IMETHODIMP nsDocLoader::OnStatus(nsIRequest* aRequest, nsISupports* ctxt,
if (!sbs) if (!sbs)
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
nsXPIDLString msg; nsXPIDLString msg;
nsresult rv = sbs->FormatStatusMessage(aStatus, aStatusArg, /* TenFourFox issue 590. Map NS_NET_STATUS_TLS_HANDSHAKE_STARTING and
NS_NET_STATUS_TLS_HANDSHAKE_ENDED to NS_NET_STATUS_CONNECTED_TO so
that we don't instantly invalidate all our locales. See also
netwerk/locales/en-US/necko.properties XXX */
nsresult rv = sbs->FormatStatusMessage(
(aStatus == NS_NET_STATUS_TLS_HANDSHAKE_STARTING ||
aStatus == NS_NET_STATUS_TLS_HANDSHAKE_ENDED) ?
NS_NET_STATUS_CONNECTED_TO : aStatus,
aStatusArg,
getter_Copies(msg)); getter_Copies(msg));
if (NS_FAILED(rv)) if (NS_FAILED(rv))
return rv; return rv;

View File

@ -313,13 +313,15 @@
ERROR(NS_NET_STATUS_WRITING, FAILURE(9)), ERROR(NS_NET_STATUS_WRITING, FAILURE(9)),
/* nsISocketTransport */ /* nsISocketTransport */
ERROR(NS_NET_STATUS_RESOLVING_HOST, FAILURE(3)), ERROR(NS_NET_STATUS_RESOLVING_HOST, FAILURE(3)),
ERROR(NS_NET_STATUS_RESOLVED_HOST, FAILURE(11)), ERROR(NS_NET_STATUS_RESOLVED_HOST, FAILURE(11)),
ERROR(NS_NET_STATUS_CONNECTING_TO, FAILURE(7)), ERROR(NS_NET_STATUS_CONNECTING_TO, FAILURE(7)),
ERROR(NS_NET_STATUS_CONNECTED_TO, FAILURE(4)), ERROR(NS_NET_STATUS_CONNECTED_TO, FAILURE(4)),
ERROR(NS_NET_STATUS_SENDING_TO, FAILURE(5)), ERROR(NS_NET_STATUS_TLS_HANDSHAKE_STARTING, FAILURE(12)),
ERROR(NS_NET_STATUS_WAITING_FOR, FAILURE(10)), ERROR(NS_NET_STATUS_TLS_HANDSHAKE_ENDED, FAILURE(13)),
ERROR(NS_NET_STATUS_RECEIVING_FROM, FAILURE(6)), ERROR(NS_NET_STATUS_SENDING_TO, FAILURE(5)),
ERROR(NS_NET_STATUS_WAITING_FOR, FAILURE(10)),
ERROR(NS_NET_STATUS_RECEIVING_FROM, FAILURE(6)),
/* nsIInterceptedChannel */ /* nsIInterceptedChannel */
/* Generic error for non-specific failures during service worker interception */ /* Generic error for non-specific failures during service worker interception */