summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/extensions.c303
-rw-r--r--lib/extensions.h2
-rw-r--r--lib/gnutls_int.h2
-rw-r--r--lib/includes/gnutls/gnutls.h.in5
-rw-r--r--lib/libgnutls.map1
-rw-r--r--lib/state.c2
6 files changed, 230 insertions, 85 deletions
diff --git a/lib/extensions.c b/lib/extensions.c
index 54587f3328..7a4d415be4 100644
--- a/lib/extensions.c
+++ b/lib/extensions.c
@@ -88,10 +88,15 @@ static extension_entry_st const *extfunc[MAX_EXT_TYPES+1] = {
NULL
};
-static gnutls_ext_parse_type_t _gnutls_ext_parse_type(uint16_t type)
+static gnutls_ext_parse_type_t _gnutls_ext_parse_type(gnutls_session_t session, uint16_t type)
{
size_t i;
+ for (i=0;i<session->internals.rexts_size;i++) {
+ if (session->internals.rexts[i].type == type)
+ return session->internals.rexts[i].parse_type;
+ }
+
for (i = 0; extfunc[i] != NULL; i++) {
if (extfunc[i]->type == type)
return extfunc[i]->parse_type;
@@ -101,10 +106,17 @@ static gnutls_ext_parse_type_t _gnutls_ext_parse_type(uint16_t type)
}
static gnutls_ext_recv_func
-_gnutls_ext_func_recv(uint16_t type, gnutls_ext_parse_type_t parse_type)
+_gnutls_ext_func_recv(gnutls_session_t session, uint16_t type, gnutls_ext_parse_type_t parse_type)
{
size_t i;
+ for (i=0;i<session->internals.rexts_size;i++) {
+ if (session->internals.rexts[i].type == type)
+ if (parse_type == GNUTLS_EXT_ANY
+ || session->internals.rexts[i].parse_type == parse_type)
+ return session->internals.rexts[i].recv_func;
+ }
+
for (i = 0; extfunc[i] != NULL; i++)
if (extfunc[i]->type == type)
if (parse_type == GNUTLS_EXT_ANY
@@ -114,10 +126,15 @@ _gnutls_ext_func_recv(uint16_t type, gnutls_ext_parse_type_t parse_type)
return NULL;
}
-static gnutls_ext_deinit_data_func _gnutls_ext_func_deinit(uint16_t type)
+static gnutls_ext_deinit_data_func _gnutls_ext_func_deinit(gnutls_session_t session, uint16_t type)
{
size_t i;
+ for (i=0;i<session->internals.rexts_size;i++) {
+ if (session->internals.rexts[i].type == type)
+ return session->internals.rexts[i].deinit_func;
+ }
+
for (i = 0; extfunc[i] != NULL; i++)
if (extfunc[i]->type == type)
return extfunc[i]->deinit_func;
@@ -125,10 +142,15 @@ static gnutls_ext_deinit_data_func _gnutls_ext_func_deinit(uint16_t type)
return NULL;
}
-static gnutls_ext_unpack_func _gnutls_ext_func_unpack(uint16_t type)
+static gnutls_ext_unpack_func _gnutls_ext_func_unpack(gnutls_session_t session, uint16_t type)
{
size_t i;
+ for (i=0;i<session->internals.rexts_size;i++) {
+ if (session->internals.rexts[i].type == type)
+ return session->internals.rexts[i].unpack_func;
+ }
+
for (i = 0; extfunc[i] != NULL; i++)
if (extfunc[i]->type == type)
return extfunc[i]->unpack_func;
@@ -230,7 +252,7 @@ _gnutls_parse_extensions(gnutls_session_t session,
sdata = &data[pos];
pos += size;
- ext_recv = _gnutls_ext_func_recv(type, parse_type);
+ ext_recv = _gnutls_ext_func_recv(session, type, parse_type);
if (ext_recv == NULL) {
_gnutls_handshake_log
("EXT[%p]: Found extension '%s/%d'\n", session,
@@ -275,14 +297,74 @@ void _gnutls_extension_list_add(gnutls_session_t session, uint16_t type)
}
}
+static
+int send_extension(gnutls_session_t session, const extension_entry_st *p,
+ gnutls_buffer_st *extdata, gnutls_ext_parse_type_t parse_type)
+{
+ int size_pos, size, ret;
+
+ if (p->send_func == NULL)
+ return 0;
+
+ if (parse_type != GNUTLS_EXT_ANY
+ && p->parse_type != parse_type)
+ return 0;
+
+ /* ensure we are sending only what we received */
+ if (session->security_parameters.entity == GNUTLS_SERVER) {
+ if ((ret =
+ _gnutls_extension_list_check(session, p->type)) < 0) {
+ return 0;
+ }
+ }
+
+ ret = _gnutls_buffer_append_prefix(extdata, 16, p->type);
+ 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 = p->send_func(session, extdata);
+ /* returning GNUTLS_E_INT_RET_0 means to send an empty
+ * extension of this type.
+ */
+ if (size > 0 || size == GNUTLS_E_INT_RET_0) {
+ if (size == GNUTLS_E_INT_RET_0)
+ size = 0;
+
+ /* write the real size */
+ _gnutls_write_uint16(size,
+ &extdata->data[size_pos]);
+
+ /* add this extension to the extension list
+ */
+ if (session->security_parameters.entity == GNUTLS_CLIENT)
+ _gnutls_extension_list_add(session, p->type);
+
+ _gnutls_handshake_log
+ ("EXT[%p]: Sending extension %s (%d bytes)\n",
+ session, p->name, size);
+ } else if (size < 0) {
+ gnutls_assert();
+ return size;
+ } else if (size == 0)
+ extdata->length -= 4; /* reset type and size */
+
+ return 0;
+}
+
int
_gnutls_gen_extensions(gnutls_session_t session,
gnutls_buffer_st * extdata,
gnutls_ext_parse_type_t parse_type)
{
int size;
- int pos, size_pos, ret;
+ int pos, ret;
size_t i, init_size = extdata->length;
+ const extension_entry_st *extp;
pos = extdata->length; /* we will store length later on */
@@ -290,58 +372,20 @@ _gnutls_gen_extensions(gnutls_session_t session,
if (ret < 0)
return gnutls_assert_val(ret);
- for (i = 0; extfunc[i] != NULL; i++) {
- const extension_entry_st *p = extfunc[i];
+ for (i=0;i<session->internals.rexts_size;i++) {
+ extp = &session->internals.rexts[i];
- if (p->send_func == NULL)
- continue;
-
- if (parse_type != GNUTLS_EXT_ANY
- && p->parse_type != parse_type)
- continue;
-
- /* ensure we are sending only what we received */
- if (session->security_parameters.entity == GNUTLS_SERVER) {
- if ((ret =
- _gnutls_extension_list_check(session, p->type)) < 0) {
- continue;
- }
- }
-
- ret = _gnutls_buffer_append_prefix(extdata, 16, p->type);
+ ret = send_extension(session, extp, extdata, parse_type);
if (ret < 0)
return gnutls_assert_val(ret);
+ }
- size_pos = extdata->length;
- ret = _gnutls_buffer_append_prefix(extdata, 16, 0);
+ for (i = 0; extfunc[i] != NULL; i++) {
+ extp = extfunc[i];
+
+ ret = send_extension(session, extp, extdata, parse_type);
if (ret < 0)
return gnutls_assert_val(ret);
-
- size = p->send_func(session, extdata);
- /* returning GNUTLS_E_INT_RET_0 means to send an empty
- * extension of this type.
- */
- if (size > 0 || size == GNUTLS_E_INT_RET_0) {
- if (size == GNUTLS_E_INT_RET_0)
- size = 0;
-
- /* write the real size */
- _gnutls_write_uint16(size,
- &extdata->data[size_pos]);
-
- /* add this extension to the extension list
- */
- if (session->security_parameters.entity == GNUTLS_CLIENT)
- _gnutls_extension_list_add(session, p->type);
-
- _gnutls_handshake_log
- ("EXT[%p]: Sending extension %s (%d bytes)\n",
- session, p->name, size);
- } else if (size < 0) {
- gnutls_assert();
- return size;
- } else if (size == 0)
- extdata->length -= 4; /* reset type and size */
}
/* remove any initial data, and the size of the header */
@@ -393,42 +437,67 @@ int ext_register(extension_entry_st * mod)
return GNUTLS_E_SUCCESS;
}
-int _gnutls_ext_pack(gnutls_session_t session, gnutls_buffer_st * packed)
+static int pack_extension(gnutls_session_t session, const extension_entry_st *extp,
+ gnutls_buffer_st *packed)
{
- unsigned int i;
int ret;
- gnutls_ext_priv_data_t data;
- int cur_size;
int size_offset;
+ int cur_size;
+ gnutls_ext_priv_data_t data;
+ int rval = 0;
+
+ ret =
+ _gnutls_ext_get_session_data(session, extp->type,
+ &data);
+ if (ret >= 0 && extp->pack_func != NULL) {
+ BUFFER_APPEND_NUM(packed, extp->type);
+
+ size_offset = packed->length;
+ BUFFER_APPEND_NUM(packed, 0);
+
+ cur_size = packed->length;
+
+ ret = extp->pack_func(data, packed);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ rval = 1;
+ /* write the actual size */
+ _gnutls_write_uint32(packed->length - cur_size,
+ packed->data + size_offset);
+ }
+
+ return rval;
+}
+
+int _gnutls_ext_pack(gnutls_session_t session, gnutls_buffer_st *packed)
+{
+ unsigned int i;
+ int ret;
int total_exts_pos;
int exts = 0;
total_exts_pos = packed->length;
BUFFER_APPEND_NUM(packed, 0);
- for (i = 0; extfunc[i] != NULL; i++) {
- ret =
- _gnutls_ext_get_session_data(session, extfunc[i]->type,
- &data);
- if (ret >= 0 && extfunc[i]->pack_func != NULL) {
- BUFFER_APPEND_NUM(packed, extfunc[i]->type);
-
- size_offset = packed->length;
- BUFFER_APPEND_NUM(packed, 0);
+ for (i = 0; i < session->internals.rexts_size; i++) {
+ ret = pack_extension(session, &session->internals.rexts[i], packed);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
- cur_size = packed->length;
+ if (ret > 0)
+ exts++;
+ }
- ret = extfunc[i]->pack_func(data, packed);
- if (ret < 0) {
- gnutls_assert();
- return ret;
- }
+ for (i = 0; extfunc[i] != NULL; i++) {
+ ret = pack_extension(session, extfunc[i], packed);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+ if (ret > 0)
exts++;
- /* write the actual size */
- _gnutls_write_uint32(packed->length - cur_size,
- packed->data + size_offset);
- }
}
_gnutls_write_uint32(exts, packed->data + total_exts_pos);
@@ -440,11 +509,10 @@ void _gnutls_ext_restore_resumed_session(gnutls_session_t session)
{
int i;
-
/* clear everything except MANDATORY extensions */
for (i = 0; i < MAX_EXT_TYPES; i++) {
if (session->internals.extension_int_data[i].set != 0 &&
- _gnutls_ext_parse_type(session->
+ _gnutls_ext_parse_type(session, session->
internals.extension_int_data[i].
type) != GNUTLS_EXT_MANDATORY) {
_gnutls_ext_unset_session_data(session,
@@ -458,7 +526,7 @@ void _gnutls_ext_restore_resumed_session(gnutls_session_t session)
for (i = 0; i < MAX_EXT_TYPES; i++) {
if (session->internals.resumed_extension_int_data[i].set !=
0
- && _gnutls_ext_parse_type(session->internals.
+ && _gnutls_ext_parse_type(session, session->internals.
resumed_extension_int_data
[i].type) !=
GNUTLS_EXT_MANDATORY) {
@@ -523,7 +591,7 @@ int _gnutls_ext_unpack(gnutls_session_t session, gnutls_buffer_st * packed)
cur_pos = packed->length;
- unpack = _gnutls_ext_func_unpack(type);
+ unpack = _gnutls_ext_func_unpack(session, type);
if (unpack == NULL) {
gnutls_assert();
return GNUTLS_E_PARSING_ERROR;
@@ -558,7 +626,7 @@ _gnutls_ext_unset_session_data(gnutls_session_t session, uint16_t type)
gnutls_ext_priv_data_t data;
int ret, i;
- deinit = _gnutls_ext_func_deinit(type);
+ deinit = _gnutls_ext_func_deinit(session, type);
ret = _gnutls_ext_get_session_data(session, type, &data);
if (ret >= 0 && deinit != NULL) {
@@ -582,7 +650,7 @@ _gnutls_ext_unset_resumed_session_data(gnutls_session_t session,
gnutls_ext_priv_data_t data;
int ret, i;
- deinit = _gnutls_ext_func_deinit(type);
+ deinit = _gnutls_ext_func_deinit(session, type);
ret = _gnutls_ext_get_resumed_session_data(session, type, &data);
if (ret >= 0 && deinit != NULL) {
@@ -606,11 +674,14 @@ void _gnutls_ext_free_session_data(gnutls_session_t session)
{
unsigned int i;
- for (i = 0; extfunc[i] != NULL; i++) {
- _gnutls_ext_unset_session_data(session, extfunc[i]->type);
+ for (i = 0; i < session->internals.rexts_size; i++) {
+ _gnutls_ext_unset_session_data(session, session->internals.rexts[i].type);
+ _gnutls_ext_unset_resumed_session_data(session,
+ session->internals.rexts[i].type);
}
for (i = 0; extfunc[i] != NULL; i++) {
+ _gnutls_ext_unset_session_data(session, extfunc[i]->type);
_gnutls_ext_unset_resumed_session_data(session,
extfunc[i]->type);
}
@@ -628,7 +699,7 @@ _gnutls_ext_set_session_data(gnutls_session_t session, uint16_t type,
unsigned int i;
gnutls_ext_deinit_data_func deinit;
- deinit = _gnutls_ext_func_deinit(type);
+ deinit = _gnutls_ext_func_deinit(session, type);
for (i = 0; i < MAX_EXT_TYPES; i++) {
if (session->internals.extension_int_data[i].type == type
@@ -751,6 +822,70 @@ gnutls_ext_register(const char *name, int type, gnutls_ext_parse_type_t parse_ty
}
/**
+ * gnutls_session_ext_register:
+ * @session: the session for which this extension will be set
+ * @name: the name of the extension to register
+ * @type: the numeric id of the extension
+ * @parse_type: the parse type of the extension (see gnutls_ext_parse_type_t)
+ * @recv_func: a function to receive the data
+ * @send_func: a function to send the data
+ * @deinit_func: a function deinitialize any private data
+ * @pack_func: a function which serializes the extension's private data (used on session packing for resumption)
+ * @unpack_func: a function which will deserialize the extension's private data
+ * @flags: must be zero
+ *
+ * This function will register a new extension type. The extension will be
+ * only usable within the registered session. If the extension type
+ * is already registered then %GNUTLS_E_ALREADY_REGISTERED will be returned.
+ *
+ * Each registered extension can store temporary data into the gnutls_session_t
+ * structure using gnutls_ext_set_data(), and they can be retrieved using
+ * gnutls_ext_get_data().
+ *
+ * Returns: %GNUTLS_E_SUCCESS on success, otherwise a negative error code.
+ *
+ * Since: 3.5.5
+ **/
+int
+gnutls_session_ext_register(gnutls_session_t session,
+ const char *name, int type, gnutls_ext_parse_type_t parse_type,
+ gnutls_ext_recv_func recv_func, gnutls_ext_send_func send_func,
+ gnutls_ext_deinit_data_func deinit_func, gnutls_ext_pack_func pack_func,
+ gnutls_ext_unpack_func unpack_func, unsigned flags)
+{
+ extension_entry_st tmp_mod;
+ extension_entry_st *exts;
+ unsigned i;
+
+ for (i = 0; extfunc[i] != NULL; i++) {
+ if (extfunc[i]->type == type)
+ return gnutls_assert_val(GNUTLS_E_ALREADY_REGISTERED);
+ }
+
+ tmp_mod.name = NULL;
+ tmp_mod.free_struct = 1;
+ tmp_mod.type = type;
+ tmp_mod.parse_type = parse_type;
+ tmp_mod.recv_func = recv_func;
+ tmp_mod.send_func = send_func;
+ tmp_mod.deinit_func = deinit_func;
+ tmp_mod.pack_func = pack_func;
+ tmp_mod.unpack_func = unpack_func;
+
+ exts = gnutls_realloc(session->internals.rexts, (session->internals.rexts_size+1)*sizeof(*exts));
+ if (exts == NULL) {
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+ }
+
+ session->internals.rexts = exts;
+
+ memcpy(&session->internals.rexts[session->internals.rexts_size], &tmp_mod, sizeof(extension_entry_st));
+ session->internals.rexts_size++;
+
+ return 0;
+}
+
+/**
* gnutls_ext_set_data:
* @session: a #gnutls_session_t opaque pointer
* @type: the numeric id of the extension
diff --git a/lib/extensions.h b/lib/extensions.h
index 891c69b3d3..b531ebae30 100644
--- a/lib/extensions.h
+++ b/lib/extensions.h
@@ -60,7 +60,7 @@ int _gnutls_ext_unpack(gnutls_session_t session,
typedef gnutls_ext_priv_data_t extension_priv_data_t;
-typedef struct {
+typedef struct extension_entry_st {
const char *name; /* const overriden when free_struct is set */
unsigned free_struct;
diff --git a/lib/gnutls_int.h b/lib/gnutls_int.h
index 3b8d6299f9..184bd8359d 100644
--- a/lib/gnutls_int.h
+++ b/lib/gnutls_int.h
@@ -1037,6 +1037,8 @@ typedef struct {
/* Needed for TCP Fast Open (TFO), set by gnutls_transport_set_fastopen() */
tfo_st tfo;
+ struct extension_entry_st *rexts;
+ unsigned rexts_size;
/* If you add anything here, check _gnutls_handshake_internal_state_clear().
*/
} internals_st;
diff --git a/lib/includes/gnutls/gnutls.h.in b/lib/includes/gnutls/gnutls.h.in
index bad3eee16a..bb3ce5d28d 100644
--- a/lib/includes/gnutls/gnutls.h.in
+++ b/lib/includes/gnutls/gnutls.h.in
@@ -2535,6 +2535,11 @@ int gnutls_ext_register(const char *name, int type, gnutls_ext_parse_type_t pars
gnutls_ext_deinit_data_func deinit_func, gnutls_ext_pack_func pack_func,
gnutls_ext_unpack_func unpack_func);
+int gnutls_session_ext_register(gnutls_session_t, const char *name, int type, gnutls_ext_parse_type_t parse_type,
+ gnutls_ext_recv_func recv_func, gnutls_ext_send_func send_func,
+ gnutls_ext_deinit_data_func deinit_func, gnutls_ext_pack_func pack_func,
+ gnutls_ext_unpack_func unpack_func, unsigned flags);
+
const char *gnutls_ext_get_name(unsigned int ext);
/* Public supplemental data related functions */
diff --git a/lib/libgnutls.map b/lib/libgnutls.map
index 38805070b3..74fbe1c5bc 100644
--- a/lib/libgnutls.map
+++ b/lib/libgnutls.map
@@ -1105,6 +1105,7 @@ GNUTLS_3_4
gnutls_transport_set_fastopen;
gnutls_oid_to_mac;
gnutls_certificate_set_ocsp_status_request_function2;
+ gnutls_session_ext_register;
local:
*;
};
diff --git a/lib/state.c b/lib/state.c
index 8b81f73cfc..0ae303c190 100644
--- a/lib/state.c
+++ b/lib/state.c
@@ -490,6 +490,8 @@ void gnutls_deinit(gnutls_session_t session)
_gnutls_free_datum(&session->internals.resumption_data);
+ gnutls_free(session->internals.rexts);
+
gnutls_credentials_clear(session);
_gnutls_selected_certs_deinit(session);