diff --git a/crypto/bytestring/cbb.c b/crypto/bytestring/cbb.c index 41770dfb0..6767b9dbe 100644 --- a/crypto/bytestring/cbb.c +++ b/crypto/bytestring/cbb.c @@ -138,8 +138,17 @@ int CBB_finish(CBB *cbb, uint8_t **out_data, size_t *out_len) { return 0; } - *out_data = cbb->base->buf; - *out_len = cbb->base->len; + if (cbb->base->can_resize && (out_data == NULL || out_len == NULL)) { + /* |out_data| and |out_len| can only be NULL if the CBB is fixed. */ + return 0; + } + + if (out_data != NULL) { + *out_data = cbb->base->buf; + } + if (out_len != NULL) { + *out_len = cbb->base->len; + } cbb->base->buf = NULL; CBB_cleanup(cbb); return 1; diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h index d1734a8a2..c042256a9 100644 --- a/include/openssl/ssl.h +++ b/include/openssl/ssl.h @@ -2496,6 +2496,7 @@ OPENSSL_EXPORT void ERR_load_SSL_strings(void); #define SSL_F_tls1_aead_ctx_init 280 #define SSL_F_tls1_check_duplicate_extensions 281 #define SSL_F_ssl3_expect_change_cipher_spec 282 +#define SSL_F_ssl23_get_v2_client_hello 283 #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/ssl23.h b/include/openssl/ssl23.h index d3228983c..0095937dd 100644 --- a/include/openssl/ssl23.h +++ b/include/openssl/ssl23.h @@ -73,8 +73,9 @@ extern "C" { /* server */ /* read from client */ -#define SSL23_ST_SR_CLNT_HELLO_A (0x210|SSL_ST_ACCEPT) -#define SSL23_ST_SR_CLNT_HELLO_B (0x211|SSL_ST_ACCEPT) +#define SSL23_ST_SR_CLNT_HELLO (0x210|SSL_ST_ACCEPT) +#define SSL23_ST_SR_V2_CLNT_HELLO (0x220|SSL_ST_ACCEPT) +#define SSL23_ST_SR_SWITCH_VERSION (0x230|SSL_ST_ACCEPT) #ifdef __cplusplus } diff --git a/ssl/s23_srvr.c b/ssl/s23_srvr.c index ecf999d88..f46e4e2e5 100644 --- a/ssl/s23_srvr.c +++ b/ssl/s23_srvr.c @@ -106,6 +106,7 @@ * (eay@cryptsoft.com). This product includes software written by Tim * Hudson (tjh@cryptsoft.com). */ +#include #include #include @@ -118,7 +119,9 @@ #include "ssl_locl.h" static const SSL_METHOD *ssl23_get_server_method(int ver); -int ssl23_get_client_hello(SSL *s); +static int ssl23_get_client_hello(SSL *s); +static int ssl23_get_v2_client_hello(SSL *s); + static const SSL_METHOD *ssl23_get_server_method(int ver) { if (ver == SSL3_VERSION) @@ -190,17 +193,38 @@ int ssl23_accept(SSL *s) ssl3_init_finished_mac(s); - s->state=SSL23_ST_SR_CLNT_HELLO_A; + s->state=SSL23_ST_SR_CLNT_HELLO; s->ctx->stats.sess_accept++; s->init_num=0; break; - case SSL23_ST_SR_CLNT_HELLO_A: - case SSL23_ST_SR_CLNT_HELLO_B: + case SSL23_ST_SR_CLNT_HELLO: + s->shutdown = 0; + ret = ssl23_get_client_hello(s); + if (ret <= 0) goto end; + break; - s->shutdown=0; - ret=ssl23_get_client_hello(s); - if (ret >= 0) cb=NULL; + case SSL23_ST_SR_V2_CLNT_HELLO: + ret = ssl23_get_v2_client_hello(s); + if (ret <= 0) goto end; + break; + + case SSL23_ST_SR_SWITCH_VERSION: + if (!ssl_init_wbio_buffer(s, 1)) + { + ret = -1; + goto end; + } + + s->state = SSL3_ST_SR_CLNT_HELLO_A; + s->method = ssl23_get_server_method(s->version); + assert(s->method != NULL); + s->handshake_func = s->method->ssl_accept; + s->init_num = 0; + + /* NULL the callback; SSL_accept will call it instead. */ + cb = NULL; + ret = SSL_accept(s); goto end; /* break; */ @@ -226,191 +250,137 @@ end: return(ret); } - -int ssl23_get_client_hello(SSL *s) +/* ssl23_get_mutual_version determines the highest supported version for a + * client which reports a highest version of |client_version|. On success, it + * returns 1 and sets |*out_version| to the negotiated version. Otherwise, it + * returns 0. */ +static int ssl23_get_mutual_version(SSL *s, int *out_version, uint16_t client_version) { - char buf_space[11]; /* Request this many bytes in initial read. - * We can detect SSL 3.0/TLS 1.0 Client Hellos - * ('type == 3') correctly only when the following - * is in a single record, which is not guaranteed by - * the protocol specification: - * Byte Content - * 0 type \ - * 1/2 version > record header - * 3/4 length / - * 5 msg_type \ - * 6-8 length > Client Hello message - * 9/10 client_version / - */ - char *buf= &(buf_space[0]); - unsigned char *p,*d,*d_len,*dd; - unsigned int i; - unsigned int csl,sil,cl; - int n=0,j; - int type=0; - int v[2]; - - if (s->state == SSL23_ST_SR_CLNT_HELLO_A) + if (client_version >= TLS1_2_VERSION && !(s->options & SSL_OP_NO_TLSv1_2)) { - /* read the initial header */ - v[0]=v[1]=0; + *out_version = TLS1_2_VERSION; + return 1; + } + if (client_version >= TLS1_1_VERSION && !(s->options & SSL_OP_NO_TLSv1_1)) + { + *out_version = TLS1_1_VERSION; + return 1; + } + if (client_version >= TLS1_VERSION && !(s->options & SSL_OP_NO_TLSv1)) + { + *out_version = TLS1_VERSION; + return 1; + } + if (client_version >= SSL3_VERSION && !(s->options & SSL_OP_NO_SSLv3)) + { + *out_version = SSL3_VERSION; + return 1; + } + return 0; + } - if (!ssl3_setup_buffers(s)) goto err; +static int ssl23_get_client_hello(SSL *s) + { + uint8_t *p; + int n = 0; - n=ssl23_read_bytes(s, sizeof buf_space); - if (n != sizeof buf_space) return(n); /* n == -1 || n == 0 */ + /* Sniff enough of the input to determine ClientHello type and the + * client version. */ + if (!ssl3_setup_buffers(s)) goto err; - p=s->packet; + /* Read the initial 11 bytes of the input. This is sufficient to + * determine the client version for a ClientHello or a + * V2ClientHello. + * + * ClientHello (assuming client_version is unfragmented): + * Byte Content + * 0 type \ + * 1-2 version > record header + * 3-4 length / + * 5 msg_type \ + * 6-8 length > Client Hello message + * 9-10 client_version / + * + * V2ClientHello: + * Byte Content + * 0-1 msg_length + * 2 msg_type + * 3-4 version + * 5-6 cipher_spec_length + * 7-8 session_id_length + * 9-10 challenge_length + */ + n = ssl23_read_bytes(s, 11); + if (n <= 0) + return n; + assert(n == 11); - memcpy(buf,p,n); + p = s->packet; - if ((p[0] & 0x80) && (p[2] == SSL2_MT_CLIENT_HELLO)) + /* 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 ", (char *)p, 4) == 0) || + (strncmp("POST ",(char *)p, 5) == 0) || + (strncmp("HEAD ",(char *)p, 5) == 0) || + (strncmp("PUT ", (char *)p, 4) == 0)) + { + OPENSSL_PUT_ERROR(SSL, ssl23_get_client_hello, SSL_R_HTTP_REQUEST); + goto err; + } + if (strncmp("CONNECT",(char *)p, 7) == 0) + { + OPENSSL_PUT_ERROR(SSL, ssl23_get_client_hello, SSL_R_HTTPS_PROXY_REQUEST); + goto err; + } + + /* Determine if this is a ClientHello or V2ClientHello. */ + if ((p[0] & 0x80) && (p[2] == SSL2_MT_CLIENT_HELLO)) + { + /* This is a V2ClientHello. Determine the version to + * use. */ + uint16_t client_version = (p[3] << 8) | p[4]; + if (!ssl23_get_mutual_version(s, &s->version, client_version)) { - /* - * SSLv2 header - */ - if ((p[3] == 0x00) && (p[4] == 0x02)) - { - v[0]=p[3]; v[1]=p[4]; - /* SSLv2 */ - if (!(s->options & SSL_OP_NO_SSLv2)) - type=1; - } - else if (p[3] == SSL3_VERSION_MAJOR) - { - v[0]=p[3]; v[1]=p[4]; - /* SSLv3/TLSv1 */ - if (p[4] >= TLS1_VERSION_MINOR) - { - if (p[4] >= TLS1_2_VERSION_MINOR && - !(s->options & SSL_OP_NO_TLSv1_2)) - { - s->version=TLS1_2_VERSION; - s->state=SSL23_ST_SR_CLNT_HELLO_B; - } - else if (p[4] >= TLS1_1_VERSION_MINOR && - !(s->options & SSL_OP_NO_TLSv1_1)) - { - s->version=TLS1_1_VERSION; - /* type=2; */ /* done later to survive restarts */ - s->state=SSL23_ST_SR_CLNT_HELLO_B; - } - else if (!(s->options & SSL_OP_NO_TLSv1)) - { - s->version=TLS1_VERSION; - /* type=2; */ /* done later to survive restarts */ - s->state=SSL23_ST_SR_CLNT_HELLO_B; - } - else if (!(s->options & SSL_OP_NO_SSLv3)) - { - s->version=SSL3_VERSION; - /* type=2; */ - s->state=SSL23_ST_SR_CLNT_HELLO_B; - } - else if (!(s->options & SSL_OP_NO_SSLv2)) - { - type=1; - } - } - else if (!(s->options & SSL_OP_NO_SSLv3)) - { - s->version=SSL3_VERSION; - /* type=2; */ - s->state=SSL23_ST_SR_CLNT_HELLO_B; - } - else if (!(s->options & SSL_OP_NO_SSLv2)) - type=1; - - } - } - else if ((p[0] == SSL3_RT_HANDSHAKE) && - (p[1] == SSL3_VERSION_MAJOR) && - (p[5] == SSL3_MT_CLIENT_HELLO) && - ((p[3] == 0 && p[4] < 5 /* silly record length? */) - || (p[9] >= p[1]))) - { - /* - * SSLv3 or tls1 header - */ - - v[0]=p[1]; /* major version (= SSL3_VERSION_MAJOR) */ - /* We must look at client_version inside the Client Hello message - * to get the correct minor version. - * However if we have only a pathologically small fragment of the - * Client Hello message, this would be difficult, and we'd have - * to read more records to find out. - * No known SSL 3.0 client fragments ClientHello like this, - * so we simply reject such connections to avoid - * protocol version downgrade attacks. */ - if (p[3] == 0 && p[4] < 6) - { - OPENSSL_PUT_ERROR(SSL, ssl23_get_client_hello, SSL_R_RECORD_TOO_SMALL); - goto err; - } - /* if major version number > 3 set minor to a value - * which will use the highest version 3 we support. - * If TLS 2.0 ever appears we will need to revise - * this.... - */ - if (p[9] > SSL3_VERSION_MAJOR) - v[1]=0xff; - else - v[1]=p[10]; /* minor version according to client_version */ - if (v[1] >= TLS1_VERSION_MINOR) - { - if (v[1] >= TLS1_2_VERSION_MINOR && - !(s->options & SSL_OP_NO_TLSv1_2)) - { - s->version=TLS1_2_VERSION; - type=3; - } - else if (v[1] >= TLS1_1_VERSION_MINOR && - !(s->options & SSL_OP_NO_TLSv1_1)) - { - s->version=TLS1_1_VERSION; - type=3; - } - else if (!(s->options & SSL_OP_NO_TLSv1)) - { - s->version=TLS1_VERSION; - type=3; - } - else if (!(s->options & SSL_OP_NO_SSLv3)) - { - s->version=SSL3_VERSION; - type=3; - } - } - else - { - /* client requests SSL 3.0 */ - if (!(s->options & SSL_OP_NO_SSLv3)) - { - s->version=SSL3_VERSION; - type=3; - } - else if (!(s->options & SSL_OP_NO_TLSv1)) - { - /* we won't be able to use TLS of course, - * but this will send an appropriate alert */ - s->version=TLS1_VERSION; - type=3; - } - } - } - else if ((strncmp("GET ", (char *)p,4) == 0) || - (strncmp("POST ",(char *)p,5) == 0) || - (strncmp("HEAD ",(char *)p,5) == 0) || - (strncmp("PUT ", (char *)p,4) == 0)) - { - OPENSSL_PUT_ERROR(SSL, ssl23_get_client_hello, SSL_R_HTTP_REQUEST); + OPENSSL_PUT_ERROR(SSL, ssl23_get_client_hello, SSL_R_UNSUPPORTED_PROTOCOL); goto err; } - else if (strncmp("CONNECT",(char *)p,7) == 0) + /* Parse the entire V2ClientHello. */ + s->state = SSL23_ST_SR_V2_CLNT_HELLO; + } + else if ((p[0] == SSL3_RT_HANDSHAKE) && + (p[1] >= SSL3_VERSION_MAJOR) && + (p[5] == SSL3_MT_CLIENT_HELLO)) + { + /* This is a fragment of a ClientHello. We look at the + * client_hello to negotiate the version. However, this + * is difficult if we have only a pathologically small + * fragment. No known client fragments ClientHello like + * this, so we simply reject such connections to avoid + * protocol version downgrade attacks. */ + uint16_t record_length = (p[3] << 8) | p[4]; + uint16_t client_version; + if (record_length < 6) { - OPENSSL_PUT_ERROR(SSL, ssl23_get_client_hello, SSL_R_HTTPS_PROXY_REQUEST); + OPENSSL_PUT_ERROR(SSL, ssl23_get_client_hello, SSL_R_RECORD_TOO_SMALL); goto err; } + + client_version = (p[9] << 8) | p[10]; + if (!ssl23_get_mutual_version(s, &s->version, client_version)) + { + OPENSSL_PUT_ERROR(SSL, ssl23_get_client_hello, SSL_R_UNSUPPORTED_PROTOCOL); + goto err; + } + + /* Reset the record-layer state for SSL3. */ + assert(s->rstate == SSL_ST_READ_HEADER); + s->s3->rbuf.left = s->packet_length; + s->s3->rbuf.offset = 0; + s->packet_length = 0; + + /* Ready to switch versions. */ + s->state = SSL23_ST_SR_SWITCH_VERSION; } if (s->version < TLS1_2_VERSION && tls1_suiteb(s)) @@ -419,183 +389,142 @@ int ssl23_get_client_hello(SSL *s) goto err; } - if (s->state == SSL23_ST_SR_CLNT_HELLO_B) - { - /* we have SSLv3/TLSv1 in an SSLv2 header - * (other cases skip this state) */ - - type=2; - p=s->packet; - v[0] = p[3]; /* == SSL3_VERSION_MAJOR */ - v[1] = p[4]; - - /* An SSLv3/TLSv1 backwards-compatible CLIENT-HELLO in an SSLv2 - * header is sent directly on the wire, not wrapped as a TLS - * record. It's format is: - * Byte Content - * 0-1 msg_length - * 2 msg_type - * 3-4 version - * 5-6 cipher_spec_length - * 7-8 session_id_length - * 9-10 challenge_length - * ... ... - */ - n=((p[0]&0x7f)<<8)|p[1]; - if (n > (1024*4)) - { - OPENSSL_PUT_ERROR(SSL, ssl23_get_client_hello, SSL_R_RECORD_TOO_LARGE); - goto err; - } - if (n < 9) - { - OPENSSL_PUT_ERROR(SSL, ssl23_get_client_hello, SSL_R_RECORD_LENGTH_MISMATCH); - goto err; - } - - j=ssl23_read_bytes(s,n+2); - /* We previously read 11 bytes, so if j > 0, we must have - * j == n+2 == s->packet_length. We have at least 11 valid - * packet bytes. */ - if (j <= 0) return(j); - - ssl3_finish_mac(s, s->packet+2, s->packet_length-2); - if (s->msg_callback) - s->msg_callback(0, SSL2_VERSION, 0, s->packet+2, s->packet_length-2, s, s->msg_callback_arg); /* CLIENT-HELLO */ - - p=s->packet; - p+=5; - n2s(p,csl); - n2s(p,sil); - n2s(p,cl); - d=(unsigned char *)s->init_buf->data; - if ((csl+sil+cl+11) != s->packet_length) /* We can't have TLS extensions in SSL 2.0 format - * Client Hello, can we? Error condition should be - * '>' otherweise */ - { - OPENSSL_PUT_ERROR(SSL, ssl23_get_client_hello, SSL_R_RECORD_LENGTH_MISMATCH); - goto err; - } - - /* record header: msg_type ... */ - *(d++) = SSL3_MT_CLIENT_HELLO; - /* ... and length (actual value will be written later) */ - d_len = d; - d += 3; - - /* client_version */ - *(d++) = SSL3_VERSION_MAJOR; /* == v[0] */ - *(d++) = v[1]; - - /* lets populate the random area */ - /* get the challenge_length */ - i=(cl > SSL3_RANDOM_SIZE)?SSL3_RANDOM_SIZE:cl; - memset(d,0,SSL3_RANDOM_SIZE); - memcpy(&(d[SSL3_RANDOM_SIZE-i]),&(p[csl+sil]),i); - d+=SSL3_RANDOM_SIZE; - - /* no session-id reuse */ - *(d++)=0; - - /* ciphers */ - j=0; - dd=d; - d+=2; - for (i=0; ipacket+s->packet_length) - { - *(d++)=*(p++); - } -#endif - - i = (d-(unsigned char *)s->init_buf->data) - 4; - l2n3((long)i, d_len); - - /* get the data reused from the init_buf */ - s->s3->tmp.reuse_message=1; - s->s3->tmp.message_type=SSL3_MT_CLIENT_HELLO; - s->s3->tmp.message_size=i; - } - - /* imaginary new state (for program structure): */ - /* s->state = SSL23_SR_CLNT_HELLO_C */ - - if (type == 1) - { - OPENSSL_PUT_ERROR(SSL, ssl23_get_client_hello, SSL_R_UNSUPPORTED_PROTOCOL); - goto err; - } - - if ((type == 2) || (type == 3)) - { - /* we have SSLv3/TLSv1 (type 2: SSL2 style, type 3: SSL3/TLS style) */ - - if (!ssl_init_wbio_buffer(s,1)) goto err; - - /* we are in this state */ - s->state=SSL3_ST_SR_CLNT_HELLO_A; - - if (type == 3) - { - /* put the 'n' bytes we have read into the input buffer - * for SSLv3 */ - s->rstate=SSL_ST_READ_HEADER; - s->packet_length=n; - if (s->s3->rbuf.buf == NULL) - if (!ssl3_setup_read_buffer(s)) - goto err; - - s->packet= &(s->s3->rbuf.buf[0]); - memcpy(s->packet,buf,n); - s->s3->rbuf.left=n; - s->s3->rbuf.offset=0; - } - else - { - s->packet_length=0; - s->s3->rbuf.left=0; - s->s3->rbuf.offset=0; - } - if (s->version == TLS1_2_VERSION) - s->method = TLSv1_2_server_method(); - else if (s->version == TLS1_1_VERSION) - s->method = TLSv1_1_server_method(); - else if (s->version == TLS1_VERSION) - s->method = TLSv1_server_method(); - else - s->method = SSLv3_server_method(); -#if 0 /* ssl3_get_client_hello does this */ - s->client_version=(v[0]<<8)|v[1]; -#endif - s->handshake_func=s->method->ssl_accept; - } - - if ((type < 1) || (type > 3)) - { - /* bad, very bad */ - OPENSSL_PUT_ERROR(SSL, ssl23_get_client_hello, SSL_R_UNKNOWN_PROTOCOL); - goto err; - } - s->init_num=0; - - if (buf != buf_space) OPENSSL_free(buf); - return(SSL_accept(s)); + return 1; err: - if (buf != buf_space) OPENSSL_free(buf); - return(-1); + return -1; + } + +static int ssl23_get_v2_client_hello(SSL *s) + { + uint8_t *p; + size_t i; + int n = 0; + + CBS v2_client_hello, cipher_specs, session_id, challenge; + size_t msg_length, 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 11 + * bytes in ssl23_get_client_hello. */ + p = s->packet; + msg_length = ((p[0] & 0x7f) << 8) | p[1]; + if (msg_length > (1024 * 4)) + { + OPENSSL_PUT_ERROR(SSL, ssl23_get_v2_client_hello, SSL_R_RECORD_TOO_LARGE); + goto err; + } + if (msg_length < 11 - 2) + { + /* Reject lengths that are too short early. We have already read + * 11 bytes, so we should not attempt to process an (invalid) + * V2ClientHello which would be shorter than that. */ + OPENSSL_PUT_ERROR(SSL, ssl23_get_v2_client_hello, SSL_R_RECORD_LENGTH_MISMATCH); + goto err; + } + n = ssl23_read_bytes(s, msg_length + 2); + if (n <= 0) + return n; + assert(n == s->packet_length); + + /* The V2ClientHello without the length is incorporated into the + * Finished hash. */ + ssl3_finish_mac(s, s->packet + 2, s->packet_length - 2); + if (s->msg_callback) + s->msg_callback(0, SSL2_VERSION, 0, s->packet+2, s->packet_length-2, s, s->msg_callback_arg); /* CLIENT-HELLO */ + + CBS_init(&v2_client_hello, s->packet + 2, s->packet_length - 2); + 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, ssl23_get_v2_client_hello, SSL_R_DECODE_ERROR); + goto err; + } + + /* 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); + i = (CBS_len(&challenge) > SSL3_RANDOM_SIZE) ? SSL3_RANDOM_SIZE : CBS_len(&challenge); + memcpy(random, CBS_data(&challenge), i); + + /* 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, ssl23_get_v2_client_hello, ERR_R_MALLOC_FAILURE); + goto err; + } + 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, ssl23_get_v2_client_hello, ERR_R_INTERNAL_ERROR); + goto err; + } + + /* 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, ssl23_get_v2_client_hello, SSL_R_DECODE_ERROR); + goto err; + } + + /* 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, ssl23_get_v2_client_hello, ERR_R_INTERNAL_ERROR); + goto err; + } + } + + /* 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, ssl23_get_v2_client_hello, ERR_R_INTERNAL_ERROR); + goto err; + } + + /* 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; + + /* Reset the record layer for SSL3. */ + assert(s->rstate == SSL_ST_READ_HEADER); + s->packet_length = 0; + s->s3->rbuf.left = 0; + s->s3->rbuf.offset = 0; + + s->state = SSL23_ST_SR_SWITCH_VERSION; + return 1; +err: + return -1; } diff --git a/ssl/ssl_error.c b/ssl/ssl_error.c index c989c84ca..7a7a3add2 100644 --- a/ssl/ssl_error.c +++ b/ssl/ssl_error.c @@ -94,6 +94,7 @@ const ERR_STRING_DATA SSL_error_string_data[] = { {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl23_connect, 0), "ssl23_connect"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl23_get_client_hello, 0), "ssl23_get_client_hello"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl23_get_server_hello, 0), "ssl23_get_server_hello"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl23_get_v2_client_hello, 0), "ssl23_get_v2_client_hello"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl23_peek, 0), "ssl23_peek"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl23_read, 0), "ssl23_read"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl23_write, 0), "ssl23_write"}, diff --git a/ssl/ssl_stat.c b/ssl/ssl_stat.c index 9a45b454f..361a2fad6 100644 --- a/ssl/ssl_stat.c +++ b/ssl/ssl_stat.c @@ -179,8 +179,9 @@ case SSL23_ST_CW_CLNT_HELLO_B: str="SSLv2/v3 write client hello B"; break; case SSL23_ST_CR_SRVR_HELLO_A: str="SSLv2/v3 read server hello A"; break; case SSL23_ST_CR_SRVR_HELLO_B: str="SSLv2/v3 read server hello B"; break; /* server */ -case SSL23_ST_SR_CLNT_HELLO_A: str="SSLv2/v3 read client hello A"; break; -case SSL23_ST_SR_CLNT_HELLO_B: str="SSLv2/v3 read client hello B"; break; +case SSL23_ST_SR_CLNT_HELLO: str="SSLv2/v3 read client hello"; break; +case SSL23_ST_SR_V2_CLNT_HELLO: str="SSLv2/v3 read v2 client hello"; break; +case SSL23_ST_SR_SWITCH_VERSION: str="SSLv2/v3 switch version"; break; #endif /* DTLS */ @@ -292,8 +293,9 @@ case SSL23_ST_CW_CLNT_HELLO_B: str="23WCHB"; break; case SSL23_ST_CR_SRVR_HELLO_A: str="23RSHA"; break; case SSL23_ST_CR_SRVR_HELLO_B: str="23RSHA"; break; /* server */ -case SSL23_ST_SR_CLNT_HELLO_A: str="23RCHA"; break; -case SSL23_ST_SR_CLNT_HELLO_B: str="23RCHB"; break; +case SSL23_ST_SR_CLNT_HELLO: str="23RCH_"; break; +case SSL23_ST_SR_V2_CLNT_HELLO: str="23R2CH"; break; +case SSL23_ST_SR_SWITCH_VERSION: str="23RSW_"; break; #endif /* DTLS */ case DTLS1_ST_CR_HELLO_VERIFY_REQUEST_A: str="DRCHVA"; break; diff --git a/ssl/test/runner/common.go b/ssl/test/runner/common.go index 8859db877..ed60a3b3b 100644 --- a/ssl/test/runner/common.go +++ b/ssl/test/runner/common.go @@ -410,6 +410,10 @@ type ProtocolBugs struct { // RenewTicketOnResume causes the server to renew the session ticket and // send a NewSessionTicket message during an abbreviated handshake. RenewTicketOnResume bool + + // SendClientVersion, if non-zero, causes the client to send a different + // TLS version in the ClientHello than the maximum supported version. + SendClientVersion uint16 } func (c *Config) serverInit() { diff --git a/ssl/test/runner/handshake_client.go b/ssl/test/runner/handshake_client.go index 9d2c1fad9..fa8407464 100644 --- a/ssl/test/runner/handshake_client.go +++ b/ssl/test/runner/handshake_client.go @@ -52,6 +52,10 @@ func (c *Conn) clientHandshake() error { duplicateExtension: c.config.Bugs.DuplicateExtension, } + if c.config.Bugs.SendClientVersion != 0 { + hello.vers = c.config.Bugs.SendClientVersion + } + possibleCipherSuites := c.config.cipherSuites() hello.cipherSuites = make([]uint16, 0, len(possibleCipherSuites)) diff --git a/ssl/test/runner/runner.go b/ssl/test/runner/runner.go index 2addab192..57bcf8746 100644 --- a/ssl/test/runner/runner.go +++ b/ssl/test/runner/runner.go @@ -85,6 +85,9 @@ type testCase struct { // resumeSession controls whether a second connection should be tested // which resumes the first session. resumeSession bool + // sendPrefix sends a prefix on the socket before actually performing a + // handshake. + sendPrefix string // flags, if not empty, contains a list of command-line flags that will // be passed to the shim program. flags []string @@ -382,9 +385,79 @@ var testCases = []testCase{ shouldFail: true, expectedError: ":RECORD_TOO_SMALL:", }, + { + testType: serverTest, + name: "MinorVersionTolerance", + config: Config{ + Bugs: ProtocolBugs{ + SendClientVersion: 0x03ff, + }, + }, + expectedVersion: VersionTLS12, + }, + { + testType: serverTest, + name: "MajorVersionTolerance", + config: Config{ + Bugs: ProtocolBugs{ + SendClientVersion: 0x0400, + }, + }, + expectedVersion: VersionTLS12, + }, + { + testType: serverTest, + name: "VersionTooLow", + config: Config{ + Bugs: ProtocolBugs{ + SendClientVersion: 0x0200, + }, + }, + shouldFail: true, + expectedError: ":UNSUPPORTED_PROTOCOL:", + }, + { + testType: serverTest, + name: "HttpGET", + sendPrefix: "GET / HTTP/1.0\n", + shouldFail: true, + expectedError: ":HTTP_REQUEST:", + }, + { + testType: serverTest, + name: "HttpPOST", + sendPrefix: "POST / HTTP/1.0\n", + shouldFail: true, + expectedError: ":HTTP_REQUEST:", + }, + { + testType: serverTest, + name: "HttpHEAD", + sendPrefix: "HEAD / HTTP/1.0\n", + shouldFail: true, + expectedError: ":HTTP_REQUEST:", + }, + { + testType: serverTest, + name: "HttpPUT", + sendPrefix: "PUT / HTTP/1.0\n", + shouldFail: true, + expectedError: ":HTTP_REQUEST:", + }, + { + testType: serverTest, + name: "HttpCONNECT", + sendPrefix: "CONNECT www.google.com:443 HTTP/1.0\n", + shouldFail: true, + expectedError: ":HTTPS_PROXY_REQUEST:", + }, } func doExchange(test *testCase, config *Config, conn net.Conn, messageLen int) error { + if _, err := conn.Write([]byte(test.sendPrefix)); err != nil { + return err + } + var tlsConn *Conn if test.testType == clientTest { tlsConn = Server(conn, config) @@ -991,7 +1064,7 @@ func addStateMachineCoverageTests(async bool, splitHandshake bool) { // Client sends a V2ClientHello. testCases = append(testCases, testCase{ testType: serverTest, - name: "SendV2ClientHello", + name: "SendV2ClientHello" + suffix, config: Config{ // Choose a cipher suite that does not involve // elliptic curves, so no extensions are