diff options
author | David Benjamin <davidben@chromium.org> | 2014-12-12 11:58:07 +0300 |
---|---|---|
committer | Adam Langley <agl@google.com> | 2014-12-14 02:22:21 +0300 |
commit | 4b755cb0da7aace30815164ef2e13031707f6b7f (patch) | |
tree | f5fee642369f116ad2d576bc8efe75c929d0e814 /ssl/s3_srvr.c | |
parent | 63c55a8e3527b60f7694e656c8aa1fa081100193 (diff) |
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
Diffstat (limited to 'ssl/s3_srvr.c')
-rw-r--r-- | ssl/s3_srvr.c | 365 |
1 files changed, 324 insertions, 41 deletions
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) { |