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); mInfoObject->GetPort(&port);
nsCString hostWithPortString; nsCString hostWithPortString;
hostWithPortString.AppendASCII(mInfoObject->GetHostName()); hostWithPortString.AppendASCII(mInfoObject->GetHostNameRaw());
hostWithPortString.AppendLiteral(":"); hostWithPortString.AppendLiteral(":");
hostWithPortString.AppendInt(port); hostWithPortString.AppendInt(port);
@ -315,7 +315,7 @@ CertErrorRunnable::CheckCertOverrides()
= do_GetService(NS_SSSERVICE_CONTRACTID, &nsrv); = do_GetService(NS_SSSERVICE_CONTRACTID, &nsrv);
if (NS_SUCCEEDED(nsrv)) { if (NS_SUCCEEDED(nsrv)) {
nsrv = sss->IsSecureHost(nsISiteSecurityService::HEADER_HSTS, nsrv = sss->IsSecureHost(nsISiteSecurityService::HEADER_HSTS,
mInfoObject->GetHostName(), mInfoObject->GetHostNameRaw(),
mProviderFlags, mProviderFlags,
&strictTransportSecurityEnabled); &strictTransportSecurityEnabled);
} }
@ -413,7 +413,7 @@ CertErrorRunnable::CheckCertOverrides()
OverridableCertErrorMessage); OverridableCertErrorMessage);
LogInvalidCertError(mInfoObject, LogInvalidCertError(mInfoObject,
nsDependentCString(mInfoObject->GetHostName()), mInfoObject->GetHostName(),
hostWithPortString, hostWithPortString,
port, port,
result->mErrorCode, result->mErrorCode,
@ -510,7 +510,7 @@ CreateCertErrorRunnable(PRErrorCode defaultErrorCodeToReport,
} }
// Check the name field against the desired hostname. // 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; collected_errors |= nsICertOverrideService::ERROR_MISMATCH;
errorCodeMismatch = SSL_ERROR_BAD_CERT_DOMAIN; errorCodeMismatch = SSL_ERROR_BAD_CERT_DOMAIN;
} }
@ -883,7 +883,7 @@ AuthCertificate(TransportSecurityInfo * infoObject, CERTCertificate * cert,
CERTCertList *verifyCertChain = nullptr; CERTCertList *verifyCertChain = nullptr;
SECOidTag evOidPolicy; SECOidTag evOidPolicy;
rv = PSM_SSL_PKIX_AuthCertificate(cert, infoObject, infoObject->GetHostName(), rv = PSM_SSL_PKIX_AuthCertificate(cert, infoObject, infoObject->GetHostNameRaw(),
&verifyCertChain, &evOidPolicy); &verifyCertChain, &evOidPolicy);
// We want to remember the CA certs in the temp db, so that the application can find the // 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 SetSecurityState(uint32_t aState);
nsresult SetShortSecurityDescription(const PRUnichar *aText); nsresult SetShortSecurityDescription(const PRUnichar *aText);
const char * GetHostName() const { const nsACString & GetHostName() const { return mHostName; }
return mHostName.get(); const char * GetHostNameRaw() const { return mHostName.get(); }
}
nsresult GetHostName(char **aHostName); nsresult GetHostName(char **aHostName);
nsresult SetHostName(const 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 // is absent at handshake time we have a resumed session. Check this before
// PreliminaryHandshakeDone() because that function also sets that flag. // PreliminaryHandshakeDone() because that function also sets that flag.
bool isResumedSession = !(infoObject->GetFirstServerHelloReceived()); bool isResumedSession = !(infoObject->GetFirstServerHelloReceived());
// Do the bookkeeping that needs to be done after the // Do the bookkeeping that needs to be done after the
// server's ServerHello...ServerHelloDone have been processed, but that doesn't // server's ServerHello...ServerHelloDone have been processed, but that doesn't
// need the handshake to be completed. // need the handshake to be completed.
PreliminaryHandshakeDone(fd); PreliminaryHandshakeDone(fd);
// If the handshake completed, then we know the site is TLS tolerant (if this nsSSLIOLayerHelpers& ioLayerHelpers
// was a TLS connection). = infoObject->SharedState().IOLayerHelpers();
nsSSLIOLayerHelpers& ioLayerHelpers = infoObject->SharedState().IOLayerHelpers();
ioLayerHelpers.rememberTolerantSite(infoObject); 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; PRBool siteSupportsSafeRenego;
rv = SSL_HandshakeNegotiatedExtension(fd, ssl_renegotiation_info_xtn, 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); 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 */ /* SSM_UserCertChoice: enum for cert choice info */
typedef enum {ASK, AUTO} SSM_UserCertChoice; typedef enum {ASK, AUTO} SSM_UserCertChoice;
@ -78,8 +93,6 @@ nsNSSSocketInfo::nsNSSSocketInfo(SharedSSLState& aState, uint32_t providerFlags)
mCertVerificationState(before_cert_verification), mCertVerificationState(before_cert_verification),
mSharedState(aState), mSharedState(aState),
mForSTARTTLS(false), mForSTARTTLS(false),
mSSL3Enabled(false),
mTLSEnabled(false),
mHandshakePending(true), mHandshakePending(true),
mHasCleartextPhase(false), mHasCleartextPhase(false),
mHandshakeInProgress(false), mHandshakeInProgress(false),
@ -101,6 +114,8 @@ nsNSSSocketInfo::nsNSSSocketInfo(SharedSSLState& aState, uint32_t providerFlags)
mSocketCreationTimestamp(TimeStamp::Now()), mSocketCreationTimestamp(TimeStamp::Now()),
mPlaintextBytesRead(0) mPlaintextBytesRead(0)
{ {
mTLSVersionRange.min = 0;
mTLSVersionRange.max = 0;
} }
NS_IMPL_ISUPPORTS_INHERITED2(nsNSSSocketInfo, TransportSecurityInfo, 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 // If this is the same hostname then the certicate status does not
// need to be considered. They are joinable. // need to be considered. They are joinable.
if (GetHostName() && hostname.Equals(GetHostName())) { if (hostname.Equals(GetHostName())) {
*_retval = true; *_retval = true;
return NS_OK; return NS_OK;
} }
@ -594,25 +609,12 @@ bool nsNSSSocketInfo::HandshakeTimeout()
void nsSSLIOLayerHelpers::Cleanup() void nsSSLIOLayerHelpers::Cleanup()
{ {
if (mTLSIntolerantSites) { mTLSIntoleranceInfo.Clear();
delete mTLSIntolerantSites;
mTLSIntolerantSites = nullptr;
}
if (mTLSTolerantSites) {
delete mTLSTolerantSites;
mTLSTolerantSites = nullptr;
}
if (mRenegoUnrestrictedSites) { if (mRenegoUnrestrictedSites) {
delete mRenegoUnrestrictedSites; delete mRenegoUnrestrictedSites;
mRenegoUnrestrictedSites = nullptr; mRenegoUnrestrictedSites = nullptr;
} }
if (mutex) {
delete mutex;
mutex = nullptr;
}
} }
static void static void
@ -641,12 +643,6 @@ nsHandleSSLError(nsNSSSocketInfo *socketInfo,
if (NS_FAILED(rv)) if (NS_FAILED(rv))
return; return;
nsXPIDLCString hostName;
socketInfo->GetHostName(getter_Copies(hostName));
int32_t port;
socketInfo->GetPort(&port);
// Try to get a nsISSLErrorListener implementation from the socket consumer. // Try to get a nsISSLErrorListener implementation from the socket consumer.
nsCOMPtr<nsIInterfaceRequestor> cb; nsCOMPtr<nsIInterfaceRequestor> cb;
socketInfo->GetNotificationCallbacks(getter_AddRefs(cb)); socketInfo->GetNotificationCallbacks(getter_AddRefs(cb));
@ -654,10 +650,11 @@ nsHandleSSLError(nsNSSSocketInfo *socketInfo,
nsCOMPtr<nsISSLErrorListener> sel = do_GetInterface(cb); nsCOMPtr<nsISSLErrorListener> sel = do_GetInterface(cb);
if (sel) { if (sel) {
nsIInterfaceRequestor *csi = static_cast<nsIInterfaceRequestor*>(socketInfo); nsIInterfaceRequestor *csi = static_cast<nsIInterfaceRequestor*>(socketInfo);
nsCString hostWithPortString = hostName;
hostWithPortString.AppendLiteral(":"); nsCString hostWithPortString;
hostWithPortString.AppendInt(port); getSiteKey(socketInfo->GetHostName(), socketInfo->GetPort(),
hostWithPortString);
bool suppressMessage = false; // obsolete, ignored bool suppressMessage = false; // obsolete, ignored
rv = sel->NotifySSLError(csi, err, hostWithPortString, &suppressMessage); rv = sel->NotifySSLError(csi, err, hostWithPortString, &suppressMessage);
} }
@ -739,58 +736,104 @@ nsSSLIOLayerConnect(PRFileDesc* fd, const PRNetAddr* addr,
} }
void void
nsSSLIOLayerHelpers::getSiteKey(nsNSSSocketInfo *socketInfo, nsCSubstring &key) nsSSLIOLayerHelpers::rememberTolerantAtVersion(const nsACString & hostName,
int16_t port, uint16_t tolerant)
{ {
int32_t port; nsCString key;
socketInfo->GetPort(&port); getSiteKey(hostName, port, key);
nsXPIDLCString host; MutexAutoLock lock(mutex);
socketInfo->GetHostName(getter_Copies(host));
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. // returns true if we should retry the handshake
// 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.
bool bool
nsSSLIOLayerHelpers::rememberPossibleTLSProblemSite(nsNSSSocketInfo *socketInfo) nsSSLIOLayerHelpers::rememberIntolerantAtVersion(const nsACString & hostName,
int16_t port,
uint16_t minVersion,
uint16_t intolerant)
{ {
nsAutoCString key; nsCString key;
getSiteKey(socketInfo, 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; return false;
} }
if (socketInfo->IsSSL3Enabled()) { IntoleranceEntry entry;
// Add this site to the list of TLS intolerant sites. if (mTLSIntoleranceInfo.Get(key, &entry)) {
addIntolerantSite(key); 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 entry.intolerant = intolerant;
} entry.AssertInvariant();
mTLSIntoleranceInfo.Put(key, entry);
return socketInfo->IsTLSEnabled();
return true;
} }
void void
nsSSLIOLayerHelpers::rememberTolerantSite(nsNSSSocketInfo *socketInfo) nsSSLIOLayerHelpers::adjustForTLSIntolerance(const nsACString & hostName,
int16_t port,
/*in/out*/ SSLVersionRange & range)
{ {
if (!socketInfo->IsTLSEnabled()) IntoleranceEntry entry;
return;
nsAutoCString key; {
getSiteKey(socketInfo, key); nsCString key;
getSiteKey(hostName, port, key);
MutexAutoLock lock(*mutex); MutexAutoLock lock(mutex);
nsSSLIOLayerHelpers::mTLSTolerantSites->PutEntry(key); 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; bool nsSSLIOLayerHelpers::nsSSLIOLayerInitialized = false;
@ -1037,8 +1080,11 @@ int32_t checkHandshake(int32_t bytesTransfered, bool wasReading,
if (!wantRetry // no decision yet if (!wantRetry // no decision yet
&& isTLSIntoleranceError(err, socketInfo->GetHasCleartextPhase())) && isTLSIntoleranceError(err, socketInfo->GetHasCleartextPhase()))
{ {
nsSSLIOLayerHelpers& helpers = socketInfo->SharedState().IOLayerHelpers(); SSLVersionRange range = socketInfo->GetTLSVersionRange();
wantRetry = helpers.rememberPossibleTLSProblemSite(socketInfo); 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 if (!wantRetry // no decision yet
&& !socketInfo->GetHasCleartextPhase()) // mirror PR_CONNECT_RESET_ERROR treament && !socketInfo->GetHasCleartextPhase()) // mirror PR_CONNECT_RESET_ERROR treament
{ {
nsSSLIOLayerHelpers& helpers = socketInfo->SharedState().IOLayerHelpers(); SSLVersionRange range = socketInfo->GetTLSVersionRange();
wantRetry = helpers.rememberPossibleTLSProblemSite(socketInfo); wantRetry = socketInfo->SharedState().IOLayerHelpers()
.rememberIntolerantAtVersion(socketInfo->GetHostName(),
socketInfo->GetPort(),
range.min, range.max);
} }
} }
} }
if (wantRetry) { 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. // We want to cause the network layer to retry the connection.
PR_SetError(PR_CONNECT_RESET_ERROR, 0); PR_SetError(PR_CONNECT_RESET_ERROR, 0);
if (wasReading) if (wasReading)
@ -1153,12 +1205,11 @@ nsSSLIOLayerPoll(PRFileDesc * fd, int16_t in_flags, int16_t *out_flags)
} }
nsSSLIOLayerHelpers::nsSSLIOLayerHelpers() nsSSLIOLayerHelpers::nsSSLIOLayerHelpers()
: mutex(nullptr) : mutex("nsSSLIOLayerHelpers.mutex")
, mTLSIntolerantSites(nullptr)
, mTLSTolerantSites(nullptr)
, mRenegoUnrestrictedSites(nullptr) , mRenegoUnrestrictedSites(nullptr)
, mTreatUnsafeNegotiationAsBroken(false) , mTreatUnsafeNegotiationAsBroken(false)
, mWarnLevelMissingRFC5746(1) , mWarnLevelMissingRFC5746(1)
, mTLSIntoleranceInfo(16)
, mFalseStartRequireNPN(true) , mFalseStartRequireNPN(true)
, mFalseStartRequireForwardSecrecy(false) , mFalseStartRequireForwardSecrecy(false)
{ {
@ -1381,11 +1432,20 @@ static int32_t PlaintextRecv(PRFileDesc *fd, void *buf, int32_t amount,
nsSSLIOLayerHelpers::~nsSSLIOLayerHelpers() nsSSLIOLayerHelpers::~nsSSLIOLayerHelpers()
{ {
Preferences::RemoveObserver(mPrefObserver, "security.ssl.renego_unrestricted_hosts"); // mPrefObserver will only be set if this->Init was called. The GTest tests
Preferences::RemoveObserver(mPrefObserver, "security.ssl.treat_unsafe_negotiation_as_broken"); // do not call Init.
Preferences::RemoveObserver(mPrefObserver, "security.ssl.warn_missing_rfc5746"); if (mPrefObserver) {
Preferences::RemoveObserver(mPrefObserver, "security.ssl.false_start.require-npn"); Preferences::RemoveObserver(mPrefObserver,
Preferences::RemoveObserver(mPrefObserver, "security.ssl.false_start.require-forward-secrecy"); "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() nsresult nsSSLIOLayerHelpers::Init()
@ -1432,16 +1492,7 @@ nsresult nsSSLIOLayerHelpers::Init()
nsSSLPlaintextLayerMethods.recv = PlaintextRecv; nsSSLPlaintextLayerMethods.recv = PlaintextRecv;
} }
mutex = new Mutex("nsSSLIOLayerHelpers.mutex"); mRenegoUnrestrictedSites = new nsTHashtable<nsCStringHashKey>(16);
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);
nsCString unrestricted_hosts; nsCString unrestricted_hosts;
Preferences::GetCString("security.ssl.renego_unrestricted_hosts", &unrestricted_hosts); Preferences::GetCString("security.ssl.renego_unrestricted_hosts", &unrestricted_hosts);
@ -1479,40 +1530,19 @@ nsresult nsSSLIOLayerHelpers::Init()
void nsSSLIOLayerHelpers::clearStoredData() void nsSSLIOLayerHelpers::clearStoredData()
{ {
mRenegoUnrestrictedSites->Clear(); mRenegoUnrestrictedSites->Clear();
mTLSTolerantSites->Clear(); mTLSIntoleranceInfo.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);
} }
void nsSSLIOLayerHelpers::setRenegoUnrestrictedSites(const nsCString &str) void nsSSLIOLayerHelpers::setRenegoUnrestrictedSites(const nsCString &str)
{ {
MutexAutoLock lock(*mutex); MutexAutoLock lock(mutex);
if (mRenegoUnrestrictedSites) { if (mRenegoUnrestrictedSites) {
delete mRenegoUnrestrictedSites; delete mRenegoUnrestrictedSites;
mRenegoUnrestrictedSites = nullptr; mRenegoUnrestrictedSites = nullptr;
} }
mRenegoUnrestrictedSites = new nsTHashtable<nsCStringHashKey>(1); mRenegoUnrestrictedSites = new nsTHashtable<nsCStringHashKey>();
if (!mRenegoUnrestrictedSites) if (!mRenegoUnrestrictedSites)
return; return;
@ -1528,31 +1558,31 @@ void nsSSLIOLayerHelpers::setRenegoUnrestrictedSites(const nsCString &str)
bool nsSSLIOLayerHelpers::isRenegoUnrestrictedSite(const nsCString &str) bool nsSSLIOLayerHelpers::isRenegoUnrestrictedSite(const nsCString &str)
{ {
MutexAutoLock lock(*mutex); MutexAutoLock lock(mutex);
return mRenegoUnrestrictedSites->Contains(str); return mRenegoUnrestrictedSites->Contains(str);
} }
void nsSSLIOLayerHelpers::setTreatUnsafeNegotiationAsBroken(bool broken) void nsSSLIOLayerHelpers::setTreatUnsafeNegotiationAsBroken(bool broken)
{ {
MutexAutoLock lock(*mutex); MutexAutoLock lock(mutex);
mTreatUnsafeNegotiationAsBroken = broken; mTreatUnsafeNegotiationAsBroken = broken;
} }
bool nsSSLIOLayerHelpers::treatUnsafeNegotiationAsBroken() bool nsSSLIOLayerHelpers::treatUnsafeNegotiationAsBroken()
{ {
MutexAutoLock lock(*mutex); MutexAutoLock lock(mutex);
return mTreatUnsafeNegotiationAsBroken; return mTreatUnsafeNegotiationAsBroken;
} }
void nsSSLIOLayerHelpers::setWarnLevelMissingRFC5746(int32_t level) void nsSSLIOLayerHelpers::setWarnLevelMissingRFC5746(int32_t level)
{ {
MutexAutoLock lock(*mutex); MutexAutoLock lock(mutex);
mWarnLevelMissingRFC5746 = level; mWarnLevelMissingRFC5746 = level;
} }
int32_t nsSSLIOLayerHelpers::getWarnLevelMissingRFC5746() int32_t nsSSLIOLayerHelpers::getWarnLevelMissingRFC5746()
{ {
MutexAutoLock lock(*mutex); MutexAutoLock lock(mutex);
return mWarnLevelMissingRFC5746; return mWarnLevelMissingRFC5746;
} }
@ -2609,31 +2639,31 @@ nsSSLIOLayerSetOptions(PRFileDesc *fd, bool forSTARTTLS,
nsAutoCString key; nsAutoCString key;
key = nsDependentCString(host) + NS_LITERAL_CSTRING(":") + nsPrintfCString("%d", port); key = nsDependentCString(host) + NS_LITERAL_CSTRING(":") + nsPrintfCString("%d", port);
if (infoObject->SharedState().IOLayerHelpers().isKnownAsIntolerantSite(key)) { SSLVersionRange range;
if (SECSuccess != SSL_OptionSet(fd, SSL_ENABLE_TLS, false)) if (SSL_VersionRangeGet(fd, &range) != SECSuccess) {
return NS_ERROR_FAILURE; 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); 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; bool enabled = infoObject->SharedState().IsOCSPStaplingEnabled();
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();
if (SECSuccess != SSL_OptionSet(fd, SSL_ENABLE_OCSP_STAPLING, enabled)) { if (SECSuccess != SSL_OptionSet(fd, SSL_ENABLE_OCSP_STAPLING, enabled)) {
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
} }

View File

@ -14,6 +14,7 @@
#include "nsDataHashtable.h" #include "nsDataHashtable.h"
#include "nsTHashtable.h" #include "nsTHashtable.h"
#include "mozilla/TimeStamp.h" #include "mozilla/TimeStamp.h"
#include "sslt.h"
namespace mozilla { namespace mozilla {
namespace psm { namespace psm {
@ -56,6 +57,9 @@ public:
void SetAllowTLSIntoleranceTimeout(bool aAllow); void SetAllowTLSIntoleranceTimeout(bool aAllow);
void SetTLSVersionRange(SSLVersionRange range) { mTLSVersionRange = range; }
SSLVersionRange GetTLSVersionRange() const { return mTLSVersionRange; };
PRStatus CloseSocketAndDestroy( PRStatus CloseSocketAndDestroy(
const nsNSSShutDownPreventionLock & proofOfLock); const nsNSSShutDownPreventionLock & proofOfLock);
@ -87,12 +91,6 @@ public:
{ {
return mCertVerificationState == waiting_for_cert_verification; 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; } void AddPlaintextBytesRead(uint64_t val) { mPlaintextBytesRead += val; }
bool IsPreliminaryHandshakeDone() const { return mPreliminaryHandshakeDone; } bool IsPreliminaryHandshakeDone() const { return mPreliminaryHandshakeDone; }
@ -125,8 +123,7 @@ private:
mozilla::psm::SharedSSLState& mSharedState; mozilla::psm::SharedSSLState& mSharedState;
bool mForSTARTTLS; bool mForSTARTTLS;
bool mSSL3Enabled; SSLVersionRange mTLSVersionRange;
bool mTLSEnabled;
bool mHandshakePending; bool mHandshakePending;
bool mHasCleartextPhase; bool mHasCleartextPhase;
bool mHandshakeInProgress; bool mHandshakeInProgress;
@ -172,36 +169,43 @@ public:
static PRIOMethods nsSSLIOLayerMethods; static PRIOMethods nsSSLIOLayerMethods;
static PRIOMethods nsSSLPlaintextLayerMethods; static PRIOMethods nsSSLPlaintextLayerMethods;
mozilla::Mutex *mutex;
nsTHashtable<nsCStringHashKey> *mTLSIntolerantSites;
nsTHashtable<nsCStringHashKey> *mTLSTolerantSites;
nsTHashtable<nsCStringHashKey> *mRenegoUnrestrictedSites; nsTHashtable<nsCStringHashKey> *mRenegoUnrestrictedSites;
bool mTreatUnsafeNegotiationAsBroken; bool mTreatUnsafeNegotiationAsBroken;
int32_t mWarnLevelMissingRFC5746; int32_t mWarnLevelMissingRFC5746;
void setTreatUnsafeNegotiationAsBroken(bool broken); void setTreatUnsafeNegotiationAsBroken(bool broken);
bool treatUnsafeNegotiationAsBroken(); bool treatUnsafeNegotiationAsBroken();
void setWarnLevelMissingRFC5746(int32_t level); void setWarnLevelMissingRFC5746(int32_t level);
int32_t getWarnLevelMissingRFC5746(); int32_t getWarnLevelMissingRFC5746();
static void getSiteKey(nsNSSSocketInfo *socketInfo, nsCSubstring &key); private:
bool rememberPossibleTLSProblemSite(nsNSSSocketInfo *socketInfo); struct IntoleranceEntry
void rememberTolerantSite(nsNSSSocketInfo *socketInfo); {
uint16_t tolerant;
uint16_t intolerant;
void addIntolerantSite(const nsCString &str); void AssertInvariant() const
void removeIntolerantSite(const nsCString &str); {
bool isKnownAsIntolerantSite(const nsCString &str); 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); void setRenegoUnrestrictedSites(const nsCString &str);
bool isRenegoUnrestrictedSite(const nsCString &str); bool isRenegoUnrestrictedSite(const nsCString &str);
void clearStoredData(); void clearStoredData();
bool mFalseStartRequireNPN; bool mFalseStartRequireNPN;
bool mFalseStartRequireForwardSecrecy; bool mFalseStartRequireForwardSecrecy;
private: private:
mozilla::Mutex mutex;
nsCOMPtr<nsIObserver> mPrefObserver; 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/. # file, You can obtain one at http://mozilla.org/MPL/2.0/.
DIRS += ['unit'] DIRS += ['unit']
TEST_DIRS += ['mochitest']
TEST_DIRS += [
'gtest',
'mochitest',
]
MODULE = 'pipnss' MODULE = 'pipnss'

View File

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

View File

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