[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[PATCH] session ticket support
From: |
Daiki Ueno |
Subject: |
[PATCH] session ticket support |
Date: |
Wed, 15 Jul 2009 05:11:45 +0900 |
User-agent: |
Gnus/5.13 (Gnus v5.13) Emacs/23.0.96 (gnu/linux) |
Hi,
The attached is an experimental patch which adds support for RFC5077
SessionTicket extension to GnuTLS. I would appreciate any comment.
Some notes:
- I added gnutls_ext_register2, since the send_params callback of
gnutls_ext_register is not currently able to send empty extension
data.
- The interface is not flexible and there are many limitations; for
example, there is no control whether to accept/reject tickets on
reception.
- For the example usage of the interface, please check
tests/session_ticket.c.
diff --git a/ChangeLog b/ChangeLog
index cf84af7..4ded5d5 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,43 @@
+2009-07-15 Daiki Ueno <address@hidden>
+
+ * doc/TODO: Remove item about session_ticket extension since it is
+ now supported.
+
+ * tests/Makefile.am (ctests): Add session_ticket.c.
+ * tests/session_ticket.c: New test.
+
+ * lib/libgnutls.map: Export gnutls_ext_register2,
+ gnutls_session_ticket_enable_client,
+ gnutls_session_ticket_enable_server.
+
+ * lib/gnutls_session_pack.c (pack_certificate_auth_info)
+ (pack_srp_auth_info, pack_anon_auth_info, pack_psk_auth_info):
+ Allocate memory for session_ticket.
+ (pack_security_parameters): Pack session_ticket.
+ (unpack_security_parameters): Unpack session_ticket.
+
+ * lib/gnutls_handshake.c (_gnutls_read_client_hello): Handle the
+ case where the server is resumed by session_ticket.
+ (_gnutls_recv_handshake): Handle NewSessionTicket handshake.
+ (_gnutls_handshake_common): Exchange NewSessionTicket handshake.
+
+ * lib/ext_session_ticket.h, lib/ext_session_ticket.c: New file.
+
+ * lib/gnutls_extensions.c (gnutls_ext_register2): New function.
+ (_gnutls_ext_init): Register session_ticket extension.
+
+ * lib/m4/hooks.m4: Add session_ticket extension checks.
+ * lib/Makefile.am (SESSION_TICKET_COBJECTS): Add ext_session_ticket.c.
+
+ * lib/gnutls_int.h: Define constants for session_ticket extension.
+ Define new struct session_ticket_server_params_st.
+ (tls_ext_st, internals_st): Add fields for session_ticket extension.
+
+ * lib/includes/gnutls/gnutls.h.in: Add prototype for
+ gnutls_ext_register2, gnutls_session_ticket_enable_client, and
+ gnutls_session_ticket_enable_server. Define new handshake
+ GNUTLS_HANDSHAKE_NEW_SESSION_TICKET.
+
2009-06-09 Simon Josefsson <address@hidden>
* gl/m4/gnulib-comp.m4, gl/m4/version-etc.m4,
diff --git a/doc/TODO b/doc/TODO
index 86f144b..d561d57 100644
--- a/doc/TODO
+++ b/doc/TODO
@@ -50,7 +50,6 @@ Current list:
be able to execve a new process that take over the current
living socket (using the fcntl close-on-exec flag) and
continue the TLS session as well.
-- Implement draft-salowey-tls-ticket-05, useful for (e.g.) EAP-FAST.
- Reduce memory footprint
- Inside gnutls_global_init, the library allocates about 64 kb of
memory in almost 4000 calls to malloc. On my desktop, there are 22
diff --git a/lib/Makefile.am b/lib/Makefile.am
index ab1def4..bbe9db2 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -63,6 +63,8 @@ PSK_COBJECTS = auth_psk.c auth_psk_passwd.c gnutls_psk.c
\
OPRFI_COBJECTS = ext_oprfi.c
+SESSION_TICKET_COBJECTS = ext_session_ticket.c
+
COBJECTS = gnutls_record.c gnutls_compress.c debug.c gnutls_cipher.c \
gnutls_buffers.c gnutls_handshake.c gnutls_num.c \
gnutls_errors.c gnutls_algorithms.c gnutls_dh.c gnutls_kx.c \
@@ -97,12 +99,14 @@ HFILES = debug.h gnutls_compress.h gnutls_cipher.h
gnutls_buffers.h \
gnutls_rsa_export.h ext_server_name.h auth_dh_common.h \
ext_srp.h gnutls_srp.h auth_srp.h auth_srp_passwd.h \
gnutls_helper.h auth_psk.h auth_psk_passwd.h \
- gnutls_supplemental.h ext_oprfi.h crypto.h random.h
+ gnutls_supplemental.h ext_oprfi.h crypto.h random.h \
+ ext_session_ticket.h
# Separate so we can create the documentation
libgnutls_la_SOURCES = $(HFILES) $(COBJECTS) $(SRP_COBJECTS) \
- $(PSK_COBJECTS) gnutls.asn pkix.asn libgnutls.map
+ $(PSK_COBJECTS) $(SESSION_TICKET_COBJECTS) \
+ gnutls.asn pkix.asn libgnutls.map
libgnutls_la_LDFLAGS = -no-undefined \
-version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE)
diff --git a/lib/ext_session_ticket.c b/lib/ext_session_ticket.c
new file mode 100644
index 0000000..4451c01
--- /dev/null
+++ b/lib/ext_session_ticket.c
@@ -0,0 +1,568 @@
+/*
+ * Copyright (C) 2009 Free Software Foundation
+ *
+ * Author: Daiki Ueno
+ *
+ * This file is part of GNUTLS.
+ *
+ * The GNUTLS library 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 library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA
+ *
+ */
+
+#include <gnutls_int.h>
+#include <gnutls_errors.h>
+#include <gnutls_datum.h>
+#include <ext_session_ticket.h>
+
+#ifdef ENABLE_SESSION_TICKET
+
+#define KEY_NAME_SIZE SESSION_TICKET_KEY_NAME_SIZE
+#define KEY_SIZE SESSION_TICKET_KEY_SIZE
+#define IV_SIZE SESSION_TICKET_IV_SIZE
+#define MAC_SECRET_SIZE SESSION_TICKET_MAC_SECRET_SIZE
+
+#define MAC_SIZE 32
+
+struct ticket {
+ opaque key_name[KEY_NAME_SIZE];
+ opaque IV[IV_SIZE];
+ opaque *encrypted_state;
+ uint16_t encrypted_state_len;
+ opaque mac[MAC_SIZE];
+};
+
+static int
+digest_ticket (const gnutls_datum_t *key, struct ticket *ticket, opaque
*digest)
+{
+ digest_hd_st digest_hd;
+ uint16_t length16;
+ int ret;
+
+ ret = _gnutls_hmac_init (&digest_hd, GNUTLS_MAC_SHA256, key->data,
+ key->size);
+ if (ret < 0)
+ {
+ gnutls_assert ();
+ return ret;
+ }
+ _gnutls_hmac (&digest_hd, ticket->key_name, KEY_NAME_SIZE);
+ _gnutls_hmac (&digest_hd, ticket->IV, IV_SIZE);
+ length16 = _gnutls_conv_uint16 (ticket->encrypted_state_len);
+ _gnutls_hmac (&digest_hd, &length16, 2);
+ _gnutls_hmac (&digest_hd, ticket->encrypted_state,
+ ticket->encrypted_state_len);
+ _gnutls_hmac_deinit (&digest_hd, digest);
+
+ return 0;
+}
+
+static int
+decrypt_ticket (gnutls_session_t session, struct ticket *ticket)
+{
+ cipher_hd_st cipher_hd;
+ gnutls_datum_t key, IV, mac_secret;
+ opaque final[32], *state;
+ uint16_t state_len;
+ time_t timestamp = time (0);
+ int ret;
+
+ /* Check the integrity of ticket using HMAC-SHA-256. */
+ mac_secret.data = session->security_parameters.extensions.
+ session_ticket_server_params.mac_secret;
+ mac_secret.size = MAC_SECRET_SIZE;
+ ret = digest_ticket (&mac_secret, ticket, final);
+ if (ret < 0)
+ {
+ gnutls_assert ();
+ return ret;
+ }
+
+ if (memcmp (ticket->mac, final, MAC_SIZE))
+ {
+ gnutls_assert ();
+ return GNUTLS_E_DECRYPTION_FAILED;
+ }
+
+ /* Decrypt encrypted_state using 128-bit AES in CBC mode. */
+ key.data = session->security_parameters.extensions.
+ session_ticket_server_params.key;
+ key.size = KEY_SIZE;
+ IV.data = ticket->IV;
+ IV.size = IV_SIZE;
+ ret = _gnutls_cipher_init (&cipher_hd, GNUTLS_CIPHER_AES_128_CBC, &key, &IV);
+ if (ret < 0)
+ {
+ gnutls_assert ();
+ return ret;
+ }
+ ret = _gnutls_cipher_decrypt (&cipher_hd, ticket->encrypted_state,
+ ticket->encrypted_state_len);
+ _gnutls_cipher_deinit (&cipher_hd);
+ if (ret < 0)
+ {
+ gnutls_assert ();
+ return ret;
+ }
+
+ /* Unpack security parameters. */
+ state = ticket->encrypted_state;
+ state_len = ticket->encrypted_state_len;
+
+ DECR_LEN (state_len, 2);
+ session->internals.resumed_security_parameters.version =
+ _gnutls_version_get (state[0], state[1]);
+ state += 2;
+
+ DECR_LEN (state_len, 2);
+ memcpy (session->internals.resumed_security_parameters.
+ current_cipher_suite.suite,
+ state, 2);
+ state += 2;
+
+ DECR_LEN (state_len, 1);
+ session->internals.resumed_security_parameters.write_compression_algorithm =
+ session->internals.resumed_security_parameters.read_compression_algorithm =
+ _gnutls_compression_get_id (*state);
+ state += 1;
+
+ DECR_LEN (state_len, GNUTLS_MASTER_SIZE);
+ memcpy (session->internals.resumed_security_parameters.master_secret,
+ state, GNUTLS_MASTER_SIZE);
+ state += GNUTLS_MASTER_SIZE;
+
+ DECR_LEN (state_len, 1);
+ switch (*state++) /* ClientAuthenticationType */
+ {
+ case 0: /* anonymous */
+ break;
+ case 1: /* certificate_based */
+ {
+ uint32_t length24 = 0;
+
+ DECR_LEN (state_len, 3);
+ length24 |= *state++;
+ length24 << 8;
+ length24 |= *state++;
+ length24 << 8;
+ length24 |= *state++;
+
+ /* FIXME: implement */
+
+ DECR_LEN (state_len, length24);
+ state += length24;
+ }
+ break;
+ case 2: /* psk */
+ {
+ uint16_t length16;
+
+ DECR_LEN (state_len, 2);
+ length16 = _gnutls_read_uint16 (state);
+
+ /* FIXME: implement */
+
+ DECR_LEN (state_len, length16);
+ state += length16;
+ }
+ break;
+ }
+
+ DECR_LEN (state_len, 4);
+ session->internals.resumed_security_parameters.timestamp =
+ _gnutls_read_uint32 (state);
+
+ if (timestamp - session->internals.resumed_security_parameters.timestamp >
+ session->internals.expire_time
+ || session->internals.resumed_security_parameters.timestamp > timestamp)
+ {
+ gnutls_assert ();
+ return GNUTLS_E_EXPIRED;
+ }
+
+ session->internals.resumed_security_parameters.entity =
+ GNUTLS_SERVER;
+
+ session->internals.resumed_security_parameters.write_bulk_cipher_algorithm =
+ session->internals.resumed_security_parameters.read_bulk_cipher_algorithm =
+ _gnutls_cipher_suite_get_cipher_algo
+ (&session->internals.resumed_security_parameters.current_cipher_suite);
+
+ session->internals.resumed_security_parameters.write_mac_algorithm =
+ session->internals.resumed_security_parameters.read_mac_algorithm =
+ _gnutls_cipher_suite_get_mac_algo
+ (&session->internals.resumed_security_parameters.current_cipher_suite);
+
+ session->internals.resumed_security_parameters.kx_algorithm =
+ _gnutls_cipher_suite_get_kx_algo
+ (&session->internals.resumed_security_parameters.current_cipher_suite);
+
+ session->internals.resumed = RESUME_TRUE;
+
+ return 0;
+}
+
+static int
+encrypt_ticket (gnutls_session_t session, struct ticket *ticket)
+{
+ cipher_hd_st cipher_hd;
+ gnutls_datum_t key, IV, mac_secret;
+ opaque *state, *p;
+ uint16_t state_len = 2 + 2 + 1 + GNUTLS_MASTER_SIZE + 1 + 4;
+ int blocksize;
+ int ret;
+
+ blocksize = _gnutls_cipher_get_block_size (GNUTLS_CIPHER_AES_128_CBC);
+ state_len = ((state_len + blocksize - 1) / blocksize) * blocksize;
+ state = gnutls_malloc (state_len);
+ if (!state)
+ {
+ gnutls_assert ();
+ return GNUTLS_E_MEMORY_ERROR;
+ }
+ memset (state, 0, state_len);
+
+ /* Pack security parameters. */
+ p = state;
+ *p++ = _gnutls_version_get_major (session->security_parameters.version);
+ *p++ = _gnutls_version_get_minor (session->security_parameters.version);
+
+ memcpy (p, session->security_parameters.current_cipher_suite.suite, 2);
+ p += 2;
+
+ *p++ = _gnutls_compression_get_num (session->security_parameters.
+ read_compression_algorithm);
+
+ memcpy (p, session->security_parameters.master_secret, GNUTLS_MASTER_SIZE);
+ p += GNUTLS_MASTER_SIZE;
+
+ /* FIXME: more authentication type support. */
+ *p++ = 0; /* anonymous */
+ _gnutls_write_uint32 (session->security_parameters.timestamp, p);
+
+ /* Encrypt state using 128-bit AES in CBC mode. */
+ key.data = session->security_parameters.extensions.
+ session_ticket_server_params.key;
+ key.size = KEY_SIZE;
+ IV.data = session->security_parameters.extensions.
+ session_ticket_server_params.IV;
+ IV.size = IV_SIZE;
+ ret = _gnutls_cipher_init (&cipher_hd, GNUTLS_CIPHER_AES_128_CBC, &key, &IV);
+ if (ret < 0)
+ {
+ gnutls_assert ();
+ gnutls_free (state);
+ return ret;
+ }
+
+ ret = _gnutls_cipher_encrypt (&cipher_hd, state, state_len);
+ _gnutls_cipher_deinit (&cipher_hd);
+ if (ret < 0)
+ {
+ gnutls_assert ();
+ gnutls_free (state);
+ return ret;
+ }
+
+ /* Fill the ticket structure to compute MAC. */
+ memcpy (ticket->key_name,
+ session->security_parameters.extensions.
+ session_ticket_server_params.key_name,
+ KEY_NAME_SIZE);
+ memcpy (ticket->IV, IV.data, IV.size);
+ ticket->encrypted_state_len = state_len;
+ ticket->encrypted_state = state;
+
+ mac_secret.data = session->security_parameters.extensions.
+ session_ticket_server_params.mac_secret;
+ mac_secret.size = MAC_SECRET_SIZE;
+ ret = digest_ticket (&mac_secret, ticket, ticket->mac);
+ if (ret < 0)
+ {
+ gnutls_assert ();
+ gnutls_free (state);
+ return ret;
+ }
+
+ return 0;
+}
+
+int
+_gnutls_session_ticket_recv_params (gnutls_session_t session,
+ const opaque * data, size_t data_size)
+{
+ if (!session->internals.session_ticket_enable)
+ return 0;
+
+ if (session->security_parameters.entity == GNUTLS_SERVER)
+ {
+ struct ticket ticket;
+ const opaque *encrypted_state;
+ int ret;
+
+ if (data_size == 0)
+ {
+ session->internals.session_ticket_renew = 1;
+ return 0;
+ }
+
+ DECR_LEN (data_size, KEY_NAME_SIZE);
+ memcpy (ticket.key_name, data, KEY_NAME_SIZE);
+ data += KEY_NAME_SIZE;
+
+ /* If the key name of the ticket does not match what we
+ hold, issue a new ticket. */
+ if (memcmp (ticket.key_name,
+ session->security_parameters.extensions.
+ session_ticket_server_params.key_name,
+ KEY_NAME_SIZE))
+ {
+ session->internals.session_ticket_renew = 1;
+ return 0;
+ }
+
+ DECR_LEN (data_size, IV_SIZE);
+ memcpy (ticket.IV, data, IV_SIZE);
+ data += IV_SIZE;
+
+ DECR_LEN (data_size, 2);
+ ticket.encrypted_state_len = _gnutls_read_uint16 (data);
+ data += 2;
+
+ encrypted_state = data;
+
+ DECR_LEN (data_size, ticket.encrypted_state_len);
+ data += ticket.encrypted_state_len;
+
+ DECR_LEN (data_size, MAC_SIZE);
+ memcpy (ticket.mac, data, MAC_SIZE);
+
+ ticket.encrypted_state = gnutls_malloc (ticket.encrypted_state_len);
+ if (!ticket.encrypted_state)
+ {
+ gnutls_assert ();
+ return GNUTLS_E_MEMORY_ERROR;
+ }
+ memcpy (ticket.encrypted_state, encrypted_state,
+ ticket.encrypted_state_len);
+
+ ret = decrypt_ticket (session, &ticket);
+ gnutls_free (ticket.encrypted_state);
+ if (ret < 0)
+ {
+ gnutls_assert ();
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+/* This function returns a positive number if we send the extension
+ data even if it is empty, zero if we do not want to send it, and a
+ negative number on failure.
+ */
+int
+_gnutls_session_ticket_send_params (gnutls_session_t session,
+ opaque * data, size_t *_data_size)
+{
+ size_t data_size = *_data_size;
+
+ if (!session->internals.session_ticket_enable)
+ return 0;
+
+ if (session->security_parameters.entity == GNUTLS_SERVER)
+ {
+ if (session->internals.session_ticket_renew)
+ {
+ *_data_size = 0;
+ return 1;
+ }
+ }
+ else
+ {
+ if (session->internals.resumed_security_parameters.extensions.
+ session_ticket_len > 0)
+ {
+ DECR_LENGTH_RET (data_size,
+ session->internals.resumed_security_parameters.
+ extensions.session_ticket_len,
+ GNUTLS_E_SHORT_MEMORY_BUFFER);
+ memcpy (data,
+ session->internals.resumed_security_parameters.extensions.
+ session_ticket,
+ session->internals.resumed_security_parameters.extensions.
+ session_ticket_len);
+ *_data_size = session->internals.resumed_security_parameters.
+ extensions.session_ticket_len;
+ return 1;
+ }
+ else
+ {
+ session->internals.session_ticket_renew = 1;
+ *_data_size = 0;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+int
+gnutls_session_ticket_enable_client (gnutls_session_t session)
+{
+ session->internals.session_ticket_enable = 1;
+ return 0;
+}
+
+int
+gnutls_session_ticket_enable_server (gnutls_session_t session,
+ const void *key, size_t keylen)
+{
+ int pos, ret;
+
+ if (keylen < KEY_SIZE + MAC_SECRET_SIZE)
+ {
+ gnutls_assert ();
+ return GNUTLS_E_INTERNAL_ERROR;
+ }
+
+ ret = _gnutls_rnd (GNUTLS_RND_RANDOM,
+ session->security_parameters.extensions.
+ session_ticket_server_params.IV,
+ IV_SIZE);
+ if (ret < 0)
+ {
+ gnutls_assert ();
+ return ret;
+ }
+
+ memcpy (session->security_parameters.extensions.
+ session_ticket_server_params.key,
+ key, KEY_SIZE);
+
+ memcpy (session->security_parameters.extensions.
+ session_ticket_server_params.mac_secret,
+ &key[KEY_SIZE], MAC_SECRET_SIZE);
+
+ session->internals.session_ticket_enable = 1;
+ return 0;
+}
+
+int
+_gnutls_send_new_session_ticket (gnutls_session_t session, int again)
+{
+ uint8_t *data = NULL, *p;
+ int data_size;
+ int ret;
+ struct ticket ticket;
+ uint16_t ticket_len;
+
+ ret = encrypt_ticket (session, &ticket);
+ if (ret < 0)
+ {
+ gnutls_assert ();
+ return ret;
+ }
+
+ ticket_len = KEY_NAME_SIZE + IV_SIZE + 2 + ticket.encrypted_state_len
+ + MAC_SIZE;
+
+ data = gnutls_malloc (4 + 2 + ticket_len);
+ if (!data)
+ {
+ gnutls_assert ();
+ gnutls_free (ticket.encrypted_state);
+ return GNUTLS_E_MEMORY_ERROR;
+ }
+
+ p = data;
+ /* FIXME: ticket lifetime is fixed to 10 days, which should be
+ customizable. */
+ _gnutls_write_uint32 (864000, p);
+ p += 4;
+
+ _gnutls_write_uint16 (ticket_len, p);
+ p += 2;
+
+ memcpy (p, ticket.key_name, KEY_NAME_SIZE);
+ p += KEY_NAME_SIZE;
+
+ memcpy (p, ticket.IV, IV_SIZE);
+ p += IV_SIZE;
+
+ _gnutls_write_uint16 (ticket.encrypted_state_len, p);
+ p += 2;
+
+ memcpy (p, ticket.encrypted_state, ticket.encrypted_state_len);
+ gnutls_free (ticket.encrypted_state);
+ p += ticket.encrypted_state_len;
+
+ memcpy (p, ticket.mac, MAC_SIZE);
+ p += MAC_SIZE;
+
+ ret = _gnutls_send_handshake (session, data, p - data,
+ GNUTLS_HANDSHAKE_NEW_SESSION_TICKET);
+ gnutls_free (data);
+
+ return ret;
+}
+
+int
+_gnutls_recv_new_session_ticket (gnutls_session_t session)
+{
+ uint8_t *data = NULL, *p;
+ int data_size;
+ uint32_t lifetime_hint;
+ uint16_t ticket_len;
+ int ret;
+
+ ret = _gnutls_recv_handshake (session, &data, &data_size,
+ GNUTLS_HANDSHAKE_NEW_SESSION_TICKET);
+ if (ret < 0)
+ {
+ gnutls_assert ();
+ return ret;
+ }
+
+ p = data;
+ DECR_LENGTH_COM (data_size, 4, goto error);
+ lifetime_hint = _gnutls_read_uint32 (p);
+ p += 4;
+
+ DECR_LENGTH_COM (data_size, 2, goto error);
+ ticket_len = _gnutls_read_uint16 (p);
+ p += 2;
+
+ DECR_LENGTH_COM (data_size, ticket_len, goto error);
+ session->security_parameters.extensions.session_ticket =
+ gnutls_malloc (ticket_len);
+ if (!session->security_parameters.extensions.session_ticket)
+ {
+ gnutls_assert ();
+ gnutls_free (data);
+ return GNUTLS_E_MEMORY_ERROR;
+ }
+ memcpy (session->security_parameters.extensions.session_ticket,
+ p, ticket_len);
+ gnutls_free (data);
+ session->security_parameters.extensions.session_ticket_len = ticket_len;
+ return 0;
+
+ error:
+ gnutls_free (data);
+ return GNUTLS_E_UNEXPECTED_PACKET_LENGTH;
+}
+
+#endif
diff --git a/lib/ext_session_ticket.h b/lib/ext_session_ticket.h
new file mode 100644
index 0000000..59ed78f
--- /dev/null
+++ b/lib/ext_session_ticket.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2009 Free Software Foundation
+ *
+ * Author: Daiki Ueno
+ *
+ * This file is part of GNUTLS.
+ *
+ * The GNUTLS library 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 library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA
+ *
+ */
+
+#ifdef ENABLE_SESSION_TICKET
+
+int _gnutls_session_ticket_recv_params (gnutls_session_t session,
+ const opaque * data, size_t data_size);
+int _gnutls_session_ticket_send_params (gnutls_session_t session,
+ opaque * data, size_t *data_size);
+
+int _gnutls_send_new_session_ticket (gnutls_session_t session, int again);
+int _gnutls_recv_new_session_ticket (gnutls_session_t session);
+
+#endif
diff --git a/lib/gnutls_constate.c b/lib/gnutls_constate.c
index 521e55f..9afd897 100644
--- a/lib/gnutls_constate.c
+++ b/lib/gnutls_constate.c
@@ -486,9 +486,12 @@ _gnutls_read_connection_state_init (gnutls_session_t
session)
}
else
{ /* RESUME_TRUE */
+ opaque *session_ticket =
+ session->security_parameters.extensions.session_ticket;
_gnutls_cpy_read_security_parameters (&session->security_parameters,
&session->internals.
resumed_security_parameters);
+ session->security_parameters.extensions.session_ticket = session_ticket;
}
@@ -668,9 +671,12 @@ _gnutls_write_connection_state_init (gnutls_session_t
session)
}
else
{ /* RESUME_TRUE */
+ opaque *session_ticket =
+ session->security_parameters.extensions.session_ticket;
_gnutls_cpy_write_security_parameters (&session->security_parameters,
&session->internals.
resumed_security_parameters);
+ session->security_parameters.extensions.session_ticket = session_ticket;
}
rc = _gnutls_set_write_keys (session);
diff --git a/lib/gnutls_extensions.c b/lib/gnutls_extensions.c
index 31769d2..4e397f8 100644
--- a/lib/gnutls_extensions.c
+++ b/lib/gnutls_extensions.c
@@ -35,6 +35,7 @@
#include <ext_server_name.h>
#include <ext_oprfi.h>
#include <ext_srp.h>
+#include <ext_session_ticket.h>
#include <gnutls_num.h>
typedef struct
@@ -44,6 +45,7 @@ typedef struct
gnutls_ext_parse_type_t parse_type;
gnutls_ext_recv_func recv_func;
gnutls_ext_send_func send_func;
+ gnutls_ext_send_func2 send_func2;
} gnutls_extension_entry;
static size_t extfunc_size = 0;
@@ -197,7 +199,7 @@ _gnutls_gen_extensions (gnutls_session_t session, opaque *
data,
int size;
uint16_t pos = 0;
opaque *sdata;
- int sdata_size;
+ size_t sdata_size;
size_t i;
if (data_size < 2)
@@ -221,12 +223,18 @@ _gnutls_gen_extensions (gnutls_session_t session, opaque
* data,
{
gnutls_extension_entry *p = &extfunc[i];
- if (p->send_func == NULL)
+ if (p->send_func == NULL && p->send_func2 == NULL)
continue;
- size = p->send_func (session, sdata, sdata_size);
+ if (p->send_func2)
+ size = p->send_func2 (session, sdata, &sdata_size);
+ else
+ size = p->send_func (session, sdata, sdata_size);
if (size > 0)
{
+ if (p->send_func2)
+ size = sdata_size;
+
if (data_size < pos + (size_t) size + 4)
{
gnutls_assert ();
@@ -324,6 +332,16 @@ _gnutls_ext_init (void)
return ret;
#endif
+#ifdef ENABLE_SESSION_TICKET
+ ret = gnutls_ext_register2 (GNUTLS_EXTENSION_SESSION_TICKET,
+ "SESSION_TICKET",
+ GNUTLS_EXT_TLS,
+ _gnutls_session_ticket_recv_params,
+ _gnutls_session_ticket_send_params);
+ if (ret != GNUTLS_E_SUCCESS)
+ return ret;
+#endif
+
return GNUTLS_E_SUCCESS;
}
@@ -371,6 +389,50 @@ gnutls_ext_register (int type,
extfunc[extfunc_size].parse_type = parse_type;
extfunc[extfunc_size].recv_func = recv_func;
extfunc[extfunc_size].send_func = send_func;
+ extfunc[extfunc_size].send_func2 = NULL;
+
+ extfunc_size++;
+
+ return GNUTLS_E_SUCCESS;
+}
+
+/**
+ * gnutls_ext_register2 - Register a handler for a TLS extension
+ * @type: the 16-bit integer referring to the extension type
+ * @name: human printable name of the extension used for debugging
+ * @parse_type: either #GNUTLS_EXT_TLS or %GNUTLS_EXT_APPLICATION.
+ * @recv_func: a function to receive extension data
+ * @send_func: a function to send extension data
+ *
+ * This function is used to register a new TLS extension handler. The
+ * only difference is that the type of the 5th argument send_func is
+ * gnutls_ext_send_func2 instead of gnutls_ext_send_func.
+ *
+ * Returns: %GNUTLS_E_SUCCESS on success, or an error code.
+ **/
+int
+gnutls_ext_register2 (int type,
+ const char *name,
+ gnutls_ext_parse_type_t parse_type,
+ gnutls_ext_recv_func recv_func,
+ gnutls_ext_send_func2 send_func)
+{
+ gnutls_extension_entry *p;
+
+ p = gnutls_realloc (extfunc, sizeof (*extfunc) * (extfunc_size + 1));
+ if (!p)
+ {
+ gnutls_assert ();
+ return GNUTLS_E_MEMORY_ERROR;
+ }
+ extfunc = p;
+
+ extfunc[extfunc_size].type = type;
+ extfunc[extfunc_size].name = name;
+ extfunc[extfunc_size].parse_type = parse_type;
+ extfunc[extfunc_size].recv_func = recv_func;
+ extfunc[extfunc_size].send_func = NULL;
+ extfunc[extfunc_size].send_func2 = send_func;
extfunc_size++;
diff --git a/lib/gnutls_handshake.c b/lib/gnutls_handshake.c
index 1ad4f52..03364c9 100644
--- a/lib/gnutls_handshake.c
+++ b/lib/gnutls_handshake.c
@@ -354,7 +354,7 @@ _gnutls_read_client_hello (gnutls_session_t session, opaque
* data,
gnutls_protocol_t adv_version;
int neg_version;
int len = datalen;
- opaque rnd[GNUTLS_RANDOM_SIZE], *suite_ptr, *comp_ptr;
+ opaque rnd[GNUTLS_RANDOM_SIZE], *suite_ptr, *comp_ptr, *session_id;
if (session->internals.v2_hello != 0)
{ /* version 2.0 */
@@ -399,7 +399,8 @@ _gnutls_read_client_hello (gnutls_session_t session, opaque
* data,
}
DECR_LEN (len, session_id_len);
- ret = _gnutls_server_restore_session (session, &data[pos], session_id_len);
+ session_id = &data[pos];
+ ret = _gnutls_server_restore_session (session, session_id, session_id_len);
pos += session_id_len;
if (ret == 0)
@@ -467,6 +468,24 @@ _gnutls_read_client_hello (gnutls_session_t session,
opaque * data,
gnutls_assert ();
return ret;
}
+
+ /* resumed by session_ticket extension */
+ if (session->internals.resumed == RESUME_TRUE)
+ {
+ /* to indicate the client that the current session is resumed */
+ memcpy (session->internals.resumed_security_parameters.session_id,
+ session_id, session_id_len);
+ session->internals.resumed_security_parameters.session_id_size =
+ session_id_len;
+
+ session->internals.resumed_security_parameters.max_record_recv_size =
+ session->security_parameters.max_record_recv_size;
+ session->internals.resumed_security_parameters.max_record_send_size =
+ session->security_parameters.max_record_send_size;
+
+ resume_copy_required_values (session);
+ return 0;
+ }
}
/* select an appropriate cipher suite
@@ -1291,6 +1310,7 @@ _gnutls_recv_handshake (gnutls_session_t session, uint8_t
** data,
case GNUTLS_HANDSHAKE_CERTIFICATE_REQUEST:
case GNUTLS_HANDSHAKE_CERTIFICATE_VERIFY:
case GNUTLS_HANDSHAKE_SUPPLEMENTAL:
+ case GNUTLS_HANDSHAKE_NEW_SESSION_TICKET:
ret = length32;
break;
default:
@@ -2684,6 +2704,12 @@ _gnutls_handshake_common (gnutls_session_t session)
ret = _gnutls_recv_handshake_final (session, TRUE);
IMED_RET ("recv handshake final", ret);
+ if (session->internals.session_ticket_renew)
+ {
+ ret = _gnutls_send_new_session_ticket (session);
+ IMED_RET ("send handshake new session ticket", ret);
+ }
+
ret = _gnutls_send_handshake_final (session, FALSE);
IMED_RET ("send handshake final", ret);
}
@@ -2693,6 +2719,12 @@ _gnutls_handshake_common (gnutls_session_t session)
ret = _gnutls_send_handshake_final (session, TRUE);
IMED_RET ("send handshake final 2", ret);
+ if (session->internals.session_ticket_renew)
+ {
+ ret = _gnutls_recv_new_session_ticket (session);
+ IMED_RET ("recv handshake new session ticket", ret);
+ }
+
ret = _gnutls_recv_handshake_final (session, FALSE);
IMED_RET ("recv handshake final 2", ret);
}
diff --git a/lib/gnutls_int.h b/lib/gnutls_int.h
index 6d6ab5d..0d9624c 100644
--- a/lib/gnutls_int.h
+++ b/lib/gnutls_int.h
@@ -87,6 +87,12 @@ typedef struct
#define MAX_LOG_SIZE 1024 /* maximum size of log message */
#define MAX_SRP_USERNAME 128
#define MAX_SERVER_NAME_SIZE 128
+#define MAX_SESSION_TICKET_SIZE 65535
+
+#define SESSION_TICKET_KEY_NAME_SIZE 16
+#define SESSION_TICKET_KEY_SIZE 16
+#define SESSION_TICKET_IV_SIZE 16
+#define SESSION_TICKET_MAC_SECRET_SIZE 32
/* we can receive up to MAX_EXT_TYPES extensions.
*/
@@ -171,6 +177,7 @@ typedef enum extensions_t
GNUTLS_EXTENSION_OPAQUE_PRF_INPUT = ENABLE_OPRFI,
#endif
GNUTLS_EXTENSION_SRP = 12,
+ GNUTLS_EXTENSION_SESSION_TICKET = 35,
GNUTLS_EXTENSION_INNER_APPLICATION = 37703
} extensions_t;
@@ -284,6 +291,14 @@ typedef struct
typedef struct
{
+ opaque key_name[SESSION_TICKET_KEY_NAME_SIZE];
+ opaque key[SESSION_TICKET_KEY_SIZE];
+ opaque IV[SESSION_TICKET_IV_SIZE];
+ opaque mac_secret[SESSION_TICKET_MAC_SECRET_SIZE];
+} session_ticket_server_params_st;
+
+typedef struct
+{
server_name_st server_names[MAX_SERVER_NAME_EXTENSIONS];
/* limit server_name extensions */
unsigned server_names_size;
@@ -304,6 +319,10 @@ typedef struct
uint16_t oprfi_client_len;
opaque *oprfi_server;
uint16_t oprfi_server_len;
+
+ opaque *session_ticket;
+ uint16_t session_ticket_len;
+ session_ticket_server_params_st session_ticket_server_params;
} tls_ext_st;
/* auth_info_t structures now MAY contain malloced
@@ -673,6 +692,8 @@ typedef struct
*/
uint16_t srp_prime_bits;
+ int session_ticket_enable, session_ticket_renew;
+
/* If you add anything here, check _gnutls_handshake_internal_state_clear().
*/
} internals_st;
diff --git a/lib/gnutls_session_pack.c b/lib/gnutls_session_pack.c
index 115f17e..dc56b0a 100644
--- a/lib/gnutls_session_pack.c
+++ b/lib/gnutls_session_pack.c
@@ -280,7 +280,8 @@ pack_certificate_auth_info (gnutls_session_t session,
/* calculate the size and allocate the data.
*/
packed_session->data =
- gnutls_malloc (packed_session->size + MAX_SEC_PARAMS);
+ gnutls_malloc (packed_session->size + MAX_SEC_PARAMS + 2 +
+ session->security_parameters.extensions.session_ticket_len);
if (packed_session->data == NULL)
{
@@ -513,7 +514,8 @@ pack_srp_auth_info (gnutls_session_t session,
gnutls_datum_t * packed_session)
/* calculate the size and allocate the data.
*/
packed_session->data =
- gnutls_malloc (packed_session->size + MAX_SEC_PARAMS);
+ gnutls_malloc (packed_session->size + MAX_SEC_PARAMS + 2 +
+ session->security_parameters.extensions.session_ticket_len);
if (packed_session->data == NULL)
{
@@ -618,7 +620,8 @@ pack_anon_auth_info (gnutls_session_t session,
/* calculate the size and allocate the data.
*/
packed_session->data =
- gnutls_malloc (packed_session->size + MAX_SEC_PARAMS);
+ gnutls_malloc (packed_session->size + MAX_SEC_PARAMS + 2 +
+ session->security_parameters.extensions.session_ticket_len);
if (packed_session->data == NULL)
{
@@ -781,7 +784,8 @@ pack_psk_auth_info (gnutls_session_t session,
gnutls_datum_t * packed_session)
/* calculate the size and allocate the data.
*/
packed_session->data =
- gnutls_malloc (packed_session->size + MAX_SEC_PARAMS);
+ gnutls_malloc (packed_session->size + MAX_SEC_PARAMS + 2 +
+ session->security_parameters.extensions.session_ticket_len);
if (packed_session->data == NULL)
{
@@ -977,9 +981,11 @@ error:
* 2 bytes the size of the first name
* x bytes the first name (MAX_SERVER_NAME_SIZE)
* and so on...
+ * 2 bytes the session ticket size
+ * x bytes the session ticket (MAX_SESSION_TICKET_SIZE)
*
* --------------------
- * MAX:
7+MAX_SRP_USERNAME+MAX_SERVER_NAME_EXTENSIONS*(3+MAX_SERVER_NAME_SIZE)
+ * MAX:
7+MAX_SRP_USERNAME+MAX_SERVER_NAME_EXTENSIONS*(3+MAX_SERVER_NAME_SIZE)+MAX_SESSION_TICKET_SIZE
*/
static int
pack_security_parameters (gnutls_session_t session,
@@ -1077,6 +1083,15 @@ pack_security_parameters (gnutls_session_t session,
session->security_parameters.extensions.server_names[i].name_length;
}
+ _gnutls_write_uint16 (session->security_parameters.extensions.
+ session_ticket_len,
+ &packed_session->data[pos]);
+ pos += 2;
+ memcpy (&packed_session->data[pos],
+ session->security_parameters.extensions.session_ticket,
+ session->security_parameters.extensions.session_ticket_len);
+ pos += session->security_parameters.extensions.session_ticket_len;
+
/* write the total size */
_gnutls_write_uint32 (pos - init - 4, &packed_session->data[init]);
packed_session->size += pos - init;
@@ -1109,7 +1124,7 @@ unpack_security_parameters (gnutls_session_t session,
return GNUTLS_E_INVALID_REQUEST;
/* a simple check for integrity */
- if (pack_size > MAX_SEC_PARAMS)
+ if (pack_size > MAX_SEC_PARAMS + 2 + MAX_SESSION_TICKET_SIZE)
{
gnutls_assert ();
return GNUTLS_E_INVALID_REQUEST;
@@ -1212,5 +1227,18 @@ unpack_security_parameters (gnutls_session_t session,
session->internals.resumed_security_parameters.extensions.
server_names[i].name_length;
}
+
+ session->internals.resumed_security_parameters.extensions.
+ session_ticket_len = _gnutls_read_uint16 (&packed_session->data[pos]);
+ pos += 2;
+ session->internals.resumed_security_parameters.extensions.session_ticket =
+ gnutls_malloc (session->internals.resumed_security_parameters.extensions.
+ session_ticket_len);
+ memcpy (session->internals.resumed_security_parameters.extensions.
+ session_ticket,
+ &packed_session->data[pos],
+ session->internals.resumed_security_parameters.extensions.
+ session_ticket_len);
+
return 0;
}
diff --git a/lib/gnutls_state.c b/lib/gnutls_state.c
index 3dd440e..08bc43f 100644
--- a/lib/gnutls_state.c
+++ b/lib/gnutls_state.c
@@ -433,6 +433,10 @@ gnutls_deinit (gnutls_session_t session)
gnutls_free (session->internals.srp_password);
}
+ gnutls_free (session->security_parameters.extensions.session_ticket);
+ gnutls_free (session->internals.resumed_security_parameters.extensions.
+ session_ticket);
+
memset (session, 0, sizeof (struct gnutls_session_int));
gnutls_free (session);
}
diff --git a/lib/includes/gnutls/gnutls.h.in b/lib/includes/gnutls/gnutls.h.in
index af27b8d..67a3eb7 100644
--- a/lib/includes/gnutls/gnutls.h.in
+++ b/lib/includes/gnutls/gnutls.h.in
@@ -221,6 +221,7 @@ extern "C" {
{ GNUTLS_HANDSHAKE_HELLO_REQUEST = 0,
GNUTLS_HANDSHAKE_CLIENT_HELLO = 1,
GNUTLS_HANDSHAKE_SERVER_HELLO = 2,
+ GNUTLS_HANDSHAKE_NEW_SESSION_TICKET = 4,
GNUTLS_HANDSHAKE_CERTIFICATE_PKT = 11,
GNUTLS_HANDSHAKE_SERVER_KEY_EXCHANGE = 12,
GNUTLS_HANDSHAKE_CERTIFICATE_REQUEST = 13,
@@ -493,6 +494,8 @@ extern "C" {
const unsigned char *data, size_t len);
typedef int (*gnutls_ext_send_func) (gnutls_session_t session,
unsigned char *data, size_t len);
+ typedef int (*gnutls_ext_send_func2) (gnutls_session_t session,
+ unsigned char *data, size_t *len);
/* This flag indicates for an extension whether
* it is useful to application level or TLS level only.
@@ -512,6 +515,12 @@ extern "C" {
gnutls_ext_recv_func recv_func,
gnutls_ext_send_func send_func);
+ int gnutls_ext_register2 (int type,
+ const char *name,
+ gnutls_ext_parse_type_t parse_type,
+ gnutls_ext_recv_func recv_func,
+ gnutls_ext_send_func2 send_func);
+
typedef enum
{
GNUTLS_NAME_DNS = 1
@@ -554,6 +563,11 @@ extern "C" {
const char *gnutls_supplemental_get_name
(gnutls_supplemental_data_format_type_t type);
+ /* SessionTicket, RFC 5077. */
+ int gnutls_session_ticket_enable_client (gnutls_session_t);
+ int gnutls_session_ticket_enable_server (gnutls_session_t,
+ const void *, size_t);
+
/* functions to set priority of cipher suites
*/
int gnutls_cipher_set_priority (gnutls_session_t session, const int *list);
diff --git a/lib/libgnutls.map b/lib/libgnutls.map
index 73dc6aa..d4cd02a 100644
--- a/lib/libgnutls.map
+++ b/lib/libgnutls.map
@@ -139,6 +139,7 @@ GNUTLS_1_4
gnutls_error_is_fatal;
gnutls_error_to_alert;
gnutls_ext_register;
+ gnutls_ext_register2;
gnutls_fingerprint;
gnutls_free;
gnutls_global_deinit;
@@ -565,6 +566,8 @@ GNUTLS_2_8
gnutls_x509_crt_get_verify_algorithm;
gnutls_x509_crt_set_crq_extensions;
gnutls_x509_crt_verify_hash;
+ gnutls_session_ticket_enable_client;
+ gnutls_session_ticket_enable_server;
} GNUTLS_1_4;
GNUTLS_PRIVATE {
diff --git a/lib/m4/hooks.m4 b/lib/m4/hooks.m4
index 7ee3e50..9d7700e 100644
--- a/lib/m4/hooks.m4
+++ b/lib/m4/hooks.m4
@@ -214,6 +214,20 @@ AC_DEFUN([LIBGNUTLS_HOOKS],
fi
AM_CONDITIONAL(ENABLE_OPENPGP, test "$ac_enable_openpgp" = "yes")
+ AC_MSG_CHECKING([whether to disable SessionTicket extension support])
+ AC_ARG_ENABLE(session-ticket,
+ AS_HELP_STRING([--disable-session-ticket],
+ [disable the SessionTicket extension support]),
+ ac_session_ticket=no)
+ if test x$ac_session_ticket != xno; then
+ AC_MSG_RESULT(no)
+ AC_DEFINE([ENABLE_SESSION_TICKET], 1, [enable SessionTicket extension])
+ else
+ ac_full=0
+ AC_MSG_RESULT(yes)
+ fi
+ AM_CONDITIONAL(ENABLE_SESSION_TICKET, test "$ac_enable_session_ticket" !=
"no")
+
# For storing integers in pointers without warnings
#
http://developer.gnome.org/doc/API/2.0/glib/glib-Type-Conversion-Macros.html#desc
AC_CHECK_SIZEOF(void *)
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 6fa092e..66c8a61 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -84,6 +84,10 @@ if ENABLE_OPRFI
ctests += oprfi
endif
+if ENABLE_SESSION_TICKET
+ctests += session_ticket
+endif
+
check_PROGRAMS = $(ctests)
TESTS = $(ctests)
diff --git a/tests/session_ticket.c b/tests/session_ticket.c
new file mode 100644
index 0000000..1b47d87
--- /dev/null
+++ b/tests/session_ticket.c
@@ -0,0 +1,394 @@
+/*
+ * Copyright (C) 2004, 2005, 2007, 2008, 2009 Free Software Foundation
+ *
+ * Author: Daiki Ueno
+ *
+ * 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 resume.c. */
+/* Parts copied from GnuTLS example programs. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <gnutls/gnutls.h>
+
+#include "tcp.c"
+
+#include "utils.h"
+
+/* A very basic TLS client, with anonymous authentication.
+ */
+
+#define MAX_BUF 1024
+#define MSG "Hello TLS"
+
+static void
+client (void)
+{
+ int ret, sd, ii;
+ gnutls_session_t session;
+ char buffer[MAX_BUF + 1];
+ gnutls_anon_client_credentials_t anoncred;
+ /* Need to enable anonymous KX specifically. */
+ const int kx_prio[] = { GNUTLS_KX_ANON_DH, 0 };
+
+ /* variables used in session resuming
+ */
+ int t;
+ gnutls_datum session_data;
+
+ gnutls_global_init ();
+
+ gnutls_anon_allocate_client_credentials (&anoncred);
+
+ for (t = 0; t < 2; t++)
+ { /* connect 2 times to the server */
+
+ /* connect to the peer
+ */
+ sd = tcp_connect ();
+
+ /* Initialize TLS session
+ */
+ gnutls_init (&session, GNUTLS_CLIENT);
+
+ /* Use default priorities */
+ gnutls_set_default_priority (session);
+ gnutls_kx_set_priority (session, kx_prio);
+
+ /* put the anonymous credentials to the current session
+ */
+ gnutls_credentials_set (session, GNUTLS_CRD_ANON, anoncred);
+
+ gnutls_session_ticket_enable_client (session);
+
+ if (t > 0)
+ {
+ /* if this is not the first time we connect */
+ gnutls_session_set_data (session, session_data.data,
+ session_data.size);
+ gnutls_free (session_data.data);
+ }
+
+ gnutls_transport_set_ptr (session, (gnutls_transport_ptr_t) sd);
+
+ /* Perform the TLS handshake
+ */
+ ret = gnutls_handshake (session);
+
+ if (ret < 0)
+ {
+ fail ("client: Handshake failed\n");
+ gnutls_perror (ret);
+ goto end;
+ }
+ else
+ {
+ success ("client: Handshake was completed\n");
+ }
+
+ if (t == 0)
+ { /* the first time we connect */
+ /* get the session data size */
+ ret = gnutls_session_get_data2 (session, &session_data);
+ if (ret < 0)
+ fail ("Getting resume data failed\n");
+ }
+ else
+ { /* the second time we connect */
+
+ /* check if we actually resumed the previous session */
+ if (gnutls_session_is_resumed (session) != 0)
+ {
+ success ("- Previous session was resumed\n");
+ }
+ else
+ {
+ success ("*** Previous session was NOT resumed\n");
+ }
+ }
+
+ gnutls_record_send (session, MSG, strlen (MSG));
+
+ ret = gnutls_record_recv (session, buffer, MAX_BUF);
+ if (ret == 0)
+ {
+ success ("client: Peer has closed the TLS connection\n");
+ goto end;
+ }
+ else if (ret < 0)
+ {
+ fail ("client: Error: %s\n", gnutls_strerror (ret));
+ goto end;
+ }
+
+ if (debug)
+ {
+ printf ("- Received %d bytes: ", ret);
+ for (ii = 0; ii < ret; ii++)
+ {
+ fputc (buffer[ii], stdout);
+ }
+ fputs ("\n", stdout);
+ }
+
+ gnutls_bye (session, GNUTLS_SHUT_RDWR);
+
+ end:
+
+ tcp_close (sd);
+
+ gnutls_deinit (session);
+ }
+
+ gnutls_anon_free_client_credentials (anoncred);
+}
+
+/* This is a sample TLS 1.0 echo server, for anonymous authentication only.
+ */
+
+#define SA struct sockaddr
+#define MAX_BUF 1024
+#define PORT 5556 /* listen to 5556 port */
+#define DH_BITS 1024
+
+/* These are global */
+gnutls_anon_server_credentials_t anoncred;
+
+static gnutls_session_t
+initialize_tls_session (void)
+{
+ gnutls_session_t session;
+ const int kx_prio[] = { GNUTLS_KX_ANON_DH, 0 };
+ static const unsigned char st_key[48] =
+ "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL";
+
+ gnutls_init (&session, GNUTLS_SERVER);
+
+ /* avoid calling all the priority functions, since the defaults
+ * are adequate.
+ */
+ gnutls_set_default_priority (session);
+ gnutls_kx_set_priority (session, kx_prio);
+
+ gnutls_credentials_set (session, GNUTLS_CRD_ANON, anoncred);
+
+ gnutls_dh_set_prime_bits (session, DH_BITS);
+
+ gnutls_session_ticket_enable_server (session, st_key, sizeof(st_key));
+
+ return session;
+}
+
+static gnutls_dh_params_t dh_params;
+
+static int
+generate_dh_params (void)
+{
+ const gnutls_datum_t p3 = { (char*) pkcs3, strlen (pkcs3) };
+ /* Generate Diffie-Hellman parameters - for use with DHE
+ * kx algorithms. These should be discarded and regenerated
+ * once a day, once a week or once a month. Depending on the
+ * security requirements.
+ */
+ gnutls_dh_params_init (&dh_params);
+ return gnutls_dh_params_import_pkcs3 (dh_params, &p3, GNUTLS_X509_FMT_PEM);
+}
+
+int err, listen_sd, i;
+int sd, ret;
+struct sockaddr_in sa_serv;
+struct sockaddr_in sa_cli;
+int client_len;
+char topbuf[512];
+gnutls_session_t session;
+char buffer[MAX_BUF + 1];
+int optval = 1;
+
+static void
+global_start (void)
+{
+ /* Socket operations
+ */
+ listen_sd = socket (AF_INET, SOCK_STREAM, 0);
+ if (err == -1)
+ {
+ perror ("socket");
+ fail ("server: socket failed\n");
+ return;
+ }
+
+ memset (&sa_serv, '\0', sizeof (sa_serv));
+ sa_serv.sin_family = AF_INET;
+ sa_serv.sin_addr.s_addr = INADDR_ANY;
+ sa_serv.sin_port = htons (PORT); /* Server Port number */
+
+ setsockopt (listen_sd, SOL_SOCKET, SO_REUSEADDR, (void *) &optval, sizeof
(int));
+
+ err = bind (listen_sd, (SA *) & sa_serv, sizeof (sa_serv));
+ if (err == -1)
+ {
+ perror ("bind");
+ fail ("server: bind failed\n");
+ return;
+ }
+
+ err = listen (listen_sd, 1024);
+ if (err == -1)
+ {
+ perror ("listen");
+ fail ("server: listen failed\n");
+ return;
+ }
+
+ success ("server: ready. Listening to port '%d'.\n", PORT);
+}
+
+static void
+global_stop (void)
+{
+ success ("global stop\n");
+
+ gnutls_anon_free_server_credentials (anoncred);
+
+ gnutls_dh_params_deinit (dh_params);
+
+ gnutls_global_deinit ();
+}
+
+static void
+server (void)
+{
+ size_t t;
+
+ /* this must be called once in the program, it is mostly for the server.
+ */
+ gnutls_global_init ();
+
+ gnutls_anon_allocate_server_credentials (&anoncred);
+
+ success ("Launched, generating DH parameters...\n");
+
+ generate_dh_params ();
+
+ gnutls_anon_set_server_dh_params (anoncred, dh_params);
+
+ for (t = 0; t < 2; t++)
+ {
+ client_len = sizeof (sa_cli);
+
+ session = initialize_tls_session ();
+
+ sd = accept (listen_sd, (SA *) & sa_cli, &client_len);
+
+ success ("server: connection from %s, port %d\n",
+ inet_ntop (AF_INET, &sa_cli.sin_addr, topbuf,
+ sizeof (topbuf)), ntohs (sa_cli.sin_port));
+
+ gnutls_transport_set_ptr (session, (gnutls_transport_ptr_t) sd);
+ ret = gnutls_handshake (session);
+ if (ret < 0)
+ {
+ close (sd);
+ gnutls_deinit (session);
+ fail ("server: Handshake has failed (%s)\n\n",
+ gnutls_strerror (ret));
+ return;
+ }
+ success ("server: Handshake was completed\n");
+
+ /* see the Getting peer's information example */
+ /* print_info(session); */
+
+ i = 0;
+ for (;;)
+ {
+ memset (buffer, 0, MAX_BUF + 1);
+ ret = gnutls_record_recv (session, buffer, MAX_BUF);
+
+ if (ret == 0)
+ {
+ success ("server: Peer has closed the GNUTLS connection\n");
+ break;
+ }
+ else if (ret < 0)
+ {
+ fail ("server: Received corrupted data(%d). Closing...\n", ret);
+ break;
+ }
+ else if (ret > 0)
+ {
+ /* echo data back to the client
+ */
+ gnutls_record_send (session, buffer, strlen (buffer));
+ }
+ }
+ /* do not wait for the peer to close the connection.
+ */
+ gnutls_bye (session, GNUTLS_SHUT_WR);
+
+ close (sd);
+
+ gnutls_deinit (session);
+ }
+
+ close (listen_sd);
+
+ success ("server: finished\n");
+}
+
+void
+doit (void)
+{
+ pid_t child;
+
+ global_start ();
+ if (error_count)
+ return;
+
+ child = fork ();
+ if (child < 0)
+ {
+ perror ("fork");
+ fail ("fork");
+ return;
+ }
+
+ if (child)
+ {
+ int status;
+ /* parent */
+ server ();
+ wait (&status);
+ }
+ else
+ client ();
+
+ global_stop ();
+}
Regards,
--
Daiki Ueno
- [PATCH] session ticket support,
Daiki Ueno <=
- Re: [PATCH] session ticket support, Nikos Mavrogiannopoulos, 2009/07/14
- Re: [PATCH] session ticket support, Nikos Mavrogiannopoulos, 2009/07/16
- Re: [PATCH] session ticket support, Daiki Ueno, 2009/07/17
- Re: [PATCH] session ticket support, Nikos Mavrogiannopoulos, 2009/07/17
- Re: [PATCH] session ticket support, Nikos Mavrogiannopoulos, 2009/07/25
- Re: [PATCH] session ticket support, Daiki Ueno, 2009/07/25
- Re: [PATCH] session ticket support, Nikos Mavrogiannopoulos, 2009/07/26
- Re: [PATCH] session ticket support, Daiki Ueno, 2009/07/27
- Re: [PATCH] session ticket support, Nikos Mavrogiannopoulos, 2009/07/29
- Re: [PATCH] session ticket support, Nikos Mavrogiannopoulos, 2009/07/30