Bug 839310: Expand TLS intolerance logic to work for versions beyond TLS 1.0, r=keeler, r=wtc

--HG--
extra : rebase_source : b83a610d08abb428f0f6894fa935712f820ddfe8
This commit is contained in:
Brian Smith 2013-10-11 00:17:19 -07:00
parent 440791afd0
commit 03dc3ced83
11 changed files with 391 additions and 168 deletions

View File

@ -299,7 +299,7 @@ CertErrorRunnable::CheckCertOverrides()
mInfoObject->GetPort(&port);
nsCString hostWithPortString;
hostWithPortString.AppendASCII(mInfoObject->GetHostName());
hostWithPortString.AppendASCII(mInfoObject->GetHostNameRaw());
hostWithPortString.AppendLiteral(":");
hostWithPortString.AppendInt(port);
@ -315,7 +315,7 @@ CertErrorRunnable::CheckCertOverrides()
= do_GetService(NS_SSSERVICE_CONTRACTID, &nsrv);
if (NS_SUCCEEDED(nsrv)) {
nsrv = sss->IsSecureHost(nsISiteSecurityService::HEADER_HSTS,
mInfoObject->GetHostName(),
mInfoObject->GetHostNameRaw(),
mProviderFlags,
&strictTransportSecurityEnabled);
}
@ -413,7 +413,7 @@ CertErrorRunnable::CheckCertOverrides()
OverridableCertErrorMessage);
LogInvalidCertError(mInfoObject,
nsDependentCString(mInfoObject->GetHostName()),
mInfoObject->GetHostName(),
hostWithPortString,
port,
result->mErrorCode,
@ -510,7 +510,7 @@ CreateCertErrorRunnable(PRErrorCode defaultErrorCodeToReport,
}
// Check the name field against the desired hostname.
if (CERT_VerifyCertName(cert, infoObject->GetHostName()) != SECSuccess) {
if (CERT_VerifyCertName(cert, infoObject->GetHostNameRaw()) != SECSuccess) {
collected_errors |= nsICertOverrideService::ERROR_MISMATCH;
errorCodeMismatch = SSL_ERROR_BAD_CERT_DOMAIN;
}
@ -883,7 +883,7 @@ AuthCertificate(TransportSecurityInfo * infoObject, CERTCertificate * cert,
CERTCertList *verifyCertChain = nullptr;
SECOidTag evOidPolicy;
rv = PSM_SSL_PKIX_AuthCertificate(cert, infoObject, infoObject->GetHostName(),
rv = PSM_SSL_PKIX_AuthCertificate(cert, infoObject, infoObject->GetHostNameRaw(),
&verifyCertChain, &evOidPolicy);
// We want to remember the CA certs in the temp db, so that the application can find the

View File

@ -49,9 +49,9 @@ public:
nsresult SetSecurityState(uint32_t aState);
nsresult SetShortSecurityDescription(const PRUnichar *aText);
const char * GetHostName() const {
return mHostName.get();
}
const nsACString & GetHostName() const { return mHostName; }
const char * GetHostNameRaw() const { return mHostName.get(); }
nsresult GetHostName(char **aHostName);
nsresult SetHostName(const char *aHostName);

View File

@ -1032,16 +1032,25 @@ void HandshakeCallback(PRFileDesc* fd, void* client_data) {
// is absent at handshake time we have a resumed session. Check this before
// PreliminaryHandshakeDone() because that function also sets that flag.
bool isResumedSession = !(infoObject->GetFirstServerHelloReceived());
// Do the bookkeeping that needs to be done after the
// server's ServerHello...ServerHelloDone have been processed, but that doesn't
// need the handshake to be completed.
PreliminaryHandshakeDone(fd);
// If the handshake completed, then we know the site is TLS tolerant (if this
// was a TLS connection).
nsSSLIOLayerHelpers& ioLayerHelpers = infoObject->SharedState().IOLayerHelpers();
ioLayerHelpers.rememberTolerantSite(infoObject);
nsSSLIOLayerHelpers& ioLayerHelpers
= infoObject->SharedState().IOLayerHelpers();
SSLVersionRange versions(infoObject->GetTLSVersionRange());
PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
("[%p] HandshakeCallback: succeeded using TLS version range (0x%04x,0x%04x)\n",
fd, static_cast<unsigned int>(versions.min),
static_cast<unsigned int>(versions.max)));
// If the handshake completed, then we know the site is TLS tolerant
ioLayerHelpers.rememberTolerantAtVersion(infoObject->GetHostName(),
infoObject->GetPort(),
versions.max);
PRBool siteSupportsSafeRenego;
rv = SSL_HandshakeNegotiatedExtension(fd, ssl_renegotiation_info_xtn,

View File

@ -64,6 +64,21 @@ NSSCleanupAutoPtrClass(void, PR_FREEIF)
static NS_DEFINE_CID(kNSSComponentCID, NS_NSSCOMPONENT_CID);
void
getSiteKey(const nsACString & hostName, uint16_t port,
/*out*/ nsCSubstring & key)
{
key = hostName;
key.AppendASCII(":");
key.AppendInt(port);
}
void
getSiteKey(const nsNSSSocketInfo & socketInfo, /*out*/ nsCSubstring & key)
{
getSiteKey(socketInfo.GetHostName(), socketInfo.GetPort(), key);
}
/* SSM_UserCertChoice: enum for cert choice info */
typedef enum {ASK, AUTO} SSM_UserCertChoice;
@ -78,8 +93,6 @@ nsNSSSocketInfo::nsNSSSocketInfo(SharedSSLState& aState, uint32_t providerFlags)
mCertVerificationState(before_cert_verification),
mSharedState(aState),
mForSTARTTLS(false),
mSSL3Enabled(false),
mTLSEnabled(false),
mHandshakePending(true),
mHasCleartextPhase(false),
mHandshakeInProgress(false),
@ -101,6 +114,8 @@ nsNSSSocketInfo::nsNSSSocketInfo(SharedSSLState& aState, uint32_t providerFlags)
mSocketCreationTimestamp(TimeStamp::Now()),
mPlaintextBytesRead(0)
{
mTLSVersionRange.min = 0;
mTLSVersionRange.max = 0;
}
NS_IMPL_ISUPPORTS_INHERITED2(nsNSSSocketInfo, TransportSecurityInfo,
@ -329,7 +344,7 @@ nsNSSSocketInfo::JoinConnection(const nsACString & npnProtocol,
// If this is the same hostname then the certicate status does not
// need to be considered. They are joinable.
if (GetHostName() && hostname.Equals(GetHostName())) {
if (hostname.Equals(GetHostName())) {
*_retval = true;
return NS_OK;
}
@ -594,25 +609,12 @@ bool nsNSSSocketInfo::HandshakeTimeout()
void nsSSLIOLayerHelpers::Cleanup()
{
if (mTLSIntolerantSites) {
delete mTLSIntolerantSites;
mTLSIntolerantSites = nullptr;
}
if (mTLSTolerantSites) {
delete mTLSTolerantSites;
mTLSTolerantSites = nullptr;
}
mTLSIntoleranceInfo.Clear();
if (mRenegoUnrestrictedSites) {
delete mRenegoUnrestrictedSites;
mRenegoUnrestrictedSites = nullptr;
}
if (mutex) {
delete mutex;
mutex = nullptr;
}
}
static void
@ -641,12 +643,6 @@ nsHandleSSLError(nsNSSSocketInfo *socketInfo,
if (NS_FAILED(rv))
return;
nsXPIDLCString hostName;
socketInfo->GetHostName(getter_Copies(hostName));
int32_t port;
socketInfo->GetPort(&port);
// Try to get a nsISSLErrorListener implementation from the socket consumer.
nsCOMPtr<nsIInterfaceRequestor> cb;
socketInfo->GetNotificationCallbacks(getter_AddRefs(cb));
@ -654,10 +650,11 @@ nsHandleSSLError(nsNSSSocketInfo *socketInfo,
nsCOMPtr<nsISSLErrorListener> sel = do_GetInterface(cb);
if (sel) {
nsIInterfaceRequestor *csi = static_cast<nsIInterfaceRequestor*>(socketInfo);
nsCString hostWithPortString = hostName;
hostWithPortString.AppendLiteral(":");
hostWithPortString.AppendInt(port);
nsCString hostWithPortString;
getSiteKey(socketInfo->GetHostName(), socketInfo->GetPort(),
hostWithPortString);
bool suppressMessage = false; // obsolete, ignored
rv = sel->NotifySSLError(csi, err, hostWithPortString, &suppressMessage);
}
@ -739,58 +736,104 @@ nsSSLIOLayerConnect(PRFileDesc* fd, const PRNetAddr* addr,
}
void
nsSSLIOLayerHelpers::getSiteKey(nsNSSSocketInfo *socketInfo, nsCSubstring &key)
nsSSLIOLayerHelpers::rememberTolerantAtVersion(const nsACString & hostName,
int16_t port, uint16_t tolerant)
{
int32_t port;
socketInfo->GetPort(&port);
nsCString key;
getSiteKey(hostName, port, key);
nsXPIDLCString host;
socketInfo->GetHostName(getter_Copies(host));
MutexAutoLock lock(mutex);
key = host + NS_LITERAL_CSTRING(":") + nsPrintfCString("%d", port);
IntoleranceEntry entry;
if (mTLSIntoleranceInfo.Get(key, &entry)) {
entry.AssertInvariant();
entry.tolerant = std::max(entry.tolerant, tolerant);
if (entry.intolerant != 0 && entry.intolerant <= entry.tolerant) {
entry.intolerant = entry.tolerant + 1;
}
} else {
entry.tolerant = tolerant;
entry.intolerant = 0;
}
entry.AssertInvariant();
mTLSIntoleranceInfo.Put(key, entry);
}
// Call this function to report a site that is possibly TLS intolerant.
// This function will return true, if the given socket is currently using TLS,
// and it's allowed to retry. Retrying only makes sense if an older
// protocol is enabled.
// returns true if we should retry the handshake
bool
nsSSLIOLayerHelpers::rememberPossibleTLSProblemSite(nsNSSSocketInfo *socketInfo)
nsSSLIOLayerHelpers::rememberIntolerantAtVersion(const nsACString & hostName,
int16_t port,
uint16_t minVersion,
uint16_t intolerant)
{
nsAutoCString key;
getSiteKey(socketInfo, key);
nsCString key;
getSiteKey(hostName, port, key);
MutexAutoLock lock(mutex);
if (intolerant <= minVersion) {
// We can't fall back any further. Assume that intolerance isn't the issue.
IntoleranceEntry entry;
if (mTLSIntoleranceInfo.Get(key, &entry)) {
entry.AssertInvariant();
entry.intolerant = 0;
entry.AssertInvariant();
mTLSIntoleranceInfo.Put(key, entry);
}
if (!socketInfo->IsTLSEnabled()) {
// We did not offer TLS but failed with an intolerant error using
// a different protocol. To give TLS a try on next connection attempt again
// drop this site from the list of intolerant sites. TLS failure might be
// caused only by a traffic congestion while the server is TLS tolerant.
removeIntolerantSite(key);
return false;
}
if (socketInfo->IsSSL3Enabled()) {
// Add this site to the list of TLS intolerant sites.
addIntolerantSite(key);
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.
return false;
}
if ((entry.intolerant != 0 && intolerant >= entry.intolerant)) {
// We already know that the server is intolerant at a lower version.
return true;
}
} else {
entry.tolerant = 0;
}
else {
return false; // doesn't make sense to retry
}
return socketInfo->IsTLSEnabled();
entry.intolerant = intolerant;
entry.AssertInvariant();
mTLSIntoleranceInfo.Put(key, entry);
return true;
}
void
nsSSLIOLayerHelpers::rememberTolerantSite(nsNSSSocketInfo *socketInfo)
nsSSLIOLayerHelpers::adjustForTLSIntolerance(const nsACString & hostName,
int16_t port,
/*in/out*/ SSLVersionRange & range)
{
if (!socketInfo->IsTLSEnabled())
return;
IntoleranceEntry entry;
nsAutoCString key;
getSiteKey(socketInfo, key);
{
nsCString key;
getSiteKey(hostName, port, key);
MutexAutoLock lock(*mutex);
nsSSLIOLayerHelpers::mTLSTolerantSites->PutEntry(key);
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;
}
}
}
bool nsSSLIOLayerHelpers::nsSSLIOLayerInitialized = false;
@ -1037,8 +1080,11 @@ int32_t checkHandshake(int32_t bytesTransfered, bool wasReading,
if (!wantRetry // no decision yet
&& isTLSIntoleranceError(err, socketInfo->GetHasCleartextPhase()))
{
nsSSLIOLayerHelpers& helpers = socketInfo->SharedState().IOLayerHelpers();
wantRetry = helpers.rememberPossibleTLSProblemSite(socketInfo);
SSLVersionRange range = socketInfo->GetTLSVersionRange();
wantRetry = socketInfo->SharedState().IOLayerHelpers()
.rememberIntolerantAtVersion(socketInfo->GetHostName(),
socketInfo->GetPort(),
range.min, range.max);
}
}
@ -1066,13 +1112,19 @@ int32_t checkHandshake(int32_t bytesTransfered, bool wasReading,
if (!wantRetry // no decision yet
&& !socketInfo->GetHasCleartextPhase()) // mirror PR_CONNECT_RESET_ERROR treament
{
nsSSLIOLayerHelpers& helpers = socketInfo->SharedState().IOLayerHelpers();
wantRetry = helpers.rememberPossibleTLSProblemSite(socketInfo);
SSLVersionRange range = socketInfo->GetTLSVersionRange();
wantRetry = socketInfo->SharedState().IOLayerHelpers()
.rememberIntolerantAtVersion(socketInfo->GetHostName(),
socketInfo->GetPort(),
range.min, range.max);
}
}
}
if (wantRetry) {
PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
("[%p] checkHandshake: will retry with lower max TLS version\n",
ssl_layer_fd));
// We want to cause the network layer to retry the connection.
PR_SetError(PR_CONNECT_RESET_ERROR, 0);
if (wasReading)
@ -1153,12 +1205,11 @@ nsSSLIOLayerPoll(PRFileDesc * fd, int16_t in_flags, int16_t *out_flags)
}
nsSSLIOLayerHelpers::nsSSLIOLayerHelpers()
: mutex(nullptr)
, mTLSIntolerantSites(nullptr)
, mTLSTolerantSites(nullptr)
: mutex("nsSSLIOLayerHelpers.mutex")
, mRenegoUnrestrictedSites(nullptr)
, mTreatUnsafeNegotiationAsBroken(false)
, mWarnLevelMissingRFC5746(1)
, mTLSIntoleranceInfo(16)
, mFalseStartRequireNPN(true)
, mFalseStartRequireForwardSecrecy(false)
{
@ -1381,11 +1432,20 @@ static int32_t PlaintextRecv(PRFileDesc *fd, void *buf, int32_t amount,
nsSSLIOLayerHelpers::~nsSSLIOLayerHelpers()
{
Preferences::RemoveObserver(mPrefObserver, "security.ssl.renego_unrestricted_hosts");
Preferences::RemoveObserver(mPrefObserver, "security.ssl.treat_unsafe_negotiation_as_broken");
Preferences::RemoveObserver(mPrefObserver, "security.ssl.warn_missing_rfc5746");
Preferences::RemoveObserver(mPrefObserver, "security.ssl.false_start.require-npn");
Preferences::RemoveObserver(mPrefObserver, "security.ssl.false_start.require-forward-secrecy");
// mPrefObserver will only be set if this->Init was called. The GTest tests
// do not call Init.
if (mPrefObserver) {
Preferences::RemoveObserver(mPrefObserver,
"security.ssl.renego_unrestricted_hosts");
Preferences::RemoveObserver(mPrefObserver,
"security.ssl.treat_unsafe_negotiation_as_broken");
Preferences::RemoveObserver(mPrefObserver,
"security.ssl.warn_missing_rfc5746");
Preferences::RemoveObserver(mPrefObserver,
"security.ssl.false_start.require-npn");
Preferences::RemoveObserver(mPrefObserver,
"security.ssl.false_start.require-forward-secrecy");
}
}
nsresult nsSSLIOLayerHelpers::Init()
@ -1432,16 +1492,7 @@ nsresult nsSSLIOLayerHelpers::Init()
nsSSLPlaintextLayerMethods.recv = PlaintextRecv;
}
mutex = new Mutex("nsSSLIOLayerHelpers.mutex");
mTLSIntolerantSites = new nsTHashtable<nsCStringHashKey>(1);
// Initialize the tolerant site hashtable to 16 items at the start seems
// reasonable as most servers are TLS tolerant. We just want to lower
// the rate of hashtable array reallocation.
mTLSTolerantSites = new nsTHashtable<nsCStringHashKey>(16);
mRenegoUnrestrictedSites = new nsTHashtable<nsCStringHashKey>(1);
mRenegoUnrestrictedSites = new nsTHashtable<nsCStringHashKey>(16);
nsCString unrestricted_hosts;
Preferences::GetCString("security.ssl.renego_unrestricted_hosts", &unrestricted_hosts);
@ -1479,40 +1530,19 @@ nsresult nsSSLIOLayerHelpers::Init()
void nsSSLIOLayerHelpers::clearStoredData()
{
mRenegoUnrestrictedSites->Clear();
mTLSTolerantSites->Clear();
mTLSIntolerantSites->Clear();
}
void nsSSLIOLayerHelpers::addIntolerantSite(const nsCString &str)
{
MutexAutoLock lock(*mutex);
// Remember intolerant site only if it is not known as tolerant
if (!mTLSTolerantSites->Contains(str))
mTLSIntolerantSites->PutEntry(str);
}
void nsSSLIOLayerHelpers::removeIntolerantSite(const nsCString &str)
{
MutexAutoLock lock(*mutex);
mTLSIntolerantSites->RemoveEntry(str);
}
bool nsSSLIOLayerHelpers::isKnownAsIntolerantSite(const nsCString &str)
{
MutexAutoLock lock(*mutex);
return mTLSIntolerantSites->Contains(str);
mTLSIntoleranceInfo.Clear();
}
void nsSSLIOLayerHelpers::setRenegoUnrestrictedSites(const nsCString &str)
{
MutexAutoLock lock(*mutex);
MutexAutoLock lock(mutex);
if (mRenegoUnrestrictedSites) {
delete mRenegoUnrestrictedSites;
mRenegoUnrestrictedSites = nullptr;
}
mRenegoUnrestrictedSites = new nsTHashtable<nsCStringHashKey>(1);
mRenegoUnrestrictedSites = new nsTHashtable<nsCStringHashKey>();
if (!mRenegoUnrestrictedSites)
return;
@ -1528,31 +1558,31 @@ void nsSSLIOLayerHelpers::setRenegoUnrestrictedSites(const nsCString &str)
bool nsSSLIOLayerHelpers::isRenegoUnrestrictedSite(const nsCString &str)
{
MutexAutoLock lock(*mutex);
MutexAutoLock lock(mutex);
return mRenegoUnrestrictedSites->Contains(str);
}
void nsSSLIOLayerHelpers::setTreatUnsafeNegotiationAsBroken(bool broken)
{
MutexAutoLock lock(*mutex);
MutexAutoLock lock(mutex);
mTreatUnsafeNegotiationAsBroken = broken;
}
bool nsSSLIOLayerHelpers::treatUnsafeNegotiationAsBroken()
{
MutexAutoLock lock(*mutex);
MutexAutoLock lock(mutex);
return mTreatUnsafeNegotiationAsBroken;
}
void nsSSLIOLayerHelpers::setWarnLevelMissingRFC5746(int32_t level)
{
MutexAutoLock lock(*mutex);
MutexAutoLock lock(mutex);
mWarnLevelMissingRFC5746 = level;
}
int32_t nsSSLIOLayerHelpers::getWarnLevelMissingRFC5746()
{
MutexAutoLock lock(*mutex);
MutexAutoLock lock(mutex);
return mWarnLevelMissingRFC5746;
}
@ -2609,31 +2639,31 @@ nsSSLIOLayerSetOptions(PRFileDesc *fd, bool forSTARTTLS,
nsAutoCString key;
key = nsDependentCString(host) + NS_LITERAL_CSTRING(":") + nsPrintfCString("%d", port);
if (infoObject->SharedState().IOLayerHelpers().isKnownAsIntolerantSite(key)) {
if (SECSuccess != SSL_OptionSet(fd, SSL_ENABLE_TLS, false))
return NS_ERROR_FAILURE;
SSLVersionRange range;
if (SSL_VersionRangeGet(fd, &range) != SECSuccess) {
return NS_ERROR_FAILURE;
}
infoObject->SharedState().IOLayerHelpers()
.adjustForTLSIntolerance(infoObject->GetHostName(), infoObject->GetPort(),
range);
PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
("[%p] nsSSLIOLayerSetOptions: using TLS version range (0x%04x,0x%04x)\n",
fd, static_cast<unsigned int>(range.min),
static_cast<unsigned int>(range.max)));
if (SSL_VersionRangeSet(fd, &range) != SECSuccess) {
return NS_ERROR_FAILURE;
}
infoObject->SetTLSVersionRange(range);
// If min == max, then we don't need the intolerance timeout since we have no
// lower version to fall back to.
if (range.min == range.max) {
infoObject->SetAllowTLSIntoleranceTimeout(false);
// We assume that protocols that use the STARTTLS mechanism should support
// modern hellos. For other protocols, if we suspect a site
// does not support TLS, let's also use V2 hellos.
// One advantage of this approach, if a site only supports the older
// hellos, it is more likely that we will get a reasonable error code
// on our single retry attempt.
}
PRBool enabled;
if (SECSuccess != SSL_OptionGet(fd, SSL_ENABLE_SSL3, &enabled)) {
return NS_ERROR_FAILURE;
}
infoObject->SetSSL3Enabled(enabled);
if (SECSuccess != SSL_OptionGet(fd, SSL_ENABLE_TLS, &enabled)) {
return NS_ERROR_FAILURE;
}
infoObject->SetTLSEnabled(enabled);
enabled = infoObject->SharedState().IsOCSPStaplingEnabled();
bool enabled = infoObject->SharedState().IsOCSPStaplingEnabled();
if (SECSuccess != SSL_OptionSet(fd, SSL_ENABLE_OCSP_STAPLING, enabled)) {
return NS_ERROR_FAILURE;
}

View File

@ -14,6 +14,7 @@
#include "nsDataHashtable.h"
#include "nsTHashtable.h"
#include "mozilla/TimeStamp.h"
#include "sslt.h"
namespace mozilla {
namespace psm {
@ -56,6 +57,9 @@ public:
void SetAllowTLSIntoleranceTimeout(bool aAllow);
void SetTLSVersionRange(SSLVersionRange range) { mTLSVersionRange = range; }
SSLVersionRange GetTLSVersionRange() const { return mTLSVersionRange; };
PRStatus CloseSocketAndDestroy(
const nsNSSShutDownPreventionLock & proofOfLock);
@ -87,12 +91,6 @@ public:
{
return mCertVerificationState == waiting_for_cert_verification;
}
bool IsSSL3Enabled() const { return mSSL3Enabled; }
void SetSSL3Enabled(bool enabled) { mSSL3Enabled = enabled; }
bool IsTLSEnabled() const { return mTLSEnabled; }
void SetTLSEnabled(bool enabled) { mTLSEnabled = enabled; }
void AddPlaintextBytesRead(uint64_t val) { mPlaintextBytesRead += val; }
bool IsPreliminaryHandshakeDone() const { return mPreliminaryHandshakeDone; }
@ -125,8 +123,7 @@ private:
mozilla::psm::SharedSSLState& mSharedState;
bool mForSTARTTLS;
bool mSSL3Enabled;
bool mTLSEnabled;
SSLVersionRange mTLSVersionRange;
bool mHandshakePending;
bool mHasCleartextPhase;
bool mHandshakeInProgress;
@ -172,36 +169,43 @@ public:
static PRIOMethods nsSSLIOLayerMethods;
static PRIOMethods nsSSLPlaintextLayerMethods;
mozilla::Mutex *mutex;
nsTHashtable<nsCStringHashKey> *mTLSIntolerantSites;
nsTHashtable<nsCStringHashKey> *mTLSTolerantSites;
nsTHashtable<nsCStringHashKey> *mRenegoUnrestrictedSites;
bool mTreatUnsafeNegotiationAsBroken;
int32_t mWarnLevelMissingRFC5746;
void setTreatUnsafeNegotiationAsBroken(bool broken);
bool treatUnsafeNegotiationAsBroken();
void setWarnLevelMissingRFC5746(int32_t level);
int32_t getWarnLevelMissingRFC5746();
static void getSiteKey(nsNSSSocketInfo *socketInfo, nsCSubstring &key);
bool rememberPossibleTLSProblemSite(nsNSSSocketInfo *socketInfo);
void rememberTolerantSite(nsNSSSocketInfo *socketInfo);
private:
struct IntoleranceEntry
{
uint16_t tolerant;
uint16_t intolerant;
void addIntolerantSite(const nsCString &str);
void removeIntolerantSite(const nsCString &str);
bool isKnownAsIntolerantSite(const nsCString &str);
void AssertInvariant() const
{
MOZ_ASSERT(intolerant == 0 || tolerant < intolerant);
}
};
nsDataHashtable<nsCStringHashKey, IntoleranceEntry> mTLSIntoleranceInfo;
public:
void rememberTolerantAtVersion(const nsACString & hostname, int16_t port,
uint16_t tolerant);
bool rememberIntolerantAtVersion(const nsACString & hostname, int16_t port,
uint16_t intolerant, uint16_t minVersion);
void adjustForTLSIntolerance(const nsACString & hostname, int16_t port,
/*in/out*/ SSLVersionRange & range);
void setRenegoUnrestrictedSites(const nsCString &str);
bool isRenegoUnrestrictedSite(const nsCString &str);
void clearStoredData();
bool mFalseStartRequireNPN;
bool mFalseStartRequireForwardSecrecy;
private:
mozilla::Mutex mutex;
nsCOMPtr<nsIObserver> mPrefObserver;
};

View File

@ -0,0 +1,16 @@
#! gmake
#
# 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/.
include $(DEPTH)/config/autoconf.mk
EXPORT_LIBRARY = 1
LOCAL_INCLUDES = \
-I$(topsrcdir)/security/manager/ssl/src \
$(NULL)
include $(topsrcdir)/config/rules.mk
include $(topsrcdir)/ipc/chromium/chromium-config.mk

View File

@ -0,0 +1,145 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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/. */
#include "nsNSSIOLayer.h"
#include "sslproto.h"
#include "gtest/gtest.h"
NS_NAMED_LITERAL_CSTRING(HOST, "example.org");
const int16_t PORT = 443;
class TLSIntoleranceTest : public ::testing::Test
{
protected:
nsSSLIOLayerHelpers helpers;
};
TEST_F(TLSIntoleranceTest, Test_1_2_through_3_0)
{
// No adjustment made when there is no entry for the site.
{
SSLVersionRange range = { SSL_LIBRARY_VERSION_3_0,
SSL_LIBRARY_VERSION_TLS_1_2 };
helpers.adjustForTLSIntolerance(HOST, PORT, range);
ASSERT_EQ(SSL_LIBRARY_VERSION_3_0, range.min);
ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_2, range.max);
ASSERT_TRUE(helpers.rememberIntolerantAtVersion(HOST, PORT,
range.min, range.max));
}
{
SSLVersionRange range = { SSL_LIBRARY_VERSION_3_0,
SSL_LIBRARY_VERSION_TLS_1_2 };
helpers.adjustForTLSIntolerance(HOST, PORT, range);
ASSERT_EQ(SSL_LIBRARY_VERSION_3_0, range.min);
ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_1, range.max);
ASSERT_TRUE(helpers.rememberIntolerantAtVersion(HOST, PORT,
range.min, range.max));
}
{
SSLVersionRange range = { SSL_LIBRARY_VERSION_3_0,
SSL_LIBRARY_VERSION_TLS_1_2 };
helpers.adjustForTLSIntolerance(HOST, PORT, range);
ASSERT_EQ(SSL_LIBRARY_VERSION_3_0, range.min);
ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_0, range.max);
ASSERT_TRUE(helpers.rememberIntolerantAtVersion(HOST, PORT,
range.min, range.max));
}
{
SSLVersionRange range = { SSL_LIBRARY_VERSION_3_0,
SSL_LIBRARY_VERSION_TLS_1_2 };
helpers.adjustForTLSIntolerance(HOST, PORT, range);
ASSERT_EQ(SSL_LIBRARY_VERSION_3_0, range.min);
ASSERT_EQ(SSL_LIBRARY_VERSION_3_0, range.max);
// false because we reached the floor set by range.min
ASSERT_FALSE(helpers.rememberIntolerantAtVersion(HOST, PORT,
range.min, range.max));
}
{
SSLVersionRange range = { SSL_LIBRARY_VERSION_3_0,
SSL_LIBRARY_VERSION_TLS_1_2 };
helpers.adjustForTLSIntolerance(HOST, PORT, range);
ASSERT_EQ(SSL_LIBRARY_VERSION_3_0, range.min);
// When rememberIntolerantAtVersion returns false, it also resets the
// intolerance information for the server.
ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_2, range.max);
}
}
TEST_F(TLSIntoleranceTest, Test_Tolerant_Overrides_Intolerant_1)
{
ASSERT_TRUE(helpers.rememberIntolerantAtVersion(HOST, PORT,
SSL_LIBRARY_VERSION_3_0,
SSL_LIBRARY_VERSION_TLS_1_0));
helpers.rememberTolerantAtVersion(HOST, PORT, SSL_LIBRARY_VERSION_TLS_1_0);
SSLVersionRange range = { SSL_LIBRARY_VERSION_3_0,
SSL_LIBRARY_VERSION_TLS_1_2 };
helpers.adjustForTLSIntolerance(HOST, PORT, range);
ASSERT_EQ(SSL_LIBRARY_VERSION_3_0, range.min);
ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_0, range.max);
}
TEST_F(TLSIntoleranceTest, Test_Tolerant_Overrides_Intolerant_2)
{
ASSERT_TRUE(helpers.rememberIntolerantAtVersion(HOST, PORT,
SSL_LIBRARY_VERSION_3_0,
SSL_LIBRARY_VERSION_TLS_1_0));
helpers.rememberTolerantAtVersion(HOST, PORT, SSL_LIBRARY_VERSION_TLS_1_1);
SSLVersionRange range = { SSL_LIBRARY_VERSION_3_0,
SSL_LIBRARY_VERSION_TLS_1_2 };
helpers.adjustForTLSIntolerance(HOST, PORT, range);
ASSERT_EQ(SSL_LIBRARY_VERSION_3_0, range.min);
ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_1, range.max);
}
TEST_F(TLSIntoleranceTest, Test_Intolerant_Does_Not_Override_Tolerant)
{
// No adjustment made when there is no entry for the site.
helpers.rememberTolerantAtVersion(HOST, PORT, SSL_LIBRARY_VERSION_TLS_1_0);
// false because we reached the floor set by rememberTolerantAtVersion.
ASSERT_FALSE(helpers.rememberIntolerantAtVersion(HOST, PORT,
SSL_LIBRARY_VERSION_3_0,
SSL_LIBRARY_VERSION_TLS_1_0));
SSLVersionRange range = { SSL_LIBRARY_VERSION_3_0,
SSL_LIBRARY_VERSION_TLS_1_2 };
helpers.adjustForTLSIntolerance(HOST, PORT, range);
ASSERT_EQ(SSL_LIBRARY_VERSION_3_0, range.min);
ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_2, range.max);
}
TEST_F(TLSIntoleranceTest, Test_Port_Is_Relevant)
{
helpers.rememberTolerantAtVersion(HOST, 1, SSL_LIBRARY_VERSION_TLS_1_2);
ASSERT_FALSE(helpers.rememberIntolerantAtVersion(HOST, 1,
SSL_LIBRARY_VERSION_3_0,
SSL_LIBRARY_VERSION_TLS_1_2));
ASSERT_TRUE(helpers.rememberIntolerantAtVersion(HOST, 2,
SSL_LIBRARY_VERSION_3_0,
SSL_LIBRARY_VERSION_TLS_1_2));
{
SSLVersionRange range = { SSL_LIBRARY_VERSION_3_0,
SSL_LIBRARY_VERSION_TLS_1_2 };
helpers.adjustForTLSIntolerance(HOST, 1, range);
ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_2, range.max);
}
{
SSLVersionRange range = { SSL_LIBRARY_VERSION_3_0,
SSL_LIBRARY_VERSION_TLS_1_2 };
helpers.adjustForTLSIntolerance(HOST, 2, range);
ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_1, range.max);
}
}

View File

@ -0,0 +1,15 @@
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# 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/.
MODULE = 'ssltest'
LIBRARY_NAME = 'ssltest'
LIBXUL_LIBRARY = True
GTEST_CPP_SOURCES += [
'TLSIntoleranceTest.cpp',
]

View File

@ -5,7 +5,11 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
DIRS += ['unit']
TEST_DIRS += ['mochitest']
TEST_DIRS += [
'gtest',
'mochitest',
]
MODULE = 'pipnss'

View File

@ -7,4 +7,3 @@
DIRS += ['tlsserver']
MODULE = 'pipnss'

View File

@ -693,6 +693,7 @@ ifdef LINK_GTEST
COMPONENT_LIBS += \
gtest \
gfxtest \
ssltest \
$(NULL)
endif