Esempio n. 1
0
/* Gather in a record and when complete, Handle that record.
 * Repeat this until the handshake is complete,
 * or until application data is available.
 *
 * Returns  1 when the handshake is completed without error, or
 *                 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
 *    and from ssl3_GatherAppDataRecord below (<- DoRecv in sslsecur.c).
 *
 * Caller must hold the recv buf lock.
 */
int
ssl3_GatherCompleteHandshake(sslSocket *ss, int flags)
{
    SSL3Ciphertext cText;
    int rv;
    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.
         */
        if (ss->ssl3.hs.restartTarget) {
            ssl_ReleaseSSL3HandshakeLock(ss);
            PORT_SetError(PR_WOULD_BLOCK_ERROR);
            return (int)SECFailure;
        }

        /* 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;
            }
        }

        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.
             */
            rv = ssl3_HandleRecord(ss, NULL, &ss->gs.buf);
        } else {
            /* 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)) {
                rv = ssl3_GatherData(ss, &ss->gs, flags);
            } 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)) {
                    ssl_GetSSL3HandshakeLock(ss);
                    dtls_CheckTimer(ss);
                    ssl_ReleaseSSL3HandshakeLock(ss);
                    /* Restore the error in case something succeeded */
                    PORT_SetError(PR_WOULD_BLOCK_ERROR);
                }
            }

            if (rv <= 0) {
                return rv;
            }

            /* 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.type = (SSL3ContentType)ss->gs.hdr[0];
            cText.version = (ss->gs.hdr[1] << 8) | ss->gs.hdr[2];

            if (IS_DTLS(ss)) {
                int i;

                cText.version = dtls_DTLSVersionToTLSVersion(cText.version);
                /* DTLS sequence number */
                cText.seq_num.high = 0;
                cText.seq_num.low = 0;
                for (i = 0; i < 4; i++) {
                    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 < 0) {
            return ss->recvdCloseNotify ? 0 : rv;
        }
        if (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;
        }

        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_WaitingForServerSecondRound(ss)) {
                keepGoing = PR_FALSE;
            } else {
                ss->ssl3.hs.canFalseStart = PR_FALSE;
            }
        }
        ssl_ReleaseSSL3HandshakeLock(ss);
    } while (keepGoing);

    ss->gs.readOffset = 0;
    ss->gs.writeOffset = ss->gs.buf.len;
    return 1;
}
Esempio n. 2
0
int
ssl3_GatherCompleteHandshake(sslSocket *ss, int flags)
{
    SSL3Ciphertext cText;
    int            rv;
    PRBool         keepGoing = PR_TRUE;

    SSL_TRC(30, ("ssl3_GatherCompleteHandshake"));

    PORT_Assert( ss->opt.noLocks || ssl_Have1stHandshakeLock(ss) );
    PORT_Assert( ss->opt.noLocks || ssl_HaveRecvBufLock(ss) );

    do {
        PRBool handleRecordNow = PR_FALSE;

        ssl_GetSSL3HandshakeLock(ss);

        if (ss->ssl3.hs.restartTarget) {
            ssl_ReleaseSSL3HandshakeLock(ss);
            PORT_SetError(PR_WOULD_BLOCK_ERROR);
            return (int) SECFailure;
        }

        if (ss->ssl3.hs.msgState.buf) {
            if (ss->ssl3.hs.msgState.len == 0) {
                ss->ssl3.hs.msgState.buf = NULL;
            } else {
                handleRecordNow = PR_TRUE;
            }
        }

        ssl_ReleaseSSL3HandshakeLock(ss);

        if (handleRecordNow) {
            rv = ssl3_HandleRecord(ss, NULL, &ss->gs.buf);
        } else {

            if (!IS_DTLS(ss)) {
                rv = ssl3_GatherData(ss, &ss->gs, flags);
            } else {
                rv = dtls_GatherData(ss, &ss->gs, flags);

                if (rv == SECFailure &&
                        (PORT_GetError() == PR_WOULD_BLOCK_ERROR)) {
                    ssl_GetSSL3HandshakeLock(ss);
                    dtls_CheckTimer(ss);
                    ssl_ReleaseSSL3HandshakeLock(ss);

                    PORT_SetError(PR_WOULD_BLOCK_ERROR);
                }
            }

            if (rv <= 0) {
                return rv;
            }

            cText.type    = (SSL3ContentType)ss->gs.hdr[0];
            cText.version = (ss->gs.hdr[1] << 8) | ss->gs.hdr[2];

            if (IS_DTLS(ss)) {
                int i;

                cText.version = dtls_DTLSVersionToTLSVersion(cText.version);

                cText.seq_num.high = 0;
                cText.seq_num.low = 0;
                for (i = 0; i < 4; i++) {
                    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) {
                PORT_Assert(ss->firstHsDone);
                PORT_Assert(cText.type == content_application_data);
                break;
            }
        }
        if (rv < 0) {
            return ss->recvdCloseNotify ? 0 : rv;
        }

        PORT_Assert(keepGoing);
        ssl_GetSSL3HandshakeLock(ss);
        if (ss->ssl3.hs.ws == idle_handshake) {
            PORT_Assert(ss->firstHsDone);
            PORT_Assert(!ss->ssl3.hs.canFalseStart);
            keepGoing = PR_FALSE;
        } else if (ss->ssl3.hs.canFalseStart) {
            PORT_Assert(!ss->firstHsDone);

            if (ssl3_WaitingForStartOfServerSecondRound(ss)) {
                keepGoing = PR_FALSE;
            } else {
                ss->ssl3.hs.canFalseStart = PR_FALSE;
            }
        }
        ssl_ReleaseSSL3HandshakeLock(ss);
    } while (keepGoing);

    ss->gs.readOffset = 0;
    ss->gs.writeOffset = ss->gs.buf.len;
    return 1;
}