diff options
author | Emilia Kasper <emilia@openssl.org> | 2015-09-22 15:20:26 +0200 |
---|---|---|
committer | Emilia Kasper <emilia@openssl.org> | 2016-03-03 13:53:26 +0100 |
commit | 062178678f5374b09f00d70796f6e692e8775aca (patch) | |
tree | ae299cf72a32514f7e5315af16977976f6083c86 /ssl | |
parent | d6c2587967f93f2f9c226bda9139ae427698f20f (diff) |
Refactor ClientHello extension parsing
1) Simplify code with better PACKET methods.
2) Make broken SNI parsing explicit. SNI was intended to be extensible
to new name types but RFC 4366 defined the syntax inextensibly, and
OpenSSL has never parsed SNI in a way that would allow adding a new name
type. RFC 6066 fixed the definition but due to broken implementations
being widespread, it appears impossible to ever extend SNI.
3) Annotate resumption behaviour. OpenSSL doesn't currently handle all
extensions correctly upon resumption. Annotate for further clean-up.
4) Send an alert on ALPN protocol mismatch.
Reviewed-by: Kurt Roeckx <kurt@openssl.org>
Diffstat (limited to 'ssl')
-rw-r--r-- | ssl/packet_locl.h | 63 | ||||
-rw-r--r-- | ssl/s3_enc.c | 2 | ||||
-rw-r--r-- | ssl/t1_enc.c | 2 | ||||
-rw-r--r-- | ssl/t1_lib.c | 513 |
4 files changed, 305 insertions, 275 deletions
diff --git a/ssl/packet_locl.h b/ssl/packet_locl.h index 48767b6728..fd1f9f4ecf 100644 --- a/ssl/packet_locl.h +++ b/ssl/packet_locl.h @@ -93,6 +93,16 @@ static ossl_inline size_t PACKET_remaining(const PACKET *pkt) } /* + * Returns a pointer to the first byte after the packet data. + * Useful for integrating with non-PACKET parsing code. + * Specifically, we use PACKET_end() to verify that a d2i_... call + * has consumed the entire packet contents. + */ +static ossl_inline const unsigned char *PACKET_end(const PACKET *pkt) +{ + return pkt->curr + pkt->remaining; +} +/* * Returns a pointer to the PACKET's current position. * For use in non-PACKETized APIs. */ @@ -452,6 +462,12 @@ __owur static ossl_inline int PACKET_strndup(const PACKET *pkt, char **data) return (*data != NULL); } +/* Returns 1 if |pkt| contains at least one 0-byte, 0 otherwise. */ +static ossl_inline int PACKET_contains_zero_byte(const PACKET *pkt) +{ + return memchr(pkt->curr, 0, pkt->remaining) != NULL; +} + /* Move the current reading position forward |len| bytes */ __owur static ossl_inline int PACKET_forward(PACKET *pkt, size_t len) { @@ -489,6 +505,28 @@ __owur static ossl_inline int PACKET_get_length_prefixed_1(PACKET *pkt, } /* + * Like PACKET_get_length_prefixed_1, but additionally, fails when there are + * leftover bytes in |pkt|. + */ +__owur static ossl_inline int PACKET_as_length_prefixed_1(PACKET *pkt, PACKET *subpkt) +{ + unsigned int length; + const unsigned char *data; + PACKET tmp = *pkt; + if (!PACKET_get_1(&tmp, &length) || + !PACKET_get_bytes(&tmp, &data, (size_t)length) || + PACKET_remaining(&tmp) != 0) { + return 0; + } + + *pkt = tmp; + subpkt->curr = data; + subpkt->remaining = length; + + return 1; +} + +/* * Reads a variable-length vector prefixed with a two-byte length, and stores * the contents in |subpkt|. |pkt| can equal |subpkt|. * Data is not copied: the |subpkt| packet will share its underlying buffer with @@ -501,6 +539,7 @@ __owur static ossl_inline int PACKET_get_length_prefixed_2(PACKET *pkt, unsigned int length; const unsigned char *data; PACKET tmp = *pkt; + if (!PACKET_get_net_2(&tmp, &length) || !PACKET_get_bytes(&tmp, &data, (size_t)length)) { return 0; @@ -514,6 +553,30 @@ __owur static ossl_inline int PACKET_get_length_prefixed_2(PACKET *pkt, } /* + * Like PACKET_get_length_prefixed_2, but additionally, fails when there are + * leftover bytes in |pkt|. + */ +__owur static ossl_inline int PACKET_as_length_prefixed_2(PACKET *pkt, + PACKET *subpkt) +{ + unsigned int length; + const unsigned char *data; + PACKET tmp = *pkt; + + if (!PACKET_get_net_2(&tmp, &length) || + !PACKET_get_bytes(&tmp, &data, (size_t)length) || + PACKET_remaining(&tmp) != 0) { + return 0; + } + + *pkt = tmp; + subpkt->curr = data; + subpkt->remaining = length; + + return 1; +} + +/* * Reads a variable-length vector prefixed with a three-byte length, and stores * the contents in |subpkt|. |pkt| can equal |subpkt|. * Data is not copied: the |subpkt| packet will share its underlying buffer with diff --git a/ssl/s3_enc.c b/ssl/s3_enc.c index d4d64d0e5e..1c493e2807 100644 --- a/ssl/s3_enc.c +++ b/ssl/s3_enc.c @@ -667,6 +667,8 @@ int ssl3_alert_code(int code) return (TLS1_AD_UNKNOWN_PSK_IDENTITY); case SSL_AD_INAPPROPRIATE_FALLBACK: return (TLS1_AD_INAPPROPRIATE_FALLBACK); + case SSL_AD_NO_APPLICATION_PROTOCOL: + return (TLS1_AD_NO_APPLICATION_PROTOCOL); default: return (-1); } diff --git a/ssl/t1_enc.c b/ssl/t1_enc.c index 1b2820bff9..21eb3283da 100644 --- a/ssl/t1_enc.c +++ b/ssl/t1_enc.c @@ -792,6 +792,8 @@ int tls1_alert_code(int code) return (TLS1_AD_UNKNOWN_PSK_IDENTITY); case SSL_AD_INAPPROPRIATE_FALLBACK: return (TLS1_AD_INAPPROPRIATE_FALLBACK); + case SSL_AD_NO_APPLICATION_PROTOCOL: + return (TLS1_AD_NO_APPLICATION_PROTOCOL); default: return (-1); } diff --git a/ssl/t1_lib.c b/ssl/t1_lib.c index f02317e09f..3aa01db7e5 100644 --- a/ssl/t1_lib.c +++ b/ssl/t1_lib.c @@ -1732,63 +1732,62 @@ unsigned char *ssl_add_serverhello_tlsext(SSL *s, unsigned char *buf, } /* - * tls1_alpn_handle_client_hello is called to process the ALPN extension in a - * ClientHello. data: the contents of the extension, not including the type - * and length. data_len: the number of bytes in |data| al: a pointer to the - * alert value to send in the event of a non-zero return. returns: 0 on - * success. + * Process the ALPN extension in a ClientHello. + * pkt: the contents of the ALPN extension, not including type and length. + * al: a pointer to the alert value to send in the event of a failure. + * returns: 1 on success, 0 on error. */ static int tls1_alpn_handle_client_hello(SSL *s, PACKET *pkt, int *al) { - unsigned int data_len; - unsigned int proto_len; const unsigned char *selected; - const unsigned char *data; unsigned char selected_len; int r; + PACKET protocol_list, save_protocol_list, protocol; - if (s->ctx->alpn_select_cb == NULL) - return 0; + *al = SSL_AD_DECODE_ERROR; - /* - * data should contain a uint16 length followed by a series of 8-bit, - * length-prefixed strings. - */ - if (!PACKET_get_net_2(pkt, &data_len) - || PACKET_remaining(pkt) != data_len - || !PACKET_peek_bytes(pkt, &data, data_len)) - goto parse_error; + if (!PACKET_as_length_prefixed_2(pkt, &protocol_list) + || PACKET_remaining(&protocol_list) < 2) { + return 0; + } + save_protocol_list = protocol_list; do { - if (!PACKET_get_1(pkt, &proto_len) - || proto_len == 0 - || !PACKET_forward(pkt, proto_len)) - goto parse_error; - } while (PACKET_remaining(pkt)); + /* Protocol names can't be empty. */ + if (!PACKET_get_length_prefixed_1(&protocol_list, &protocol) + || PACKET_remaining(&protocol) == 0) { + return 0; + } + } while (PACKET_remaining(&protocol_list) != 0); - r = s->ctx->alpn_select_cb(s, &selected, &selected_len, data, data_len, + if (s->ctx->alpn_select_cb == NULL) + return 1; + + r = s->ctx->alpn_select_cb(s, &selected, &selected_len, + PACKET_data(&save_protocol_list), + PACKET_remaining(&save_protocol_list), s->ctx->alpn_select_cb_arg); if (r == SSL_TLSEXT_ERR_OK) { OPENSSL_free(s->s3->alpn_selected); s->s3->alpn_selected = OPENSSL_malloc(selected_len); if (s->s3->alpn_selected == NULL) { *al = SSL_AD_INTERNAL_ERROR; - return -1; + return 0; } memcpy(s->s3->alpn_selected, selected, selected_len); s->s3->alpn_selected_len = selected_len; + } else { + *al = SSL_AD_NO_APPLICATION_PROTOCOL; + return 0; } - return 0; - parse_error: - *al = SSL_AD_DECODE_ERROR; - return -1; + return 1; } #ifndef OPENSSL_NO_EC /*- * ssl_check_for_safari attempts to fingerprint Safari using OS X - * SecureTransport using the TLS extension block in |d|, of length |n|. + * SecureTransport using the TLS extension block in |pkt|. * Safari, since 10.6, sends exactly these extensions, in this order: * SNI, * elliptic_curves @@ -1801,9 +1800,9 @@ static int tls1_alpn_handle_client_hello(SSL *s, PACKET *pkt, int *al) */ static void ssl_check_for_safari(SSL *s, const PACKET *pkt) { - unsigned int type, size; - const unsigned char *eblock1, *eblock2; - PACKET tmppkt; + unsigned int type; + PACKET sni, tmppkt; + size_t ext_len; static const unsigned char kSafariExtensionsBlock[] = { 0x00, 0x0a, /* elliptic_curves extension */ @@ -1817,10 +1816,7 @@ static void ssl_check_for_safari(SSL *s, const PACKET *pkt) 0x00, 0x02, /* 2 bytes */ 0x01, /* 1 point format */ 0x00, /* uncompressed */ - }; - - /* The following is only present in TLS 1.2 */ - static const unsigned char kSafariTLS12ExtensionsBlock[] = { + /* The following is only present in TLS 1.2 */ 0x00, 0x0d, /* signature_algorithms */ 0x00, 0x0c, /* 12 bytes */ 0x00, 0x0a, /* 10 bytes */ @@ -1831,51 +1827,46 @@ static void ssl_check_for_safari(SSL *s, const PACKET *pkt) 0x02, 0x03, /* SHA-1/ECDSA */ }; + /* Length of the common prefix (first two extensions). */ + static const size_t kSafariCommonExtensionsLength = 18; + tmppkt = *pkt; if (!PACKET_forward(&tmppkt, 2) - || !PACKET_get_net_2(&tmppkt, &type) - || !PACKET_get_net_2(&tmppkt, &size) - || !PACKET_forward(&tmppkt, size)) + || !PACKET_get_net_2(&tmppkt, &type) + || !PACKET_get_length_prefixed_2(&tmppkt, &sni)) { return; + } if (type != TLSEXT_TYPE_server_name) return; - if (TLS1_get_client_version(s) >= TLS1_2_VERSION) { - const size_t len1 = sizeof(kSafariExtensionsBlock); - const size_t len2 = sizeof(kSafariTLS12ExtensionsBlock); - - if (!PACKET_get_bytes(&tmppkt, &eblock1, len1) - || !PACKET_get_bytes(&tmppkt, &eblock2, len2) - || PACKET_remaining(&tmppkt)) - return; - if (memcmp(eblock1, kSafariExtensionsBlock, len1) != 0) - return; - if (memcmp(eblock2, kSafariTLS12ExtensionsBlock, len2) != 0) - return; - } else { - const size_t len = sizeof(kSafariExtensionsBlock); - - if (!PACKET_get_bytes(&tmppkt, &eblock1, len) - || PACKET_remaining(&tmppkt)) - return; - if (memcmp(eblock1, kSafariExtensionsBlock, len) != 0) - return; - } + ext_len = TLS1_get_client_version(s) >= TLS1_2_VERSION ? + sizeof(kSafariExtensionsBlock) : kSafariCommonExtensionsLength; - s->s3->is_probably_safari = 1; + s->s3->is_probably_safari = PACKET_equal(&tmppkt, kSafariExtensionsBlock, + ext_len); } #endif /* !OPENSSL_NO_EC */ +/* + * Parse ClientHello extensions and stash extension info in various parts of + * the SSL object. Verify that there are no duplicate extensions. + * + * Behaviour upon resumption is extension-specific. If the extension has no + * effect during resumption, it is parsed (to verify its format) but otherwise + * ignored. + * + * Consumes the entire packet in |pkt|. Returns 1 on success and 0 on failure. + * Upon failure, sets |al| to the appropriate alert. + */ static int ssl_scan_clienthello_tlsext(SSL *s, PACKET *pkt, int *al) { unsigned int type; - unsigned int size; - unsigned int len; - const unsigned char *data; int renegotiate_seen = 0; + PACKET extensions; + *al = SSL_AD_DECODE_ERROR; s->servername_done = 0; s->tlsext_status_type = -1; #ifndef OPENSSL_NO_NEXTPROTONEG @@ -1911,29 +1902,29 @@ static int ssl_scan_clienthello_tlsext(SSL *s, PACKET *pkt, int *al) if (PACKET_remaining(pkt) == 0) goto ri_check; - if (!PACKET_get_net_2(pkt, &len)) - goto err; - - if (PACKET_remaining(pkt) != len) - goto err; - - if (!tls1_check_duplicate_extensions(pkt)) - goto err; + if (!PACKET_as_length_prefixed_2(pkt, &extensions)) + return 0; - while (PACKET_get_net_2(pkt, &type) && PACKET_get_net_2(pkt, &size)) { - PACKET subpkt; + if (!tls1_check_duplicate_extensions(&extensions)) + return 0; - if (!PACKET_peek_bytes(pkt, &data, size)) - goto err; + /* + * We parse all extensions to ensure the ClientHello is well-formed but, + * unless an extension specifies otherwise, we ignore extensions upon + * resumption. + */ + while (PACKET_get_net_2(&extensions, &type)) { + PACKET extension; + if (!PACKET_get_length_prefixed_2(&extensions, &extension)) + return 0; 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; + s->tlsext_debug_cb(s, 0, type, PACKET_data(&extension), + PACKET_remaining(&extension), + s->tlsext_debug_arg); if (type == TLSEXT_TYPE_renegotiate) { - if (!ssl_parse_clienthello_renegotiate_ext(s, &subpkt, al)) + if (!ssl_parse_clienthello_renegotiate_ext(s, &extension, al)) return 0; renegotiate_seen = 1; } else if (s->version == SSL3_VERSION) { @@ -1964,219 +1955,185 @@ static int ssl_scan_clienthello_tlsext(SSL *s, PACKET *pkt, int *al) */ else if (type == TLSEXT_TYPE_server_name) { - const unsigned char *sdata; unsigned int servname_type; - unsigned int dsize; - PACKET ssubpkt; - - if (!PACKET_get_net_2(&subpkt, &dsize) - || !PACKET_get_sub_packet(&subpkt, &ssubpkt, dsize)) - goto err; - - 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) - switch (servname_type) { - case TLSEXT_NAMETYPE_host_name: - if (!s->hit) { - if (s->session->tlsext_hostname) - goto err; - - if (len > TLSEXT_MAXLEN_host_name) { - *al = TLS1_AD_UNRECOGNIZED_NAME; - return 0; - } - if ((s->session->tlsext_hostname = - OPENSSL_malloc(len + 1)) == NULL) { - *al = TLS1_AD_INTERNAL_ERROR; - return 0; - } - 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); - s->session->tlsext_hostname = NULL; - *al = TLS1_AD_UNRECOGNIZED_NAME; - return 0; - } - s->servername_done = 1; - - } 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; - } + PACKET sni, hostname; + + if (!PACKET_as_length_prefixed_2(&extension, &sni) + /* ServerNameList must be at least 1 byte long. */ + || PACKET_remaining(&sni) == 0) { + return 0; } - /* We shouldn't have any bytes left */ - if (PACKET_remaining(&ssubpkt) != 0) - goto err; + /* + * Although the server_name extension was intended to be + * extensible to new name types, RFC 4366 defined the + * syntax inextensibly and OpenSSL 1.0.x parses it as + * such. + * RFC 6066 corrected the mistake but adding new name types + * is nevertheless no longer feasible, so act as if no other + * SNI types can exist, to simplify parsing. + * + * Also note that the RFC permits only one SNI value per type, + * i.e., we can only have a single hostname. + */ + if (!PACKET_get_1(&sni, &servname_type) + || servname_type != TLSEXT_NAMETYPE_host_name + || !PACKET_as_length_prefixed_2(&sni, &hostname)) { + return 0; + } + + if (!s->hit) { + if (PACKET_remaining(&hostname) > TLSEXT_MAXLEN_host_name) { + *al = TLS1_AD_UNRECOGNIZED_NAME; + return 0; + } + + if (PACKET_contains_zero_byte(&hostname)) { + *al = TLS1_AD_UNRECOGNIZED_NAME; + return 0; + } + + if (!PACKET_strndup(&hostname, &s->session->tlsext_hostname)) { + *al = TLS1_AD_INTERNAL_ERROR; + return 0; + } + + s->servername_done = 1; + } else { + /* + * TODO(openssl-team): if the SNI doesn't match, we MUST + * fall back to a full handshake. + */ + s->servername_done = s->session->tlsext_hostname + && PACKET_equal(&hostname, s->session->tlsext_hostname, + strlen(s->session->tlsext_hostname)); + } } #ifndef OPENSSL_NO_SRP else if (type == TLSEXT_TYPE_srp) { - 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; - 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 - || PACKET_remaining(&subpkt)) - goto err; + PACKET srp_I; + + if (!PACKET_as_length_prefixed_1(&extension, &srp_I)) + return 0; + + if (PACKET_contains_zero_byte(&srp_I)) + return 0; + + /* + * TODO(openssl-team): currently, we re-authenticate the user + * upon resumption. Instead, we MUST ignore the login. + */ + if (!PACKET_strndup(&srp_I, &s->srp_ctx.login)) { + *al = TLS1_AD_INTERNAL_ERROR; + return 0; + } } #endif #ifndef OPENSSL_NO_EC else if (type == TLSEXT_TYPE_ec_point_formats) { - unsigned int ecpointformatlist_length; + PACKET ec_point_format_list; - if (!PACKET_get_1(&subpkt, &ecpointformatlist_length) - || ecpointformatlist_length == 0) - goto err; + if (!PACKET_as_length_prefixed_1(&extension, + &ec_point_format_list) + || PACKET_remaining(&ec_point_format_list) == 0) { + return 0; + } if (!s->hit) { - OPENSSL_free(s->session->tlsext_ecpointformatlist); - s->session->tlsext_ecpointformatlist = NULL; - s->session->tlsext_ecpointformatlist_length = 0; - if ((s->session->tlsext_ecpointformatlist = - OPENSSL_malloc(ecpointformatlist_length)) == NULL) { + if (!PACKET_memdup(&ec_point_format_list, + &s->session->tlsext_ecpointformatlist, + &s->session->tlsext_ecpointformatlist_length)) { *al = TLS1_AD_INTERNAL_ERROR; return 0; } - s->session->tlsext_ecpointformatlist_length = - 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 int ellipticcurvelist_length; + PACKET elliptic_curve_list; - /* 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; + /* Each NamedCurve is 2 bytes and we must have at least 1. */ + if (!PACKET_as_length_prefixed_2(&extension, + &elliptic_curve_list) + || PACKET_remaining(&elliptic_curve_list) == 0 + || (PACKET_remaining(&elliptic_curve_list) % 2) != 0) { + return 0; + } if (!s->hit) { - if (s->session->tlsext_ellipticcurvelist) - goto err; - - s->session->tlsext_ellipticcurvelist_length = 0; - if ((s->session->tlsext_ellipticcurvelist = - OPENSSL_malloc(ellipticcurvelist_length)) == NULL) { + if (!PACKET_memdup(&elliptic_curve_list, + &s->session->tlsext_ellipticcurvelist, + &s->session->tlsext_ellipticcurvelist_length)) { *al = TLS1_AD_INTERNAL_ERROR; return 0; } - s->session->tlsext_ellipticcurvelist_length = - 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 (!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))) { + if (s->tls_session_ticket_ext_cb && + !s->tls_session_ticket_ext_cb(s, PACKET_data(&extension), + PACKET_remaining(&extension), + s->tls_session_ticket_ext_cb_arg)) { *al = TLS1_AD_INTERNAL_ERROR; return 0; } } else if (type == TLSEXT_TYPE_signature_algorithms) { - 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) != 0 - || !tls1_save_sigalgs(s, data, dsize)) { - goto err; + PACKET supported_sig_algs; + + if (!PACKET_as_length_prefixed_2(&extension, &supported_sig_algs) + || (PACKET_remaining(&supported_sig_algs) % 2) != 0 + || PACKET_remaining(&supported_sig_algs) == 0) { + return 0; + } + + if (!s->hit) { + if (!tls1_save_sigalgs(s, PACKET_data(&supported_sig_algs), + PACKET_remaining(&supported_sig_algs))) { + return 0; + } } } else if (type == TLSEXT_TYPE_status_request) { - PACKET ssubpkt; + const unsigned char *ext_data; - if (!PACKET_get_1(&subpkt, - (unsigned int *)&s->tlsext_status_type)) - goto err; + if (!PACKET_get_1(&extension, + (unsigned int *)&s->tlsext_status_type)) { + return 0; + } if (s->tlsext_status_type == TLSEXT_STATUSTYPE_ocsp) { - const unsigned char *sdata; - unsigned int dsize; - /* Read in responder_id_list */ - if (!PACKET_get_net_2(&subpkt, &dsize) - || !PACKET_get_sub_packet(&subpkt, &ssubpkt, dsize)) - goto err; - - while (PACKET_remaining(&ssubpkt)) { + PACKET responder_id_list, exts; + if (!PACKET_get_length_prefixed_2(&extension, &responder_id_list)) + return 0; + + while (PACKET_remaining(&responder_id_list) > 0) { OCSP_RESPID *id; - unsigned int idsize; + PACKET responder_id; + const unsigned char *id_data; - if (PACKET_remaining(&ssubpkt) < 4 - || !PACKET_get_net_2(&ssubpkt, &idsize) - || !PACKET_get_bytes(&ssubpkt, &data, idsize)) { - goto err; + if (!PACKET_get_length_prefixed_2(&responder_id_list, + &responder_id) + || PACKET_remaining(&responder_id) == 0) { + return 0; } - sdata = data; - data += idsize; - id = d2i_OCSP_RESPID(NULL, &sdata, idsize); - if (!id) - goto err; - if (data != sdata) { - OCSP_RESPID_free(id); - goto err; + + if (s->tlsext_ocsp_ids == NULL + && (s->tlsext_ocsp_ids = + sk_OCSP_RESPID_new_null()) == NULL) { + *al = SSL_AD_INTERNAL_ERROR; + return 0; } - if (!s->tlsext_ocsp_ids - && !(s->tlsext_ocsp_ids = - sk_OCSP_RESPID_new_null())) { + + id_data = PACKET_data(&responder_id); + id = d2i_OCSP_RESPID(NULL, &id_data, + PACKET_remaining(&responder_id)); + if (id == NULL) + return 0; + + if (id_data != PACKET_end(&responder_id)) { OCSP_RESPID_free(id); - *al = SSL_AD_INTERNAL_ERROR; return 0; } + if (!sk_OCSP_RESPID_push(s->tlsext_ocsp_ids, id)) { OCSP_RESPID_free(id); *al = SSL_AD_INTERNAL_ERROR; @@ -2185,33 +2142,34 @@ static int ssl_scan_clienthello_tlsext(SSL *s, PACKET *pkt, int *al) } /* Read in request_extensions */ - if (!PACKET_get_net_2(&subpkt, &dsize) - || !PACKET_get_bytes(&subpkt, &data, dsize) - || PACKET_remaining(&subpkt)) { - goto err; - } - sdata = data; - if (dsize > 0) { + if (!PACKET_as_length_prefixed_2(&extension, &exts)) + return 0; + + if (PACKET_remaining(&exts) > 0) { + ext_data = PACKET_data(&exts); sk_X509_EXTENSION_pop_free(s->tlsext_ocsp_exts, X509_EXTENSION_free); s->tlsext_ocsp_exts = - d2i_X509_EXTENSIONS(NULL, &sdata, dsize); - if (!s->tlsext_ocsp_exts || (data + dsize != sdata)) - goto err; + d2i_X509_EXTENSIONS(NULL, &ext_data, + PACKET_remaining(&exts)); + if (s->tlsext_ocsp_exts == NULL + || ext_data != PACKET_end(&exts)) { + return 0; + } } - } /* * We don't know what to do with any other type * so ignore it. */ - else + } else { s->tlsext_status_type = -1; + } } #ifndef OPENSSL_NO_HEARTBEATS else if (SSL_IS_DTLS(s) && type == TLSEXT_TYPE_heartbeat) { unsigned int hbtype; - if (!PACKET_get_1(&subpkt, &hbtype) - || PACKET_remaining(&subpkt)) { + if (!PACKET_get_1(&extension, &hbtype) + || PACKET_remaining(&extension)) { *al = SSL_AD_DECODE_ERROR; return 0; } @@ -2255,8 +2213,8 @@ static int ssl_scan_clienthello_tlsext(SSL *s, PACKET *pkt, int *al) #endif 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, &subpkt, al) != 0) + s->s3->tmp.finish_md_len == 0) { + if (!tls1_alpn_handle_client_hello(s, &extension, al)) return 0; #ifndef OPENSSL_NO_NEXTPROTONEG /* ALPN takes precedence over NPN. */ @@ -2268,7 +2226,7 @@ static int ssl_scan_clienthello_tlsext(SSL *s, PACKET *pkt, int *al) #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, &subpkt, al)) + if (ssl_parse_clienthello_use_srtp_ext(s, &extension, al)) return 0; } #endif @@ -2289,14 +2247,17 @@ static int ssl_scan_clienthello_tlsext(SSL *s, PACKET *pkt, int *al) * ServerHello may be later returned. */ else if (!s->hit) { - if (custom_ext_parse(s, 1, type, data, size, al) <= 0) + if (custom_ext_parse(s, 1, type, PACKET_data(&extension), + PACKET_remaining(&extension), al) <= 0) return 0; } } - /* Spurious data on the end */ - if (PACKET_remaining(pkt) != 0) - goto err; + if (PACKET_remaining(pkt) != 0) { + /* tls1_check_duplicate_extensions should ensure this never happens. */ + *al = SSL_AD_INTERNAL_ERROR; + return 0; + } ri_check: @@ -2310,10 +2271,13 @@ static int ssl_scan_clienthello_tlsext(SSL *s, PACKET *pkt, int *al) return 0; } + /* + * This function currently has no state to clean up, so it returns directly. + * If parsing fails at any point, the function returns early. + * The SSL object may be left with partial data from extensions, but it must + * then no longer be used, and clearing it up will free the leftovers. + */ return 1; -err: - *al = SSL_AD_DECODE_ERROR; - return 0; } int ssl_parse_clienthello_tlsext(SSL *s, PACKET *pkt) @@ -2324,7 +2288,6 @@ int ssl_parse_clienthello_tlsext(SSL *s, PACKET *pkt) ssl3_send_alert(s, SSL3_AL_FATAL, al); return 0; } - if (ssl_check_clienthello_tlsext_early(s) <= 0) { SSLerr(SSL_F_SSL_PARSE_CLIENTHELLO_TLSEXT, SSL_R_CLIENTHELLO_TLSEXT); return 0; |