Commit 657da85e authored by Matt Caswell's avatar Matt Caswell
Browse files

Move TLS CCS processing into the state machine



The handling of incoming CCS records is a little strange. Since CCS is not
a handshake message it is handled differently to normal handshake messages.
Unfortunately whilst technically it is not a handhshake message the reality
is that it must be processed in accordance with the state of the handshake.
Currently CCS records are processed entirely within the record layer. In
order to ensure that it is handled in accordance with the handshake state
a flag is used to indicate that it is an acceptable time to receive a CCS.

Previously this flag did not exist (see CVE-2014-0224), but the flag should
only really be considered a workaround for the problem that CCS is not
visible to the state machine.

Outgoing CCS messages are already handled within the state machine.

This patch makes CCS visible to the TLS state machine. A separate commit
will handle DTLS.

Reviewed-by: default avatarTim Hudson <tjh@openssl.org>
parent 9ceb2426
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -1943,6 +1943,7 @@ void ERR_load_SSL_strings(void);
# define SSL_F_SSL3_GET_CERTIFICATE_REQUEST               135
# define SSL_F_SSL3_GET_CERT_STATUS                       289
# define SSL_F_SSL3_GET_CERT_VERIFY                       136
# define SSL_F_SSL3_GET_CHANGE_CIPHER_SPEC                348
# define SSL_F_SSL3_GET_CLIENT_CERTIFICATE                137
# define SSL_F_SSL3_GET_CLIENT_HELLO                      138
# define SSL_F_SSL3_GET_CLIENT_KEY_EXCHANGE               139
+3 −5
Original line number Diff line number Diff line
@@ -365,11 +365,6 @@ extern "C" {
# define TLS1_FLAGS_TLS_PADDING_BUG              0x0

# define TLS1_FLAGS_SKIP_CERT_VERIFY             0x0010
/*
 * Set when the handshake is ready to process peer's ChangeCipherSpec message.
 * Cleared after the message has been processed.
 */
# define SSL3_FLAGS_CCS_OK                       0x0080

/* Set if we encrypt then mac instead of usual mac then encrypt */
# define TLS1_FLAGS_ENCRYPT_THEN_MAC             0x0100
@@ -499,6 +494,9 @@ extern "C" {
# endif
# define DTLS1_MT_HELLO_VERIFY_REQUEST    3

/* Dummy message type for handling CCS like a normal handshake message */
# define SSL3_MT_CHANGE_CIPHER_SPEC              0x0101

# define SSL3_MT_CCS                             1

/* These are used when changing over to a new cipher */
+6 −6
Original line number Diff line number Diff line
@@ -679,7 +679,7 @@ dtls1_reassemble_fragment(SSL *s, const struct hm_header_st *msg_hdr, int *ok)
        unsigned char devnull[256];

        while (frag_len) {
            i = s->method->ssl_read_bytes(s, SSL3_RT_HANDSHAKE,
            i = s->method->ssl_read_bytes(s, SSL3_RT_HANDSHAKE, NULL,
                                          devnull,
                                          frag_len >
                                          sizeof(devnull) ? sizeof(devnull) :
@@ -692,7 +692,7 @@ dtls1_reassemble_fragment(SSL *s, const struct hm_header_st *msg_hdr, int *ok)
    }

    /* read the body of the fragment (header has already been read */
    i = s->method->ssl_read_bytes(s, SSL3_RT_HANDSHAKE,
    i = s->method->ssl_read_bytes(s, SSL3_RT_HANDSHAKE, NULL,
                                  frag->fragment + msg_hdr->frag_off,
                                  frag_len, 0);
    if ((unsigned long)i != frag_len)
@@ -775,7 +775,7 @@ dtls1_process_out_of_seq_message(SSL *s, const struct hm_header_st *msg_hdr,
        unsigned char devnull[256];

        while (frag_len) {
            i = s->method->ssl_read_bytes(s, SSL3_RT_HANDSHAKE,
            i = s->method->ssl_read_bytes(s, SSL3_RT_HANDSHAKE, NULL,
                                          devnull,
                                          frag_len >
                                          sizeof(devnull) ? sizeof(devnull) :
@@ -801,7 +801,7 @@ dtls1_process_out_of_seq_message(SSL *s, const struct hm_header_st *msg_hdr,
            /*
             * read the body of the fragment (header has already been read
             */
            i = s->method->ssl_read_bytes(s, SSL3_RT_HANDSHAKE,
            i = s->method->ssl_read_bytes(s, SSL3_RT_HANDSHAKE, NULL,
                                          frag->fragment, frag_len, 0);
            if ((unsigned long)i != frag_len)
                i = -1;
@@ -851,7 +851,7 @@ dtls1_get_message_fragment(SSL *s, int st1, int stn, long max, int *ok)
    }

    /* read handshake message header */
    i = s->method->ssl_read_bytes(s, SSL3_RT_HANDSHAKE, wire,
    i = s->method->ssl_read_bytes(s, SSL3_RT_HANDSHAKE, NULL, wire,
                                  DTLS1_HM_HEADER_LENGTH, 0);
    if (i <= 0) {               /* nbio, or an error */
        s->rwstate = SSL_READING;
@@ -926,7 +926,7 @@ dtls1_get_message_fragment(SSL *s, int st1, int stn, long max, int *ok)
        unsigned char *p =
            (unsigned char *)s->init_buf->data + DTLS1_HM_HEADER_LENGTH;

        i = s->method->ssl_read_bytes(s, SSL3_RT_HANDSHAKE,
        i = s->method->ssl_read_bytes(s, SSL3_RT_HANDSHAKE, NULL,
                                      &p[frag_off], frag_len, 0);

        /*
+2 −1
Original line number Diff line number Diff line
@@ -395,7 +395,8 @@ int dtls1_process_buffered_records(SSL *s)
 *     Application data protocol
 *             none of our business
 */
int dtls1_read_bytes(SSL *s, int type, unsigned char *buf, int len, int peek)
int dtls1_read_bytes(SSL *s, int type, int *recvd_type, unsigned char *buf,
                     int len, int peek)
{
    int al, i, j, ret;
    unsigned int n;
+35 −47
Original line number Diff line number Diff line
@@ -955,8 +955,9 @@ int ssl3_write_pending(SSL *s, int type, const unsigned char *buf,
 * (possibly multiple records if we still don't have anything to return).
 *
 * This function must handle any surprises the peer may have for us, such as
 * Alert records (e.g. close_notify), ChangeCipherSpec records (not really
 * a surprise, but handled as if it were), or renegotiation requests.
 * Alert records (e.g. close_notify) or renegotiation requests. ChangeCipherSpec
 * messages are treated as if they were handshake messages *if* the |recd_type|
 * argument is non NULL.
 * Also if record payloads contain fragments too small to process, we store
 * them until there is enough for the respective protocol (the record protocol
 * may use arbitrary fragmentation and even interleaving):
@@ -971,7 +972,8 @@ int ssl3_write_pending(SSL *s, int type, const unsigned char *buf,
 *     Application data protocol
 *             none of our business
 */
int ssl3_read_bytes(SSL *s, int type, unsigned char *buf, int len, int peek)
int ssl3_read_bytes(SSL *s, int type, int *recvd_type, unsigned char *buf,
                    int len, int peek)
{
    int al, i, j, ret;
    unsigned int n;
@@ -1066,9 +1068,14 @@ int ssl3_read_bytes(SSL *s, int type, unsigned char *buf, int len, int peek)
        return (0);
    }

    if (type == SSL3_RECORD_get_type(rr)) {
        /* SSL3_RT_APPLICATION_DATA or
         * SSL3_RT_HANDSHAKE */
    if (type == SSL3_RECORD_get_type(rr)
            || (SSL3_RECORD_get_type(rr) == SSL3_RT_CHANGE_CIPHER_SPEC
                && type == SSL3_RT_HANDSHAKE && recvd_type != NULL)) {
        /*
         * SSL3_RT_APPLICATION_DATA or
         * SSL3_RT_HANDSHAKE or
         * SSL3_RT_CHANGE_CIPHER_SPEC
         */
        /*
         * make sure that we are not getting application data when we are
         * doing a handshake for the first time
@@ -1080,6 +1087,17 @@ int ssl3_read_bytes(SSL *s, int type, unsigned char *buf, int len, int peek)
            goto f_err;
        }

        if (type == SSL3_RT_HANDSHAKE
                && SSL3_RECORD_get_type(rr) == SSL3_RT_CHANGE_CIPHER_SPEC
                && s->rlayer.handshake_fragment_len > 0) {
            al = SSL_AD_UNEXPECTED_MESSAGE;
            SSLerr(SSL_F_SSL3_READ_BYTES, SSL_R_CCS_RECEIVED_EARLY);
            goto f_err;
        }

        if (recvd_type != NULL)
            *recvd_type = SSL3_RECORD_get_type(rr);

        if (len <= 0)
            return (len);

@@ -1105,9 +1123,16 @@ int ssl3_read_bytes(SSL *s, int type, unsigned char *buf, int len, int peek)

    /*
     * If we get here, then type != rr->type; if we have a handshake message,
     * then it was unexpected (Hello Request or Client Hello).
     * then it was unexpected (Hello Request or Client Hello) or invalid (we
     * were actually expecting a CCS).
     */

    if (rr->type == SSL3_RT_HANDSHAKE && type == SSL3_RT_CHANGE_CIPHER_SPEC) {
        al = SSL_AD_UNEXPECTED_MESSAGE;
        SSLerr(SSL_F_SSL3_READ_BYTES, SSL_R_UNEXPECTED_MESSAGE);
        goto f_err;
    }

    /*
     * Lets just double check that we've not got an SSLv2 record
     */
@@ -1344,47 +1369,11 @@ int ssl3_read_bytes(SSL *s, int type, unsigned char *buf, int len, int peek)
    }

    if (SSL3_RECORD_get_type(rr) == SSL3_RT_CHANGE_CIPHER_SPEC) {
        /*
         * 'Change Cipher Spec' is just a single byte, so we know exactly
         * what the record payload has to look like
         */
        if ((SSL3_RECORD_get_length(rr) != 1)
            || (SSL3_RECORD_get_off(rr) != 0)
            || (SSL3_RECORD_get_data(rr)[0] != SSL3_MT_CCS)) {
            al = SSL_AD_ILLEGAL_PARAMETER;
            SSLerr(SSL_F_SSL3_READ_BYTES, SSL_R_BAD_CHANGE_CIPHER_SPEC);
            goto f_err;
        }

        /* Check we have a cipher to change to */
        if (s->s3->tmp.new_cipher == NULL) {
        al = SSL_AD_UNEXPECTED_MESSAGE;
        SSLerr(SSL_F_SSL3_READ_BYTES, SSL_R_CCS_RECEIVED_EARLY);
        goto f_err;
    }

        if (!(s->s3->flags & SSL3_FLAGS_CCS_OK)) {
            al = SSL_AD_UNEXPECTED_MESSAGE;
            SSLerr(SSL_F_SSL3_READ_BYTES, SSL_R_CCS_RECEIVED_EARLY);
            goto f_err;
        }

        s->s3->flags &= ~SSL3_FLAGS_CCS_OK;

        SSL3_RECORD_set_length(rr, 0);

        if (s->msg_callback)
            s->msg_callback(0, s->version, SSL3_RT_CHANGE_CIPHER_SPEC,
                            SSL3_RECORD_get_data(rr), 1, s,
                            s->msg_callback_arg);

        s->s3->change_cipher_spec = 1;
        if (!ssl3_do_change_cipher_spec(s))
            goto err;
        else
            goto start;
    }

    /*
     * Unexpected handshake message (Client Hello, or protocol violation)
     */
@@ -1477,7 +1466,6 @@ int ssl3_read_bytes(SSL *s, int type, unsigned char *buf, int len, int peek)

 f_err:
    ssl3_send_alert(s, SSL3_AL_FATAL, al);
 err:
    return (-1);
}

Loading