summaryrefslogtreecommitdiffstats
path: root/security/nss/lib/ssl/ssl3gthr.c
diff options
context:
space:
mode:
Diffstat (limited to 'security/nss/lib/ssl/ssl3gthr.c')
-rw-r--r--security/nss/lib/ssl/ssl3gthr.c251
1 files changed, 170 insertions, 81 deletions
diff --git a/security/nss/lib/ssl/ssl3gthr.c b/security/nss/lib/ssl/ssl3gthr.c
index 64a1878f7..f9c741746 100644
--- a/security/nss/lib/ssl/ssl3gthr.c
+++ b/security/nss/lib/ssl/ssl3gthr.c
@@ -389,7 +389,6 @@ dtls_GatherData(sslSocket *ss, sslGather *gs, int flags)
* application data is available.
* Returns 0 if ssl3_GatherData hits EOF.
* Returns -1 on read error, or PR_WOULD_BLOCK_ERROR, or handleRecord error.
- * Returns -2 on SECWouldBlock return from ssl3_HandleRecord.
*
* Called from ssl_GatherRecord1stHandshake in sslcon.c,
* and from SSL_ForceHandshake in sslsecur.c
@@ -408,7 +407,7 @@ ssl3_GatherCompleteHandshake(sslSocket *ss, int flags)
SSL_TRC(3, ("%d: SSL3[%d] Cannot gather data; fatal alert already sent",
SSL_GETPID(), ss->fd));
PORT_SetError(SSL_ERROR_HANDSHAKE_FAILED);
- return SECFailure;
+ return -1;
}
SSL_TRC(30, ("%d: SSL3[%d]: ssl3_GatherCompleteHandshake",
@@ -422,7 +421,6 @@ ssl3_GatherCompleteHandshake(sslSocket *ss, int flags)
PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss));
do {
- PRBool handleRecordNow = PR_FALSE;
PRBool processingEarlyData;
ssl_GetSSL3HandshakeLock(ss);
@@ -436,96 +434,82 @@ ssl3_GatherCompleteHandshake(sslSocket *ss, int flags)
if (ss->ssl3.hs.restartTarget) {
ssl_ReleaseSSL3HandshakeLock(ss);
PORT_SetError(PR_WOULD_BLOCK_ERROR);
- return (int)SECFailure;
+ return -1;
}
- /* Treat an empty msgState like a NULL msgState. (Most of the time
- * when ssl3_HandleHandshake returns SECWouldBlock, it leaves
- * behind a non-NULL but zero-length msgState).
- * Test: async_cert_restart_server_sends_hello_request_first_in_separate_record
- */
- if (ss->ssl3.hs.msgState.buf) {
- if (ss->ssl3.hs.msgState.len == 0) {
- ss->ssl3.hs.msgState.buf = NULL;
- } else {
- handleRecordNow = PR_TRUE;
+ /* If we have a detached record layer, don't ever gather. */
+ if (ss->recordWriteCallback) {
+ PRBool done = ss->firstHsDone;
+ ssl_ReleaseSSL3HandshakeLock(ss);
+ if (done) {
+ return 1;
}
+ PORT_SetError(PR_WOULD_BLOCK_ERROR);
+ return -1;
}
ssl_ReleaseSSL3HandshakeLock(ss);
- if (handleRecordNow) {
- /* ssl3_HandleHandshake previously returned SECWouldBlock and the
- * as-yet-unprocessed plaintext of that previous handshake record.
- * We need to process it now before we overwrite it with the next
- * handshake record.
- */
- SSL_DBG(("%d: SSL3[%d]: resuming handshake",
- SSL_GETPID(), ss->fd));
- PORT_Assert(!IS_DTLS(ss));
- rv = ssl3_HandleNonApplicationData(ss, ssl_ct_handshake,
- 0, 0, &ss->gs.buf);
- } else {
- /* State for SSLv2 client hello support. */
- ssl2Gather ssl2gs = { PR_FALSE, 0 };
- ssl2Gather *ssl2gs_ptr = NULL;
+ /* State for SSLv2 client hello support. */
+ ssl2Gather ssl2gs = { PR_FALSE, 0 };
+ ssl2Gather *ssl2gs_ptr = NULL;
- if (ss->sec.isServer && ss->opt.enableV2CompatibleHello &&
- ss->ssl3.hs.ws == wait_client_hello) {
- ssl2gs_ptr = &ssl2gs;
- }
+ /* If we're a server and waiting for a client hello, accept v2. */
+ if (ss->sec.isServer && ss->opt.enableV2CompatibleHello &&
+ ss->ssl3.hs.ws == wait_client_hello) {
+ ssl2gs_ptr = &ssl2gs;
+ }
- /* bring in the next sslv3 record. */
- if (ss->recvdCloseNotify) {
- /* RFC 5246 Section 7.2.1:
- * Any data received after a closure alert is ignored.
- */
- return 0;
- }
+ /* bring in the next sslv3 record. */
+ if (ss->recvdCloseNotify) {
+ /* RFC 5246 Section 7.2.1:
+ * Any data received after a closure alert is ignored.
+ */
+ return 0;
+ }
- if (!IS_DTLS(ss)) {
- /* Passing a non-NULL ssl2gs here enables detection of
- * SSLv2-compatible ClientHello messages. */
- rv = ssl3_GatherData(ss, &ss->gs, flags, ssl2gs_ptr);
- } else {
- rv = dtls_GatherData(ss, &ss->gs, flags);
-
- /* If we got a would block error, that means that no data was
- * available, so we check the timer to see if it's time to
- * retransmit */
- if (rv == SECFailure &&
- (PORT_GetError() == PR_WOULD_BLOCK_ERROR)) {
- dtls_CheckTimer(ss);
- /* Restore the error in case something succeeded */
- PORT_SetError(PR_WOULD_BLOCK_ERROR);
- }
+ if (!IS_DTLS(ss)) {
+ /* If we're a server waiting for a ClientHello then pass
+ * ssl2gs to support SSLv2 ClientHello messages. */
+ rv = ssl3_GatherData(ss, &ss->gs, flags, ssl2gs_ptr);
+ } else {
+ rv = dtls_GatherData(ss, &ss->gs, flags);
+
+ /* If we got a would block error, that means that no data was
+ * available, so we check the timer to see if it's time to
+ * retransmit */
+ if (rv == SECFailure &&
+ (PORT_GetError() == PR_WOULD_BLOCK_ERROR)) {
+ dtls_CheckTimer(ss);
+ /* Restore the error in case something succeeded */
+ PORT_SetError(PR_WOULD_BLOCK_ERROR);
}
+ }
- if (rv <= 0) {
- return rv;
- }
+ if (rv <= 0) {
+ return rv;
+ }
- if (ssl2gs.isV2) {
- rv = ssl3_HandleV2ClientHello(ss, ss->gs.inbuf.buf,
- ss->gs.inbuf.len,
- ssl2gs.padding);
- if (rv < 0) {
- return rv;
- }
- } else {
- /* decipher it, and handle it if it's a handshake.
- * If it's application data, ss->gs.buf will not be empty upon return.
- * If it's a change cipher spec, alert, or handshake message,
- * ss->gs.buf.len will be 0 when ssl3_HandleRecord returns SECSuccess.
- *
- * cText only needs to be valid for this next function call, so
- * it can borrow gs.hdr.
- */
- cText.hdr = ss->gs.hdr;
- cText.hdrLen = ss->gs.hdrLen;
- cText.buf = &ss->gs.inbuf;
- rv = ssl3_HandleRecord(ss, &cText);
+ if (ssl2gs.isV2) {
+ rv = ssl3_HandleV2ClientHello(ss, ss->gs.inbuf.buf,
+ ss->gs.inbuf.len,
+ ssl2gs.padding);
+ if (rv < 0) {
+ return rv;
}
+ } else {
+ /* decipher it, and handle it if it's a handshake.
+ * If it's application data, ss->gs.buf will not be empty upon return.
+ * If it's a change cipher spec, alert, or handshake message,
+ * ss->gs.buf.len will be 0 when ssl3_HandleRecord returns SECSuccess.
+ *
+ * cText only needs to be valid for this next function call, so
+ * it can borrow gs.hdr.
+ */
+ cText.hdr = ss->gs.hdr;
+ cText.hdrLen = ss->gs.hdrLen;
+ cText.buf = &ss->gs.inbuf;
+ rv = ssl3_HandleRecord(ss, &cText);
}
if (rv < 0) {
return ss->recvdCloseNotify ? 0 : rv;
@@ -575,7 +559,7 @@ ssl3_GatherCompleteHandshake(sslSocket *ss, int flags)
* delivered to the application before the handshake completes. */
ssl_ReleaseSSL3HandshakeLock(ss);
PORT_SetError(PR_WOULD_BLOCK_ERROR);
- return SECWouldBlock;
+ return -1;
}
ssl_ReleaseSSL3HandshakeLock(ss);
} while (keepGoing);
@@ -596,7 +580,6 @@ ssl3_GatherCompleteHandshake(sslSocket *ss, int flags)
* Returns 1 when application data is available.
* Returns 0 if ssl3_GatherData hits EOF.
* Returns -1 on read error, or PR_WOULD_BLOCK_ERROR, or handleRecord error.
- * Returns -2 on SECWouldBlock return from ssl3_HandleRecord.
*
* Called from DoRecv in sslsecur.c
* Caller must hold the recv buf lock.
@@ -616,3 +599,109 @@ ssl3_GatherAppDataRecord(sslSocket *ss, int flags)
return rv;
}
+
+SECStatus
+SSLExp_RecordLayerData(PRFileDesc *fd, PRUint16 epoch,
+ SSLContentType contentType,
+ const PRUint8 *data, unsigned int len)
+{
+ SECStatus rv;
+ sslSocket *ss = ssl_FindSocket(fd);
+ if (!ss) {
+ return SECFailure;
+ }
+ if (IS_DTLS(ss) || data == NULL || len == 0) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ /* Run any handshake function. If SSL_RecordLayerData is the only way that
+ * the handshake is driven, then this is necessary to ensure that
+ * ssl_BeginClientHandshake or ssl_BeginServerHandshake is called. Note that
+ * the other function that might be set to ss->handshake,
+ * ssl3_GatherCompleteHandshake, does nothing when this function is used. */
+ ssl_Get1stHandshakeLock(ss);
+ rv = ssl_Do1stHandshake(ss);
+ if (rv != SECSuccess && PORT_GetError() != PR_WOULD_BLOCK_ERROR) {
+ goto early_loser; /* Rely on the existing code. */
+ }
+
+ /* Don't allow application data before handshake completion. */
+ if (contentType == ssl_ct_application_data && !ss->firstHsDone) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ goto early_loser;
+ }
+
+ /* Then we can validate the epoch. */
+ PRErrorCode epochError;
+ ssl_GetSpecReadLock(ss);
+ if (epoch < ss->ssl3.crSpec->epoch) {
+ epochError = SEC_ERROR_INVALID_ARGS; /* Too c/old. */
+ } else if (epoch > ss->ssl3.crSpec->epoch) {
+ epochError = PR_WOULD_BLOCK_ERROR; /* Too warm/new. */
+ } else {
+ epochError = 0; /* Just right. */
+ }
+ ssl_ReleaseSpecReadLock(ss);
+ if (epochError) {
+ PORT_SetError(epochError);
+ goto early_loser;
+ }
+
+ /* If the handshake is still running, we need to run that. */
+ ssl_Get1stHandshakeLock(ss);
+ rv = ssl_Do1stHandshake(ss);
+ if (rv != SECSuccess && PORT_GetError() != PR_WOULD_BLOCK_ERROR) {
+ ssl_Release1stHandshakeLock(ss);
+ return SECFailure;
+ }
+
+ /* Finally, save the data... */
+ ssl_GetRecvBufLock(ss);
+ rv = sslBuffer_Append(&ss->gs.buf, data, len);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+ /* ...and process it. Just saving application data is enough for it to be
+ * available to PR_Read(). */
+ if (contentType != ssl_ct_application_data) {
+ rv = ssl3_HandleNonApplicationData(ss, contentType, 0, 0, &ss->gs.buf);
+ /* This occasionally blocks, but that's OK here. */
+ if (rv != SECSuccess && PORT_GetError() != PR_WOULD_BLOCK_ERROR) {
+ goto loser;
+ }
+ }
+
+ ssl_ReleaseRecvBufLock(ss);
+ ssl_Release1stHandshakeLock(ss);
+ return SECSuccess;
+
+loser:
+ /* Make sure that any data is not used again. */
+ ss->gs.buf.len = 0;
+ ssl_ReleaseRecvBufLock(ss);
+early_loser:
+ ssl_Release1stHandshakeLock(ss);
+ return SECFailure;
+}
+
+SECStatus
+SSLExp_GetCurrentEpoch(PRFileDesc *fd, PRUint16 *readEpoch,
+ PRUint16 *writeEpoch)
+{
+ sslSocket *ss = ssl_FindSocket(fd);
+ if (!ss) {
+ return SECFailure;
+ }
+
+ ssl_GetSpecReadLock(ss);
+ if (readEpoch) {
+ *readEpoch = ss->ssl3.crSpec->epoch;
+ }
+ if (writeEpoch) {
+ *writeEpoch = ss->ssl3.cwSpec->epoch;
+ }
+ ssl_ReleaseSpecReadLock(ss);
+ return SECSuccess;
+}