Bug 713933: Add the NSS patch for this bug (rather than the PSM patch

for bug 658222) to security/patches.
This commit is contained in:
Wan-Teh Chang 2013-08-01 15:49:16 -07:00
parent ddff2a6c30
commit 975bc25be6
3 changed files with 837 additions and 1578 deletions

View File

@ -1,6 +1,6 @@
This directory contains patches that were added locally
on top of the NSS release.
bug-658222-false-start.patch: First iteration of false start. The patch will be
bug-713933-false-start.patch: First iteration of false start. The patch will be
further modified to make it work for Chromium and
other projects before being checked in.

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,836 @@
# HG changeset patch
# User Patrick McManus <mcmanus@ducksong.com>
# Date 1372656196 25200
# Node ID f6bc026a0c368178b4d327bf05de785305161d72
# Parent 89a5e4356ad1f7bc9d9d24f6409c6d963dde3ca4
Bug 713933: Make false start work with asynchronous certificate verification, r=bsmith
diff --git a/security/nss/lib/ssl/ssl.def b/security/nss/lib/ssl/ssl.def
--- a/security/nss/lib/ssl/ssl.def
+++ b/security/nss/lib/ssl/ssl.def
@@ -158,8 +158,15 @@ SSL_SetSRTPCiphers;
;+};
;+NSS_3.15 { # NSS 3.15 release
;+ global:
SSL_PeerStapledOCSPResponses;
SSL_SetStapledOCSPResponses;
;+ local:
;+*;
;+};
+;+NSS_3.15.2 { # NSS 3.15.2 release
+;+ global:
+SSL_SetCanFalseStartCallback;
+SSL_DefaultCanFalseStart;
+;+ local:
+;+*;
+;+};
diff --git a/security/nss/lib/ssl/ssl.h b/security/nss/lib/ssl/ssl.h
--- a/security/nss/lib/ssl/ssl.h
+++ b/security/nss/lib/ssl/ssl.h
@@ -116,24 +116,32 @@ SSL_IMPORT PRFileDesc *DTLS_ImportFD(PRF
#define SSL_REQUIRE_SAFE_NEGOTIATION 21 /* Peer must send Signaling */
/* Cipher Suite Value (SCSV) or */
/* Renegotiation Info (RI) */
/* extension in ALL handshakes. */
/* default: off */
#define SSL_ENABLE_FALSE_START 22 /* Enable SSL false start (off by */
/* default, applies only to */
/* clients). False start is a */
-/* mode where an SSL client will start sending application data before */
-/* verifying the server's Finished message. This means that we could end up */
-/* sending data to an imposter. However, the data will be encrypted and */
-/* only the true server can derive the session key. Thus, so long as the */
-/* cipher isn't broken this is safe. Because of this, False Start will only */
-/* occur on RSA or DH ciphersuites where the cipher's key length is >= 80 */
-/* bits. The advantage of False Start is that it saves a round trip for */
-/* client-speaks-first protocols when performing a full handshake. */
+/* mode where an SSL client will start sending application data before
+ * verifying the server's Finished message. This means that we could end up
+ * sending data to an imposter. However, the data will be encrypted and
+ * only the true server can derive the session key. Thus, so long as the
+ * cipher isn't broken this is safe. The advantage of false start is that
+ * it saves a round trip for client-speaks-first protocols when performing a
+ * full handshake.
+ *
+ * See SSL_DefaultCanFalseStart for the default criteria that NSS uses to
+ * determine whether to false start or not. See SSL_SetCanFalseStartCallback
+ * for how to change that criteria. In addition to those criteria, false start
+ * will only be done when the server selects a cipher suite with an effective
+ * key length of 80 bits or more (including RC4-128). Also, see
+ * SSL_HandshakeCallback for a description on how false start affects when the
+ * handshake callback gets called.
+ */
/* For SSL 3.0 and TLS 1.0, by default we prevent chosen plaintext attacks
* on SSL CBC mode cipher suites (see RFC 4346 Section F.3) by splitting
* non-empty application_data records into two records; the first record has
* only the first byte of plaintext, and the second has the rest.
*
* This only prevents the attack in the sending direction; the connection may
* still be vulnerable to such attacks if the peer does not implement a similar
@@ -648,24 +656,69 @@ SSL_IMPORT SECStatus SSL_SetMaxServerCac
/* called in child to inherit SID Cache variables.
* If envString is NULL, this function will use the value of the environment
* variable "SSL_INHERITANCE", otherwise the string value passed in will be
* used.
*/
SSL_IMPORT SECStatus SSL_InheritMPServerSIDCache(const char * envString);
/*
-** Set the callback on a particular socket that gets called when we finish
-** performing a handshake.
+** Set the callback that normally gets called when the TLS handshake
+** is complete. If false start is not enabled, then the handshake callback is
+** called after verifying the peer's Finished message and before sending
+** outgoing application data and before processing incoming application data.
+**
+** If false start is enabled and there is a custom CanFalseStartCallback
+** callback set, then the handshake callback gets called after the peer's
+** Finished message has been verified, which may be after application data is
+** sent.
+**
+** If false start is enabled and there is not a custom CanFalseStartCallback
+** callback established with SSL_SetCanFalseStartCallback then the handshake
+** callback gets called before any application data is sent, which may be
+** before the peer's Finished message has been verified.
*/
typedef void (PR_CALLBACK *SSLHandshakeCallback)(PRFileDesc *fd,
void *client_data);
SSL_IMPORT SECStatus SSL_HandshakeCallback(PRFileDesc *fd,
SSLHandshakeCallback cb, void *client_data);
+/* Applications that wish to customize TLS false start should set this callback
+** function. NSS will invoke the functon to determine if a particular
+** connection should use false start or not. SECSuccess indicates that the
+** callback completed successfully, and if so *canFalseStart indicates if false
+** start can be used. If the callback does not return SECSuccess then the
+** handshake will be canceled.
+**
+** Applications that do not set the callback will use an internal set of
+** criteria to determine if the connection should false start. If
+** the callback is set false start will never be used without invoking the
+** callback function, but some connections (e.g. resumed connections) will
+** never use false start and therefore will not invoke the callback.
+**
+** NSS's internal criteria for this connection can be evaluated by calling
+** SSL_DefaultCanFalseStart() from the custom callback.
+**
+** See the description of SSL_HandshakeCallback for important information on
+** how registering a custom false start callback affects when the handshake
+** callback gets called.
+**/
+typedef SECStatus (PR_CALLBACK *SSLCanFalseStartCallback)(
+ PRFileDesc *fd, void *arg, PRBool *canFalseStart);
+
+SSL_IMPORT SECStatus SSL_SetCanFalseStartCallback(
+ PRFileDesc *fd, SSLCanFalseStartCallback callback, void *arg);
+
+/* A utility function that can be called from a custom CanFalseStartCallback
+** function to determine what NSS would have done for this connection if the
+** custom callback was not implemented.
+**/
+SSL_IMPORT SECStatus SSL_DefaultCanFalseStart(PRFileDesc *fd,
+ PRBool *canFalseStart);
+
/*
** For the server, request a new handshake. For the client, begin a new
** handshake. If flushCache is non-zero, the SSL3 cache entry will be
** flushed first, ensuring that a full SSL handshake will be done.
** If flushCache is zero, and an SSL connection is established, it will
** do the much faster session restart handshake. This will change the
** session keys without doing another private key operation.
*/
diff --git a/security/nss/lib/ssl/ssl3con.c b/security/nss/lib/ssl/ssl3con.c
--- a/security/nss/lib/ssl/ssl3con.c
+++ b/security/nss/lib/ssl/ssl3con.c
@@ -6664,45 +6664,61 @@ loser:
PORT_SetError(errCode);
rv = SECFailure;
done:
if (arena != NULL)
PORT_FreeArena(arena, PR_FALSE);
return rv;
}
-PRBool
-ssl3_CanFalseStart(sslSocket *ss) {
- PRBool rv;
+static SECStatus
+ssl3_CheckFalseStart(sslSocket *ss)
+{
+ SECStatus rv;
+ PRBool maybeFalseStart = PR_TRUE;
PORT_Assert( ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss) );
-
- /* XXX: does not take into account whether we are waiting for
- * SSL_AuthCertificateComplete or SSL_RestartHandshakeAfterCertReq. If/when
- * that is done, this function could return different results each time it
- * would be called.
- */
+ PORT_Assert( !ss->ssl3.hs.authCertificatePending );
+
+ /* An attacker can control the selected ciphersuite so we only wish to
+ * do False Start in the case that the selected ciphersuite is
+ * sufficiently strong that the attack can gain no advantage.
+ * Therefore we always require an 80-bit cipher. */
ssl_GetSpecReadLock(ss);
- rv = ss->opt.enableFalseStart &&
- !ss->sec.isServer &&
- !ss->ssl3.hs.isResuming &&
- ss->ssl3.cwSpec &&
-
- /* An attacker can control the selected ciphersuite so we only wish to
- * do False Start in the case that the selected ciphersuite is
- * sufficiently strong that the attack can gain no advantage.
- * Therefore we require an 80-bit cipher and a forward-secret key
- * exchange. */
- ss->ssl3.cwSpec->cipher_def->secret_key_size >= 10 &&
- (ss->ssl3.hs.kea_def->kea == kea_dhe_dss ||
- ss->ssl3.hs.kea_def->kea == kea_dhe_rsa ||
- ss->ssl3.hs.kea_def->kea == kea_ecdhe_ecdsa ||
- ss->ssl3.hs.kea_def->kea == kea_ecdhe_rsa);
+ if (ss->ssl3.cwSpec->cipher_def->secret_key_size < 10) {
+ ss->ssl3.hs.canFalseStart = PR_FALSE;
+ maybeFalseStart = PR_FALSE;
+ }
ssl_ReleaseSpecReadLock(ss);
+ if (!maybeFalseStart) {
+ return SECSuccess;
+ }
+
+ if (!ss->canFalseStartCallback) {
+ rv = SSL_DefaultCanFalseStart(ss->fd, &ss->ssl3.hs.canFalseStart);
+
+ if (rv == SECSuccess &&
+ ss->ssl3.hs.canFalseStart && ss->handshakeCallback) {
+ /* Call the handshake callback here for backwards compatibility
+ * with applications that were using false start before
+ * canFalseStartCallback was added.
+ */
+ (ss->handshakeCallback)(ss->fd, ss->handshakeCallbackData);
+ }
+ } else {
+ rv = (ss->canFalseStartCallback)(ss->fd,
+ ss->canFalseStartCallbackData,
+ &ss->ssl3.hs.canFalseStart);
+ }
+
+ if (rv != SECSuccess) {
+ ss->ssl3.hs.canFalseStart = PR_FALSE;
+ }
+
return rv;
}
static SECStatus ssl3_SendClientSecondRound(sslSocket *ss);
/* Called from ssl3_HandleHandshakeMessage() when it has deciphered a complete
* ssl3 Server Hello Done message.
* Caller must hold Handshake and RecvBuf locks.
@@ -6722,16 +6738,17 @@ ssl3_HandleServerHelloDone(sslSocket *ss
ws != wait_server_cert &&
ws != wait_server_key &&
ws != wait_cert_request) {
SSL3_SendAlert(ss, alert_fatal, unexpected_message);
PORT_SetError(SSL_ERROR_RX_UNEXPECTED_HELLO_DONE);
return SECFailure;
}
+ ss->enoughFirstHsDone = PR_TRUE;
rv = ssl3_SendClientSecondRound(ss);
return rv;
}
/* Called from ssl3_HandleServerHelloDone and ssl3_AuthCertificateComplete.
*
* Caller must hold Handshake and RecvBuf locks.
@@ -6820,35 +6837,47 @@ ssl3_SendClientSecondRound(sslSocket *ss
/* XXX: If the server's certificate hasn't been authenticated by this
* point, then we may be leaking this NPN message to an attacker.
*/
if (!ss->firstHsDone) {
rv = ssl3_SendNextProto(ss);
if (rv != SECSuccess) {
goto loser; /* err code was set. */
}
+
+ if (ss->opt.enableFalseStart) {
+ if (!ss->ssl3.hs.authCertificatePending) {
+ rv = ssl3_CheckFalseStart(ss);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ } else {
+ /* The certificate authentication and the server's Finished
+ * message are going to race each other. If the certificate
+ * authentication wins, then we will try to false start. If the
+ * server's Finished message wins, then ssl3_HandleFinished will
+ * reset restartTarget to ssl3_FinishHandshake.
+ */
+ ss->ssl3.hs.restartTarget = ssl3_CheckFalseStart;
+ }
+ }
}
rv = ssl3_SendFinished(ss, 0);
if (rv != SECSuccess) {
goto loser; /* err code was set. */
}
ssl_ReleaseXmitBufLock(ss); /*******************************/
if (ssl3_ExtensionNegotiated(ss, ssl_session_ticket_xtn))
ss->ssl3.hs.ws = wait_new_session_ticket;
else
ss->ssl3.hs.ws = wait_change_cipher;
- /* Do the handshake callback for sslv3 here, if we can false start. */
- if (ss->handshakeCallback != NULL && ssl3_CanFalseStart(ss)) {
- (ss->handshakeCallback)(ss->fd, ss->handshakeCallbackData);
- }
-
return SECSuccess;
loser:
ssl_ReleaseXmitBufLock(ss);
return rv;
}
/*
@@ -9411,23 +9440,16 @@ ssl3_AuthCertificate(sslSocket *ss)
if (ss->sec.isServer) {
errCode = SSL_ERROR_FEATURE_NOT_SUPPORTED_FOR_SERVERS;
rv = SECFailure;
goto loser;
}
ss->ssl3.hs.authCertificatePending = PR_TRUE;
rv = SECSuccess;
-
- /* XXX: Async cert validation and False Start don't work together
- * safely yet; if we leave False Start enabled, we may end up false
- * starting (sending application data) before we
- * SSL_AuthCertificateComplete has been called.
- */
- ss->opt.enableFalseStart = PR_FALSE;
}
if (rv != SECSuccess) {
ssl3_SendAlertForCertError(ss, errCode);
goto loser;
}
}
@@ -10065,16 +10087,21 @@ xmit_loser:
ssl_ReleaseSpecReadLock(ss); /*************************************/
/* If the wrap failed, we don't cache the sid.
* The connection continues normally however.
*/
ss->ssl3.hs.cacheSID = rv == SECSuccess;
}
+ /* Cancel false start check since we already completed the handshake */
+ if (ss->ssl3.hs.restartTarget == ssl3_CheckFalseStart) {
+ ss->ssl3.hs.restartTarget = NULL;
+ }
+
if (ss->ssl3.hs.authCertificatePending) {
if (ss->ssl3.hs.restartTarget) {
PR_NOT_REACHED("ssl3_HandleFinished: unexpected restartTarget");
PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
return SECFailure;
}
ss->ssl3.hs.restartTarget = ssl3_FinishHandshake;
@@ -10083,33 +10110,41 @@ xmit_loser:
rv = ssl3_FinishHandshake(ss);
return rv;
}
SECStatus
ssl3_FinishHandshake(sslSocket * ss)
{
+ PRBool falseStarted;
+
PORT_Assert( ss->opt.noLocks || ssl_HaveRecvBufLock(ss) );
PORT_Assert( ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss) );
PORT_Assert( ss->ssl3.hs.restartTarget == NULL );
/* The first handshake is now completed. */
ss->handshake = NULL;
ss->firstHsDone = PR_TRUE;
+ ss->enoughFirstHsDone = PR_TRUE;
if (ss->ssl3.hs.cacheSID) {
(*ss->sec.cache)(ss->sec.ci.sid);
ss->ssl3.hs.cacheSID = PR_FALSE;
}
ss->ssl3.hs.ws = idle_handshake;
-
- /* Do the handshake callback for sslv3 here, if we cannot false start. */
- if (ss->handshakeCallback != NULL && !ssl3_CanFalseStart(ss)) {
+ falseStarted = ss->ssl3.hs.canFalseStart;
+ ss->ssl3.hs.canFalseStart = PR_FALSE; /* False Start phase is complete */
+
+ /* Call the handshake callback for sslv3 here, unless we called it already
+ * for the case where false start was done without a canFalseStartCallback.
+ */
+ if (ss->handshakeCallback != NULL &&
+ !(falseStarted && !ss->canFalseStartCallback)) {
(ss->handshakeCallback)(ss->fd, ss->handshakeCallbackData);
}
return SECSuccess;
}
/* Called from ssl3_HandleHandshake() when it has gathered a complete ssl3
* hanshake message.
diff --git a/security/nss/lib/ssl/ssl3gthr.c b/security/nss/lib/ssl/ssl3gthr.c
--- a/security/nss/lib/ssl/ssl3gthr.c
+++ b/security/nss/lib/ssl/ssl3gthr.c
@@ -369,19 +369,17 @@ ssl3_GatherCompleteHandshake(sslSocket *
return ss->recvdCloseNotify ? 0 : rv;
}
/* If we kicked off a false start in ssl3_HandleServerHelloDone, break
* out of this loop early without finishing the handshake.
*/
if (ss->opt.enableFalseStart) {
ssl_GetSSL3HandshakeLock(ss);
- canFalseStart = (ss->ssl3.hs.ws == wait_change_cipher ||
- ss->ssl3.hs.ws == wait_new_session_ticket) &&
- ssl3_CanFalseStart(ss);
+ canFalseStart = ss->ssl3.hs.canFalseStart;
ssl_ReleaseSSL3HandshakeLock(ss);
}
} while (ss->ssl3.hs.ws != idle_handshake &&
!canFalseStart &&
ss->gs.buf.len == 0);
ss->gs.readOffset = 0;
ss->gs.writeOffset = ss->gs.buf.len;
diff --git a/security/nss/lib/ssl/sslauth.c b/security/nss/lib/ssl/sslauth.c
--- a/security/nss/lib/ssl/sslauth.c
+++ b/security/nss/lib/ssl/sslauth.c
@@ -55,17 +55,16 @@ SSL_LocalCertificate(PRFileDesc *fd)
/* NEED LOCKS IN HERE. */
SECStatus
SSL_SecurityStatus(PRFileDesc *fd, int *op, char **cp, int *kp0, int *kp1,
char **ip, char **sp)
{
sslSocket *ss;
const char *cipherName;
PRBool isDes = PR_FALSE;
- PRBool enoughFirstHsDone = PR_FALSE;
ss = ssl_FindSocket(fd);
if (!ss) {
SSL_DBG(("%d: SSL[%d]: bad socket in SecurityStatus",
SSL_GETPID(), fd));
return SECFailure;
}
@@ -73,24 +72,17 @@ SSL_SecurityStatus(PRFileDesc *fd, int *
if (kp0) *kp0 = 0;
if (kp1) *kp1 = 0;
if (ip) *ip = 0;
if (sp) *sp = 0;
if (op) {
*op = SSL_SECURITY_STATUS_OFF;
}
- if (ss->firstHsDone) {
- enoughFirstHsDone = PR_TRUE;
- } else if (ss->version >= SSL_LIBRARY_VERSION_3_0 &&
- ssl3_CanFalseStart(ss)) {
- enoughFirstHsDone = PR_TRUE;
- }
-
- if (ss->opt.useSecurity && enoughFirstHsDone) {
+ if (ss->opt.useSecurity && ss->enoughFirstHsDone) {
if (ss->version < SSL_LIBRARY_VERSION_3_0) {
cipherName = ssl_cipherName[ss->sec.cipherType];
} else {
cipherName = ssl3_cipherName[ss->sec.cipherType];
}
PORT_Assert(cipherName);
if (cipherName) {
if (PORT_Strstr(cipherName, "DES")) isDes = PR_TRUE;
diff --git a/security/nss/lib/ssl/sslimpl.h b/security/nss/lib/ssl/sslimpl.h
--- a/security/nss/lib/ssl/sslimpl.h
+++ b/security/nss/lib/ssl/sslimpl.h
@@ -863,16 +863,18 @@ const ssl3CipherSuiteDef *suite_def;
* in progress. */
unsigned char cookie[32]; /* The cookie */
unsigned char cookieLen; /* The length of the cookie */
PRIntervalTime rtTimerStarted; /* When the timer was started */
DTLSTimerCb rtTimerCb; /* The function to call on expiry */
PRUint32 rtTimeoutMs; /* The length of the current timeout
* used for backoff (in ms) */
PRUint32 rtRetries; /* The retry counter */
+ PRBool canFalseStart; /* Can/did we False Start */
+
} SSL3HandshakeState;
/*
** This is the "ssl3" struct, as in "ss->ssl3".
** note:
** usually, crSpec == cwSpec and prSpec == pwSpec.
@@ -1111,16 +1113,20 @@ struct sslSocketStr {
sslOptions opt;
/* Enabled version range */
SSLVersionRange vrange;
/* State flags */
unsigned long clientAuthRequested;
unsigned long delayDisabled; /* Nagle delay disabled */
unsigned long firstHsDone; /* first handshake is complete. */
+ unsigned long enoughFirstHsDone; /* enough of the handshake is done
+ * for callbacks to be able to
+ * retrieve channel security
+ * parameters from callback functions. */
unsigned long handshakeBegun;
unsigned long lastWriteBlocked;
unsigned long recvdCloseNotify; /* received SSL EOF. */
unsigned long TCPconnected;
unsigned long appDataBuffered;
unsigned long peerRequestedProtection; /* from old renegotiation */
/* version of the protocol to use */
@@ -1151,16 +1157,18 @@ const unsigned char * preferredCipher;
SSLGetClientAuthData getClientAuthData;
void *getClientAuthDataArg;
SSLSNISocketConfig sniSocketConfig;
void *sniSocketConfigArg;
SSLBadCertHandler handleBadCert;
void *badCertArg;
SSLHandshakeCallback handshakeCallback;
void *handshakeCallbackData;
+ SSLCanFalseStartCallback canFalseStartCallback;
+ void *canFalseStartCallbackData;
void *pkcs11PinArg;
SSLNextProtoCallback nextProtoCallback;
void *nextProtoArg;
PRIntervalTime rTimeout; /* timeout for NSPR I/O */
PRIntervalTime wTimeout; /* timeout for NSPR I/O */
PRIntervalTime cTimeout; /* timeout for NSPR I/O */
@@ -1353,17 +1361,16 @@ extern int ssl3_SendApplicationDat
extern PRBool ssl_FdIsBlocking(PRFileDesc *fd);
extern PRBool ssl_SocketIsBlocking(sslSocket *ss);
extern void ssl3_SetAlwaysBlock(sslSocket *ss);
extern SECStatus ssl_EnableNagleDelay(sslSocket *ss, PRBool enabled);
-extern PRBool ssl3_CanFalseStart(sslSocket *ss);
extern SECStatus
ssl3_CompressMACEncryptRecord(ssl3CipherSpec * cwSpec,
PRBool isServer,
PRBool isDTLS,
PRBool capRecordVersion,
SSL3ContentType type,
const SSL3Opaque * pIn,
PRUint32 contentLen,
diff --git a/security/nss/lib/ssl/sslinfo.c b/security/nss/lib/ssl/sslinfo.c
--- a/security/nss/lib/ssl/sslinfo.c
+++ b/security/nss/lib/ssl/sslinfo.c
@@ -21,41 +21,33 @@ ssl_GetCompressionMethodName(SSLCompress
}
SECStatus
SSL_GetChannelInfo(PRFileDesc *fd, SSLChannelInfo *info, PRUintn len)
{
sslSocket * ss;
SSLChannelInfo inf;
sslSessionID * sid;
- PRBool enoughFirstHsDone = PR_FALSE;
if (!info || len < sizeof inf.length) {
PORT_SetError(SEC_ERROR_INVALID_ARGS);
return SECFailure;
}
ss = ssl_FindSocket(fd);
if (!ss) {
SSL_DBG(("%d: SSL[%d]: bad socket in SSL_GetChannelInfo",
SSL_GETPID(), fd));
return SECFailure;
}
memset(&inf, 0, sizeof inf);
inf.length = PR_MIN(sizeof inf, len);
- if (ss->firstHsDone) {
- enoughFirstHsDone = PR_TRUE;
- } else if (ss->version >= SSL_LIBRARY_VERSION_3_0 &&
- ssl3_CanFalseStart(ss)) {
- enoughFirstHsDone = PR_TRUE;
- }
-
- if (ss->opt.useSecurity && enoughFirstHsDone) {
+ if (ss->opt.useSecurity && ss->enoughFirstHsDone) {
sid = ss->sec.ci.sid;
inf.protocolVersion = ss->version;
inf.authKeyBits = ss->sec.authKeyBits;
inf.keaKeyBits = ss->sec.keaKeyBits;
if (ss->version < SSL_LIBRARY_VERSION_3_0) { /* SSL2 */
inf.cipherSuite = ss->sec.cipherType | 0xff00;
inf.compressionMethod = ssl_compression_null;
inf.compressionMethodName = "N/A";
diff --git a/security/nss/lib/ssl/sslreveal.c b/security/nss/lib/ssl/sslreveal.c
--- a/security/nss/lib/ssl/sslreveal.c
+++ b/security/nss/lib/ssl/sslreveal.c
@@ -72,40 +72,33 @@ SSL_RevealURL(PRFileDesc * fd)
SECStatus
SSL_HandshakeNegotiatedExtension(PRFileDesc * socket,
SSLExtensionType extId,
PRBool *pYes)
{
/* some decisions derived from SSL_GetChannelInfo */
sslSocket * sslsocket = NULL;
- PRBool enoughFirstHsDone = PR_FALSE;
if (!pYes) {
PORT_SetError(SEC_ERROR_INVALID_ARGS);
return SECFailure;
}
sslsocket = ssl_FindSocket(socket);
if (!sslsocket) {
SSL_DBG(("%d: SSL[%d]: bad socket in HandshakeNegotiatedExtension",
SSL_GETPID(), socket));
return SECFailure;
}
*pYes = PR_FALSE;
- if (sslsocket->firstHsDone) {
- enoughFirstHsDone = PR_TRUE;
- } else if (sslsocket->ssl3.initialized && ssl3_CanFalseStart(sslsocket)) {
- enoughFirstHsDone = PR_TRUE;
- }
-
/* according to public API SSL_GetChannelInfo, this doesn't need a lock */
- if (sslsocket->opt.useSecurity && enoughFirstHsDone) {
+ if (sslsocket->opt.useSecurity && sslsocket->enoughFirstHsDone) {
if (sslsocket->ssl3.initialized) { /* SSL3 and TLS */
/* now we know this socket went through ssl3_InitState() and
* ss->xtnData got initialized, which is the only member accessed by
* ssl3_ExtensionNegotiated();
* Member xtnData appears to get accessed in functions that handle
* the handshake (hello messages and extension sending),
* therefore the handshake lock should be sufficient.
*/
diff --git a/security/nss/lib/ssl/sslsecur.c b/security/nss/lib/ssl/sslsecur.c
--- a/security/nss/lib/ssl/sslsecur.c
+++ b/security/nss/lib/ssl/sslsecur.c
@@ -103,20 +103,22 @@ ssl_Do1stHandshake(sslSocket *ss)
SSL_TRC(3, ("%d: SSL[%d]: handshake is completed",
SSL_GETPID(), ss->fd));
/* call handshake callback for ssl v2 */
/* for v3 this is done in ssl3_HandleFinished() */
if ((ss->handshakeCallback != NULL) && /* has callback */
(!ss->firstHsDone) && /* only first time */
(ss->version < SSL_LIBRARY_VERSION_3_0)) { /* not ssl3 */
- ss->firstHsDone = PR_TRUE;
+ ss->firstHsDone = PR_TRUE;
+ ss->enoughFirstHsDone = PR_TRUE;
(ss->handshakeCallback)(ss->fd, ss->handshakeCallbackData);
}
- ss->firstHsDone = PR_TRUE;
+ ss->firstHsDone = PR_TRUE;
+ ss->enoughFirstHsDone = PR_TRUE;
ss->gs.writeOffset = 0;
ss->gs.readOffset = 0;
break;
}
rv = (*ss->handshake)(ss);
++loopCount;
/* This code must continue to loop on SECWouldBlock,
* or any positive value. See XXX_1 comments.
@@ -201,31 +203,34 @@ SSL_ResetHandshake(PRFileDesc *s, PRBool
SSL_LOCK_READER(ss);
SSL_LOCK_WRITER(ss);
/* Reset handshake state */
ssl_Get1stHandshakeLock(ss);
ss->firstHsDone = PR_FALSE;
+ ss->enoughFirstHsDone = PR_FALSE;
if ( asServer ) {
ss->handshake = ssl2_BeginServerHandshake;
ss->handshaking = sslHandshakingAsServer;
} else {
ss->handshake = ssl2_BeginClientHandshake;
ss->handshaking = sslHandshakingAsClient;
}
ss->nextHandshake = 0;
ss->securityHandshake = 0;
ssl_GetRecvBufLock(ss);
status = ssl_InitGather(&ss->gs);
ssl_ReleaseRecvBufLock(ss);
ssl_GetSSL3HandshakeLock(ss);
+ ss->ssl3.hs.canFalseStart = PR_FALSE;
+ ss->ssl3.hs.restartTarget = NULL;
/*
** Blow away old security state and get a fresh setup.
*/
ssl_GetXmitBufLock(ss);
ssl_ResetSecurityInfo(&ss->sec, PR_TRUE);
status = ssl_CreateSecurityInfo(ss);
ssl_ReleaseXmitBufLock(ss);
@@ -326,16 +331,84 @@ SSL_HandshakeCallback(PRFileDesc *fd, SS
ss->handshakeCallbackData = client_data;
ssl_ReleaseSSL3HandshakeLock(ss);
ssl_Release1stHandshakeLock(ss);
return SECSuccess;
}
+/* Register an application callback to be called when false start may happen.
+** Acquires and releases HandshakeLock.
+*/
+SECStatus
+SSL_SetCanFalseStartCallback(PRFileDesc *fd, SSLCanFalseStartCallback cb,
+ void *client_data)
+{
+ sslSocket *ss;
+
+ ss = ssl_FindSocket(fd);
+ if (!ss) {
+ SSL_DBG(("%d: SSL[%d]: bad socket in SSL_SetCanFalseStartCallback",
+ SSL_GETPID(), fd));
+ return SECFailure;
+ }
+
+ if (!ss->opt.useSecurity) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ ssl_Get1stHandshakeLock(ss);
+ ssl_GetSSL3HandshakeLock(ss);
+
+ ss->canFalseStartCallback = cb;
+ ss->canFalseStartCallbackData = client_data;
+
+ ssl_ReleaseSSL3HandshakeLock(ss);
+ ssl_Release1stHandshakeLock(ss);
+
+ return SECSuccess;
+}
+
+/* A utility function that can be called from a custom CanFalseStartCallback
+** function to determine what NSS would have done for this connection if the
+** custom callback was not implemented.
+*/
+SECStatus
+SSL_DefaultCanFalseStart(PRFileDesc *fd, PRBool *canFalseStart)
+{
+ sslSocket *ss;
+ *canFalseStart = PR_FALSE;
+ ss = ssl_FindSocket(fd);
+ if (!ss) {
+ SSL_DBG(("%d: SSL[%d]: bad socket in SSL_DefaultCanFalseStart",
+ SSL_GETPID(), fd));
+ return SECFailure;
+ }
+
+ if (!ss->ssl3.initialized) {
+ PORT_SetError(SEC_ERROR_NOT_INITIALIZED);
+ return SECFailure;
+ }
+
+ if (ss->version <= SSL_LIBRARY_VERSION_3_0) {
+ PORT_SetError(SSL_ERROR_FEATURE_NOT_SUPPORTED_FOR_VERSION);
+ return SECFailure;
+ }
+
+ /* Require a forward-secret key exchange. */
+ *canFalseStart = ss->ssl3.hs.kea_def->kea == kea_dhe_dss ||
+ ss->ssl3.hs.kea_def->kea == kea_dhe_rsa ||
+ ss->ssl3.hs.kea_def->kea == kea_ecdhe_ecdsa ||
+ ss->ssl3.hs.kea_def->kea == kea_ecdhe_rsa;
+
+ return SECSuccess;
+}
+
/* Try to make progress on an SSL handshake by attempting to read the
** next handshake from the peer, and sending any responses.
** For non-blocking sockets, returns PR_ERROR_WOULD_BLOCK if it cannot
** read the next handshake from the underlying socket.
** For SSLv2, returns when handshake is complete or fatal error occurs.
** For SSLv3, returns when handshake is complete, or application data has
** arrived that must be taken by application before handshake can continue,
** or a fatal error occurs.
@@ -1190,22 +1263,17 @@ ssl_SecureSend(sslSocket *ss, const unsi
if (len > 0)
ss->writerThread = PR_GetCurrentThread();
/* If any of these is non-zero, the initial handshake is not done. */
if (!ss->firstHsDone) {
PRBool canFalseStart = PR_FALSE;
ssl_Get1stHandshakeLock(ss);
if (ss->version >= SSL_LIBRARY_VERSION_3_0) {
ssl_GetSSL3HandshakeLock(ss);
- if ((ss->ssl3.hs.ws == wait_change_cipher ||
- ss->ssl3.hs.ws == wait_finished ||
- ss->ssl3.hs.ws == wait_new_session_ticket) &&
- ssl3_CanFalseStart(ss)) {
- canFalseStart = PR_TRUE;
- }
+ canFalseStart = ss->ssl3.hs.canFalseStart;
ssl_ReleaseSSL3HandshakeLock(ss);
}
if (!canFalseStart &&
(ss->handshake || ss->nextHandshake || ss->securityHandshake)) {
rv = ssl_Do1stHandshake(ss);
}
ssl_Release1stHandshakeLock(ss);
}
diff --git a/security/nss/lib/ssl/sslsock.c b/security/nss/lib/ssl/sslsock.c
--- a/security/nss/lib/ssl/sslsock.c
+++ b/security/nss/lib/ssl/sslsock.c
@@ -2336,19 +2336,23 @@ ssl_Poll(PRFileDesc *fd, PRInt16 how_fla
** The code should select on write, not read.
*/
new_flags ^= PR_POLL_READ; /* don't select on read. */
new_flags |= PR_POLL_WRITE; /* do select on write. */
}
} else if (new_flags & PR_POLL_WRITE) {
/* The caller is trying to write, but the handshake is
** blocked waiting for data to read, and the first
- ** handshake has been sent. so do NOT to poll on write.
+ ** handshake has been sent. So do NOT to poll on write
+ ** unless we did false start.
*/
- new_flags ^= PR_POLL_WRITE; /* don't select on write. */
+ if (!(ss->version >= SSL_LIBRARY_VERSION_3_0 &&
+ ss->ssl3.hs.canFalseStart)) {
+ new_flags ^= PR_POLL_WRITE; /* don't select on write. */
+ }
new_flags |= PR_POLL_READ; /* do select on read. */
}
}
} else if ((new_flags & PR_POLL_READ) && (SSL_DataPending(fd) > 0)) {
*p_out_flags = PR_POLL_READ; /* it's ready already. */
return new_flags;
} else if ((ss->lastWriteBlocked) && (how_flags & PR_POLL_READ) &&
(ss->pendingBuf.len != 0)) { /* write data waiting to be sent */