diff options
author | Simon Josefsson <simon@josefsson.org> | 2008-08-13 13:26:28 +0200 |
---|---|---|
committer | Simon Josefsson <simon@josefsson.org> | 2008-08-13 13:26:28 +0200 |
commit | f348ac6b6faed0b53c170a72cbc32d8237064990 (patch) | |
tree | a63237dee6bc9820153e10f2959237e1ff340712 | |
parent | e564e7709921b1cd588119984ed3abcd0c788820 (diff) | |
download | gnutls-f348ac6b6faed0b53c170a72cbc32d8237064990.tar.gz |
Add API to set callback to extract TLS Finished messages.
-rw-r--r-- | NEWS | 6 | ||||
-rw-r--r-- | includes/gnutls/gnutls.h.in | 9 | ||||
-rw-r--r-- | lib/gnutls_handshake.c | 2 | ||||
-rw-r--r-- | lib/gnutls_int.h | 3 | ||||
-rw-r--r-- | lib/gnutls_state.c | 31 | ||||
-rw-r--r-- | tests/Makefile.am | 3 | ||||
-rw-r--r-- | tests/finished.c | 275 |
7 files changed, 328 insertions, 1 deletions
@@ -9,10 +9,16 @@ See the end for copying conditions. ** from a private key. The function is gnutls_x509_crq_set_key_rsa_raw. +** libgnutls: New API to set a callback to extract TLS Finished data. +The function to register is gnutls_session_set_finished_function and +it takes a callback of the gnutls_finished_callback_func type. + ** doc: Add doxygen files in doc/doxygen/. ** API and ABI modifications: gnutls_x509_crq_set_key_rsa_raw: ADDED +gnutls_session_set_finished_function: ADDED +gnutls_finished_callback_func: ADDED * Version 2.5.2 (released 2008-07-08) diff --git a/includes/gnutls/gnutls.h.in b/includes/gnutls/gnutls.h.in index 13d507bdfe..81e363d241 100644 --- a/includes/gnutls/gnutls.h.in +++ b/includes/gnutls/gnutls.h.in @@ -608,6 +608,15 @@ extern "C" const void *gnutls_session_get_client_random (gnutls_session_t session); const void *gnutls_session_get_master_secret (gnutls_session_t session); + typedef void + (*gnutls_finished_callback_func) (gnutls_session_t session, + const void *finished, + size_t len); + void + gnutls_session_set_finished_function (gnutls_session_t session, + gnutls_finished_callback_func + finished_func); + /* checks if this session is a resumed one */ int gnutls_session_is_resumed (gnutls_session_t session); diff --git a/lib/gnutls_handshake.c b/lib/gnutls_handshake.c index bb00966480..06a326d398 100644 --- a/lib/gnutls_handshake.c +++ b/lib/gnutls_handshake.c @@ -569,6 +569,8 @@ _gnutls_send_finished (gnutls_session_t session, int again) return ret; } + if (session->internals.finished_func) + session->internals.finished_func (session, data, data_size); } ret = diff --git a/lib/gnutls_int.h b/lib/gnutls_int.h index 87d1debe6a..9e6ed88d13 100644 --- a/lib/gnutls_int.h +++ b/lib/gnutls_int.h @@ -639,6 +639,9 @@ typedef struct gnutls_sign_func sign_func; void *sign_func_userdata; + /* Callback to extract TLS Finished message. */ + gnutls_finished_callback_func finished_func; + /* If you add anything here, check _gnutls_handshake_internal_state_clear(). */ } internals_st; diff --git a/lib/gnutls_state.c b/lib/gnutls_state.c index 0b4a11f100..e425d8da5a 100644 --- a/lib/gnutls_state.c +++ b/lib/gnutls_state.c @@ -1113,6 +1113,37 @@ gnutls_session_get_master_secret (gnutls_session_t session) } /** + * gnutls_session_set_finished_function: + * @session: is a #gnutls_session_t structure. + * @finished_func: a #gnutls_finished_callback_func callback. + * + * Register a callback function for the session that will be called + * when a TLS Finished message has been generated. The function is + * typically used to copy away the TLS finished message for later use + * as a channel binding or similar purpose. + * + * The callback should follow this prototype: + * + * void callback (gnutls_session_t session, const void *finished, size_t len); + * + * The @finished parameter will contain the binary TLS finished + * message, and @len will contains its length. For SSLv3 connections, + * the @len parameter will be 36 and for TLS connections it will be + * 12. + * + * It is recommended that the function returns quickly in order to not + * delay the handshake. Use the function to store a copy of the TLS + * finished message for later use. + **/ +void +gnutls_session_set_finished_function (gnutls_session_t session, + gnutls_finished_callback_func + finished_func) +{ + session->internals.finished_func = finished_func; +} + +/** * gnutls_session_is_resumed - check whether this session is a resumed one * @session: is a #gnutls_session_t structure. * diff --git a/tests/Makefile.am b/tests/Makefile.am index 47febc976b..3de120b686 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -40,7 +40,8 @@ noinst_LTLIBRARIES = libutils.la libutils_la_SOURCES = utils.h utils.c ctests = simple openssl gc set_pkcs12_cred certder mpi \ - certificate_set_x509_crl dn parse_ca moredn crypto_rng mini + certificate_set_x509_crl dn parse_ca moredn crypto_rng mini \ + finished openssl_LDADD = $(LDADD) ../libextra/libgnutls-openssl.la if HAVE_FORK diff --git a/tests/finished.c b/tests/finished.c new file mode 100644 index 0000000000..88c84cbd29 --- /dev/null +++ b/tests/finished.c @@ -0,0 +1,275 @@ +/* + * Copyright (C) 2008 Free Software Foundation + * + * Author: Simon Josefsson + * + * This file is part of GNUTLS. + * + * GNUTLS is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * GNUTLS 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNUTLS; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/* Based on mini.c. */ + +#if HAVE_CONFIG_H +# include <config.h> +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <gnutls/gnutls.h> + +#include "utils.h" + +static void +tls_log_func (int level, const char *str) +{ + fprintf (stderr, "|<%d>| %s", level, str); +} + +char *to_server; +size_t to_server_len; + +char *to_client; +size_t to_client_len; + +ssize_t +client_pull (gnutls_transport_ptr_t tr, void *data, size_t len) +{ + success ("client_pull len %d has %d\n", len, to_client_len); + + if (to_client_len < len) + { + gnutls_transport_set_global_errno (EAGAIN); + return -1; + } + + memcpy (data, to_client, len); + + memmove (to_client, to_client + len, to_client_len - len); + to_client_len -= len; + + return len; +} + +ssize_t +client_push (gnutls_transport_ptr_t tr, const void *data, size_t len) +{ + size_t newlen = to_server_len + len; + char *tmp; + + success ("client_push len %d has %d\n", len, to_server_len); + hexprint (data, len); + + tmp = realloc (to_server, newlen); + if (!tmp) + { + fail ("Memory allocation failure...\n"); + exit (1); + } + to_server = tmp; + + memcpy (to_server + to_server_len, data, len); + to_server_len = newlen; + + return len; +} + +ssize_t +server_pull (gnutls_transport_ptr_t tr, void *data, size_t len) +{ + success ("server_pull len %d has %d\n", len, to_server_len); + + if (to_server_len < len) + { + gnutls_transport_set_global_errno (EAGAIN); + return -1; + } + + memcpy (data, to_server, len); + + memmove (to_server, to_server + len, to_server_len - len); + to_server_len -= len; + + return len; +} + +ssize_t +server_push (gnutls_transport_ptr_t tr, const void *data, size_t len) +{ + size_t newlen = to_client_len + len; + char *tmp; + + success ("server_push len %d has %d\n", len, to_client_len); + + hexprint (data, len); + + tmp = realloc (to_client, newlen); + if (!tmp) + { + fail ("Memory allocation failure...\n"); + exit (1); + } + to_client = tmp; + + memcpy (to_client + to_client_len, data, len); + to_client_len = newlen; + + return len; +} + +void +client_finished_callback (gnutls_session_t session, + const void *finished, + size_t len) +{ + success ("client finished (length %d)\n", len); + hexprint (finished, len); +} + +void +server_finished_callback (gnutls_session_t session, + const void *finished, + size_t len) +{ + success ("server finished (length %d)\n", len); + hexprint (finished, len); +} + +#define MAX_BUF 1024 +#define MSG "Hello TLS" + +void +doit (void) +{ + /* Server stuff. */ + gnutls_anon_server_credentials_t s_anoncred; + const gnutls_datum_t p3 = { pkcs3, strlen (pkcs3) }; + static gnutls_dh_params_t dh_params; + gnutls_session_t server; + int sret = GNUTLS_E_AGAIN; + /* Client stuff. */ + gnutls_anon_client_credentials_t c_anoncred; + gnutls_session_t client; + int n, cret = GNUTLS_E_AGAIN; + /* Need to enable anonymous KX specifically. */ + const int kx_prio[] = { GNUTLS_KX_ANON_DH, 0 }; + char buffer[MAX_BUF + 1]; + ssize_t ns; + int ret; + + /* General init. */ + gnutls_global_init (); + gnutls_global_set_log_function (tls_log_func); + if (debug) + gnutls_global_set_log_level (4711); + + /* Init server */ + gnutls_anon_allocate_server_credentials (&s_anoncred); + gnutls_dh_params_init (&dh_params); + gnutls_dh_params_import_pkcs3 (dh_params, &p3, GNUTLS_X509_FMT_PEM); + gnutls_anon_set_server_dh_params (s_anoncred, dh_params); + gnutls_init (&server, GNUTLS_SERVER); + gnutls_set_default_priority (server); + gnutls_kx_set_priority (server, kx_prio); + gnutls_credentials_set (server, GNUTLS_CRD_ANON, s_anoncred); + gnutls_dh_set_prime_bits (server, 1024); + gnutls_transport_set_push_function (server, server_push); + gnutls_transport_set_pull_function (server, server_pull); + gnutls_session_set_finished_function (server, server_finished_callback); + + /* Init client */ + gnutls_anon_allocate_client_credentials (&c_anoncred); + gnutls_init (&client, GNUTLS_CLIENT); + gnutls_set_default_priority (client); + gnutls_kx_set_priority (client, kx_prio); + gnutls_credentials_set (client, GNUTLS_CRD_ANON, c_anoncred); + gnutls_transport_set_push_function (client, client_push); + gnutls_transport_set_pull_function (client, client_pull); + gnutls_session_set_finished_function (client, client_finished_callback); + + do + { + if (cret == GNUTLS_E_AGAIN) + { + success ("loop invoking client:\n"); + cret = gnutls_handshake (client); + success ("client %d: %s\n", cret, gnutls_strerror (cret)); + } + + if (sret == GNUTLS_E_AGAIN) + { + success ("loop invoking server:\n"); + sret = gnutls_handshake (server); + success ("server %d: %s\n", sret, gnutls_strerror (sret)); + } + } + while (cret == GNUTLS_E_AGAIN || sret == GNUTLS_E_AGAIN); + + success ("Handshake established\n"); + + ns = gnutls_record_send (client, MSG, strlen (MSG)); + success ("client: sent %d\n", ns); + + ret = gnutls_record_recv (server, buffer, MAX_BUF); + if (ret == 0) + fail ("server: didn't receive any data\n"); + else if (ret < 0) + fail ("server: error: %s\n", gnutls_strerror (ret)); + else + { + printf ("server: received %d: ", ret); + for (n = 0; n < ret; n++) + fputc (buffer[n], stdout); + fputs ("\n", stdout); + } + + ns = gnutls_record_send (server, MSG, strlen (MSG)); + success ("server: sent %d\n", ns); + + ret = gnutls_record_recv (client, buffer, MAX_BUF); + if (ret == 0) + { + fail ("client: Peer has closed the TLS connection\n"); + } + else if (ret < 0) + { + fail ("client: Error: %s\n", gnutls_strerror (ret)); + } + else + { + printf ("client: received %d: ", ret); + for (n = 0; n < ret; n++) + fputc (buffer[n], stdout); + fputs ("\n", stdout); + } + + gnutls_bye (client, GNUTLS_SHUT_RDWR); + gnutls_bye (server, GNUTLS_SHUT_RDWR); + + gnutls_deinit (client); + gnutls_deinit (server); + + free (to_server); + free (to_client); + + gnutls_anon_free_client_credentials (c_anoncred); + gnutls_anon_free_server_credentials (s_anoncred); + + gnutls_dh_params_deinit (dh_params); + + gnutls_global_deinit (); +} |