]> git.ipfire.org Git - thirdparty/gnutls.git/commitdiff
Added gnutls_handshake_set_hook_function() to allow hooks on arbitrary handshake...
authorNikos Mavrogiannopoulos <nmav@gnutls.org>
Fri, 14 Jun 2013 13:31:05 +0000 (15:31 +0200)
committerNikos Mavrogiannopoulos <nmav@gnutls.org>
Fri, 14 Jun 2013 14:22:37 +0000 (16:22 +0200)
NEWS
lib/debug.c
lib/debug.h
lib/gnutls_handshake.c
lib/gnutls_int.h
lib/gnutls_state.c
lib/includes/gnutls/gnutls.h.in
lib/libgnutls.map
tests/mini-x509-callbacks.c

diff --git a/NEWS b/NEWS
index eaa57086205ff4d46421abcc7df450cf66ac2749..e9102d954681dd621537ab580037266b70b39870 100644 (file)
--- 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)
index 1658d71b830d394896f6ae9f557c012bd5a619e2..cff40b2d119a2cbb5ababe3786dbc08ddac9f1e0 100644 (file)
@@ -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";
-
     }
 }
index 72e83b869abeedcd97800927a2fb6fc112b05ebf..e8b9a3428e3b9374b7b71d5c16ccf89f7ded7561 100644 (file)
  */
 
 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);
index ab04e7be8ad038b09ae44feff5e540bcba325c0b..e440c9b807f8a64af53c6a4dd5a8aba97f6e5227 100644 (file)
@@ -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;
index 8c6766c8f241e407c81f0278dd9e2a8d75ce2c4d..c4f2ee8e3a7cddc68205ee25927c04d2094eabc0 100644 (file)
@@ -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()
index 412a1511014c7c0227266ebbb46ee7c6ac29c734..a5b8ec8c99b37db6b8dc6cb671323a58c9d6b4b4 100644 (file)
@@ -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;
+}
+
index b539b10b4021e48ca88dd8f0151a40ade7744bb8..724ee8a438806a5386a0cad27e082ee2535b5203 100644 (file)
@@ -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,
index 41b3a8354132516657238fea82b1faf3abf890e6..32c3ba46a2da1e44fed30bf72160157d34dd3e43 100644 (file)
@@ -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 {
index 3d7dac23eeb2ff40c7a3bb86b117a0aea278d97a..7984f3126d2e5d3e718ce3079199cf3686be433f 100644 (file)
@@ -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);