From: Nikos Mavrogiannopoulos Date: Fri, 14 Jun 2013 13:31:05 +0000 (+0200) Subject: Added gnutls_handshake_set_hook_function() to allow hooks on arbitrary handshake... X-Git-Tag: gnutls_3_2_2~53 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=df856bdd513bfa12fa50a1fcb5846b0c1a9dc765;p=thirdparty%2Fgnutls.git Added gnutls_handshake_set_hook_function() to allow hooks on arbitrary handshake messages. --- diff --git a/NEWS b/NEWS index eaa5708620..e9102d9546 100644 --- a/NEWS +++ b/NEWS @@ -15,6 +15,8 @@ gnutls_certificate_set_trust_list: Added gnutls_cipher_get_tag_size: Added gnutls_record_overhead_size: Added gnutls_record_overhead_size2: Added +gnutls_handshake_set_hook_function: Added +gnutls_handshake_description_get_name: Added * Version 3.2.1 (released 2013-06-01) diff --git a/lib/debug.c b/lib/debug.c index 1658d71b83..cff40b2d11 100644 --- a/lib/debug.c +++ b/lib/debug.c @@ -73,11 +73,19 @@ _gnutls_packet2str (content_type_t packet) } } +/** + * gnutls_handshake_description_get_name: + * @type: is a handshake message description + * + * Convert a #gnutls_handshake_description_t value to a string. + * + * Returns: a string that contains the name of the specified handshake + * message or %NULL. + **/ const char * -_gnutls_handshake2str (gnutls_handshake_description_t handshake) +gnutls_handshake_description_get_name (gnutls_handshake_description_t type) { - - switch (handshake) + switch (type) { case GNUTLS_HANDSHAKE_HELLO_REQUEST: return "HELLO REQUEST"; @@ -129,6 +137,5 @@ _gnutls_handshake2str (gnutls_handshake_description_t handshake) break; default: return "Unknown Handshake packet"; - } } diff --git a/lib/debug.h b/lib/debug.h index 72e83b869a..e8b9a3428e 100644 --- a/lib/debug.h +++ b/lib/debug.h @@ -21,6 +21,11 @@ */ const char *_gnutls_packet2str (content_type_t packet); -const char *_gnutls_handshake2str (gnutls_handshake_description_t handshake); +inline static const char* _gnutls_handshake2str(unsigned x) +{ +const char* s = gnutls_handshake_description_get_name(x); + if (s == NULL) return "Unknown Handshake packet"; + else return s; +} void _gnutls_dump_mpi (const char *prefix, bigint_t a); void _gnutls_dump_vector (const char *prefix, const uint8_t *a, size_t a_size); diff --git a/lib/gnutls_handshake.c b/lib/gnutls_handshake.c index ab04e7be8a..e440c9b807 100644 --- a/lib/gnutls_handshake.c +++ b/lib/gnutls_handshake.c @@ -1141,8 +1141,20 @@ _gnutls_send_empty_handshake (gnutls_session_t session, return _gnutls_send_handshake (session, bufel, type); } +inline +static int call_hook_func(gnutls_session_t session, gnutls_handshake_description_t type, + unsigned post, unsigned incoming) +{ + if (session->internals.h_hook == NULL) + return 0; + else + { + if (session->internals.h_type == type || session->internals.h_type == GNUTLS_HANDSHAKE_ANY) + return session->internals.h_hook(session, type, post, incoming); - + return 0; + } +} /* This function sends a handshake message of type 'type' containing the * data specified here. If the previous _gnutls_send_handshake() returned @@ -1210,6 +1222,14 @@ _gnutls_send_handshake (gnutls_session_t session, mbuffer_st * bufel, return ret; } + ret = call_hook_func(session, type, 0, 0); + if (ret < 0) + { + gnutls_assert (); + _mbuffer_xfree(&bufel); + return ret; + } + session->internals.last_handshake_out = type; ret = _gnutls_handshake_io_cache_int (session, type, bufel); @@ -1244,6 +1264,13 @@ _gnutls_send_handshake (gnutls_session_t session, mbuffer_st * bufel, break; } + ret = call_hook_func(session, type, 1, 0); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + return ret; } @@ -1342,7 +1369,6 @@ _gnutls_handshake_hash_add_sent (gnutls_session_t session, return 0; } - /* This function will receive handshake messages of the given types, * and will pass the message to the right place in order to be processed. * E.g. for the SERVER_HELLO message (if it is expected), it will be @@ -1371,6 +1397,13 @@ _gnutls_recv_handshake (gnutls_session_t session, session->internals.last_handshake_in = hsk.htype; + ret = call_hook_func(session, hsk.htype, 0, 1); + if (ret < 0) + { + gnutls_assert (); + goto cleanup; + } + ret = _gnutls_handshake_hash_add_recvd (session, hsk.htype, hsk.header, hsk.header_size, hsk.data.data, hsk.data.length); @@ -1396,8 +1429,6 @@ _gnutls_recv_handshake (gnutls_session_t session, goto cleanup; } - goto cleanup; /* caller doesn't need dataptr */ - break; case GNUTLS_HANDSHAKE_HELLO_VERIFY_REQUEST: ret = _gnutls_recv_hello_verify_request (session, hsk.data.data, hsk.data.length); @@ -1411,8 +1442,6 @@ _gnutls_recv_handshake (gnutls_session_t session, and ClientHello needs to be sent again. */ ret = 1; - goto cleanup; /* caller doesn't need dataptr */ - break; case GNUTLS_HANDSHAKE_SERVER_HELLO_DONE: if (hsk.data.length == 0) @@ -1444,6 +1473,13 @@ _gnutls_recv_handshake (gnutls_session_t session, goto cleanup; } + ret = call_hook_func(session, hsk.htype, 1, 1); + if (ret < 0) + { + gnutls_assert (); + goto cleanup; + } + if (buf) { *buf = hsk.data; diff --git a/lib/gnutls_int.h b/lib/gnutls_int.h index 8c6766c8f2..c4f2ee8e3a 100644 --- a/lib/gnutls_int.h +++ b/lib/gnutls_int.h @@ -750,7 +750,6 @@ typedef struct unsigned int packets_dropped; } dtls_st; - typedef union { void *ptr; @@ -868,6 +867,9 @@ typedef struct /* post client hello callback (server side only) */ gnutls_handshake_post_client_hello_func user_hello_func; + /* handshake hook function */ + gnutls_handshake_hook_func h_hook; + unsigned int h_type; /* the hooked type */ /* holds the selected certificate and key. * use _gnutls_selected_certs_deinit() and _gnutls_selected_certs_set() diff --git a/lib/gnutls_state.c b/lib/gnutls_state.c index 412a151101..a5b8ec8c99 100644 --- a/lib/gnutls_state.c +++ b/lib/gnutls_state.c @@ -486,7 +486,7 @@ gnutls_deinit (gnutls_session_t session) _gnutls_mpi_release (&session->key.rsa[1]); _gnutls_mpi_release (&session->key.dh_secret); - + gnutls_free (session); } @@ -1247,6 +1247,7 @@ gnutls_handshake_set_post_client_hello_function (gnutls_session_t session, session->internals.user_hello_func = func; } + /** * gnutls_session_enable_compatibility_mode: * @session: is a #gnutls_session_t structure. @@ -1407,3 +1408,34 @@ gnutls_handshake_set_random (gnutls_session_t session, const gnutls_datum_t* ran return 0; } +/** + * gnutls_handshake_set_hook_function: + * @session: is a #gnutls_session_t structure. + * @htype: the %gnutls_handshake_description_t of the message to hook at. + * @func: is the function to be called + * + * This function will set a callback to be called after or before the specified + * handshake message has been received or generated. This is a + * generalization of gnutls_handshake_set_post_client_hello_function(). + * + * This callback must return 0 on success or a gnutls error code to + * terminate the handshake. + * + * Note to hook at all handshake messages use an @htype of %GNUTLS_HANDSHAKE_ANY. + * + * Warning: You should not use this function to terminate the + * handshake based on client input unless you know what you are + * doing. Before the handshake is finished there is no way to know if + * there is a man-in-the-middle attack being performed. + * + * Returns: %GNUTLS_E_SUCCESS on success, or an error code. + **/ +void +gnutls_handshake_set_hook_function (gnutls_session_t session, + unsigned int htype, + gnutls_handshake_hook_func func) +{ + session->internals.h_hook = func; + session->internals.h_type = htype; +} + diff --git a/lib/includes/gnutls/gnutls.h.in b/lib/includes/gnutls/gnutls.h.in index b539b10b40..724ee8a438 100644 --- a/lib/includes/gnutls/gnutls.h.in +++ b/lib/includes/gnutls/gnutls.h.in @@ -434,6 +434,11 @@ extern "C" GNUTLS_HANDSHAKE_CLIENT_HELLO_V2 = 1024, } gnutls_handshake_description_t; +#define GNUTLS_HANDSHAKE_ANY ((unsigned int)-1) + +const char * +gnutls_handshake_description_get_name (gnutls_handshake_description_t type); + /** * gnutls_certificate_status_t: * @GNUTLS_CERT_INVALID: The certificate is not signed by one of the @@ -1167,6 +1172,24 @@ gnutls_ecc_curve_t gnutls_ecc_curve_get(gnutls_session_t session); gnutls_datum_t session_entry); time_t gnutls_db_check_entry_time (gnutls_datum_t *entry); + /** + * gnutls_handshake_hook_func: + * @session: the current session + * @htype: the type of the handshake message (%gnutls_handshake_description_t) + * @post: non zero if this is an post-process/generation call and zero otherwise + * @incoming: non zero if this is an incoming message and zero if this is an outgoing message + * + * Function prototype for handshake hooks. It is set using + * gnutls_handshake_set_hook_function(). + * + * Returns: Non zero on error. + */ + + typedef int (*gnutls_handshake_hook_func) (gnutls_session_t, unsigned int htype, unsigned int post, unsigned int incoming); + void gnutls_handshake_set_hook_function (gnutls_session_t session, + unsigned int htype, + gnutls_handshake_hook_func func); + typedef int (*gnutls_handshake_post_client_hello_func) (gnutls_session_t); void gnutls_handshake_set_post_client_hello_function (gnutls_session_t session, diff --git a/lib/libgnutls.map b/lib/libgnutls.map index 41b3a83541..32c3ba46a2 100644 --- a/lib/libgnutls.map +++ b/lib/libgnutls.map @@ -915,6 +915,8 @@ GNUTLS_3_1_0 { gnutls_cipher_get_tag_size; gnutls_record_overhead_size; gnutls_record_overhead_size2; + gnutls_handshake_set_hook_function; + gnutls_handshake_description_get_name; } GNUTLS_3_0_0; GNUTLS_PRIVATE { diff --git a/tests/mini-x509-callbacks.c b/tests/mini-x509-callbacks.c index 3d7dac23ee..7984f3126d 100644 --- a/tests/mini-x509-callbacks.c +++ b/tests/mini-x509-callbacks.c @@ -52,6 +52,35 @@ static int post_client_hello_callback (gnutls_session_t session) return 0; } +unsigned int msg_order[] = { + GNUTLS_HANDSHAKE_CLIENT_HELLO, + GNUTLS_HANDSHAKE_SERVER_HELLO, + GNUTLS_HANDSHAKE_CERTIFICATE_PKT, + GNUTLS_HANDSHAKE_SERVER_KEY_EXCHANGE, + GNUTLS_HANDSHAKE_CERTIFICATE_REQUEST, + GNUTLS_HANDSHAKE_SERVER_HELLO_DONE, + GNUTLS_HANDSHAKE_CERTIFICATE_PKT, + GNUTLS_HANDSHAKE_CLIENT_KEY_EXCHANGE, + GNUTLS_HANDSHAKE_FINISHED, + GNUTLS_HANDSHAKE_FINISHED, +}; + +static int handshake_callback (gnutls_session_t session, unsigned int htype, unsigned int post, unsigned int incoming) +{ +static unsigned idx = 0; + + if (post > 0) + { + if (msg_order[idx] != htype) + { + fail("%s: %s, expected %s\n", incoming!=0?"Received":"Sent", gnutls_handshake_description_get_name(htype), gnutls_handshake_description_get_name(msg_order[idx])); + exit(1); + } + idx++; + } + return 0; +} + static int server_callback (gnutls_session_t session) { @@ -137,6 +166,7 @@ void doit(void) gnutls_certificate_set_verify_function (serverx509cred, server_callback); gnutls_certificate_server_set_request (server, GNUTLS_CERT_REQUEST); gnutls_handshake_set_post_client_hello_function (server, post_client_hello_callback); + gnutls_handshake_set_hook_function (server, GNUTLS_HANDSHAKE_ANY, handshake_callback); /* Init client */ gnutls_certificate_allocate_credentials (&clientx509cred);