From 9ceb2426b0a7972434a49a34e78bdcc6437e04ad Mon Sep 17 00:00:00 2001 From: Matt Caswell Date: Thu, 16 Apr 2015 10:06:25 +0100 Subject: [PATCH] PACKETise ClientHello processing Uses the new PACKET code to process the incoming ClientHello including all extensions etc. Reviewed-by: Tim Hudson --- ssl/d1_srtp.c | 57 +++---- ssl/s3_srvr.c | 169 ++++++++++---------- ssl/ssl_locl.h | 17 +-- ssl/ssl_sess.c | 11 +- ssl/t1_lib.c | 407 +++++++++++++++++++++++++++---------------------- ssl/t1_reneg.c | 19 +-- 6 files changed, 344 insertions(+), 336 deletions(-) diff --git a/ssl/d1_srtp.c b/ssl/d1_srtp.c index 19cf6ff396..4384edabc6 100644 --- a/ssl/d1_srtp.c +++ b/ssl/d1_srtp.c @@ -266,38 +266,18 @@ int ssl_add_clienthello_use_srtp_ext(SSL *s, unsigned char *p, int *len, return 0; } -int ssl_parse_clienthello_use_srtp_ext(SSL *s, unsigned char *d, int len, - int *al) +int ssl_parse_clienthello_use_srtp_ext(SSL *s, PACKET *pkt, int *al) { SRTP_PROTECTION_PROFILE *sprof; STACK_OF(SRTP_PROTECTION_PROFILE) *srvr; - int ct; - int mki_len; + unsigned int ct, mki_len, id; int i, srtp_pref; - unsigned int id; - - /* Length value + the MKI length */ - if (len < 3) { - SSLerr(SSL_F_SSL_PARSE_CLIENTHELLO_USE_SRTP_EXT, - SSL_R_BAD_SRTP_PROTECTION_PROFILE_LIST); - *al = SSL_AD_DECODE_ERROR; - return 1; - } - - /* Pull off the length of the cipher suite list */ - n2s(d, ct); - len -= 2; + PACKET subpkt; - /* Check that it is even */ - if (ct % 2) { - SSLerr(SSL_F_SSL_PARSE_CLIENTHELLO_USE_SRTP_EXT, - SSL_R_BAD_SRTP_PROTECTION_PROFILE_LIST); - *al = SSL_AD_DECODE_ERROR; - return 1; - } - - /* Check that lengths are consistent */ - if (len < (ct + 1)) { + /* Pull off the length of the cipher suite list and check it is even */ + if (!PACKET_get_net_2(pkt, &ct) + || (ct & 1) != 0 + || !PACKET_get_sub_packet(pkt, &subpkt, ct)) { SSLerr(SSL_F_SSL_PARSE_CLIENTHELLO_USE_SRTP_EXT, SSL_R_BAD_SRTP_PROTECTION_PROFILE_LIST); *al = SSL_AD_DECODE_ERROR; @@ -309,10 +289,13 @@ int ssl_parse_clienthello_use_srtp_ext(SSL *s, unsigned char *d, int len, /* Search all profiles for a match initially */ srtp_pref = sk_SRTP_PROTECTION_PROFILE_num(srvr); - while (ct) { - n2s(d, id); - ct -= 2; - len -= 2; + while (PACKET_remaining(&subpkt)) { + if (!PACKET_get_net_2(&subpkt, &id)) { + SSLerr(SSL_F_SSL_PARSE_CLIENTHELLO_USE_SRTP_EXT, + SSL_R_BAD_SRTP_PROTECTION_PROFILE_LIST); + *al = SSL_AD_DECODE_ERROR; + return 1; + } /* * Only look for match in profiles of higher preference than @@ -333,11 +316,15 @@ int ssl_parse_clienthello_use_srtp_ext(SSL *s, unsigned char *d, int len, /* * Now extract the MKI value as a sanity check, but discard it for now */ - mki_len = *d; - d++; - len--; + if (!PACKET_get_1(pkt, &mki_len)) { + SSLerr(SSL_F_SSL_PARSE_CLIENTHELLO_USE_SRTP_EXT, + SSL_R_BAD_SRTP_PROTECTION_PROFILE_LIST); + *al = SSL_AD_DECODE_ERROR; + return 1; + } - if (mki_len != len) { + if (!PACKET_forward(pkt, mki_len) + || PACKET_remaining(pkt)) { SSLerr(SSL_F_SSL_PARSE_CLIENTHELLO_USE_SRTP_EXT, SSL_R_BAD_SRTP_MKI_VALUE); *al = SSL_AD_DECODE_ERROR; diff --git a/ssl/s3_srvr.c b/ssl/s3_srvr.c index 718ca2c31b..bc7f84f2d1 100644 --- a/ssl/s3_srvr.c +++ b/ssl/s3_srvr.c @@ -862,11 +862,11 @@ int ssl3_send_hello_request(SSL *s) int ssl3_get_client_hello(SSL *s) { - int i, complen, j, ok, al = SSL_AD_INTERNAL_ERROR, ret = -1; - unsigned int cookie_len; + int i, ok, al = SSL_AD_INTERNAL_ERROR, ret = -1; + unsigned int j, cipherlen, complen; + unsigned int cookie_len = 0; long n; unsigned long id; - unsigned char *p, *d; SSL_CIPHER *c; #ifndef OPENSSL_NO_COMP unsigned char *q = NULL; @@ -874,6 +874,8 @@ int ssl3_get_client_hello(SSL *s) #endif STACK_OF(SSL_CIPHER) *ciphers = NULL; int protverr = 1; + PACKET pkt; + unsigned char *sess, *cdata; if (s->state == SSL3_ST_SR_CLNT_HELLO_C && !s->first_packet) goto retry_cert; @@ -897,10 +899,12 @@ int ssl3_get_client_hello(SSL *s) if (!ok) return ((int)n); s->first_packet = 0; - d = p = (unsigned char *)s->init_msg; + PACKET_buf_init(&pkt, s->init_msg, n); /* First lets get s->client_version set correctly */ if (RECORD_LAYER_is_sslv2_record(&s->rlayer)) { + unsigned int version; + unsigned int mt; /*- * An SSLv3/TLSv1 backwards-compatible CLIENT-HELLO in an SSLv2 * header is sent directly on the wire, not wrapped as a TLS @@ -916,7 +920,8 @@ int ssl3_get_client_hello(SSL *s) * ... ... */ - if (p[0] != SSL2_MT_CLIENT_HELLO) { + if (!PACKET_get_1(&pkt, &mt) + || mt != SSL2_MT_CLIENT_HELLO) { /* * Should never happen. We should have tested this in the record * layer in order to have determined that this is a SSLv2 record @@ -926,13 +931,18 @@ int ssl3_get_client_hello(SSL *s) goto err; } - if ((p[1] == 0x00) && (p[2] == 0x02)) { + if (!PACKET_get_net_2(&pkt, &version)) { + /* No protocol version supplied! */ + SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO, SSL_R_UNKNOWN_PROTOCOL); + goto err; + } + if (version == 0x0002) { /* This is real SSLv2. We don't support it. */ SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO, SSL_R_UNKNOWN_PROTOCOL); goto err; - } else if (p[1] == SSL3_VERSION_MAJOR) { + } else if ((version & 0xff00) == (SSL3_VERSION_MAJOR << 8)) { /* SSLv3/TLS */ - s->client_version = (((int)p[1]) << 8) | (int)p[2]; + s->client_version = version; } else { /* No idea what protocol this is */ SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO, SSL_R_UNKNOWN_PROTOCOL); @@ -940,20 +950,14 @@ int ssl3_get_client_hello(SSL *s) } } else { /* - * 2 bytes for client version, SSL3_RANDOM_SIZE bytes for random, 1 byte - * for session id length + * use version from inside client hello, not from record header (may + * differ: see RFC 2246, Appendix E, second paragraph) */ - if (n < 2 + SSL3_RANDOM_SIZE + 1) { + if(!PACKET_get_net_2(&pkt, (unsigned int *)&s->client_version)) { al = SSL_AD_DECODE_ERROR; SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO, SSL_R_LENGTH_TOO_SHORT); goto f_err; } - - /* - * use version from inside client hello, not from record header (may - * differ: see RFC 2246, Appendix E, second paragraph) - */ - s->client_version = (((int)p[0]) << 8) | (int)p[1]; } /* Do SSL/TLS version negotiation if applicable */ @@ -1032,15 +1036,9 @@ int ssl3_get_client_hello(SSL *s) */ unsigned int csl, sil, cl; - p += 3; - n2s(p, csl); - n2s(p, sil); - n2s(p, cl); - - if (csl + sil + cl + MIN_SSL2_RECORD_LEN != (unsigned int) n) { - SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO, SSL_R_RECORD_LENGTH_MISMATCH); - al = SSL_AD_DECODE_ERROR; - goto f_err; + if (!PACKET_get_net_2(&pkt, &csl) + || !PACKET_get_net_2(&pkt, &sil) + || !PACKET_get_net_2(&pkt, &cl)) { } if (csl == 0) { @@ -1050,7 +1048,13 @@ int ssl3_get_client_hello(SSL *s) goto f_err; } - if (ssl_bytes_to_cipher_list(s, p, csl, &(ciphers), 1) == NULL) { + if (!PACKET_get_bytes(&pkt, &cdata, csl)) { + SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO, SSL_R_RECORD_LENGTH_MISMATCH); + al = SSL_AD_DECODE_ERROR; + goto f_err; + } + + if (ssl_bytes_to_cipher_list(s, cdata, csl, &(ciphers), 1) == NULL) { goto err; } @@ -1058,6 +1062,11 @@ int ssl3_get_client_hello(SSL *s) * Ignore any session id. We don't allow resumption in a backwards * compatible ClientHello */ + if (!PACKET_forward(&pkt, sil)) { + SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO, SSL_R_RECORD_LENGTH_MISMATCH); + al = SSL_AD_DECODE_ERROR; + goto f_err; + } s->hit = 0; if (!ssl_get_new_session(s, 1)) @@ -1066,17 +1075,27 @@ int ssl3_get_client_hello(SSL *s) /* Load the client random */ i = (cl > SSL3_RANDOM_SIZE) ? SSL3_RANDOM_SIZE : cl; memset(s->s3->client_random, 0, SSL3_RANDOM_SIZE); - memcpy(s->s3->client_random, &(p[csl + sil]), i); - - /* Set p to end of packet to ensure we don't look for extensions */ - p = d + n; + if (!PACKET_peek_copy_bytes(&pkt, s->s3->client_random, i) + || !PACKET_forward(&pkt, cl) + || !PACKET_remaining(&pkt) == 0) { + SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO, SSL_R_RECORD_LENGTH_MISMATCH); + al = SSL_AD_DECODE_ERROR; + goto f_err; + } /* No compression, so set complen to 0 */ complen = 0; } else { /* If we get here we've got SSLv3+ in an SSLv3+ record */ - p += 2; + /* load the client random and get the session-id */ + if (!PACKET_copy_bytes(&pkt, s->s3->client_random, SSL3_RANDOM_SIZE) + || !PACKET_get_1(&pkt, &j) + || !PACKET_get_bytes(&pkt, &sess, j)) { + al = SSL_AD_DECODE_ERROR; + SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO, SSL_R_LENGTH_TOO_SHORT); + goto f_err; + } /* * If we require cookies and this ClientHello doesn't contain one, just @@ -1084,34 +1103,17 @@ int ssl3_get_client_hello(SSL *s) * cookie length... */ if (SSL_get_options(s) & SSL_OP_COOKIE_EXCHANGE) { - unsigned int session_length, cookie_length; - session_length = *(p + SSL3_RANDOM_SIZE); - - if (p + SSL3_RANDOM_SIZE + session_length + 1 >= d + n) { + if (!PACKET_peek_1(&pkt, &cookie_len)) { al = SSL_AD_DECODE_ERROR; SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO, SSL_R_LENGTH_TOO_SHORT); goto f_err; } - cookie_length = *(p + SSL3_RANDOM_SIZE + session_length + 1); - if (cookie_length == 0) + if (cookie_len == 0) return 1; } - /* load the client random */ - memcpy(s->s3->client_random, p, SSL3_RANDOM_SIZE); - p += SSL3_RANDOM_SIZE; - - /* get the session-id */ - j = *(p++); - - if (p + j > d + n) { - al = SSL_AD_DECODE_ERROR; - SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO, SSL_R_LENGTH_TOO_SHORT); - goto f_err; - } - s->hit = 0; /* * Versions before 0.9.7 always allow clients to resume sessions in @@ -1131,7 +1133,7 @@ int ssl3_get_client_hello(SSL *s) if (!ssl_get_new_session(s, 1)) goto err; } else { - i = ssl_get_prev_session(s, p, j, d + n); + i = ssl_get_prev_session(s, &pkt, sess, j); /* * Only resume if the session's version matches the negotiated * version. @@ -1153,23 +1155,12 @@ int ssl3_get_client_hello(SSL *s) } } - p += j; - if (SSL_IS_DTLS(s)) { - /* cookie stuff */ - if (p + 1 > d + n) { + if (!PACKET_get_1(&pkt, &cookie_len)) { al = SSL_AD_DECODE_ERROR; SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO, SSL_R_LENGTH_TOO_SHORT); goto f_err; } - cookie_len = *(p++); - - if (p + cookie_len > d + n) { - al = SSL_AD_DECODE_ERROR; - SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO, SSL_R_LENGTH_TOO_SHORT); - goto f_err; - } - /* * The ClientHello may contain a cookie even if the * HelloVerify message has not been sent--make sure that it @@ -1185,7 +1176,13 @@ int ssl3_get_client_hello(SSL *s) /* verify the cookie if appropriate option is set. */ if ((SSL_get_options(s) & SSL_OP_COOKIE_EXCHANGE) && cookie_len > 0) { - memcpy(s->d1->rcvd_cookie, p, cookie_len); + /* Get cookie */ + if (!PACKET_copy_bytes(&pkt, s->d1->rcvd_cookie, + cookie_len)) { + al = SSL_AD_DECODE_ERROR; + SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO, SSL_R_LENGTH_TOO_SHORT); + goto f_err; + } if (s->ctx->app_verify_cookie_cb != NULL) { if (s->ctx->app_verify_cookie_cb(s, s->d1->rcvd_cookie, @@ -1206,9 +1203,15 @@ int ssl3_get_client_hello(SSL *s) } /* Set to -2 so if successful we return 2 */ ret = -2; + } else { + /* Skip over cookie */ + if (!PACKET_forward(&pkt, cookie_len)) { + al = SSL_AD_DECODE_ERROR; + SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO, SSL_R_LENGTH_TOO_SHORT); + goto f_err; + } } - p += cookie_len; if (s->method->version == DTLS_ANY_VERSION) { /* Select version to use */ if (s->client_version <= DTLS1_2_VERSION && @@ -1236,30 +1239,28 @@ int ssl3_get_client_hello(SSL *s) } } - if (p + 2 > d + n) { + if (!PACKET_get_net_2(&pkt, &cipherlen)) { al = SSL_AD_DECODE_ERROR; SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO, SSL_R_LENGTH_TOO_SHORT); goto f_err; } - n2s(p, i); - if (i == 0) { + if (cipherlen == 0) { al = SSL_AD_ILLEGAL_PARAMETER; SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO, SSL_R_NO_CIPHERS_SPECIFIED); goto f_err; } - /* i bytes of cipher data + 1 byte for compression length later */ - if ((p + i + 1) > (d + n)) { + if (!PACKET_get_bytes(&pkt, &cdata, cipherlen)) { /* not enough data */ al = SSL_AD_DECODE_ERROR; SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO, SSL_R_LENGTH_MISMATCH); goto f_err; } - if (ssl_bytes_to_cipher_list(s, p, i, &(ciphers), 0) == NULL) { + + if (ssl_bytes_to_cipher_list(s, cdata, cipherlen, &(ciphers), 0) == NULL) { goto err; } - p += i; /* If it is a hit, check that the cipher is in the list */ if (s->hit) { @@ -1316,22 +1317,22 @@ int ssl3_get_client_hello(SSL *s) } /* compression */ - complen = *(p++); - if ((p + complen) > (d + n)) { + if (!PACKET_get_1(&pkt, &complen) + || !PACKET_get_bytes(&pkt, &cdata, complen)) { /* not enough data */ al = SSL_AD_DECODE_ERROR; SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO, SSL_R_LENGTH_MISMATCH); goto f_err; } + #ifndef OPENSSL_NO_COMP - q = p; + q = cdata; #endif for (j = 0; j < complen; j++) { - if (p[j] == 0) + if (cdata[j] == 0) break; } - p += complen; if (j >= complen) { /* no compress */ al = SSL_AD_DECODE_ERROR; @@ -1342,7 +1343,7 @@ int ssl3_get_client_hello(SSL *s) /* TLS extensions */ if (s->version >= SSL3_VERSION) { - if (!ssl_parse_clienthello_tlsext(s, &p, d, n)) { + if (!ssl_parse_clienthello_tlsext(s, &pkt)) { SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO, SSL_R_PARSE_TLSEXT); goto err; } @@ -1407,6 +1408,7 @@ int ssl3_get_client_hello(SSL *s) /* This only happens if we have a cache hit */ if (s->session->compress_meth != 0) { int m, comp_id = s->session->compress_meth; + unsigned int k; /* Perform sanity checks on resumed compression algorithm */ /* Can't disable compression */ if (!ssl_allow_compression(s)) { @@ -1428,11 +1430,11 @@ int ssl3_get_client_hello(SSL *s) goto f_err; } /* Look for resumed method in compression list */ - for (m = 0; m < complen; m++) { - if (q[m] == comp_id) + for (k = 0; k < complen; k++) { + if (q[k] == comp_id) break; } - if (m >= complen) { + if (k >= complen) { al = SSL_AD_ILLEGAL_PARAMETER; SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO, SSL_R_REQUIRED_COMPRESSSION_ALGORITHM_MISSING); @@ -1442,7 +1444,8 @@ int ssl3_get_client_hello(SSL *s) comp = NULL; else if (ssl_allow_compression(s) && s->ctx->comp_methods) { /* See if we have a match */ - int m, nn, o, v, done = 0; + int m, nn, v, done = 0; + unsigned int o; nn = sk_SSL_COMP_num(s->ctx->comp_methods); for (m = 0; m < nn; m++) { diff --git a/ssl/ssl_locl.h b/ssl/ssl_locl.h index 579b09b270..09975664e9 100644 --- a/ssl/ssl_locl.h +++ b/ssl/ssl_locl.h @@ -1854,8 +1854,8 @@ __owur CERT *ssl_cert_dup(CERT *cert); void ssl_cert_clear_certs(CERT *c); void ssl_cert_free(CERT *c); __owur int ssl_get_new_session(SSL *s, int session); -__owur int ssl_get_prev_session(SSL *s, unsigned char *session, int len, - const unsigned char *limit); +__owur int ssl_get_prev_session(SSL *s, PACKET *pkt, unsigned char *session, + int len); __owur SSL_SESSION *ssl_session_dup(SSL_SESSION *src, int ticket); __owur int ssl_cipher_id_cmp(const SSL_CIPHER *a, const SSL_CIPHER *b); DECLARE_OBJ_BSEARCH_GLOBAL_CMP_FN(SSL_CIPHER, SSL_CIPHER, ssl_cipher_id); @@ -2088,8 +2088,7 @@ __owur unsigned char *ssl_add_clienthello_tlsext(SSL *s, unsigned char *buf, unsigned char *limit, int *al); __owur unsigned char *ssl_add_serverhello_tlsext(SSL *s, unsigned char *buf, unsigned char *limit, int *al); -__owur int ssl_parse_clienthello_tlsext(SSL *s, unsigned char **data, - unsigned char *d, int n); +__owur int ssl_parse_clienthello_tlsext(SSL *s, PACKET *pkt); __owur int tls1_set_server_sigalgs(SSL *s); __owur int ssl_check_clienthello_tlsext_late(SSL *s); __owur int ssl_parse_serverhello_tlsext(SSL *s, unsigned char **data, @@ -2104,8 +2103,8 @@ __owur int tls1_process_heartbeat(SSL *s, unsigned char *p, unsigned int length) __owur int dtls1_process_heartbeat(SSL *s, unsigned char *p, unsigned int length); # endif -__owur int tls1_process_ticket(SSL *s, unsigned char *session_id, int len, - const unsigned char *limit, SSL_SESSION **ret); +__owur int tls1_process_ticket(SSL *s, PACKET *pkt, unsigned char *session_id, + int len, SSL_SESSION **ret); __owur int tls12_get_sigandhash(unsigned char *p, const EVP_PKEY *pk, const EVP_MD *md); @@ -2134,8 +2133,7 @@ __owur int ssl_parse_serverhello_renegotiate_ext(SSL *s, unsigned char *d, int l int *al); __owur int ssl_add_clienthello_renegotiate_ext(SSL *s, unsigned char *p, int *len, int maxlen); -__owur int ssl_parse_clienthello_renegotiate_ext(SSL *s, unsigned char *d, int len, - int *al); +__owur int ssl_parse_clienthello_renegotiate_ext(SSL *s, PACKET *pkt, int *al); __owur long ssl_get_algorithm2(SSL *s); __owur size_t tls12_copy_sigalgs(SSL *s, unsigned char *out, const unsigned char *psig, size_t psiglen); @@ -2149,8 +2147,7 @@ __owur int ssl_cipher_disabled(SSL *s, const SSL_CIPHER *c, int op); __owur int ssl_add_clienthello_use_srtp_ext(SSL *s, unsigned char *p, int *len, int maxlen); -__owur int ssl_parse_clienthello_use_srtp_ext(SSL *s, unsigned char *d, int len, - int *al); +__owur int ssl_parse_clienthello_use_srtp_ext(SSL *s, PACKET *pkt, int *al); __owur int ssl_add_serverhello_use_srtp_ext(SSL *s, unsigned char *p, int *len, int maxlen); __owur int ssl_parse_serverhello_use_srtp_ext(SSL *s, unsigned char *d, int len, diff --git a/ssl/ssl_sess.c b/ssl/ssl_sess.c index 9063bca415..26a3c43f24 100644 --- a/ssl/ssl_sess.c +++ b/ssl/ssl_sess.c @@ -547,8 +547,8 @@ int ssl_get_new_session(SSL *s, int session) * - Both for new and resumed sessions, s->tlsext_ticket_expected is set to 1 * if the server should issue a new session ticket (to 0 otherwise). */ -int ssl_get_prev_session(SSL *s, unsigned char *session_id, int len, - const unsigned char *limit) +int ssl_get_prev_session(SSL *s, PACKET *pkt, unsigned char *session_id, + int len) { /* This is used only by servers. */ @@ -560,16 +560,11 @@ int ssl_get_prev_session(SSL *s, unsigned char *session_id, int len, if (len < 0 || len > SSL_MAX_SSL_SESSION_ID_LENGTH) goto err; - if (session_id + len > limit) { - fatal = 1; - goto err; - } - if (len == 0) try_session_cache = 0; /* sets s->tlsext_ticket_expected */ - r = tls1_process_ticket(s, session_id, len, limit, &ret); + r = tls1_process_ticket(s, pkt, session_id, len, &ret); switch (r) { case -1: /* Error during processing */ fatal = 1; diff --git a/ssl/t1_lib.c b/ssl/t1_lib.c index 47abf2b9f9..c0dd35f9c8 100644 --- a/ssl/t1_lib.c +++ b/ssl/t1_lib.c @@ -1756,46 +1756,33 @@ unsigned char *ssl_add_serverhello_tlsext(SSL *s, unsigned char *buf, * alert value to send in the event of a non-zero return. returns: 0 on * success. */ -static int tls1_alpn_handle_client_hello(SSL *s, const unsigned char *data, - unsigned data_len, int *al) +static int tls1_alpn_handle_client_hello(SSL *s, PACKET *pkt, int *al) { - unsigned i; - unsigned proto_len; + unsigned int data_len; + unsigned int proto_len; const unsigned char *selected; + unsigned char *data; unsigned char selected_len; int r; if (s->ctx->alpn_select_cb == NULL) return 0; - if (data_len < 2) - goto parse_error; - /* * data should contain a uint16 length followed by a series of 8-bit, * length-prefixed strings. */ - i = ((unsigned)data[0]) << 8 | ((unsigned)data[1]); - data_len -= 2; - data += 2; - if (data_len != i) - goto parse_error; - - if (data_len < 2) + if (!PACKET_get_net_2(pkt, &data_len) + || PACKET_remaining(pkt) != data_len + || !PACKET_peek_bytes(pkt, &data, data_len)) goto parse_error; - for (i = 0; i < data_len;) { - proto_len = data[i]; - i++; - - if (proto_len == 0) - goto parse_error; - - if (i + proto_len < i || i + proto_len > data_len) + do { + if (!PACKET_get_1(pkt, &proto_len) + || proto_len == 0 + || !PACKET_forward(pkt, proto_len)) goto parse_error; - - i += proto_len; - } + } while (PACKET_remaining(pkt)); r = s->ctx->alpn_select_cb(s, &selected, &selected_len, data, data_len, s->ctx->alpn_select_cb_arg); @@ -1830,10 +1817,11 @@ static int tls1_alpn_handle_client_hello(SSL *s, const unsigned char *data, * Sadly we cannot differentiate 10.6, 10.7 and 10.8.4 (which work), from * 10.8..10.8.3 (which don't work). */ -static void ssl_check_for_safari(SSL *s, const unsigned char *data, - const unsigned char *d, int n) +static void ssl_check_for_safari(SSL *s, PACKET *pkt) { - unsigned short type, size; + unsigned int type, size; + unsigned char *eblock1, *eblock2; + static const unsigned char kSafariExtensionsBlock[] = { 0x00, 0x0a, /* elliptic_curves extension */ 0x00, 0x08, /* 8 bytes */ @@ -1860,38 +1848,34 @@ static void ssl_check_for_safari(SSL *s, const unsigned char *data, 0x02, 0x03, /* SHA-1/ECDSA */ }; - if (data >= (d + n - 2)) + if (!PACKET_forward(pkt, 2) + || !PACKET_get_net_2(pkt, &type) + || !PACKET_get_net_2(pkt, &size) + || !PACKET_forward(pkt, size)) return; - data += 2; - - if (data > (d + n - 4)) - return; - n2s(data, type); - n2s(data, size); if (type != TLSEXT_TYPE_server_name) return; - if (data + size > d + n) - return; - data += size; - if (TLS1_get_client_version(s) >= TLS1_2_VERSION) { const size_t len1 = sizeof(kSafariExtensionsBlock); const size_t len2 = sizeof(kSafariTLS12ExtensionsBlock); - if (data + len1 + len2 != d + n) + if (!PACKET_get_bytes(pkt, &eblock1, len1) + || !PACKET_get_bytes(pkt, &eblock2, len2) + || PACKET_remaining(pkt)) return; - if (memcmp(data, kSafariExtensionsBlock, len1) != 0) + if (memcmp(eblock1, kSafariExtensionsBlock, len1) != 0) return; - if (memcmp(data + len1, kSafariTLS12ExtensionsBlock, len2) != 0) + if (memcmp(eblock2, kSafariTLS12ExtensionsBlock, len2) != 0) return; } else { const size_t len = sizeof(kSafariExtensionsBlock); - if (data + len != d + n) + if (!PACKET_get_bytes(pkt, &eblock1, len) + || PACKET_remaining(pkt)) return; - if (memcmp(data, kSafariExtensionsBlock, len) != 0) + if (memcmp(eblock1, kSafariExtensionsBlock, len) != 0) return; } @@ -1899,13 +1883,12 @@ static void ssl_check_for_safari(SSL *s, const unsigned char *data, } #endif /* !OPENSSL_NO_EC */ -static int ssl_scan_clienthello_tlsext(SSL *s, unsigned char **p, - unsigned char *d, int n, int *al) +static int ssl_scan_clienthello_tlsext(SSL *s, PACKET *pkt, int *al) { - unsigned short type; - unsigned short size; - unsigned short len; - unsigned char *data = *p; + unsigned int type; + unsigned int size; + unsigned int len; + unsigned char *data; int renegotiate_seen = 0; s->servername_done = 0; @@ -1923,8 +1906,8 @@ static int ssl_scan_clienthello_tlsext(SSL *s, unsigned char **p, #ifndef OPENSSL_NO_EC if (s->options & SSL_OP_SAFARI_ECDHE_ECDSA_BUG) - ssl_check_for_safari(s, data, d, n); -#endif /* !OPENSSL_NO_EC */ + ssl_check_for_safari(s, pkt); +# endif /* !OPENSSL_NO_EC */ /* Clear any signature algorithms extension received */ OPENSSL_free(s->s3->tmp.peer_sigalgs); @@ -1940,27 +1923,26 @@ static int ssl_scan_clienthello_tlsext(SSL *s, unsigned char **p, s->srtp_profile = NULL; - if (data == d + n) + if (PACKET_remaining(pkt) == 0) goto ri_check; - if (data > (d + n - 2)) - goto err; - - n2s(data, len); - - if (data > (d + n - len)) + if (!PACKET_get_net_2(pkt, &len)) goto err; - while (data <= (d + n - 4)) { - n2s(data, type); - n2s(data, size); + while (PACKET_get_net_2(pkt, &type) && PACKET_get_net_2(pkt, &size)) { + PACKET subpkt; - if (data + size > (d + n)) + if (!PACKET_peek_bytes(pkt, &data, size)) goto err; + if (s->tlsext_debug_cb) s->tlsext_debug_cb(s, 0, type, data, size, s->tlsext_debug_arg); + + if (!PACKET_get_sub_packet(pkt, &subpkt, size)) + goto err; + if (type == TLSEXT_TYPE_renegotiate) { - if (!ssl_parse_clienthello_renegotiate_ext(s, data, size, al)) + if (!ssl_parse_clienthello_renegotiate_ext(s, &subpkt, al)) return 0; renegotiate_seen = 1; } else if (s->version == SSL3_VERSION) { @@ -1992,23 +1974,18 @@ static int ssl_scan_clienthello_tlsext(SSL *s, unsigned char **p, else if (type == TLSEXT_TYPE_server_name) { unsigned char *sdata; - int servname_type; - int dsize; + unsigned int servname_type; + unsigned int dsize; + PACKET ssubpkt; - if (size < 2) + if (!PACKET_get_net_2(&subpkt, &dsize) + || !PACKET_get_sub_packet(&subpkt, &ssubpkt, dsize)) goto err; - n2s(data, dsize); - size -= 2; - if (dsize > size) - goto err; - - sdata = data; - while (dsize > 3) { - servname_type = *(sdata++); - n2s(sdata, len); - dsize -= 3; - if (len > dsize) + while (PACKET_remaining(&ssubpkt) > 3) { + if (!PACKET_get_1(&ssubpkt, &servname_type) + || !PACKET_get_net_2(&ssubpkt, &len) + || PACKET_remaining(&ssubpkt) < len) goto err; if (s->servername_done == 0) @@ -2027,7 +2004,13 @@ static int ssl_scan_clienthello_tlsext(SSL *s, unsigned char **p, *al = TLS1_AD_INTERNAL_ERROR; return 0; } - memcpy(s->session->tlsext_hostname, sdata, len); + if (!PACKET_copy_bytes(&ssubpkt, + (unsigned char *)s->session + ->tlsext_hostname, + len)) { + *al = SSL_AD_DECODE_ERROR; + return 0; + } s->session->tlsext_hostname[len] = '\0'; if (strlen(s->session->tlsext_hostname) != len) { OPENSSL_free(s->session->tlsext_hostname); @@ -2037,48 +2020,55 @@ static int ssl_scan_clienthello_tlsext(SSL *s, unsigned char **p, } s->servername_done = 1; - } else + } else { + if (!PACKET_get_bytes(&ssubpkt, &sdata, len)) { + *al = SSL_AD_DECODE_ERROR; + return 0; + } s->servername_done = s->session->tlsext_hostname && strlen(s->session->tlsext_hostname) == len && strncmp(s->session->tlsext_hostname, (char *)sdata, len) == 0; + } break; default: break; } - - dsize -= len; } - if (dsize != 0) + /* We shouldn't have any bytes left */ + if (PACKET_remaining(&ssubpkt)) goto err; } #ifndef OPENSSL_NO_SRP else if (type == TLSEXT_TYPE_srp) { - if (size == 0 || ((len = data[0])) != (size - 1)) - goto err; - if (s->srp_ctx.login != NULL) + if (!PACKET_get_1(&subpkt, &len) + || s->srp_ctx.login != NULL) goto err; + if ((s->srp_ctx.login = OPENSSL_malloc(len + 1)) == NULL) return -1; - memcpy(s->srp_ctx.login, &data[1], len); + if (!PACKET_copy_bytes(&subpkt, (unsigned char *)s->srp_ctx.login, + len)) + goto err; s->srp_ctx.login[len] = '\0'; - if (strlen(s->srp_ctx.login) != len) + if (strlen(s->srp_ctx.login) != len + || PACKET_remaining(&subpkt)) goto err; } #endif #ifndef OPENSSL_NO_EC else if (type == TLSEXT_TYPE_ec_point_formats) { - unsigned char *sdata = data; - int ecpointformatlist_length = *(sdata++); + unsigned int ecpointformatlist_length; - if (ecpointformatlist_length != size - 1 || - ecpointformatlist_length < 1) + if (!PACKET_get_1(&subpkt, &ecpointformatlist_length) + || ecpointformatlist_length == 0) goto err; + if (!s->hit) { OPENSSL_free(s->session->tlsext_ecpointformatlist); s->session->tlsext_ecpointformatlist = NULL; @@ -2090,19 +2080,26 @@ static int ssl_scan_clienthello_tlsext(SSL *s, unsigned char **p, } s->session->tlsext_ecpointformatlist_length = ecpointformatlist_length; - memcpy(s->session->tlsext_ecpointformatlist, sdata, - ecpointformatlist_length); + if (!PACKET_copy_bytes(&subpkt, + s->session->tlsext_ecpointformatlist, + ecpointformatlist_length)) + goto err; + } else if (!PACKET_forward(&subpkt, ecpointformatlist_length)) { + goto err; + } + /* We should have consumed all the bytes by now */ + if (PACKET_remaining(&subpkt)) { + *al = TLS1_AD_DECODE_ERROR; + return 0; } } else if (type == TLSEXT_TYPE_elliptic_curves) { - unsigned char *sdata = data; - int ellipticcurvelist_length = (*(sdata++) << 8); - ellipticcurvelist_length += (*(sdata++)); + unsigned int ellipticcurvelist_length; - if (ellipticcurvelist_length != size - 2 || - ellipticcurvelist_length < 1 || - /* Each NamedCurve is 2 bytes. */ - ellipticcurvelist_length & 1) - goto err; + /* Each NamedCurve is 2 bytes and we must have at least 1 */ + if (!PACKET_get_net_2(&subpkt, &ellipticcurvelist_length) + || ellipticcurvelist_length == 0 + || (ellipticcurvelist_length & 1) != 0) + goto err; if (!s->hit) { if (s->session->tlsext_ellipticcurvelist) @@ -2116,54 +2113,63 @@ static int ssl_scan_clienthello_tlsext(SSL *s, unsigned char **p, } s->session->tlsext_ellipticcurvelist_length = ellipticcurvelist_length; - memcpy(s->session->tlsext_ellipticcurvelist, sdata, - ellipticcurvelist_length); + if (!PACKET_copy_bytes(&subpkt, + s->session->tlsext_ellipticcurvelist, + ellipticcurvelist_length)) + goto err; + } else if (!PACKET_forward(&subpkt, ellipticcurvelist_length)) { + goto err; + } + /* We should have consumed all the bytes by now */ + if (PACKET_remaining(&subpkt)) { + goto err; } } #endif /* OPENSSL_NO_EC */ else if (type == TLSEXT_TYPE_session_ticket) { - if (s->tls_session_ticket_ext_cb && - !s->tls_session_ticket_ext_cb(s, data, size, - s->tls_session_ticket_ext_cb_arg)) - { + if (!PACKET_forward(&subpkt, size) + || (s->tls_session_ticket_ext_cb && + !s->tls_session_ticket_ext_cb(s, data, size, + s->tls_session_ticket_ext_cb_arg))) { *al = TLS1_AD_INTERNAL_ERROR; return 0; } } else if (type == TLSEXT_TYPE_signature_algorithms) { - int dsize; - if (s->s3->tmp.peer_sigalgs || size < 2) - goto err; - n2s(data, dsize); - size -= 2; - if (dsize != size || dsize & 1 || !dsize) - goto err; - if (!tls1_save_sigalgs(s, data, dsize)) + unsigned int dsize; + + if (s->s3->tmp.peer_sigalgs + || !PACKET_get_net_2(&subpkt, &dsize) + || (dsize & 1) != 0 + || (dsize == 0) + || !PACKET_get_bytes(&subpkt, &data, dsize) + || PACKET_remaining(&subpkt) + || !tls1_save_sigalgs(s, data, dsize)) { goto err; + } } else if (type == TLSEXT_TYPE_status_request) { + PACKET ssubpkt; - if (size < 5) + if (!PACKET_get_1(&subpkt, + (unsigned int *)&s->tlsext_status_type)) goto err; - s->tlsext_status_type = *data++; - size--; if (s->tlsext_status_type == TLSEXT_STATUSTYPE_ocsp) { const unsigned char *sdata; - int dsize; + unsigned int dsize; /* Read in responder_id_list */ - n2s(data, dsize); - size -= 2; - if (dsize > size) + if (!PACKET_get_net_2(&subpkt, &dsize) + || !PACKET_get_sub_packet(&subpkt, &ssubpkt, dsize)) goto err; - while (dsize > 0) { + + while (PACKET_remaining(&ssubpkt)) { OCSP_RESPID *id; - int idsize; - if (dsize < 4) - goto err; - n2s(data, idsize); - dsize -= 2 + idsize; - size -= 2 + idsize; - if (dsize < 0) + unsigned int idsize; + + if (PACKET_remaining(&ssubpkt) < 4 + || !PACKET_get_net_2(&ssubpkt, &idsize) + || !PACKET_get_bytes(&ssubpkt, &data, idsize)) { goto err; + } sdata = data; data += idsize; id = d2i_OCSP_RESPID(NULL, &sdata, idsize); @@ -2188,12 +2194,11 @@ static int ssl_scan_clienthello_tlsext(SSL *s, unsigned char **p, } /* Read in request_extensions */ - if (size < 2) - goto err; - n2s(data, dsize); - size -= 2; - if (dsize != size) + if (!PACKET_get_net_2(&subpkt, &dsize) + || !PACKET_get_bytes(&subpkt, &data, dsize) + || PACKET_remaining(&subpkt)) { goto err; + } sdata = data; if (dsize > 0) { sk_X509_EXTENSION_pop_free(s->tlsext_ocsp_exts, @@ -2212,7 +2217,14 @@ static int ssl_scan_clienthello_tlsext(SSL *s, unsigned char **p, } #ifndef OPENSSL_NO_HEARTBEATS else if (type == TLSEXT_TYPE_heartbeat) { - switch (data[0]) { + unsigned int hbtype; + + if (!PACKET_get_1(&subpkt, &hbtype) + || PACKET_remaining(&subpkt)) { + *al = SSL_AD_DECODE_ERROR; + return 0; + } + switch (hbtype) { case 0x01: /* Client allows us to send HB requests */ s->tlsext_heartbeat |= SSL_TLSEXT_HB_ENABLED; break; @@ -2253,7 +2265,7 @@ static int ssl_scan_clienthello_tlsext(SSL *s, unsigned char **p, else if (type == TLSEXT_TYPE_application_layer_protocol_negotiation && s->ctx->alpn_select_cb && s->s3->tmp.finish_md_len == 0) { - if (tls1_alpn_handle_client_hello(s, data, size, al) != 0) + if (tls1_alpn_handle_client_hello(s, &subpkt, al) != 0) return 0; #ifndef OPENSSL_NO_NEXTPROTONEG /* ALPN takes precedence over NPN. */ @@ -2265,7 +2277,7 @@ static int ssl_scan_clienthello_tlsext(SSL *s, unsigned char **p, #ifndef OPENSSL_NO_SRTP else if (SSL_IS_DTLS(s) && SSL_get_srtp_profiles(s) && type == TLSEXT_TYPE_use_srtp) { - if (ssl_parse_clienthello_use_srtp_ext(s, data, size, al)) + if (ssl_parse_clienthello_use_srtp_ext(s, &subpkt, al)) return 0; } #endif @@ -2288,16 +2300,12 @@ static int ssl_scan_clienthello_tlsext(SSL *s, unsigned char **p, if (custom_ext_parse(s, 1, type, data, size, al) <= 0) return 0; } - - data += size; } /* Spurious data on the end */ - if (data != d + n) + if (PACKET_remaining(pkt) != 0) goto err; - *p = data; - ri_check: /* Need RI if renegotiating */ @@ -2316,12 +2324,11 @@ err: return 0; } -int ssl_parse_clienthello_tlsext(SSL *s, unsigned char **p, unsigned char *d, - int n) +int ssl_parse_clienthello_tlsext(SSL *s, PACKET *pkt) { int al = -1; custom_ext_init(&s->cert->srv_ext); - if (ssl_scan_clienthello_tlsext(s, p, d, n, &al) <= 0) { + if (ssl_scan_clienthello_tlsext(s, pkt, &al) <= 0) { ssl3_send_alert(s, SSL3_AL_FATAL, al); return 0; } @@ -2934,12 +2941,12 @@ int ssl_parse_serverhello_tlsext(SSL *s, unsigned char **p, unsigned char *d, * s->ctx->tlsext_ticket_key_cb asked to renew the client's ticket. * Otherwise, s->tlsext_ticket_expected is set to 0. */ -int tls1_process_ticket(SSL *s, unsigned char *session_id, int len, - const unsigned char *limit, SSL_SESSION **ret) +int tls1_process_ticket(SSL *s, PACKET *pkt, unsigned char *session_id, + int len, SSL_SESSION **ret) { - /* Point after session ID in client hello */ - const unsigned char *p = session_id + len; - unsigned short i; + unsigned int i; + size_t bookmark = 0; + int retv = -1; *ret = NULL; s->tlsext_ticket_expected = 0; @@ -2950,46 +2957,60 @@ int tls1_process_ticket(SSL *s, unsigned char *session_id, int len, */ if (!tls_use_ticket(s)) return 0; - if ((s->version <= SSL3_VERSION) || !limit) + if ((s->version <= SSL3_VERSION)) return 0; - if (p >= limit) + + if (!PACKET_get_bookmark(pkt, &bookmark)) { return -1; + } + /* Skip past DTLS cookie */ if (SSL_IS_DTLS(s)) { - i = *(p++); - p += i; - if (p >= limit) - return -1; + if (!PACKET_get_1(pkt, &i) + || !PACKET_forward(pkt, i)) { + retv = -1; + goto end; + } } - /* Skip past cipher list */ - n2s(p, i); - p += i; - if (p >= limit) - return -1; - /* Skip past compression algorithm list */ - i = *(p++); - p += i; - if (p > limit) - return -1; + /* Skip past cipher list and compression algorithm list */ + if (!PACKET_get_net_2(pkt, &i) + || !PACKET_forward(pkt, i) + || !PACKET_get_1(pkt, &i) + || !PACKET_forward(pkt, i)) { + retv = -1; + goto end; + } + /* Now at start of extensions */ - if ((p + 2) >= limit) - return 0; - n2s(p, i); - while ((p + 4) <= limit) { - unsigned short type, size; - n2s(p, type); - n2s(p, size); - if (p + size > limit) - return 0; + if (!PACKET_get_net_2(pkt, &i)) { + retv = 0; + goto end; + } + while (PACKET_remaining (pkt) >= 4) { + unsigned int type, size; + + if (!PACKET_get_net_2(pkt, &type) + || !PACKET_get_net_2(pkt, &size)) { + /* Shouldn't ever happen */ + retv = -1; + goto end; + } + if (PACKET_remaining(pkt) < size) { + retv = 0; + goto end; + } if (type == TLSEXT_TYPE_session_ticket) { int r; + unsigned char *etick; + if (size == 0) { /* * The client will accept a ticket but doesn't currently have * one. */ s->tlsext_ticket_expected = 1; - return 1; + retv = 1; + goto end; } if (s->tls_session_secret_cb) { /* @@ -2998,25 +3019,39 @@ int tls1_process_ticket(SSL *s, unsigned char *session_id, int len, * abbreviated handshake based on external mechanism to * calculate the master secret later. */ - return 2; + retv = 2; + goto end; } - r = tls_decrypt_ticket(s, p, size, session_id, len, ret); + if (!PACKET_get_bytes(pkt, &etick, size)) { + /* Shouldn't ever happen */ + retv = -1; + goto end; + } + r = tls_decrypt_ticket(s, etick, size, session_id, len, ret); switch (r) { case 2: /* ticket couldn't be decrypted */ s->tlsext_ticket_expected = 1; - return 2; + retv = 2; + break; case 3: /* ticket was decrypted */ - return r; + retv = r; + break; case 4: /* ticket decrypted but need to renew */ s->tlsext_ticket_expected = 1; - return 3; + retv = 3; + break; default: /* fatal error */ - return -1; + retv = -1; + break; } + goto end; } - p += size; } - return 0; + retv = 0; +end: + if (!PACKET_goto_bookmark(pkt, bookmark)) + return -1; + return retv; } /*- diff --git a/ssl/t1_reneg.c b/ssl/t1_reneg.c index b9a35c7fc2..22a71fed49 100644 --- a/ssl/t1_reneg.c +++ b/ssl/t1_reneg.c @@ -143,23 +143,14 @@ int ssl_add_clienthello_renegotiate_ext(SSL *s, unsigned char *p, int *len, /* * Parse the client's renegotiation binding and abort if it's not right */ -int ssl_parse_clienthello_renegotiate_ext(SSL *s, unsigned char *d, int len, - int *al) +int ssl_parse_clienthello_renegotiate_ext(SSL *s, PACKET *pkt, int *al) { - int ilen; + unsigned int ilen; + unsigned char *d; /* Parse the length byte */ - if (len < 1) { - SSLerr(SSL_F_SSL_PARSE_CLIENTHELLO_RENEGOTIATE_EXT, - SSL_R_RENEGOTIATION_ENCODING_ERR); - *al = SSL_AD_ILLEGAL_PARAMETER; - return 0; - } - ilen = *d; - d++; - - /* Consistency check */ - if ((ilen + 1) != len) { + if (!PACKET_get_1(pkt, &ilen) + || !PACKET_get_bytes(pkt, &d, ilen)) { SSLerr(SSL_F_SSL_PARSE_CLIENTHELLO_RENEGOTIATE_EXT, SSL_R_RENEGOTIATION_ENCODING_ERR); *al = SSL_AD_ILLEGAL_PARAMETER; -- 2.34.1