diff options
-rw-r--r-- | lib/Makefile.am | 4 | ||||
-rw-r--r-- | lib/algorithms/ciphersuites.c | 3 | ||||
-rw-r--r-- | lib/extensions.c | 427 | ||||
-rw-r--r-- | lib/extensions.h | 21 | ||||
-rw-r--r-- | lib/extv.c | 344 | ||||
-rw-r--r-- | lib/extv.h | 69 | ||||
-rw-r--r-- | lib/gnutls_int.h | 38 | ||||
-rw-r--r-- | lib/handshake.c | 93 | ||||
-rw-r--r-- | lib/state.c | 2 | ||||
-rw-r--r-- | lib/x509.c | 3 |
10 files changed, 575 insertions, 429 deletions
diff --git a/lib/Makefile.am b/lib/Makefile.am index 3442a6c443..98256a03a6 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -79,7 +79,7 @@ COBJECTS = range.c record.c compress.c debug.c cipher.c \ safe-memfuncs.c system/inet_pton.c atfork.c atfork.h randomart.c \ system-keys.h urls.c urls.h prf.c auto-verify.c dh-session.c \ cert-session.c handshake-checks.c dtls-sw.c dh-primes.c openpgp_compat.c \ - crypto-selftests.c crypto-selftests-pk.c secrets.c + crypto-selftests.c crypto-selftests-pk.c secrets.c extv.c if WINDOWS COBJECTS += system/keys-win.c @@ -110,7 +110,7 @@ HFILES = abstract_int.h debug.h cipher.h \ srp.h auth/srp_kx.h auth/srp_passwd.h \ file.h supplemental.h crypto.h random.h system.h\ locks.h mbuffers.h ecc.h pin.h fips.h \ - priority_options.h secrets.h + priority_options.h secrets.h extv.h if ENABLE_PKCS11 HFILES += pkcs11_int.h pkcs11x.h diff --git a/lib/algorithms/ciphersuites.c b/lib/algorithms/ciphersuites.c index 0c562012f6..6448d7ab79 100644 --- a/lib/algorithms/ciphersuites.c +++ b/lib/algorithms/ciphersuites.c @@ -31,6 +31,7 @@ #include <auth/anon.h> #include <auth/psk.h> #include <ext/safe_renegotiation.h> +#include "extv.h" #ifndef ENABLE_SSL3 # define GNUTLS_SSL3 GNUTLS_TLS1 @@ -1457,7 +1458,7 @@ _gnutls_figure_common_ciphersuite(gnutls_session_t session, * by RFC4492, probably to allow SSLv2 hellos negotiate elliptic curve * ciphersuites */ if (session->internals.cand_ec_group == NULL && - _gnutls_extension_list_check(session, GNUTLS_EXTENSION_SUPPORTED_ECC) < 0) { + _gnutls_extv_check_saved(&session->internals.hello_ext, GNUTLS_EXTENSION_SUPPORTED_ECC) < 0) { session->internals.cand_ec_group = _gnutls_id_to_group(DEFAULT_EC_GROUP); } diff --git a/lib/extensions.c b/lib/extensions.c index c3ff1f5bde..2b94f016b7 100644 --- a/lib/extensions.c +++ b/lib/extensions.c @@ -48,14 +48,11 @@ #include <ext/key_share.h> #include <ext/etm.h> #include <num.h> - -static void -unset_ext_data(gnutls_session_t session, const struct extension_entry_st *, unsigned idx); +#include "extv.h" static int ext_register(extension_entry_st * mod); -static void unset_resumed_ext_data(gnutls_session_t session, const struct extension_entry_st *, unsigned idx); -static extension_entry_st const *extfunc[MAX_EXT_TYPES+1] = { +extension_entry_st const *_gnutls_extfunc[MAX_EXT_TYPES+1] = { &ext_mod_max_record_size, &ext_mod_ext_master_secret, &ext_mod_supported_versions, @@ -91,22 +88,22 @@ static extension_entry_st const *extfunc[MAX_EXT_TYPES+1] = { NULL }; -static const extension_entry_st * -_gnutls_ext_ptr(gnutls_session_t session, uint16_t id, gnutls_ext_parse_type_t parse_type) +const extension_entry_st * +_gnutls_ext_ptr(tls_ext_vals_st *v, uint16_t id, gnutls_ext_parse_type_t parse_type) { unsigned i; const extension_entry_st *e; - for (i=0;i<session->internals.rexts_size;i++) { - if (session->internals.rexts[i].id == id) { - e = &session->internals.rexts[i]; + for (i=0;i<v->rexts_size;i++) { + if (v->rexts[i].id == id) { + e = &v->rexts[i]; goto done; } } - for (i = 0; extfunc[i] != NULL; i++) { - if (extfunc[i]->id == id) { - e = extfunc[i]; + for (i = 0; _gnutls_extfunc[i] != NULL; i++) { + if (_gnutls_extfunc[i]->id == id) { + e = _gnutls_extfunc[i]; goto done; } } @@ -134,282 +131,17 @@ const char *gnutls_ext_get_name(unsigned int ext) { size_t i; - for (i = 0; extfunc[i] != NULL; i++) - if (extfunc[i]->id == ext) - return extfunc[i]->name; + for (i = 0; _gnutls_extfunc[i] != NULL; i++) + if (_gnutls_extfunc[i]->id == ext) + return _gnutls_extfunc[i]->name; return NULL; } -/* Checks if the extension @id provided has been requested - * by us (in client side). In that case it returns zero, - * otherwise a negative error value. - */ -int -_gnutls_extension_list_check(gnutls_session_t session, uint16_t id) -{ - unsigned i; - - for (i = 0; i < session->internals.used_exts_size; i++) { - if (id == session->internals.used_exts[i]->id) - return 0; - } - - return GNUTLS_E_RECEIVED_ILLEGAL_EXTENSION; -} - -/* Adds the extension we want to send in the extensions list. - * This list is used in client side to check whether the (later) received - * extensions are the ones we requested. - * - * In server side, this list is used to ensure we don't send - * extensions that we didn't receive a corresponding value. - * - * Returns zero if failed, non-zero on success. - */ -static unsigned _gnutls_extension_list_add(gnutls_session_t session, const struct extension_entry_st *e, unsigned check_dup) -{ - unsigned i; - - if (check_dup) { - for (i=0;i<session->internals.used_exts_size;i++) { - if (session->internals.used_exts[i]->id == e->id) - return 0; - } - } - - if (session->internals.used_exts_size < MAX_EXT_TYPES) { - session->internals.used_exts[session-> - internals.used_exts_size] - = e; - session->internals.used_exts_size++; - return 1; - } else { - _gnutls_handshake_log - ("extensions: Increase MAX_EXT_TYPES\n"); - return 0; - } -} void _gnutls_extension_list_add_sr(gnutls_session_t session) { - _gnutls_extension_list_add(session, &ext_mod_sr, 1); -} - - -int -_gnutls_parse_extensions(gnutls_session_t session, - gnutls_ext_flags_t msg, - gnutls_ext_parse_type_t parse_type, - const uint8_t * data, int data_size) -{ - int next, ret; - int pos = 0; - uint16_t id; - const uint8_t *sdata; - const extension_entry_st *ext; - uint16_t size; - - if (data_size == 0) - return 0; - - DECR_LENGTH_RET(data_size, 2, GNUTLS_E_UNEXPECTED_EXTENSIONS_LENGTH); - next = _gnutls_read_uint16(data); - pos += 2; - - DECR_LENGTH_RET(data_size, next, GNUTLS_E_UNEXPECTED_EXTENSIONS_LENGTH); - - if (next == 0 && data_size == 0) /* field is present, but has zero length? Ignore it. */ - return 0; - else if (data_size > 0) /* forbid unaccounted data */ - return gnutls_assert_val(GNUTLS_E_UNEXPECTED_EXTENSIONS_LENGTH); - - do { - DECR_LENGTH_RET(next, 2, GNUTLS_E_UNEXPECTED_EXTENSIONS_LENGTH); - id = _gnutls_read_uint16(&data[pos]); - pos += 2; - - if (session->security_parameters.entity == GNUTLS_CLIENT) { - if ((ret = - _gnutls_extension_list_check(session, id)) < 0) { - _gnutls_debug_log("EXT[%p]: Received unexpected extension '%s/%d'\n", session, - gnutls_ext_get_name(id), (int)id); - gnutls_assert(); - return ret; - } - } - - DECR_LENGTH_RET(next, 2, GNUTLS_E_UNEXPECTED_EXTENSIONS_LENGTH); - size = _gnutls_read_uint16(&data[pos]); - pos += 2; - - DECR_LENGTH_RET(next, size, GNUTLS_E_UNEXPECTED_EXTENSIONS_LENGTH); - sdata = &data[pos]; - pos += size; - - ext = _gnutls_ext_ptr(session, id, parse_type); - if (ext == NULL || ext->recv_func == NULL) { - _gnutls_handshake_log - ("EXT[%p]: Ignoring extension '%s/%d'\n", session, - gnutls_ext_get_name(id), id); - - continue; - } - - - if ((ext->validity & msg) == 0) { - - _gnutls_debug_log("EXT[%p]: Received unexpected extension (%s/%d) for '%s'\n", session, - gnutls_ext_get_name(id), (int)id, - ext_msg_validity_to_str(msg)); - return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_EXTENSION); - } - - if (session->security_parameters.entity == GNUTLS_SERVER) { - ret = _gnutls_extension_list_add(session, ext, 1); - if (ret == 0) - return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_EXTENSION); - } - - _gnutls_handshake_log - ("EXT[%p]: Parsing extension '%s/%d' (%d bytes)\n", - session, gnutls_ext_get_name(id), id, - size); - - if ((ret = ext->recv_func(session, sdata, size)) < 0) { - gnutls_assert(); - return ret; - } - } - while (next > 2); - - /* forbid leftovers */ - if (next > 0) - return gnutls_assert_val(GNUTLS_E_UNEXPECTED_EXTENSIONS_LENGTH); - - return 0; - -} - -static -int send_extension(gnutls_session_t session, const extension_entry_st *p, - gnutls_buffer_st *extdata, - gnutls_ext_flags_t msg, - gnutls_ext_parse_type_t parse_type) -{ - int size_pos, appended, ret; - size_t size_prev; - - if (p->send_func == NULL) - return 0; - - if (parse_type != GNUTLS_EXT_ANY - && p->parse_type != parse_type) - return 0; - - if ((msg & p->validity) == 0) { - _gnutls_handshake_log("EXT[%p]: Not sending extension (%s/%d) for '%s'\n", session, - gnutls_ext_get_name(p->id), (int)p->id, - ext_msg_validity_to_str(msg)); - return 0; - } - - /* ensure we don't send something twice (i.e, overriden extensions in - * client), and ensure we are sending only what we received in server. */ - ret = _gnutls_extension_list_check(session, p->id); - - if (session->security_parameters.entity == GNUTLS_SERVER) { - if (ret < 0) /* not advertized */ - return 0; - } else { - if (ret == 0) /* already sent */ - return 0; - } - - ret = _gnutls_buffer_append_prefix(extdata, 16, p->id); - if (ret < 0) - return gnutls_assert_val(ret); - - size_pos = extdata->length; - ret = _gnutls_buffer_append_prefix(extdata, 16, 0); - if (ret < 0) - return gnutls_assert_val(ret); - - size_prev = extdata->length; - ret = p->send_func(session, extdata); - if (ret < 0 && ret != GNUTLS_E_INT_RET_0) { - return gnutls_assert_val(ret); - } - - /* returning GNUTLS_E_INT_RET_0 means to send an empty - * extension of this type. - */ - appended = extdata->length - size_prev; - - if (appended > 0 || ret == GNUTLS_E_INT_RET_0) { - if (ret == GNUTLS_E_INT_RET_0) - appended = 0; - - /* write the real size */ - _gnutls_write_uint16(appended, - &extdata->data[size_pos]); - - /* add this extension to the extension list - */ - if (session->security_parameters.entity == GNUTLS_CLIENT) - _gnutls_extension_list_add(session, p, 0); - - _gnutls_handshake_log - ("EXT[%p]: Sending extension %s/%d (%d bytes)\n", - session, p->name, p->id, appended); - } else if (appended == 0) - extdata->length -= 4; /* reset type and size */ - - return 0; -} - -int -_gnutls_gen_extensions(gnutls_session_t session, - gnutls_buffer_st * extdata, - gnutls_ext_flags_t msg, - gnutls_ext_parse_type_t parse_type) -{ - int size; - int pos, ret; - size_t i, init_size = extdata->length; - - pos = extdata->length; /* we will store length later on */ - - ret = _gnutls_buffer_append_prefix(extdata, 16, 0); - if (ret < 0) - return gnutls_assert_val(ret); - - for (i=0; i < session->internals.rexts_size; i++) { - ret = send_extension(session, &session->internals.rexts[i], extdata, msg, parse_type); - if (ret < 0) - return gnutls_assert_val(ret); - } - - /* send_extension() ensures we don't send duplicates, in case - * of overriden extensions */ - for (i = 0; extfunc[i] != NULL; i++) { - ret = send_extension(session, extfunc[i], extdata, msg, parse_type); - if (ret < 0) - return gnutls_assert_val(ret); - } - - /* remove any initial data, and the size of the header */ - size = extdata->length - init_size - 2; - - if (size > UINT16_MAX) /* sent too many extensions */ - return gnutls_assert_val(GNUTLS_E_HANDSHAKE_TOO_LARGE); - - if (size > 0) - _gnutls_write_uint16(size, &extdata->data[pos]); - else if (size == 0) - extdata->length -= 2; /* the length bytes */ - - return size; + _gnutls_extv_add_saved(&session->internals.hello_ext, &ext_mod_sr, 1); } /* Global deinit and init of global extensions */ @@ -421,11 +153,11 @@ int _gnutls_ext_init(void) void _gnutls_ext_deinit(void) { unsigned i; - for (i = 0; extfunc[i] != NULL; i++) { - if (extfunc[i]->free_struct != 0) { - gnutls_free((void*)extfunc[i]->name); - gnutls_free((void*)extfunc[i]); - extfunc[i] = NULL; + for (i = 0; _gnutls_extfunc[i] != NULL; i++) { + if (_gnutls_extfunc[i]->free_struct != 0) { + gnutls_free((void*)_gnutls_extfunc[i]->name); + gnutls_free((void*)_gnutls_extfunc[i]); + _gnutls_extfunc[i] = NULL; } } } @@ -435,7 +167,7 @@ int ext_register(extension_entry_st * mod) { unsigned i = 0; - while(extfunc[i] != NULL) { + while(_gnutls_extfunc[i] != NULL) { i++; } @@ -443,8 +175,8 @@ int ext_register(extension_entry_st * mod) return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); } - extfunc[i] = mod; - extfunc[i+1] = NULL; + _gnutls_extfunc[i] = mod; + _gnutls_extfunc[i+1] = NULL; return GNUTLS_E_SUCCESS; } @@ -490,12 +222,13 @@ int _gnutls_ext_pack(gnutls_session_t session, gnutls_buffer_st *packed) int ret; int total_exts_pos; int exts = 0; + tls_ext_vals_st *v = &session->internals.hello_ext; total_exts_pos = packed->length; BUFFER_APPEND_NUM(packed, 0); - for (i = 0; i < session->internals.used_exts_size; i++) { - ret = pack_extension(session, session->internals.used_exts[i], packed); + for (i = 0; i < v->used_exts_size; i++) { + ret = pack_extension(session, v->used_exts[i], packed); if (ret < 0) return gnutls_assert_val(ret); @@ -515,19 +248,20 @@ _gnutls_ext_set_resumed_session_data(gnutls_session_t session, { int i; const struct extension_entry_st *ext; + tls_ext_vals_st *vals = &session->internals.hello_ext; - ext = _gnutls_ext_ptr(session, id, GNUTLS_EXT_ANY); + ext = _gnutls_ext_ptr(vals, id, GNUTLS_EXT_ANY); for (i = 0; i < MAX_EXT_TYPES; i++) { - if (session->internals.ext_data[i].id == id - || (!session->internals.ext_data[i].resumed_set && !session->internals.ext_data[i].set)) { + if (vals->ext_data[i].id == id + || (!vals->ext_data[i].resumed_set && !vals->ext_data[i].set)) { - if (session->internals.ext_data[i].resumed_set != 0) - unset_resumed_ext_data(session, ext, i); + if (vals->ext_data[i].resumed_set != 0) + _gnutls_extv_data_unset_resumed(&vals->ext_data[i], ext); - session->internals.ext_data[i].id = id; - session->internals.ext_data[i].resumed_priv = data; - session->internals.ext_data[i].resumed_set = 1; + vals->ext_data[i].id = id; + vals->ext_data[i].resumed_priv = data; + vals->ext_data[i].resumed_set = 1; return; } } @@ -549,7 +283,7 @@ int _gnutls_ext_unpack(gnutls_session_t session, gnutls_buffer_st * packed) cur_pos = packed->length; - ext = _gnutls_ext_ptr(session, id, GNUTLS_EXT_ANY); + ext = _gnutls_ext_ptr(&session->internals.hello_ext, id, GNUTLS_EXT_ANY); if (ext == NULL || ext->unpack_func == NULL) { gnutls_assert(); return GNUTLS_E_PARSING_ERROR; @@ -577,61 +311,30 @@ int _gnutls_ext_unpack(gnutls_session_t session, gnutls_buffer_st * packed) return ret; } -static void -unset_ext_data(gnutls_session_t session, const struct extension_entry_st *ext, unsigned idx) -{ - if (session->internals.ext_data[idx].set == 0) - return; - - if (ext && ext->deinit_func && session->internals.ext_data[idx].priv != NULL) - ext->deinit_func(session->internals.ext_data[idx].priv); - session->internals.ext_data[idx].set = 0; -} - void _gnutls_ext_unset_session_data(gnutls_session_t session, - uint16_t id) + uint16_t id) { int i; const struct extension_entry_st *ext; - ext = _gnutls_ext_ptr(session, id, GNUTLS_EXT_ANY); + ext = _gnutls_ext_ptr(&session->internals.hello_ext, id, GNUTLS_EXT_ANY); for (i = 0; i < MAX_EXT_TYPES; i++) { - if (session->internals.ext_data[i].id == id) { - unset_ext_data(session, ext, i); + if (session->internals.hello_ext.ext_data[i].id == id) { + _gnutls_extv_data_unset(&session->internals.hello_ext.ext_data[i], ext); return; } } } -static void unset_resumed_ext_data(gnutls_session_t session, const struct extension_entry_st *ext, unsigned idx) -{ - if (session->internals.ext_data[idx].resumed_set == 0) - return; - if (ext && ext->deinit_func && session->internals.ext_data[idx].resumed_priv) { - ext->deinit_func(session->internals.ext_data[idx].resumed_priv); - } - session->internals.ext_data[idx].resumed_set = 0; -} /* Deinitializes all data that are associated with TLS extensions. */ void _gnutls_ext_free_session_data(gnutls_session_t session) { - unsigned int i; - const struct extension_entry_st *ext; - - for (i = 0; i < MAX_EXT_TYPES; i++) { - if (!session->internals.ext_data[i].set && !session->internals.ext_data[i].resumed_set) - continue; - - ext = _gnutls_ext_ptr(session, session->internals.ext_data[i].id, GNUTLS_EXT_ANY); - - unset_ext_data(session, ext, i); - unset_resumed_ext_data(session, ext, i); - } + _gnutls_extv_deinit(&session->internals.hello_ext); } /* This function allows an extension to store data in the current session @@ -644,19 +347,20 @@ _gnutls_ext_set_session_data(gnutls_session_t session, uint16_t id, { unsigned int i; const struct extension_entry_st *ext; + tls_ext_vals_st *v = &session->internals.hello_ext; - ext = _gnutls_ext_ptr(session, id, GNUTLS_EXT_ANY); + ext = _gnutls_ext_ptr(v, id, GNUTLS_EXT_ANY); for (i = 0; i < MAX_EXT_TYPES; i++) { - if (session->internals.ext_data[i].id == id || - (!session->internals.ext_data[i].set && !session->internals.ext_data[i].resumed_set)) { + if (v->ext_data[i].id == id || + (!v->ext_data[i].set && !v->ext_data[i].resumed_set)) { - if (session->internals.ext_data[i].set != 0) { - unset_ext_data(session, ext, i); + if (v->ext_data[i].set != 0) { + _gnutls_extv_data_unset(&v->ext_data[i], ext); } - session->internals.ext_data[i].id = id; - session->internals.ext_data[i].priv = data; - session->internals.ext_data[i].set = 1; + v->ext_data[i].id = id; + v->ext_data[i].priv = data; + v->ext_data[i].set = 1; return; } } @@ -667,13 +371,14 @@ _gnutls_ext_get_session_data(gnutls_session_t session, uint16_t id, gnutls_ext_priv_data_t * data) { int i; + tls_ext_vals_st *v = &session->internals.hello_ext; for (i = 0; i < MAX_EXT_TYPES; i++) { - if (session->internals.ext_data[i].set != 0 && - session->internals.ext_data[i].id == id) + if (v->ext_data[i].set != 0 && + v->ext_data[i].id == id) { *data = - session->internals.ext_data[i].priv; + v->ext_data[i].priv; return 0; } } @@ -686,12 +391,13 @@ _gnutls_ext_get_resumed_session_data(gnutls_session_t session, gnutls_ext_priv_data_t * data) { int i; + tls_ext_vals_st *v = &session->internals.hello_ext; for (i = 0; i < MAX_EXT_TYPES; i++) { - if (session->internals.ext_data[i].resumed_set != 0 - && session->internals.ext_data[i].id == id) { + if (v->ext_data[i].resumed_set != 0 + && v->ext_data[i].id == id) { *data = - session->internals.ext_data[i].resumed_priv; + v->ext_data[i].resumed_priv; return 0; } } @@ -736,8 +442,8 @@ gnutls_ext_register(const char *name, int id, gnutls_ext_parse_type_t parse_type int ret; unsigned i; - for (i = 0; extfunc[i] != NULL; i++) { - if (extfunc[i]->id == id) + for (i = 0; _gnutls_extfunc[i] != NULL; i++) { + if (_gnutls_extfunc[i]->id == id) return gnutls_assert_val(GNUTLS_E_ALREADY_REGISTERED); } @@ -813,14 +519,15 @@ gnutls_session_ext_register(gnutls_session_t session, extension_entry_st tmp_mod; extension_entry_st *exts; unsigned i; + tls_ext_vals_st *v = &session->internals.hello_ext; /* reject handling any extensions which modify the TLS handshake * in any way, or are mapped to an exported API. */ - for (i = 0; extfunc[i] != NULL; i++) { - if (extfunc[i]->id == id) { + for (i = 0; _gnutls_extfunc[i] != NULL; i++) { + if (_gnutls_extfunc[i]->id == id) { if (!(flags & GNUTLS_EXT_FLAG_OVERRIDE_INTERNAL)) { return gnutls_assert_val(GNUTLS_E_ALREADY_REGISTERED); - } else if (extfunc[i]->cannot_be_overriden) { + } else if (_gnutls_extfunc[i]->cannot_be_overriden) { return gnutls_assert_val(GNUTLS_E_ALREADY_REGISTERED); } break; @@ -842,15 +549,15 @@ gnutls_session_ext_register(gnutls_session_t session, tmp_mod.validity = GNUTLS_EXT_FLAG_CLIENT_HELLO|GNUTLS_EXT_FLAG_TLS12_SERVER_HELLO|GNUTLS_EXT_FLAG_EE; } - exts = gnutls_realloc(session->internals.rexts, (session->internals.rexts_size+1)*sizeof(*exts)); + exts = gnutls_realloc(v->rexts, (v->rexts_size+1)*sizeof(*exts)); if (exts == NULL) { return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); } - session->internals.rexts = exts; + v->rexts = exts; - memcpy(&session->internals.rexts[session->internals.rexts_size], &tmp_mod, sizeof(extension_entry_st)); - session->internals.rexts_size++; + memcpy(&v->rexts[v->rexts_size], &tmp_mod, sizeof(extension_entry_st)); + v->rexts_size++; return 0; } diff --git a/lib/extensions.h b/lib/extensions.h index 3ef9d445c2..ce36a034a0 100644 --- a/lib/extensions.h +++ b/lib/extensions.h @@ -25,26 +25,20 @@ #include <gnutls/gnutls.h> -int _gnutls_parse_extensions(gnutls_session_t session, - gnutls_ext_flags_t msg, - gnutls_ext_parse_type_t parse_type, - const uint8_t * data, int data_size); -int _gnutls_gen_extensions(gnutls_session_t session, - gnutls_buffer_st * extdata, - gnutls_ext_flags_t msg, - gnutls_ext_parse_type_t); + +/* Session and generic TLS extensions handling functions */ + int _gnutls_ext_init(void); void _gnutls_ext_deinit(void); void _gnutls_extension_list_add_sr(gnutls_session_t session); -int _gnutls_extension_list_check(gnutls_session_t session, uint16_t type); void _gnutls_ext_free_session_data(gnutls_session_t session); /* functions to be used by extensions internally */ -void _gnutls_ext_unset_session_data(gnutls_session_t session, - uint16_t type); +void _gnutls_ext_unset_session_data(gnutls_session_t session, uint16_t id); + void _gnutls_ext_set_session_data(gnutls_session_t session, uint16_t type, gnutls_ext_priv_data_t); int _gnutls_ext_get_session_data(gnutls_session_t session, uint16_t type, @@ -118,4 +112,9 @@ typedef struct extension_entry_st { int _gnutls_ext_register(extension_entry_st *); +const extension_entry_st * +_gnutls_ext_ptr(tls_ext_vals_st *v, uint16_t id, gnutls_ext_parse_type_t parse_type); + +extern extension_entry_st const *_gnutls_extfunc[]; + #endif diff --git a/lib/extv.c b/lib/extv.c new file mode 100644 index 0000000000..fe9126465a --- /dev/null +++ b/lib/extv.c @@ -0,0 +1,344 @@ +/* + * Copyright (C) 2001-2016 Free Software Foundation, Inc. + * Copyright (C) 2015-2017 Red Hat, Inc. + * + * Author: Nikos Mavrogiannopoulos, Simon Josefsson + * + * This file is part of GnuTLS. + * + * The GnuTLS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ + +/* Functions that relate to TLS extension parsing. + */ + +#include "gnutls_int.h" +#include "extv.h" +#include "extensions.h" + +void +_gnutls_extv_data_unset_resumed(struct ext_data_st *data, const struct extension_entry_st *ext) +{ + if (data->resumed_set == 0) + return; + + if (ext && ext->deinit_func && data->resumed_priv) { + ext->deinit_func(data->resumed_priv); + } + data->resumed_set = 0; +} + +void +_gnutls_extv_data_unset(struct ext_data_st *data, const struct extension_entry_st *ext) +{ + if (data->set == 0) + return; + + if (ext && ext->deinit_func && data->priv != NULL) + ext->deinit_func(data->priv); + data->set = 0; +} + +void _gnutls_extv_deinit(tls_ext_vals_st *v) +{ + unsigned int i; + const struct extension_entry_st *ext; + + v->used_exts_size = 0; + + for (i = 0; i < MAX_EXT_TYPES; i++) { + if (!v->ext_data[i].set && !v->ext_data[i].resumed_set) + continue; + + ext = _gnutls_ext_ptr(v, v->ext_data[i].id, GNUTLS_EXT_ANY); + + _gnutls_extv_data_unset(&v->ext_data[i], ext); + _gnutls_extv_data_unset_resumed(&v->ext_data[i], ext); + } + + gnutls_free(v->rexts); + v->rexts = NULL; +} + +/* Checks if the extension @id provided has been requested + * by us (in client side). In that case it returns zero, + * otherwise a negative error value. + */ +int +_gnutls_extv_check_saved(tls_ext_vals_st *v, uint16_t id) +{ + unsigned i; + + for (i = 0; i < v->used_exts_size; i++) { + if (id == v->used_exts[i]->id) + return 0; + } + + return GNUTLS_E_RECEIVED_ILLEGAL_EXTENSION; +} + +/* Adds the extension we want to send in the extensions list. + * This list is used in client side to check whether the (later) received + * extensions are the ones we requested. + * + * In server side, this list is used to ensure we don't send + * extensions that we didn't receive a corresponding value. + * + * Returns zero if failed, non-zero on success. + */ +unsigned _gnutls_extv_add_saved(tls_ext_vals_st *v, const struct extension_entry_st *e, unsigned check_dup) +{ + unsigned i; + + if (check_dup) { + for (i=0;i<v->used_exts_size;i++) { + if (v->used_exts[i]->id == e->id) + return 0; + } + } + + if (v->used_exts_size < MAX_EXT_TYPES) { + v->used_exts[v->used_exts_size] = e; + v->used_exts_size++; + return 1; + } else { + _gnutls_handshake_log + ("extensions: Increase MAX_EXT_TYPES\n"); + return 0; + } +} + +int +_gnutls_extv_parse(gnutls_session_t session, + gnutls_ext_flags_t msg, + gnutls_ext_parse_type_t parse_type, + const uint8_t * data, int data_size, + tls_ext_vals_st *out, + unsigned extv_flags) +{ + int next, ret; + int pos = 0; + uint16_t id; + const uint8_t *sdata; + const extension_entry_st *ext; + uint16_t size; + + if (data_size == 0) + return 0; + + DECR_LENGTH_RET(data_size, 2, GNUTLS_E_UNEXPECTED_EXTENSIONS_LENGTH); + next = _gnutls_read_uint16(data); + pos += 2; + + DECR_LENGTH_RET(data_size, next, GNUTLS_E_UNEXPECTED_EXTENSIONS_LENGTH); + + if (next == 0 && data_size == 0) /* field is present, but has zero length? Ignore it. */ + return 0; + else if (data_size > 0) /* forbid unaccounted data */ + return gnutls_assert_val(GNUTLS_E_UNEXPECTED_EXTENSIONS_LENGTH); + + do { + DECR_LENGTH_RET(next, 2, GNUTLS_E_UNEXPECTED_EXTENSIONS_LENGTH); + id = _gnutls_read_uint16(&data[pos]); + pos += 2; + + if (extv_flags & EXTV_CHECK_UNADVERTIZED) { + if ((ret = + _gnutls_extv_check_saved(out, id)) < 0) { + _gnutls_debug_log("EXT[%p]: Received unexpected extension '%s/%d'\n", session, + gnutls_ext_get_name(id), (int)id); + gnutls_assert(); + return ret; + } + } + + DECR_LENGTH_RET(next, 2, GNUTLS_E_UNEXPECTED_EXTENSIONS_LENGTH); + size = _gnutls_read_uint16(&data[pos]); + pos += 2; + + DECR_LENGTH_RET(next, size, GNUTLS_E_UNEXPECTED_EXTENSIONS_LENGTH); + sdata = &data[pos]; + pos += size; + + ext = _gnutls_ext_ptr(out, id, parse_type); + if (ext == NULL || ext->recv_func == NULL) { + _gnutls_handshake_log + ("EXT[%p]: Ignoring extension '%s/%d'\n", session, + gnutls_ext_get_name(id), id); + + continue; + } + + + if ((ext->validity & msg) == 0) { + + _gnutls_debug_log("EXT[%p]: Received unexpected extension (%s/%d) for '%s'\n", session, + gnutls_ext_get_name(id), (int)id, + ext_msg_validity_to_str(msg)); + return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_EXTENSION); + } + + if (extv_flags & EXTV_SAVE_RECEIVED) { + ret = _gnutls_extv_add_saved(out, ext, 1); + if (ret == 0) + return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_EXTENSION); + } + + _gnutls_handshake_log + ("EXT[%p]: Parsing extension '%s/%d' (%d bytes)\n", + session, gnutls_ext_get_name(id), id, + size); + + if ((ret = ext->recv_func(session, sdata, size)) < 0) { + gnutls_assert(); + return ret; + } + } + while (next > 2); + + /* forbid leftovers */ + if (next > 0) + return gnutls_assert_val(GNUTLS_E_UNEXPECTED_EXTENSIONS_LENGTH); + + return 0; +} + +static +int send_extension(gnutls_session_t session, + tls_ext_vals_st *v, + const extension_entry_st *p, + gnutls_buffer_st *extdata, + gnutls_ext_flags_t msg, + gnutls_ext_parse_type_t parse_type, + unsigned extv_flags) +{ + int size_pos, appended, ret; + size_t size_prev; + + if (p->send_func == NULL) + return 0; + + if (parse_type != GNUTLS_EXT_ANY + && p->parse_type != parse_type) + return 0; + + if ((msg & p->validity) == 0) { + _gnutls_handshake_log("EXT[%p]: Not sending extension (%s/%d) for '%s'\n", session, + gnutls_ext_get_name(p->id), (int)p->id, + ext_msg_validity_to_str(msg)); + return 0; + } + + /* ensure we don't send something twice (i.e, overriden extensions in + * client), and ensure we are sending only what we received in server. */ + ret = _gnutls_extv_check_saved(v, p->id); + + if (extv_flags & EXTV_SEND_SAVED_ONLY) { + if (ret < 0) /* not advertized */ + return 0; + } + + if (session->security_parameters.entity == GNUTLS_CLIENT) { + if (ret == 0) /* already sent */ + return 0; + } + + ret = _gnutls_buffer_append_prefix(extdata, 16, p->id); + if (ret < 0) + return gnutls_assert_val(ret); + + size_pos = extdata->length; + ret = _gnutls_buffer_append_prefix(extdata, 16, 0); + if (ret < 0) + return gnutls_assert_val(ret); + + size_prev = extdata->length; + ret = p->send_func(session, extdata); + if (ret < 0 && ret != GNUTLS_E_INT_RET_0) { + return gnutls_assert_val(ret); + } + + /* returning GNUTLS_E_INT_RET_0 means to send an empty + * extension of this type. + */ + appended = extdata->length - size_prev; + + if (appended > 0 || ret == GNUTLS_E_INT_RET_0) { + if (ret == GNUTLS_E_INT_RET_0) + appended = 0; + + /* write the real size */ + _gnutls_write_uint16(appended, + &extdata->data[size_pos]); + + /* add this extension to the extension list + */ + if (session->security_parameters.entity == GNUTLS_CLIENT) + _gnutls_extv_add_saved(v, p, 0); + + _gnutls_handshake_log + ("EXT[%p]: Sending extension %s/%d (%d bytes)\n", + session, p->name, p->id, appended); + } else if (appended == 0) + extdata->length -= 4; /* reset type and size */ + + return 0; +} + +int +_gnutls_extv_gen(gnutls_session_t session, + tls_ext_vals_st *v, + gnutls_buffer_st * extdata, + gnutls_ext_flags_t msg, + gnutls_ext_parse_type_t parse_type, + unsigned extv_flags) +{ + int size; + int pos, ret; + size_t i, init_size = extdata->length; + + pos = extdata->length; /* we will store length later on */ + + ret = _gnutls_buffer_append_prefix(extdata, 16, 0); + if (ret < 0) + return gnutls_assert_val(ret); + + for (i=0; i < v->rexts_size; i++) { + ret = send_extension(session, v, &v->rexts[i], extdata, msg, parse_type, extv_flags); + if (ret < 0) + return gnutls_assert_val(ret); + } + + /* send_extension() ensures we don't send duplicates, in case + * of overriden extensions */ + for (i = 0; _gnutls_extfunc[i] != NULL; i++) { + ret = send_extension(session, v, _gnutls_extfunc[i], extdata, msg, parse_type, extv_flags); + if (ret < 0) + return gnutls_assert_val(ret); + } + + /* remove any initial data, and the size of the header */ + size = extdata->length - init_size - 2; + + if (size > UINT16_MAX) /* sent too many extensions */ + return gnutls_assert_val(GNUTLS_E_HANDSHAKE_TOO_LARGE); + + if (size > 0) + _gnutls_write_uint16(size, &extdata->data[pos]); + else if (size == 0) + extdata->length -= 2; /* the length bytes */ + + return size; +} diff --git a/lib/extv.h b/lib/extv.h new file mode 100644 index 0000000000..ed706019e4 --- /dev/null +++ b/lib/extv.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2017 Red Hat, Inc. + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GnuTLS. + * + * The GnuTLS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ + +#ifndef GNUTLS_EXTV_H +#define GNUTLS_EXTV_H + +#include <gnutls/gnutls.h> + +/* ext-val handling functions */ + +#define _gnutls_extv_init(v) \ + memset(v, 0, sizeof(tls_ext_vals_st)) + +void _gnutls_extv_deinit(tls_ext_vals_st *v); + +/* check whether any non-advertized extension is present. + * Requires a vals updated by extv_gen */ +#define EXTV_CHECK_UNADVERTIZED 1 + +/* Server-behavior for hello. Save extensions received, and + * check for duplicates */ +#define EXTV_SAVE_RECEIVED (1<<1) + +/* When sending, do not send any extensions that have not + * been advertized by the other party */ +#define EXTV_SEND_SAVED_ONLY (1<<2) + +int _gnutls_extv_parse(gnutls_session_t session, + gnutls_ext_flags_t msg, + gnutls_ext_parse_type_t parse_type, + const uint8_t * data, int data_size, + tls_ext_vals_st *out, + unsigned extv_flags); + +int _gnutls_extv_gen(gnutls_session_t session, + tls_ext_vals_st *vals, + gnutls_buffer_st * extdata, + gnutls_ext_flags_t msg, + gnutls_ext_parse_type_t, + unsigned extv_flags); + +void +_gnutls_extv_data_unset(struct ext_data_st *data, const struct extension_entry_st *ext); +void +_gnutls_extv_data_unset_resumed(struct ext_data_st *data, const struct extension_entry_st *ext); + +int _gnutls_extv_check_saved(tls_ext_vals_st *v, uint16_t id); +unsigned _gnutls_extv_add_saved(tls_ext_vals_st *v, const struct extension_entry_st *e, unsigned check_dup); + +#endif diff --git a/lib/gnutls_int.h b/lib/gnutls_int.h index 52c1c72e65..db3d655b25 100644 --- a/lib/gnutls_int.h +++ b/lib/gnutls_int.h @@ -853,6 +853,26 @@ typedef struct tfo_st { socklen_t connect_addrlen; } tfo_st; +typedef struct tls_ext_vals_st +{ + /* In case of a client holds the extensions we sent to the peer; + * otherwise the extensions we received from the client. + */ + const struct extension_entry_st *used_exts[MAX_EXT_TYPES]; + unsigned used_exts_size; + + struct extension_entry_st *rexts; + unsigned rexts_size; + + struct ext_data_st { + uint16_t id; + gnutls_ext_priv_data_t priv; + gnutls_ext_priv_data_t resumed_priv; + uint8_t set; + uint8_t resumed_set; + } ext_data[MAX_EXT_TYPES]; +} tls_ext_vals_st; + typedef struct { /* holds all the parsed data received by the record layer */ mbuffer_head_st record_buffer; @@ -1121,22 +1141,8 @@ typedef struct { struct gnutls_supplemental_entry_st *rsup; unsigned rsup_size; - struct extension_entry_st *rexts; - unsigned rexts_size; - - struct { - uint16_t id; - gnutls_ext_priv_data_t priv; - gnutls_ext_priv_data_t resumed_priv; - uint8_t set; - uint8_t resumed_set; - } ext_data[MAX_EXT_TYPES]; - - /* In case of a client holds the extensions we sent to the peer; - * otherwise the extensions we received from the client. - */ - const struct extension_entry_st *used_exts[MAX_EXT_TYPES]; - unsigned used_exts_size; + /* extensions received during client or server hello */ + tls_ext_vals_st hello_ext; /* this is not the negotiated max_record_recv_size, but the actual maximum * receive size */ diff --git a/lib/handshake.c b/lib/handshake.c index cfaa290505..4745e2e72f 100644 --- a/lib/handshake.c +++ b/lib/handshake.c @@ -38,6 +38,7 @@ #include "hash_int.h" #include "db.h" #include "extensions.h" +#include "extv.h" #include "supplemental.h" #include "auth.h" #include "sslv2_compat.h" @@ -79,7 +80,7 @@ handshake_hash_buffer_empty(gnutls_session_t session) _gnutls_buffers_log("BUF[HSK]: Emptied buffer\n"); - session->internals.used_exts_size = 0; + session->internals.hello_ext.used_exts_size = 0; session->internals.handshake_hash_buffer_prev_len = 0; session->internals.handshake_hash_buffer.length = 0; return; @@ -544,9 +545,11 @@ read_client_hello(gnutls_session_t session, uint8_t * data, * resumed ones. */ ret = - _gnutls_parse_extensions(session, GNUTLS_EXT_FLAG_CLIENT_HELLO, + _gnutls_extv_parse(session, GNUTLS_EXT_FLAG_CLIENT_HELLO, GNUTLS_EXT_MANDATORY, - ext_ptr, ext_size); + ext_ptr, ext_size, + &session->internals.hello_ext, + EXTV_SAVE_RECEIVED); if (ret < 0) { gnutls_assert(); return ret; @@ -585,9 +588,11 @@ read_client_hello(gnutls_session_t session, uint8_t * data, * Unconditionally try to parse extensions; safe renegotiation uses them in * sslv3 and higher, even though sslv3 doesn't officially support them. */ - ret = _gnutls_parse_extensions(session, GNUTLS_EXT_FLAG_CLIENT_HELLO, + ret = _gnutls_extv_parse(session, GNUTLS_EXT_FLAG_CLIENT_HELLO, GNUTLS_EXT_APPLICATION, - ext_ptr, ext_size); + ext_ptr, ext_size, + &session->internals.hello_ext, + EXTV_SAVE_RECEIVED); /* len is the rest of the parsed length */ if (ret < 0) { gnutls_assert(); @@ -603,8 +608,10 @@ read_client_hello(gnutls_session_t session, uint8_t * data, /* Session tickets are parsed in this point */ ret = - _gnutls_parse_extensions(session, GNUTLS_EXT_FLAG_CLIENT_HELLO, - GNUTLS_EXT_TLS, ext_ptr, ext_size); + _gnutls_extv_parse(session, GNUTLS_EXT_FLAG_CLIENT_HELLO, + GNUTLS_EXT_TLS, ext_ptr, ext_size, + &session->internals.hello_ext, + EXTV_SAVE_RECEIVED); if (ret < 0) { gnutls_assert(); return ret; @@ -658,8 +665,10 @@ read_client_hello(gnutls_session_t session, uint8_t * data, /* call extensions that are intended to be parsed after the ciphersuite/cert * are known. */ ret = - _gnutls_parse_extensions(session, GNUTLS_EXT_FLAG_CLIENT_HELLO, - _GNUTLS_EXT_TLS_POST_CS, ext_ptr, ext_size); + _gnutls_extv_parse(session, GNUTLS_EXT_FLAG_CLIENT_HELLO, + _GNUTLS_EXT_TLS_POST_CS, ext_ptr, ext_size, + &session->internals.hello_ext, + EXTV_SAVE_RECEIVED); if (ret < 0) { gnutls_assert(); return ret; @@ -1572,9 +1581,11 @@ read_server_hello(gnutls_session_t session, DECR_LEN(len, 2 + 1); ret = - _gnutls_parse_extensions(session, GNUTLS_EXT_FLAG_TLS12_SERVER_HELLO, + _gnutls_extv_parse(session, GNUTLS_EXT_FLAG_TLS12_SERVER_HELLO, GNUTLS_EXT_MANDATORY, - &data[pos], len); + &data[pos], len, + &session->internals.hello_ext, + EXTV_CHECK_UNADVERTIZED); if (ret < 0) { gnutls_assert(); return ret; @@ -1610,34 +1621,42 @@ read_server_hello(gnutls_session_t session, /* Parse extensions in order. */ ret = - _gnutls_parse_extensions(session, - ext_parse_flag, - GNUTLS_EXT_MANDATORY, - &data[pos], len); + _gnutls_extv_parse(session, + ext_parse_flag, + GNUTLS_EXT_MANDATORY, + &data[pos], len, + &session->internals.hello_ext, + EXTV_CHECK_UNADVERTIZED); if (ret < 0) return gnutls_assert_val(ret); ret = - _gnutls_parse_extensions(session, - ext_parse_flag, - GNUTLS_EXT_APPLICATION, - &data[pos], len); + _gnutls_extv_parse(session, + ext_parse_flag, + GNUTLS_EXT_APPLICATION, + &data[pos], len, + &session->internals.hello_ext, + EXTV_CHECK_UNADVERTIZED); if (ret < 0) return gnutls_assert_val(ret); ret = - _gnutls_parse_extensions(session, - ext_parse_flag, - GNUTLS_EXT_TLS, - &data[pos], len); + _gnutls_extv_parse(session, + ext_parse_flag, + GNUTLS_EXT_TLS, + &data[pos], len, + &session->internals.hello_ext, + EXTV_CHECK_UNADVERTIZED); if (ret < 0) return gnutls_assert_val(ret); ret = - _gnutls_parse_extensions(session, - ext_parse_flag, - _GNUTLS_EXT_TLS_POST_CS, - &data[pos], len); + _gnutls_extv_parse(session, + ext_parse_flag, + _GNUTLS_EXT_TLS_POST_CS, + &data[pos], len, + &session->internals.hello_ext, + EXTV_CHECK_UNADVERTIZED); if (ret < 0) return gnutls_assert_val(ret); @@ -1835,9 +1854,10 @@ static int send_client_hello(gnutls_session_t session, int again) } ret = - _gnutls_gen_extensions(session, &extdata, + _gnutls_extv_gen(session, &session->internals.hello_ext, + &extdata, GNUTLS_EXT_FLAG_CLIENT_HELLO, - type); + type, 0); if (ret < 0) { gnutls_assert(); goto cleanup; @@ -1900,12 +1920,13 @@ static int send_server_hello(gnutls_session_t session, int again) ext_parse_flag = GNUTLS_EXT_FLAG_TLS12_SERVER_HELLO; ret = - _gnutls_gen_extensions(session, &extdata, - ext_parse_flag, - (session->internals.resumed == - RESUME_TRUE) ? - GNUTLS_EXT_MANDATORY : - GNUTLS_EXT_ANY); + _gnutls_extv_gen(session, &session->internals.hello_ext, + &extdata, + ext_parse_flag, + (session->internals.resumed == RESUME_TRUE) ? + GNUTLS_EXT_MANDATORY : + GNUTLS_EXT_ANY, + EXTV_SEND_SAVED_ONLY); if (ret < 0) { gnutls_assert(); goto fail; @@ -2250,7 +2271,7 @@ int gnutls_handshake(gnutls_session_t session) if (ret < 0) return gnutls_assert_val(ret); - session->internals.used_exts_size = 0; + session->internals.hello_ext.used_exts_size = 0; session->internals.crt_requested = 0; session->internals.handshake_in_progress = 1; session->internals.vc_status = -1; diff --git a/lib/state.c b/lib/state.c index 6167fb954f..68937cd998 100644 --- a/lib/state.c +++ b/lib/state.c @@ -397,8 +397,6 @@ void gnutls_deinit(gnutls_session_t session) _gnutls_free_datum(&session->internals.resumption_data); - gnutls_free(session->internals.rexts); - gnutls_free(session->internals.rsup); gnutls_credentials_clear(session); diff --git a/lib/x509.c b/lib/x509.c index 29435647b0..64e5d4e743 100644 --- a/lib/x509.c +++ b/lib/x509.c @@ -48,6 +48,7 @@ #include "read-file.h" #include "system-keys.h" #include "urls.h" +#include "extv.h" #ifdef _WIN32 #include <wincrypt.h> #endif @@ -235,7 +236,7 @@ _gnutls_ocsp_verify_mandatory_stapling(gnutls_session_t session, * * To proceed, first check whether we have requested the certificate status */ - if (_gnutls_extension_list_check(session, GNUTLS_EXTENSION_STATUS_REQUEST) < 0) { + if (_gnutls_extv_check_saved(&session->internals.hello_ext, GNUTLS_EXTENSION_STATUS_REQUEST) < 0) { return 0; } |