--- a/lib/ssl/ssl.def
+++ b/lib/ssl/ssl.def
@@ -161,11 +161,13 @@ SSL_SetSRTPCiphers;
SSL_PeerStapledOCSPResponses;
SSL_SetStapledOCSPResponses;
;+ local:
;+*;
;+};
;+NSS_3.15.3 { # NSS 3.15.3 release
;+ global:
SSL_PeerCertificateChain;
+SSL_RecommendedCanFalseStart;
+SSL_SetCanFalseStartCallback;
;+ local:
;+*;
;+};
--- a/lib/ssl/ssl.h
+++ b/lib/ssl/ssl.h
@@ -116,24 +116,27 @@ 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.
+ *
+ * In addition to enabling this option, the application must register a
+ * callback using the SSL_SetCanFalseStartCallback function.
+ */
/* 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
@@ -657,24 +660,55 @@ 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 gets called when a TLS handshake is complete. The
+** handshake callback is called after verifying the peer's Finished message and
+** before processing incoming application data.
+**
+** For the initial handshake: If the handshake false started (see
+** SSL_ENABLE_FALSE_START), then application data may already have been sent
+** before the handshake callback is called. If we did not false start then the
+** callback will get called before any application data is sent.
*/
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 enable TLS false start must 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. NSS's recommended criteria can be evaluated by
+** calling SSL_RecommendedCanFalseStart.
+**
+** If no false start callback is registered then false start will never be
+** done, even if the SSL_ENABLE_FALSE_START option is enabled.
+**/
+typedef SECStatus (PR_CALLBACK *SSLCanFalseStartCallback)(
+ PRFileDesc *fd, void *arg, PRBool *canFalseStart);
+
+SSL_IMPORT SECStatus SSL_SetCanFalseStartCallback(
+ PRFileDesc *fd, SSLCanFalseStartCallback callback, void *arg);
+
+/* This function sets *canFalseStart according to the recommended criteria for
+** false start. These criteria may change from release to release and may depend
+** on which handshake features have been negotiated and/or properties of the
+** certifciates/keys used on the connection.
+*/
+SSL_IMPORT SECStatus SSL_RecommendedCanFalseStart(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.
*/
--- a/lib/ssl/ssl3con.c
+++ b/lib/ssl/ssl3con.c
@@ -2727,17 +2727,17 @@ ssl3_SendRecord( sslSocket * ss
sslBuffer * wrBuf = &ss->sec.writeBuf;
SECStatus rv;
PRInt32 totalSent = 0;
PRBool capRecordVersion;
SSL_TRC(3, ("%d: SSL3[%d] SendRecord type: %s nIn=%d",
SSL_GETPID(), ss->fd, ssl3_DecodeContentType(type),
nIn));
- PRINT_BUF(3, (ss, "Send record (plain text)", pIn, nIn));
+ PRINT_BUF(50, (ss, "Send record (plain text)", pIn, nIn));
PORT_Assert( ss->opt.noLocks || ssl_HaveXmitBufLock(ss) );
capRecordVersion = ((flags & ssl_SEND_FLAG_CAP_RECORD_VERSION) != 0);
if (capRecordVersion) {
/* ssl_SEND_FLAG_CAP_RECORD_VERSION can only be used with the
* TLS initial ClientHello. */
@@ -6984,46 +6984,83 @@ loser:
PORT_SetError(errCode);
rv = SECFailure;
done:
if (arena != NULL)
PORT_FreeArena(arena, PR_FALSE);
return rv;
}
+static SECStatus
+ssl3_CheckFalseStart(sslSocket *ss)
+{
+ PORT_Assert( ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss) );
+ PORT_Assert( !ss->ssl3.hs.authCertificatePending );
+ PORT_Assert( !ss->ssl3.hs.canFalseStart );
+
+ if (!ss->canFalseStartCallback) {
+ SSL_TRC(3, ("%d: SSL[%d]: no false start callback so no false start",
+ SSL_GETPID(), ss->fd));
+ } else {
+ PRBool maybeFalseStart;
+ SECStatus rv;
+
+ /* 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);
+ maybeFalseStart = ss->ssl3.cwSpec->cipher_def->secret_key_size >= 10;
+ ssl_ReleaseSpecReadLock(ss);
+
+ if (!maybeFalseStart) {
+ SSL_TRC(3, ("%d: SSL[%d]: no false start due to weak cipher",
+ SSL_GETPID(), ss->fd));
+ } else {
+ rv = (ss->canFalseStartCallback)(ss->fd,
+ ss->canFalseStartCallbackData,
+ &ss->ssl3.hs.canFalseStart);
+ if (rv == SECSuccess) {
+ SSL_TRC(3, ("%d: SSL[%d]: false start callback returned %s",
+ SSL_GETPID(), ss->fd,
+ ss->ssl3.hs.canFalseStart ? "TRUE" : "FALSE"));
+ } else {
+ SSL_TRC(3, ("%d: SSL[%d]: false start callback failed (%s)",
+ SSL_GETPID(), ss->fd,
+ PR_ErrorToName(PR_GetError())));
+ }
+ return rv;
+ }
+ }
+
+ ss->ssl3.hs.canFalseStart = PR_FALSE;
+ return SECSuccess;
+}
+
PRBool
-ssl3_CanFalseStart(sslSocket *ss) {
- PRBool rv;
+ssl3_WaitingForStartOfServerSecondRound(sslSocket *ss)
+{
+ PRBool result = PR_FALSE;
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.
- */
-
- 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);
- ssl_ReleaseSpecReadLock(ss);
- return rv;
+ switch (ss->ssl3.hs.ws) {
+ case wait_new_session_ticket:
+ result = PR_TRUE;
+ break;
+ case wait_change_cipher:
+ result = !ssl3_ExtensionNegotiated(ss, ssl_session_ticket_xtn);
+ break;
+ case wait_finished:
+ break;
+ default:
+ PR_NOT_REACHED("ssl3_WaitingForStartOfServerSecondRound");
+ }
+
+ return result;
}
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.
*/
@@ -7103,16 +7140,19 @@ ssl3_SendClientSecondRound(sslSocket *ss
*/
if (ss->ssl3.hs.restartTarget) {
PR_NOT_REACHED("unexpected ss->ssl3.hs.restartTarget");
PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
return SECFailure;
}
if (ss->ssl3.hs.authCertificatePending &&
(sendClientCert || ss->ssl3.sendEmptyCert || ss->firstHsDone)) {
+ SSL_TRC(3, ("%d: SSL3[%p]: deferring ssl3_SendClientSecondRound because"
+ " certificate authentication is still pending.",
+ SSL_GETPID(), ss->fd));
ss->ssl3.hs.restartTarget = ssl3_SendClientSecondRound;
return SECWouldBlock;
}
ssl_GetXmitBufLock(ss); /*******************************/
if (ss->ssl3.sendEmptyCert) {
ss->ssl3.sendEmptyCert = PR_FALSE;
@@ -7140,42 +7180,75 @@ ssl3_SendClientSecondRound(sslSocket *ss
}
}
rv = ssl3_SendChangeCipherSpecs(ss);
if (rv != SECSuccess) {
goto loser; /* err code was set. */
}
- /* XXX: If the server's certificate hasn't been authenticated by this
- * point, then we may be leaking this NPN message to an attacker.
+ /* This must be done after we've set ss->ssl3.cwSpec in
+ * ssl3_SendChangeCipherSpecs because SSL_GetChannelInfo uses information
+ * from cwSpec. This must be done before we call ssl3_CheckFalseStart
+ * because the false start callback (if any) may need the information from
+ * the functions that depend on this being set.
*/
+ ss->enoughFirstHsDone = PR_TRUE;
+
if (!ss->firstHsDone) {
+ /* XXX: If the server's certificate hasn't been authenticated by this
+ * point, then we may be leaking this NPN message to an attacker.
+ */
rv = ssl3_SendNextProto(ss);
if (rv != SECSuccess) {
goto loser; /* err code was set. */
}
+
+ if (ss->opt.enableFalseStart) {
+ if (!ss->ssl3.hs.authCertificatePending) {
+ /* When we fix bug 589047, we will need to know whether we are
+ * false starting before we try to flush the client second
+ * round to the network. With that in mind, we purposefully
+ * call ssl3_CheckFalseStart before calling ssl3_SendFinished,
+ * which includes a call to ssl3_FlushHandshake, so that
+ * no application develops a reliance on such flushing being
+ * done before its false start callback is called.
+ */
+ ssl_ReleaseXmitBufLock(ss);
+ rv = ssl3_CheckFalseStart(ss);
+ ssl_GetXmitBufLock(ss);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ } else {
+ /* The certificate authentication and the server's Finished
+ * message are racing each other. If the certificate
+ * authentication wins, then we will try to false start in
+ * ssl3_AuthCertificateComplete.
+ */
+ SSL_TRC(3, ("%d: SSL3[%p]: deferring false start check because"
+ " certificate authentication is still pending.",
+ SSL_GETPID(), ss->fd));
+ }
+ }
}
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);
- }
+ PORT_Assert(ssl3_WaitingForStartOfServerSecondRound(ss));
return SECSuccess;
loser:
ssl_ReleaseXmitBufLock(ss);
return rv;
}
@@ -9740,23 +9813,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;
}
}
@@ -9870,26 +9936,54 @@ ssl3_AuthCertificateComplete(sslSocket *
if (error != 0) {
ss->ssl3.hs.restartTarget = ssl3_AlwaysFail;
ssl3_SendAlertForCertError(ss, error);
rv = SECSuccess;
} else if (ss->ssl3.hs.restartTarget != NULL) {
sslRestartTarget target = ss->ssl3.hs.restartTarget;
ss->ssl3.hs.restartTarget = NULL;
+
+ if (target == ssl3_FinishHandshake) {
+ SSL_TRC(3,("%d: SSL3[%p]: certificate authentication lost the race"
+ " with peer's finished message", SSL_GETPID(), ss->fd));
+ }
+
rv = target(ss);
/* Even if we blocked here, we have accomplished enough to claim
* success. Any remaining work will be taken care of by subsequent
* calls to SSL_ForceHandshake/PR_Send/PR_Read/etc.
*/
if (rv == SECWouldBlock) {
rv = SECSuccess;
}
} else {
- rv = SECSuccess;
+ SSL_TRC(3, ("%d: SSL3[%p]: certificate authentication won the race with"
+ " peer's finished message", SSL_GETPID(), ss->fd));
+
+ PORT_Assert(!ss->firstHsDone);
+ PORT_Assert(!ss->sec.isServer);
+ PORT_Assert(!ss->ssl3.hs.isResuming);
+ PORT_Assert(ss->ssl3.hs.ws == wait_new_session_ticket ||
+ ss->ssl3.hs.ws == wait_change_cipher ||
+ ss->ssl3.hs.ws == wait_finished);
+
+ /* ssl3_SendClientSecondRound deferred the false start check because
+ * certificate authentication was pending, so we do it now if we still
+ * haven't received any of the server's second round yet.
+ */
+ if (ss->opt.enableFalseStart &&
+ !ss->firstHsDone &&
+ !ss->sec.isServer &&
+ !ss->ssl3.hs.isResuming &&
+ ssl3_WaitingForStartOfServerSecondRound(ss)) {
+ rv = ssl3_CheckFalseStart(ss);
+ } else {
+ rv = SECSuccess;
+ }
}
done:
ssl_ReleaseSSL3HandshakeLock(ss);
ssl_ReleaseRecvBufLock(ss);
return rv;
}
@@ -10340,19 +10434,16 @@ ssl3_HandleFinished(sslSocket *ss, SSL3O
}
xmit_loser:
ssl_ReleaseXmitBufLock(ss); /*************************************/
if (rv != SECSuccess) {
return rv;
}
- ss->gs.writeOffset = 0;
- ss->gs.readOffset = 0;
-
if (ss->ssl3.hs.kea_def->kea == kea_ecdhe_rsa) {
effectiveExchKeyType = kt_rsa;
} else {
effectiveExchKeyType = ss->ssl3.hs.kea_def->exchKeyType;
}
if (sid->cached == never_cached && !ss->opt.noCache && ss->sec.cache) {
/* fill in the sid */
@@ -10407,38 +10498,38 @@ xmit_loser:
ss->ssl3.hs.restartTarget = ssl3_FinishHandshake;
return SECWouldBlock;
}
rv = ssl3_FinishHandshake(ss);
return rv;
}
+/* The return type is SECStatus instead of void because this function needs
+ * to have type sslRestartTarget.
+ */
SECStatus
ssl3_FinishHandshake(sslSocket * ss)
{
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;
if (ss->ssl3.hs.cacheSID) {
(*ss->sec.cache)(ss->sec.ci.sid);
ss->ssl3.hs.cacheSID = PR_FALSE;
}
+ ss->ssl3.hs.canFalseStart = PR_FALSE; /* False Start phase is complete */
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)) {
- (ss->handshakeCallback)(ss->fd, ss->handshakeCallbackData);
- }
+ ssl_FinishHandshake(ss);
return SECSuccess;
}
/* Called from ssl3_HandleHandshake() when it has gathered a complete ssl3
* hanshake message.
* Caller must hold Handshake and RecvBuf locks.
*/
@@ -11393,17 +11484,16 @@ process_it:
/* XXX Send an alert ??? */
PORT_SetError(SSL_ERROR_RX_UNKNOWN_RECORD_TYPE);
rv = SECFailure;
break;
}
ssl_ReleaseSSL3HandshakeLock(ss);
return rv;
-
}
/*
* Initialization functions
*/
/* Called from ssl3_InitState, immediately below. */
/* Caller must hold the SpecWriteLock. */
--- a/lib/ssl/ssl3gthr.c
+++ b/lib/ssl/ssl3gthr.c
@@ -270,21 +270,27 @@ dtls_GatherData(sslSocket *ss, sslGather
*
* Caller must hold the recv buf lock.
*/
int
ssl3_GatherCompleteHandshake(sslSocket *ss, int flags)
{
SSL3Ciphertext cText;
int rv;
- PRBool canFalseStart = PR_FALSE;
+ PRBool keepGoing = PR_TRUE;
SSL_TRC(30, ("ssl3_GatherCompleteHandshake"));
+ /* ssl3_HandleRecord may end up eventually calling ssl_FinishHandshake,
+ * which requires the 1stHandshakeLock, which must be acquired before the
+ * RecvBufLock.
+ */
+ PORT_Assert( ss->opt.noLocks || ssl_Have1stHandshakeLock(ss) );
PORT_Assert( ss->opt.noLocks || ssl_HaveRecvBufLock(ss) );
+
do {
PRBool handleRecordNow = PR_FALSE;
ssl_GetSSL3HandshakeLock(ss);
/* Without this, we may end up wrongly reporting
* SSL_ERROR_RX_UNEXPECTED_* errors if we receive any records from the
* peer while we are waiting to be restarted.
@@ -359,34 +365,62 @@ ssl3_GatherCompleteHandshake(sslSocket *
cText.seq_num.high <<= 8; cText.seq_num.low <<= 8;
cText.seq_num.high |= ss->gs.hdr[3 + i];
cText.seq_num.low |= ss->gs.hdr[7 + i];
}
}
cText.buf = &ss->gs.inbuf;
rv = ssl3_HandleRecord(ss, &cText, &ss->gs.buf);
+
+ if (rv == (int) SECSuccess && ss->gs.buf.len > 0) {
+ /* We have application data to return to the application. This
+ * prioritizes returning application data to the application over
+ * completing any renegotiation handshake we may be doing.
+ */
+ PORT_Assert(ss->firstHsDone);
+ PORT_Assert(cText.type == content_application_data);
+ break;
+ }
}
if (rv < 0) {
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);
- ssl_ReleaseSSL3HandshakeLock(ss);
+ PORT_Assert(keepGoing);
+ ssl_GetSSL3HandshakeLock(ss);
+ if (ss->ssl3.hs.ws == idle_handshake) {
+ /* We are done with the current handshake so stop trying to
+ * handshake. Note that it would be safe to test ss->firstHsDone
+ * instead of ss->ssl3.hs.ws. By testing ss->ssl3.hs.ws instead,
+ * we prioritize completing a renegotiation handshake over sending
+ * application data.
+ */
+ PORT_Assert(ss->firstHsDone);
+ PORT_Assert(!ss->ssl3.hs.canFalseStart);
+ keepGoing = PR_FALSE;
+ } else if (ss->ssl3.hs.canFalseStart) {
+ /* Prioritize sending application data over trying to complete
+ * the handshake if we're false starting.
+ *
+ * If we were to do this check at the beginning of the loop instead
+ * of here, then this function would become be a no-op after
+ * receiving the ServerHelloDone in the false start case, and we
+ * would never complete the handshake.
+ */
+ PORT_Assert(!ss->firstHsDone);
+
+ if (ssl3_WaitingForStartOfServerSecondRound(ss)) {
+ keepGoing = PR_FALSE;
+ } else {
+ ss->ssl3.hs.canFalseStart = PR_FALSE;
+ }
}
- } while (ss->ssl3.hs.ws != idle_handshake &&
- !canFalseStart &&
- ss->gs.buf.len == 0);
+ ssl_ReleaseSSL3HandshakeLock(ss);
+ } while (keepGoing);
ss->gs.readOffset = 0;
ss->gs.writeOffset = ss->gs.buf.len;
return 1;
}
/* Repeatedly gather in a record and when complete, Handle that record.
* Repeat this until some application data is received.
@@ -399,15 +433,18 @@ ssl3_GatherCompleteHandshake(sslSocket *
* Called from DoRecv in sslsecur.c
* Caller must hold the recv buf lock.
*/
int
ssl3_GatherAppDataRecord(sslSocket *ss, int flags)
{
int rv;
+ /* ssl3_GatherCompleteHandshake requires both of these locks. */
+ PORT_Assert( ss->opt.noLocks || ssl_Have1stHandshakeLock(ss) );
PORT_Assert( ss->opt.noLocks || ssl_HaveRecvBufLock(ss) );
+
do {
rv = ssl3_GatherCompleteHandshake(ss, flags);
} while (rv > 0 && ss->gs.buf.len == 0);
return rv;
}
--- a/lib/ssl/sslauth.c
+++ b/lib/ssl/sslauth.c
@@ -95,17 +95,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;
}
@@ -113,24 +112,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;
--- a/lib/ssl/sslimpl.h
+++ b/lib/ssl/sslimpl.h
@@ -854,16 +854,18 @@ const ssl3CipherSuiteDef *suite_def;
PRBool authCertificatePending;
/* Which function should SSL_RestartHandshake* call if we're blocked?
* One of NULL, ssl3_SendClientSecondRound, ssl3_FinishHandshake,
* or ssl3_AlwaysFail */
sslRestartTarget restartTarget;
/* Shared state between ssl3_HandleFinished and ssl3_FinishHandshake */
PRBool cacheSID;
+ PRBool canFalseStart; /* Can/did we False Start */
+
/* clientSigAndHash contains the contents of the signature_algorithms
* extension (if any) from the client. This is only valid for TLS 1.2
* or later. */
SSL3SignatureAndHashAlgorithm *clientSigAndHash;
unsigned int numClientSigAndHash;
/* This group of values is used for DTLS */
PRUint16 sendMessageSeq; /* The sending message sequence
@@ -1128,16 +1130,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 first handshake is
+ * done for callbacks to be able to
+ * retrieve channel security
+ * parameters from the SSL socket. */
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 */
@@ -1168,16 +1174,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 */
@@ -1370,17 +1378,29 @@ 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 void ssl_FinishHandshake(sslSocket *ss);
+
+/* Returns PR_TRUE if we are still waiting for the server to respond to our
+ * client second round. Once we've received any part of the server's second
+ * round then we don't bother trying to false start since it is almost always
+ * the case that the NewSessionTicket, ChangeCipherSoec, and Finished messages
+ * were sent in the same packet and we want to process them all at the same
+ * time. If we were to try to false start in the middle of the server's second
+ * round, then we would increase the number of I/O operations
+ * (SSL_ForceHandshake/PR_Recv/PR_Send/etc.) needed to finish the handshake.
+ */
+extern PRBool ssl3_WaitingForStartOfServerSecondRound(sslSocket *ss);
+
extern SECStatus
ssl3_CompressMACEncryptRecord(ssl3CipherSpec * cwSpec,
PRBool isServer,
PRBool isDTLS,
PRBool capRecordVersion,
SSL3ContentType type,
const SSL3Opaque * pIn,
PRUint32 contentLen,
--- a/lib/ssl/sslinfo.c
+++ b/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";
--- a/lib/ssl/sslsecur.c
+++ b/lib/ssl/sslsecur.c
@@ -92,33 +92,23 @@ ssl_Do1stHandshake(sslSocket *ss)
ss->nextHandshake = 0;
}
if (ss->handshake == 0) {
/* Previous handshake finished. Switch to security handshake */
ss->handshake = ss->securityHandshake;
ss->securityHandshake = 0;
}
if (ss->handshake == 0) {
- ssl_GetRecvBufLock(ss);
- ss->gs.recordLen = 0;
- ssl_ReleaseRecvBufLock(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->handshakeCallback)(ss->fd, ss->handshakeCallbackData);
+ /* for v3 this is done in ssl3_FinishHandshake */
+ if (!ss->firstHsDone && ss->version < SSL_LIBRARY_VERSION_3_0) {
+ ssl_GetRecvBufLock(ss);
+ ss->gs.recordLen = 0;
+ ssl_FinishHandshake(ss);
+ ssl_ReleaseRecvBufLock(ss);
}
- ss->firstHsDone = 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.
*/
} while (rv != SECFailure); /* was (rv >= 0); XXX_1 */
@@ -129,16 +119,34 @@ ssl_Do1stHandshake(sslSocket *ss)
if (rv == SECWouldBlock) {
PORT_SetError(PR_WOULD_BLOCK_ERROR);
rv = SECFailure;
}
return rv;
}
+void
+ssl_FinishHandshake(sslSocket *ss)
+{
+ PORT_Assert( ss->opt.noLocks || ssl_Have1stHandshakeLock(ss) );
+ PORT_Assert( ss->opt.noLocks || ssl_HaveRecvBufLock(ss) );
+
+ SSL_TRC(3, ("%d: SSL[%d]: handshake is completed", SSL_GETPID(), ss->fd));
+
+ ss->firstHsDone = PR_TRUE;
+ ss->enoughFirstHsDone = PR_TRUE;
+ ss->gs.writeOffset = 0;
+ ss->gs.readOffset = 0;
+
+ if (ss->handshakeCallback) {
+ (ss->handshakeCallback)(ss->fd, ss->handshakeCallbackData);
+ }
+}
+
/*
* Handshake function that blocks. Used to force a
* retry on a connection on the next read/write.
*/
static SECStatus
ssl3_AlwaysBlock(sslSocket *ss)
{
PORT_SetError(PR_WOULD_BLOCK_ERROR); /* perhaps redundant. */
@@ -201,31 +209,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 +337,81 @@ 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 *arg)
+{
+ 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 = arg;
+
+ ssl_ReleaseSSL3HandshakeLock(ss);
+ ssl_Release1stHandshakeLock(ss);
+
+ return SECSuccess;
+}
+
+SECStatus
+SSL_RecommendedCanFalseStart(PRFileDesc *fd, PRBool *canFalseStart)
+{
+ sslSocket *ss;
+
+ *canFalseStart = PR_FALSE;
+ ss = ssl_FindSocket(fd);
+ if (!ss) {
+ SSL_DBG(("%d: SSL[%d]: bad socket in SSL_RecommendedCanFalseStart",
+ SSL_GETPID(), fd));
+ return SECFailure;
+ }
+
+ if (!ss->ssl3.initialized) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ if (ss->version < SSL_LIBRARY_VERSION_3_0) {
+ PORT_SetError(SSL_ERROR_FEATURE_NOT_SUPPORTED_FOR_SSL2);
+ 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.
@@ -519,16 +595,19 @@ ssl_SendSavedWriteData(sslSocket *ss)
*/
static int
DoRecv(sslSocket *ss, unsigned char *out, int len, int flags)
{
int rv;
int amount;
int available;
+ /* ssl3_GatherAppDataRecord may call ssl_FinishHandshake, which needs the
+ * 1stHandshakeLock. */
+ ssl_Get1stHandshakeLock(ss);
ssl_GetRecvBufLock(ss);
available = ss->gs.writeOffset - ss->gs.readOffset;
if (available == 0) {
/* Get some more data */
if (ss->version >= SSL_LIBRARY_VERSION_3_0) {
/* Wait for application data to arrive. */
rv = ssl3_GatherAppDataRecord(ss, 0);
@@ -585,16 +664,17 @@ DoRecv(sslSocket *ss, unsigned char *out
rv = amount;
SSL_TRC(30, ("%d: SSL[%d]: amount=%d available=%d",
SSL_GETPID(), ss->fd, amount, available));
PRINT_BUF(4, (ss, "DoRecv receiving plaintext:", out, amount));
done:
ssl_ReleaseRecvBufLock(ss);
+ ssl_Release1stHandshakeLock(ss);
return rv;
}
/************************************************************************/
/*
** Return SSLKEAType derived from cert's Public Key algorithm info.
*/
@@ -1151,17 +1231,18 @@ ssl_SecureRead(sslSocket *ss, unsigned c
{
return ssl_SecureRecv(ss, buf, len, 0);
}
/* Caller holds the SSL Socket's write lock. SSL_LOCK_WRITER(ss) */
int
ssl_SecureSend(sslSocket *ss, const unsigned char *buf, int len, int flags)
{
- int rv = 0;
+ int rv = 0;
+ PRBool falseStart = PR_FALSE;
SSL_TRC(2, ("%d: SSL[%d]: SecureSend: sending %d bytes",
SSL_GETPID(), ss->fd, len));
if (ss->shutdownHow & ssl_SHUTDOWN_SEND) {
PORT_SetError(PR_SOCKET_SHUTDOWN_ERROR);
rv = PR_FAILURE;
goto done;
@@ -1186,29 +1267,24 @@ ssl_SecureSend(sslSocket *ss, const unsi
if (rv < 0) {
goto done;
}
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) {
+ if (ss->opt.enableFalseStart &&
+ 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;
- }
+ falseStart = ss->ssl3.hs.canFalseStart;
ssl_ReleaseSSL3HandshakeLock(ss);
}
- if (!canFalseStart &&
+ if (!falseStart &&
(ss->handshake || ss->nextHandshake || ss->securityHandshake)) {
rv = ssl_Do1stHandshake(ss);
}
ssl_Release1stHandshakeLock(ss);
}
if (rv < 0) {
ss->writerThread = NULL;
goto done;
@@ -1223,16 +1299,27 @@ ssl_SecureSend(sslSocket *ss, const unsi
}
PORT_Assert(buf != NULL);
if (!buf) {
PORT_SetError(PR_INVALID_ARGUMENT_ERROR);
rv = PR_FAILURE;
goto done;
}
+ if (!ss->firstHsDone) {
+ PORT_Assert(ss->version >= SSL_LIBRARY_VERSION_3_0);
+#ifdef DEBUG
+ ssl_GetSSL3HandshakeLock(ss);
+ PORT_Assert(ss->ssl3.hs.canFalseStart);
+ ssl_ReleaseSSL3HandshakeLock(ss);
+#endif
+ SSL_TRC(3, ("%d: SSL[%d]: SecureSend: sending data due to false start",
+ SSL_GETPID(), ss->fd));
+ }
+
/* Send out the data using one of these functions:
* ssl2_SendClear, ssl2_SendStream, ssl2_SendBlock,
* ssl3_SendApplicationData
*/
ssl_GetXmitBufLock(ss);
rv = (*ss->sec.send)(ss, buf, len, flags);
ssl_ReleaseXmitBufLock(ss);
ss->writerThread = NULL;
--- a/lib/ssl/sslsock.c
+++ b/lib/ssl/sslsock.c
@@ -262,16 +262,18 @@ ssl_DupSocket(sslSocket *os)
ss->getClientAuthData = os->getClientAuthData;
ss->getClientAuthDataArg = os->getClientAuthDataArg;
ss->sniSocketConfig = os->sniSocketConfig;
ss->sniSocketConfigArg = os->sniSocketConfigArg;
ss->handleBadCert = os->handleBadCert;
ss->badCertArg = os->badCertArg;
ss->handshakeCallback = os->handshakeCallback;
ss->handshakeCallbackData = os->handshakeCallbackData;
+ ss->canFalseStartCallback = os->canFalseStartCallback;
+ ss->canFalseStartCallbackData = os->canFalseStartCallbackData;
ss->pkcs11PinArg = os->pkcs11PinArg;
/* Create security data */
rv = ssl_CopySecurityInfo(ss, os);
if (rv != SECSuccess) {
goto loser;
}
}
@@ -2254,20 +2256,24 @@ 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. */
- new_flags |= PR_POLL_READ; /* do select on read. */
+ 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 */
new_flags |= PR_POLL_WRITE; /* also select on write. */