diff --git a/mozilla/security/manager/ssl/src/nsNSSIOLayer.cpp b/mozilla/security/manager/ssl/src/nsNSSIOLayer.cpp index 0d5ecc95fa7..3f190e71444 100644 --- a/mozilla/security/manager/ssl/src/nsNSSIOLayer.cpp +++ b/mozilla/security/manager/ssl/src/nsNSSIOLayer.cpp @@ -191,7 +191,7 @@ nsNSSSocketInfo::nsNSSSocketInfo() mHasCleartextPhase(PR_FALSE), mHandshakeInProgress(PR_FALSE), mAllowTLSIntoleranceTimeout(PR_TRUE), - mBlockedOnBadCertUI(PR_FALSE), + mBadCertUIStatus(bcuis_not_shown), mHandshakeStartTime(0), mPort(0), mCAChain(nsnull) @@ -532,9 +532,10 @@ void nsNSSSocketInfo::SetHandshakeInProgress(PRBool aIsIn) } } -void nsNSSSocketInfo::SetBlockedOnBadCertUI(PRBool aCurrentlyBlockedOnUI) +void nsNSSSocketInfo::SetBadCertUIStatus(nsNSSSocketInfo::BadCertUIStatusType aNewStatus) { - if (mBlockedOnBadCertUI && !aCurrentlyBlockedOnUI) + if (mBadCertUIStatus == bcuis_active && + aNewStatus == bcuis_was_shown) { // we were blocked and going back to unblocked, // so let's reset the handshake start time, in order to ensure @@ -542,7 +543,7 @@ void nsNSSSocketInfo::SetBlockedOnBadCertUI(PRBool aCurrentlyBlockedOnUI) mHandshakeStartTime = PR_IntervalNow(); } - mBlockedOnBadCertUI = aCurrentlyBlockedOnUI; + mBadCertUIStatus = aNewStatus; } void nsNSSSocketInfo::SetAllowTLSIntoleranceTimeout(PRBool aAllow) @@ -554,7 +555,8 @@ void nsNSSSocketInfo::SetAllowTLSIntoleranceTimeout(PRBool aAllow) PRBool nsNSSSocketInfo::HandshakeTimeout() { - if (!mHandshakeInProgress || !mAllowTLSIntoleranceTimeout || mBlockedOnBadCertUI) + if (!mHandshakeInProgress || !mAllowTLSIntoleranceTimeout || + mBadCertUIStatus == bcuis_active) return PR_FALSE; return ((PRIntervalTime)(PR_IntervalNow() - mHandshakeStartTime) @@ -858,7 +860,7 @@ nsDumpBuffer(unsigned char *buf, PRIntn len) #endif static PRBool -isTLSIntoleranceError(PRInt32 err, PRBool withInitialCleartext) +isNonSSLErrorThatWeAllowToRetry(PRInt32 err, PRBool withInitialCleartext) { switch (err) { @@ -868,6 +870,29 @@ isTLSIntoleranceError(PRInt32 err, PRBool withInitialCleartext) break; case PR_END_OF_FILE_ERROR: + return PR_TRUE; + } + + return PR_FALSE; +} + +static PRBool +isTLSIntoleranceError(PRInt32 err, PRBool withInitialCleartext) +{ + // This function is supposed to decide, which error codes should + // be used to conclude server is TLS intolerant. + // Note this only happens during the initial SSL handshake. + // + // When not using a proxy we'll see a connection reset error. + // When using a proxy, we'll see an end of file error. + // In addition check for some error codes where it is reasonable + // to retry without TLS. + + if (isNonSSLErrorThatWeAllowToRetry(err, withInitialCleartext)) + return PR_TRUE; + + switch (err) + { case SSL_ERROR_BAD_MAC_ALERT: case SSL_ERROR_BAD_MAC_READ: case SSL_ERROR_HANDSHAKE_FAILURE_ALERT: @@ -889,8 +914,42 @@ isTLSIntoleranceError(PRInt32 err, PRBool withInitialCleartext) return PR_FALSE; } +static PRBool +isClosedConnectionAfterBadCertUIWasShown(PRInt32 bytesTransfered, + PRBool wasReading, + PRInt32 err, + nsNSSSocketInfo::BadCertUIStatusType aBadCertUIStatus) +{ + if (aBadCertUIStatus != nsNSSSocketInfo::bcuis_not_shown) + { + // Bad cert UI was shown for this socket. + // Server timeout possible. + // Retry on a simple connection close. + + if (wasReading && 0 == bytesTransfered) + return PR_TRUE; + + if (0 > bytesTransfered) + { + switch (err) + { + case PR_CONNECT_RESET_ERROR: + case PR_END_OF_FILE_ERROR: + return PR_TRUE; + default: + break; + } + } + } + + return PR_FALSE; +} + PRInt32 -nsSSLThread::checkHandshake(PRInt32 bytesTransfered, PRFileDesc* ssl_layer_fd, nsNSSSocketInfo *socketInfo) +nsSSLThread::checkHandshake(PRInt32 bytesTransfered, + PRBool wasReading, + PRFileDesc* ssl_layer_fd, + nsNSSSocketInfo *socketInfo) { // This is where we work around all of those SSL servers that don't // conform to the SSL spec and shutdown a connection when we request @@ -918,42 +977,69 @@ nsSSLThread::checkHandshake(PRInt32 bytesTransfered, PRFileDesc* ssl_layer_fd, n // We'll make a note of the current time, // and use this to measure the elapsed time since handshake begin. + // Do NOT assume TLS intolerance on a closed connection after bad cert ui was shown. + // Simply retry. + // This depends on the fact that Cert UI will not be shown again, + // should the user override the bad cert. + PRBool handleHandshakeResultNow; socketInfo->GetHandshakePending(&handleHandshakeResultNow); + PRBool wantRetry = PR_FALSE; + if (0 > bytesTransfered) { PRInt32 err = PR_GetError(); - PRBool wantRetry = PR_FALSE; - + if (handleHandshakeResultNow) { - // Let's see if there was an error set by the SSL libraries that we - // should tell the user about. if (PR_WOULD_BLOCK_ERROR == err) { socketInfo->SetHandshakeInProgress(PR_TRUE); return bytesTransfered; } - - PRBool withInitialCleartext = socketInfo->GetHasCleartextPhase(); - // When not using a proxy we'll see a connection reset error. - // When using a proxy, we'll see an end of file error. - // In addition check for some error codes where it is reasonable - // to retry without TLS. + wantRetry = + isClosedConnectionAfterBadCertUIWasShown(bytesTransfered, + wasReading, + err, + socketInfo->GetBadCertUIStatus()); - if (isTLSIntoleranceError(err, withInitialCleartext)) { + if (!wantRetry // no decision yet + && isTLSIntoleranceError(err, socketInfo->GetHasCleartextPhase())) + { wantRetry = nsSSLIOLayerHelpers::rememberPossibleTLSProblemSite(ssl_layer_fd, socketInfo); - - if (wantRetry) { - // We want to cause the network layer to retry the connection. - PR_SetError(PR_CONNECT_RESET_ERROR, 0); - } } } + // This is the common place where we trigger an error message on a SSL socket. + // This might be reached at any time of the connection. if (!wantRetry && (IS_SSL_ERROR(err) || IS_SEC_ERROR(err))) { nsHandleSSLError(socketInfo, err); } } + else if (wasReading && 0 == bytesTransfered) // zero bytes on reading, socket closed + { + if (handleHandshakeResultNow) + { + wantRetry = + isClosedConnectionAfterBadCertUIWasShown(bytesTransfered, + wasReading, + 0, + socketInfo->GetBadCertUIStatus()); + + if (!wantRetry // no decision yet + && !socketInfo->GetHasCleartextPhase()) // mirror PR_CONNECT_RESET_ERROR treament + { + wantRetry = + nsSSLIOLayerHelpers::rememberPossibleTLSProblemSite(ssl_layer_fd, socketInfo); + } + } + } + + if (wantRetry) { + // We want to cause the network layer to retry the connection. + PR_SetError(PR_CONNECT_RESET_ERROR, 0); + if (wasReading) + bytesTransfered = -1; + } // TLS intolerant servers only cause the first transfer to fail, so let's // set the HandshakePending attribute to false so that we don't try the logic @@ -2290,7 +2376,7 @@ nsNSSBadCertHandler(void *arg, PRFileDesc *sslSocket) return SECFailure; } NS_ADDREF(nssCert); - infoObject->SetBlockedOnBadCertUI(PR_TRUE); + infoObject->SetBadCertUIStatus(nsNSSSocketInfo::bcuis_active); while (rv != SECSuccess) { //Func nsContinueDespiteCertError does the same set of checks as func. //nsCertErrorNeedsDialog. So, removing call to nsCertErrorNeedsDialog @@ -2301,7 +2387,7 @@ nsNSSBadCertHandler(void *arg, PRFileDesc *sslSocket) rv = verifyCertAgain(peerCert, sslSocket, infoObject); error = PR_GetError(); } - infoObject->SetBlockedOnBadCertUI(PR_FALSE); + infoObject->SetBadCertUIStatus(nsNSSSocketInfo::bcuis_was_shown); NS_RELEASE(nssCert); CERT_DestroyCertificate(peerCert); if (rv != SECSuccess) { diff --git a/mozilla/security/manager/ssl/src/nsNSSIOLayer.h b/mozilla/security/manager/ssl/src/nsNSSIOLayer.h index a07781fb9ce..c378a015e44 100644 --- a/mozilla/security/manager/ssl/src/nsNSSIOLayer.h +++ b/mozilla/security/manager/ssl/src/nsNSSIOLayer.h @@ -162,7 +162,13 @@ public: PRBool HandshakeTimeout(); void SetAllowTLSIntoleranceTimeout(PRBool aAllow); - void SetBlockedOnBadCertUI(PRBool aCurrentlyBlockedOnUI); + + enum BadCertUIStatusType { + bcuis_not_shown, bcuis_active, bcuis_was_shown + }; + + void SetBadCertUIStatus(BadCertUIStatusType aNewStatus); + BadCertUIStatusType GetBadCertUIStatus() { return mBadCertUIStatus; } nsresult GetExternalErrorReporting(PRBool* state); nsresult SetExternalErrorReporting(PRBool aState); @@ -190,7 +196,7 @@ protected: PRPackedBool mHasCleartextPhase; PRPackedBool mHandshakeInProgress; PRPackedBool mAllowTLSIntoleranceTimeout; - PRPackedBool mBlockedOnBadCertUI; + BadCertUIStatusType mBadCertUIStatus; PRIntervalTime mHandshakeStartTime; PRInt32 mPort; nsXPIDLCString mHostName; diff --git a/mozilla/security/manager/ssl/src/nsSSLThread.cpp b/mozilla/security/manager/ssl/src/nsSSLThread.cpp index ab6d660defd..72104c4bf3b 100644 --- a/mozilla/security/manager/ssl/src/nsSSLThread.cpp +++ b/mozilla/security/manager/ssl/src/nsSSLThread.cpp @@ -519,7 +519,7 @@ PRInt32 nsSSLThread::requestRead(nsNSSSocketInfo *si, void *buf, PRInt32 amount, { restoreOriginalSocket_locked(si); PR_SetError(PR_CONNECT_RESET_ERROR, 0); - checkHandshake(-1, si->mFd->lower, si); + checkHandshake(-1, PR_TRUE, si->mFd->lower, si); return -1; } } @@ -746,7 +746,7 @@ PRInt32 nsSSLThread::requestWrite(nsNSSSocketInfo *si, const void *buf, PRInt32 { restoreOriginalSocket_locked(si); PR_SetError(PR_CONNECT_RESET_ERROR, 0); - checkHandshake(-1, si->mFd->lower, si); + checkHandshake(-1, PR_FALSE, si->mFd->lower, si); return -1; } } @@ -978,7 +978,7 @@ void nsSSLThread::Run(void) PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("[%p] wrote %d bytes\n", (void*)fd, bytesWritten)); #endif - bytesWritten = checkHandshake(bytesWritten, realFileDesc, mBusySocket); + bytesWritten = checkHandshake(bytesWritten, PR_FALSE, realFileDesc, mBusySocket); if (bytesWritten < 0) { // give the error back to caller mBusySocket->mThreadData->mPRErrorCode = PR_GetError(); @@ -998,7 +998,7 @@ void nsSSLThread::Run(void) PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("[%p] read %d bytes\n", (void*)fd, bytesRead)); DEBUG_DUMP_BUFFER((unsigned char*)buf, bytesRead); #endif - bytesRead = checkHandshake(bytesRead, realFileDesc, mBusySocket); + bytesRead = checkHandshake(bytesRead, PR_TRUE, realFileDesc, mBusySocket); if (bytesRead < 0) { // give the error back to caller mBusySocket->mThreadData->mPRErrorCode = PR_GetError(); diff --git a/mozilla/security/manager/ssl/src/nsSSLThread.h b/mozilla/security/manager/ssl/src/nsSSLThread.h index 21b19326d8f..e72062fcafc 100644 --- a/mozilla/security/manager/ssl/src/nsSSLThread.h +++ b/mozilla/security/manager/ssl/src/nsSSLThread.h @@ -85,6 +85,7 @@ private: // Called from SSL thread only static PRInt32 checkHandshake(PRInt32 bytesTransfered, + PRBool wasReading, PRFileDesc* fd, nsNSSSocketInfo *socketInfo);