From 4b755cb0da7aace30815164ef2e13031707f6b7f Mon Sep 17 00:00:00 2001 From: David Benjamin Date: Fri, 12 Dec 2014 03:58:07 -0500 Subject: [PATCH] Implement the V2ClientHello sniff in version-locked methods. Tested manually by replacing SSLv23_method() with TLSv1_2_method() in bssl_shim. This is a large chunk of code which is not run in SSLv23_method(), but it will be run after unification. It's split out separately to ease review. Change-Id: I6bd241daca17aa0f9b3e36e51864a29755a41097 --- include/openssl/ssl.h | 2 + include/openssl/ssl3.h | 9 + ssl/s3_lib.c | 7 + ssl/s3_srvr.c | 365 ++++++++++++++++++++++++++++++++++++----- ssl/ssl_error.c | 2 + ssl/ssl_locl.h | 2 + 6 files changed, 346 insertions(+), 41 deletions(-) diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h index 1bc96040..e5d7a511 100644 --- a/include/openssl/ssl.h +++ b/include/openssl/ssl.h @@ -2444,6 +2444,8 @@ OPENSSL_EXPORT void ERR_load_SSL_strings(void); #define SSL_F_SSL_SESSION_to_bytes_full 292 #define SSL_F_SSL_accept 293 #define SSL_F_SSL_connect 294 +#define SSL_F_ssl3_get_v2_client_hello 295 +#define SSL_F_ssl3_get_initial_bytes 296 #define SSL_R_UNABLE_TO_FIND_ECDH_PARAMETERS 100 #define SSL_R_DECRYPTION_FAILED_OR_BAD_RECORD_MAC 101 #define SSL_R_INVALID_NULL_CMD_NAME 102 diff --git a/include/openssl/ssl3.h b/include/openssl/ssl3.h index 7c30d9b7..34c483e5 100644 --- a/include/openssl/ssl3.h +++ b/include/openssl/ssl3.h @@ -369,6 +369,12 @@ typedef struct ssl3_state_st * known. Otherwise the version has not been negotiated yet. */ char have_version; + /* sniff_buffer is used by the server in the initial handshake + * to read a V2ClientHello before the record layer is + * initialized. */ + BUF_MEM *sniff_buffer; + size_t sniff_buffer_len; + SSL3_BUFFER rbuf; /* read IO goes into here */ SSL3_BUFFER wbuf; /* write IO goes into here */ @@ -589,6 +595,8 @@ typedef struct ssl3_state_st /* extra state */ #define SSL3_ST_SW_FLUSH (0x100|SSL_ST_ACCEPT) /* read from client */ +#define SSL3_ST_SR_INITIAL_BYTES (0x240|SSL_ST_ACCEPT) +#define SSL3_ST_SR_V2_CLIENT_HELLO (0x241|SSL_ST_ACCEPT) /* Do not change the number values, they do matter */ #define SSL3_ST_SR_CLNT_HELLO_A (0x110|SSL_ST_ACCEPT) #define SSL3_ST_SR_CLNT_HELLO_B (0x111|SSL_ST_ACCEPT) @@ -624,6 +632,7 @@ typedef struct ssl3_state_st #define SSL3_ST_SR_CHANNEL_ID_B (0x231|SSL_ST_ACCEPT) #define SSL3_ST_SR_FINISHED_A (0x1C0|SSL_ST_ACCEPT) #define SSL3_ST_SR_FINISHED_B (0x1C1|SSL_ST_ACCEPT) + /* write to client */ #define SSL3_ST_SW_CHANGE_A (0x1D0|SSL_ST_ACCEPT) #define SSL3_ST_SW_CHANGE_B (0x1D1|SSL_ST_ACCEPT) diff --git a/ssl/s3_lib.c b/ssl/s3_lib.c index 8176b2a5..f4d47759 100644 --- a/ssl/s3_lib.c +++ b/ssl/s3_lib.c @@ -1011,6 +1011,9 @@ void ssl3_free(SSL *s) if(s == NULL) return; + if (s->s3->sniff_buffer != NULL) + BUF_MEM_free(s->s3->sniff_buffer); + ssl3_cleanup_key_block(s); if (s->s3->rbuf.buf != NULL) ssl3_release_read_buffer(s); @@ -1053,6 +1056,10 @@ void ssl3_clear(SSL *s) * ssl3_new. rbuf, wbuf, and init_extra are preserved, but * this may not serve anything more than saving a malloc. */ + if (s->s3->sniff_buffer != NULL) + BUF_MEM_free(s->s3->sniff_buffer); + s->s3->sniff_buffer = NULL; + ssl3_cleanup_key_block(s); if (s->s3->tmp.ca_names != NULL) sk_X509_NAME_pop_free(s->s3->tmp.ca_names,X509_NAME_free); diff --git a/ssl/s3_srvr.c b/ssl/s3_srvr.c index 1d3285d2..b320140f 100644 --- a/ssl/s3_srvr.c +++ b/ssl/s3_srvr.c @@ -172,6 +172,10 @@ #include "../crypto/internal.h" #include "../crypto/dh/internal.h" +/* INITIAL_SNIFF_BUFFER_SIZE is the number of bytes read in the initial sniff + * buffer. */ +#define INITIAL_SNIFF_BUFFER_SIZE 8 + int ssl3_accept(SSL *s) { BUF_MEM *buf = NULL; @@ -207,55 +211,33 @@ int ssl3_accept(SSL *s) switch (s->state) { case SSL_ST_RENEGOTIATE: - s->renegotiate=1; - /* s->state=SSL_ST_ACCEPT; */ + /* This state is the renegotiate entry point. It sends a + * HelloRequest and nothing else. */ + s->renegotiate = 1; - case SSL_ST_ACCEPT: - case SSL_ST_BEFORE|SSL_ST_ACCEPT: - - if (cb != NULL) cb(s,SSL_CB_HANDSHAKE_START,1); + if (cb != NULL) + cb(s, SSL_CB_HANDSHAKE_START, 1); if (s->init_buf == NULL) { - if ((buf=BUF_MEM_new()) == NULL) - { - ret= -1; - goto end; - } - if (!BUF_MEM_grow(buf,SSL3_RT_MAX_PLAIN_LENGTH)) + buf = BUF_MEM_new(); + if (!buf || !BUF_MEM_grow(buf, SSL3_RT_MAX_PLAIN_LENGTH)) { - ret= -1; + ret = -1; goto end; } - s->init_buf=buf; + s->init_buf = buf; buf = NULL; } + s->init_num = 0; if (!ssl3_setup_buffers(s)) { - ret= -1; + ret = -1; goto end; } - s->init_num=0; - - if (s->state != SSL_ST_RENEGOTIATE) - { - /* Ok, we now need to push on a buffering BIO so that - * the output is sent in a way that TCP likes :-) - */ - if (!ssl_init_wbio_buffer(s,1)) { ret= -1; goto end; } - - if (!ssl3_init_finished_mac(s)) - { - OPENSSL_PUT_ERROR(SSL, ssl3_accept, ERR_R_INTERNAL_ERROR); - ret = -1; - goto end; - } - s->state=SSL3_ST_SR_CLNT_HELLO_A; - s->ctx->stats.sess_accept++; - } - else if (!s->s3->send_connection_binding && + if (!s->s3->send_connection_binding && !(s->options & SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION)) { /* Server attempting to renegotiate with @@ -267,13 +249,9 @@ int ssl3_accept(SSL *s) ret = -1; goto end; } - else - { - /* s->state == SSL_ST_RENEGOTIATE, - * we will just send a HelloRequest */ - s->ctx->stats.sess_accept_renegotiate++; - s->state=SSL3_ST_SW_HELLO_REQ_A; - } + + s->ctx->stats.sess_accept_renegotiate++; + s->state = SSL3_ST_SW_HELLO_REQ_A; break; case SSL3_ST_SW_HELLO_REQ_A: @@ -298,6 +276,75 @@ int ssl3_accept(SSL *s) s->state=SSL_ST_OK; break; + case SSL_ST_ACCEPT: + case SSL_ST_BEFORE|SSL_ST_ACCEPT: + /* This state is the entry point for the handshake + * itself (initial and renegotiation). */ + if (cb != NULL) + cb(s, SSL_CB_HANDSHAKE_START, 1); + + if (s->init_buf == NULL) + { + buf = BUF_MEM_new(); + if (!buf || !BUF_MEM_grow(buf, SSL3_RT_MAX_PLAIN_LENGTH)) + { + ret = -1; + goto end; + } + s->init_buf = buf; + buf = NULL; + } + s->init_num = 0; + + if (!ssl3_init_finished_mac(s)) + { + OPENSSL_PUT_ERROR(SSL, ssl3_accept, ERR_R_INTERNAL_ERROR); + ret = -1; + goto end; + } + + if (!s->s3->have_version) + { + /* This is the initial handshake. The record + * layer has not been initialized yet. Sniff for + * a V2ClientHello before reading a ClientHello + * normally. */ + assert(s->s3->rbuf.buf == NULL); + assert(s->s3->wbuf.buf == NULL); + s->state = SSL3_ST_SR_INITIAL_BYTES; + } + else + { + /* Enable a write buffer. This groups handshake + * messages within a flight into a single + * write. */ + if (!ssl3_setup_buffers(s) || + !ssl_init_wbio_buffer(s, 1)) + { + ret = -1; + goto end; + } + s->state = SSL3_ST_SR_CLNT_HELLO_A; + } + s->ctx->stats.sess_accept++; + break; + + case SSL3_ST_SR_INITIAL_BYTES: + ret = ssl3_get_initial_bytes(s); + if (ret <= 0) + goto end; + /* ssl3_get_initial_bytes sets s->state to one of + * SSL3_ST_SR_V2_CLIENT_HELLO or SSL3_ST_SR_CLNT_HELLO_A + * on success. */ + break; + + case SSL3_ST_SR_V2_CLIENT_HELLO: + ret = ssl3_get_v2_client_hello(s); + if (ret <= 0) + goto end; + s->state = SSL3_ST_SR_CLNT_HELLO_A; + break; + case SSL3_ST_SR_CLNT_HELLO_A: case SSL3_ST_SR_CLNT_HELLO_B: case SSL3_ST_SR_CLNT_HELLO_C: @@ -674,6 +721,242 @@ end: return(ret); } +static int ssl3_read_sniff_buffer(SSL *s, size_t n) + { + if (s->s3->sniff_buffer == NULL) + { + s->s3->sniff_buffer = BUF_MEM_new(); + } + if (s->s3->sniff_buffer == NULL || + !BUF_MEM_grow(s->s3->sniff_buffer, n)) + { + return -1; + } + + while (s->s3->sniff_buffer_len < n) + { + int ret; + + s->rwstate = SSL_READING; + ret = BIO_read(s->rbio, + s->s3->sniff_buffer->data + s->s3->sniff_buffer_len, + n - s->s3->sniff_buffer_len); + if (ret <= 0) + return ret; + s->rwstate = SSL_NOTHING; + s->s3->sniff_buffer_len += ret; + } + return 1; + } + +int ssl3_get_initial_bytes(SSL *s) + { + int ret; + const uint8_t *p; + + /* Read the first 8 bytes. To recognize a ClientHello or V2ClientHello + * only needs the first 6 bytes, but 8 is needed to recognize CONNECT + * below. */ + ret = ssl3_read_sniff_buffer(s, INITIAL_SNIFF_BUFFER_SIZE); + if (ret <= 0) + return ret; + assert(s->s3->sniff_buffer_len >= INITIAL_SNIFF_BUFFER_SIZE); + p = (const uint8_t *)s->s3->sniff_buffer->data; + + /* Some dedicated error codes for protocol mixups should the application + * wish to interpret them differently. (These do not overlap with + * ClientHello or V2ClientHello.) */ + if (strncmp("GET ", (const char *)p, 4) == 0 || + strncmp("POST ", (const char *)p, 5) == 0 || + strncmp("HEAD ", (const char *)p, 5) == 0 || + strncmp("PUT ", (const char *)p, 4) == 0) + { + OPENSSL_PUT_ERROR(SSL, ssl3_get_initial_bytes, SSL_R_HTTP_REQUEST); + return -1; + } + if (strncmp("CONNECT ", (const char *)p, 8) == 0) + { + OPENSSL_PUT_ERROR(SSL, ssl3_get_initial_bytes, SSL_R_HTTPS_PROXY_REQUEST); + return -1; + } + + /* Determine if this is a ClientHello or V2ClientHello. */ + + if (p[0] & 0x80 && p[2] == SSL2_MT_CLIENT_HELLO && + p[3] >= SSL3_VERSION_MAJOR) + { + /* This is a V2ClientHello. */ + s->state = SSL3_ST_SR_V2_CLIENT_HELLO; + return 1; + } + if (p[0] == SSL3_RT_HANDSHAKE && p[1] >= SSL3_VERSION_MAJOR && + p[5] == SSL3_MT_CLIENT_HELLO) + { + /* This is a ClientHello. Initialize the record layer with the + * already consumed data and continue the handshake. */ + if (!ssl3_setup_buffers(s) || !ssl_init_wbio_buffer(s, 1)) + { + return -1; + } + assert(s->rstate == SSL_ST_READ_HEADER); + memcpy(s->s3->rbuf.buf, p, s->s3->sniff_buffer_len); + s->s3->rbuf.offset = 0; + s->s3->rbuf.left = s->s3->sniff_buffer_len; + s->packet_length = 0; + + BUF_MEM_free(s->s3->sniff_buffer); + s->s3->sniff_buffer = NULL; + s->s3->sniff_buffer_len = 0; + + s->state = SSL3_ST_SR_CLNT_HELLO_A; + return 1; + } + + OPENSSL_PUT_ERROR(SSL, ssl3_get_initial_bytes, SSL_R_UNKNOWN_PROTOCOL); + return -1; + } + +int ssl3_get_v2_client_hello(SSL *s) + { + const uint8_t *p; + int ret; + CBS v2_client_hello, cipher_specs, session_id, challenge; + size_t msg_length, rand_len, len; + uint8_t msg_type; + uint16_t version, cipher_spec_length, session_id_length, challenge_length; + CBB client_hello, hello_body, cipher_suites; + uint8_t random[SSL3_RANDOM_SIZE]; + + /* Read the remainder of the V2ClientHello. We have previously read 8 + * bytes in ssl3_get_initial_bytes. */ + assert(s->s3->sniff_buffer_len >= INITIAL_SNIFF_BUFFER_SIZE); + p = (const uint8_t *)s->s3->sniff_buffer->data; + msg_length = ((p[0] & 0x7f) << 8) | p[1]; + if (msg_length > (1024 * 4)) + { + OPENSSL_PUT_ERROR(SSL, ssl3_get_v2_client_hello, SSL_R_RECORD_TOO_LARGE); + return -1; + } + if (msg_length < INITIAL_SNIFF_BUFFER_SIZE - 2) + { + /* Reject lengths that are too short early. We have already read + * 8 bytes, so we should not attempt to process an (invalid) + * V2ClientHello which would be shorter than that. */ + OPENSSL_PUT_ERROR(SSL, ssl3_get_v2_client_hello, SSL_R_RECORD_LENGTH_MISMATCH); + return -1; + } + + ret = ssl3_read_sniff_buffer(s, msg_length + 2); + if (ret <= 0) + return ret; + assert(s->s3->sniff_buffer_len == msg_length + 2); + CBS_init(&v2_client_hello, + (const uint8_t *)s->s3->sniff_buffer->data + 2, msg_length); + + /* The V2ClientHello without the length is incorporated into the + * Finished hash. */ + ssl3_finish_mac(s, CBS_data(&v2_client_hello), CBS_len(&v2_client_hello)); + if (s->msg_callback) + s->msg_callback(0, SSL2_VERSION, 0, CBS_data(&v2_client_hello), + CBS_len(&v2_client_hello), s, s->msg_callback_arg); + + if (!CBS_get_u8(&v2_client_hello, &msg_type) || + !CBS_get_u16(&v2_client_hello, &version) || + !CBS_get_u16(&v2_client_hello, &cipher_spec_length) || + !CBS_get_u16(&v2_client_hello, &session_id_length) || + !CBS_get_u16(&v2_client_hello, &challenge_length) || + !CBS_get_bytes(&v2_client_hello, &cipher_specs, cipher_spec_length) || + !CBS_get_bytes(&v2_client_hello, &session_id, session_id_length) || + !CBS_get_bytes(&v2_client_hello, &challenge, challenge_length) || + CBS_len(&v2_client_hello) != 0) + { + OPENSSL_PUT_ERROR(SSL, ssl3_get_v2_client_hello, SSL_R_DECODE_ERROR); + return -1; + } + + /* msg_type has already been checked. */ + assert(msg_type == SSL2_MT_CLIENT_HELLO); + + /* The client_random is the V2ClientHello challenge. Truncate or + * left-pad with zeros as needed. */ + memset(random, 0, SSL3_RANDOM_SIZE); + rand_len = CBS_len(&challenge); + if (rand_len > SSL3_RANDOM_SIZE) + rand_len = SSL3_RANDOM_SIZE; + memcpy(random + (SSL3_RANDOM_SIZE - rand_len), CBS_data(&challenge), rand_len); + + /* Write out an equivalent SSLv3 ClientHello. */ + if (!CBB_init_fixed(&client_hello, (uint8_t *)s->init_buf->data, s->init_buf->max)) + { + OPENSSL_PUT_ERROR(SSL, ssl3_get_v2_client_hello, ERR_R_MALLOC_FAILURE); + return -1; + } + if (!CBB_add_u8(&client_hello, SSL3_MT_CLIENT_HELLO) || + !CBB_add_u24_length_prefixed(&client_hello, &hello_body) || + !CBB_add_u16(&hello_body, version) || + !CBB_add_bytes(&hello_body, random, SSL3_RANDOM_SIZE) || + /* No session id. */ + !CBB_add_u8(&hello_body, 0) || + !CBB_add_u16_length_prefixed(&hello_body, &cipher_suites)) + { + CBB_cleanup(&client_hello); + OPENSSL_PUT_ERROR(SSL, ssl3_get_v2_client_hello, ERR_R_INTERNAL_ERROR); + return -1; + } + + /* Copy the cipher suites. */ + while (CBS_len(&cipher_specs) > 0) + { + uint32_t cipher_spec; + if (!CBS_get_u24(&cipher_specs, &cipher_spec)) + { + CBB_cleanup(&client_hello); + OPENSSL_PUT_ERROR(SSL, ssl3_get_v2_client_hello, SSL_R_DECODE_ERROR); + return -1; + } + + /* Skip SSLv2 ciphers. */ + if ((cipher_spec & 0xff0000) != 0) + continue; + if (!CBB_add_u16(&cipher_suites, cipher_spec)) + { + CBB_cleanup(&client_hello); + OPENSSL_PUT_ERROR(SSL, ssl3_get_v2_client_hello, ERR_R_INTERNAL_ERROR); + return -1; + } + } + + /* Add the null compression scheme and finish. */ + if (!CBB_add_u8(&hello_body, 1) || + !CBB_add_u8(&hello_body, 0) || + !CBB_finish(&client_hello, NULL, &len)) + { + CBB_cleanup(&client_hello); + OPENSSL_PUT_ERROR(SSL, ssl3_get_v2_client_hello, ERR_R_INTERNAL_ERROR); + return -1; + } + + /* Mark the message for "re"-use by the version-specific + * method. */ + s->s3->tmp.reuse_message = 1; + s->s3->tmp.message_type = SSL3_MT_CLIENT_HELLO; + /* The handshake message header is 4 bytes. */ + s->s3->tmp.message_size = len - 4; + + /* Initialize the record layer. */ + if (!ssl3_setup_buffers(s) || !ssl_init_wbio_buffer(s, 1)) + { + return -1; + } + + /* Drop the sniff buffer. */ + BUF_MEM_free(s->s3->sniff_buffer); + s->s3->sniff_buffer = NULL; + s->s3->sniff_buffer_len = 0; + + return 1; + } + int ssl3_send_hello_request(SSL *s) { diff --git a/ssl/ssl_error.c b/ssl/ssl_error.c index 4d09ee4b..f9be633b 100644 --- a/ssl/ssl_error.c +++ b/ssl/ssl_error.c @@ -126,6 +126,7 @@ const ERR_STRING_DATA SSL_error_string_data[] = { {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_get_client_hello, 0), "ssl3_get_client_hello"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_get_client_key_exchange, 0), "ssl3_get_client_key_exchange"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_get_finished, 0), "ssl3_get_finished"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_get_initial_bytes, 0), "ssl3_get_initial_bytes"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_get_message, 0), "ssl3_get_message"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_get_new_session_ticket, 0), "ssl3_get_new_session_ticket"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_get_next_proto, 0), "ssl3_get_next_proto"}, @@ -134,6 +135,7 @@ const ERR_STRING_DATA SSL_error_string_data[] = { {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_get_server_done, 0), "ssl3_get_server_done"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_get_server_hello, 0), "ssl3_get_server_hello"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_get_server_key_exchange, 0), "ssl3_get_server_key_exchange"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_get_v2_client_hello, 0), "ssl3_get_v2_client_hello"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_handshake_mac, 0), "ssl3_handshake_mac"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_read_bytes, 0), "ssl3_read_bytes"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_read_n, 0), "ssl3_read_n"}, diff --git a/ssl/ssl_locl.h b/ssl/ssl_locl.h index fdc06f79..7ca8e143 100644 --- a/ssl/ssl_locl.h +++ b/ssl/ssl_locl.h @@ -945,6 +945,8 @@ int ssl3_send_channel_id(SSL *s); int dtls1_client_hello(SSL *s); /* some server-only functions */ +int ssl3_get_initial_bytes(SSL *s); +int ssl3_get_v2_client_hello(SSL *s); int ssl3_get_client_hello(SSL *s); int ssl3_send_server_hello(SSL *s); int ssl3_send_hello_request(SSL *s);