]> git.ipfire.org Git - thirdparty/gnutls.git/commitdiff
session ticket support
authorDaiki Ueno <ueno@unixuser.org>
Wed, 19 Aug 2009 07:53:07 +0000 (16:53 +0900)
committerSimon Josefsson <simon@josefsson.org>
Wed, 19 Aug 2009 12:54:50 +0000 (14:54 +0200)
Signed-off-by: Simon Josefsson <simon@josefsson.org>
18 files changed:
doc/TODO
lib/Makefile.am
lib/ext_session_ticket.c [new file with mode: 0644]
lib/ext_session_ticket.h [new file with mode: 0644]
lib/gnutls_constate.c
lib/gnutls_extensions.c
lib/gnutls_handshake.c
lib/gnutls_int.h
lib/gnutls_session_pack.c
lib/gnutls_state.c
lib/includes/gnutls/gnutls.h.in
lib/libgnutls.map
lib/m4/hooks.m4
src/cli.c
src/cli.gaa
src/serv.c
src/serv.gaa
tests/resume.c

index 86f144ba6804660ac016bf335553eec82098e3ac..d561d57e973381a7b9b07f3493834a734c1f5868 100644 (file)
--- 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
index efc89dac0a9f5ba9ae0dc55ab766493327b8e19a..ad189b90ad099b884c039cb2dcb5b85fa03dc816 100644 (file)
@@ -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 (file)
index 0000000..b8cd8a2
--- /dev/null
@@ -0,0 +1,622 @@
+/*
+ * 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 <gnutls_algorithms.h>
+#include <gnutls_handshake.h>
+#include <gnutls_num.h>
+#include <gnutls_constate.h>
+#include <gnutls_session_pack.h>
+#include <random.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, state;
+  opaque final[32];
+  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_key->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_key->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.data = ticket->encrypted_state;
+  state.size = ticket->encrypted_state_len;
+  ret = _gnutls_session_unpack (session, &state);
+  if (ret < 0)
+    {
+      gnutls_assert ();
+      return ret;
+    }
+
+  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 = 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, state, encrypted_state;
+  int blocksize;
+  int ret;
+
+  /* Pack security parameters. */
+  ret = _gnutls_session_pack (session, &state);
+  if (ret < 0)
+    {
+      gnutls_assert ();
+      return ret;
+    }
+  blocksize = _gnutls_cipher_get_block_size (GNUTLS_CIPHER_AES_128_CBC);
+  encrypted_state.size = ((state.size + blocksize - 1) / blocksize) * blocksize;
+  encrypted_state.data = gnutls_malloc (encrypted_state.size);
+  if (!encrypted_state.data)
+    {
+      gnutls_assert ();
+      _gnutls_free_datum (&state);
+      return GNUTLS_E_MEMORY_ERROR;
+    }
+  memset (encrypted_state.data, 0, encrypted_state.size);
+  memcpy (encrypted_state.data, state.data, state.size);
+  _gnutls_free_datum (&state);
+
+  /* Encrypt state using 128-bit AES in CBC mode. */
+  key.data = session->security_parameters.extensions.session_ticket_key->key;
+  key.size = KEY_SIZE;
+  IV.data = session->security_parameters.extensions.session_ticket_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_datum (&encrypted_state);
+      return ret;
+    }
+
+  ret = _gnutls_cipher_encrypt (&cipher_hd, encrypted_state.data,
+                               encrypted_state.size);
+  _gnutls_cipher_deinit (&cipher_hd);
+  if (ret < 0)
+    {
+      gnutls_assert ();
+      _gnutls_free_datum (&encrypted_state);
+      return ret;
+    }
+
+  /* Fill the ticket structure to compute MAC. */
+  memcpy (ticket->key_name,
+         session->security_parameters.extensions.
+         session_ticket_key->key_name,
+         KEY_NAME_SIZE);
+  memcpy (ticket->IV, IV.data, IV.size);
+  ticket->encrypted_state_len = encrypted_state.size;
+  ticket->encrypted_state = encrypted_state.data;
+
+  mac_secret.data = session->security_parameters.extensions.
+    session_ticket_key->mac_secret;
+  mac_secret.size = MAC_SECRET_SIZE;
+  ret = digest_ticket (&mac_secret, ticket, ticket->mac);
+  if (ret < 0)
+    {
+      gnutls_assert ();
+      _gnutls_free_datum (&encrypted_state);
+      return ret;
+    }
+
+  return 0;
+}
+
+int
+_gnutls_session_ticket_recv_params (gnutls_session_t session,
+                                   const opaque * data, size_t _data_size)
+{
+  ssize_t data_size = _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;
+
+      /* The client requested a new session ticket. */
+      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 the one that we
+        hold, issue a new ticket. */
+      if (memcmp (ticket.key_name,
+                 session->security_parameters.extensions.
+                 session_ticket_key->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)
+       {
+         session->internals.session_ticket_renew = 1;
+         return 0;
+       }
+    }
+  else
+    {
+      if (data_size == 0)
+       {
+         session->internals.session_ticket_renew = 1;
+         return 0;
+       }
+    }
+
+  return 0;
+}
+
+/* returns a positive number if we send the extension data, 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)
+{
+  ssize_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)
+       {
+         return GNUTLS_E_INT_RET_0;
+       }
+    }
+  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);
+
+         return session->internals.resumed_security_parameters.
+           extensions.session_ticket_len;
+       }
+      else
+       {
+         return GNUTLS_E_INT_RET_0;
+       }
+    }
+  return 0;
+}
+
+/**
+ * gnutls_session_ticket_key_generate:
+ * @key: is a pointer to a #gnutls_datum_t which will contain a newly
+ * created key.
+ *
+ * Generate a random key to encrypt security parameters within
+ * SessionTicket.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, or an
+ * error code.
+ **/
+int
+gnutls_session_ticket_key_generate (gnutls_datum_t *key)
+{
+  int ret;
+
+  key->size = sizeof(struct gnutls_session_ticket_key_st);
+  key->data = gnutls_malloc (key->size);
+  if (!key->data)
+    {
+      gnutls_assert ();
+      return GNUTLS_E_MEMORY_ERROR;
+    }
+
+  ret = _gnutls_rnd (GNUTLS_RND_RANDOM, key->data, key->size);
+  if (ret < 0)
+    {
+      gnutls_assert ();
+      _gnutls_free_datum (key);
+      return ret;
+    }
+
+  return 0;
+}
+
+/**
+ * gnutls_session_ticket_enable_client:
+ * @session: is a #gnutls_session_t structure.
+ *
+ * Request that the client should attempt session resumption using
+ * SessionTicket.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, or an
+ * error code.
+ **/
+int
+gnutls_session_ticket_enable_client (gnutls_session_t session)
+{
+  if (!session)
+    {
+      gnutls_assert ();
+      return GNUTLS_E_INVALID_REQUEST;
+    }
+
+  session->internals.session_ticket_enable = 1;
+  return 0;
+}
+
+/**
+ * gnutls_session_ticket_enable_server:
+ * @session: is a #gnutls_session_t structure.
+ * @key: key to encrypt session parameters.
+ *
+ * Request that the server should attempt session resumption using
+ * SessionTicket.  @key must be initialized with
+ * gnutls_session_ticket_key_generate().
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, or an
+ * error code.
+ **/
+int
+gnutls_session_ticket_enable_server (gnutls_session_t session,
+                                    const gnutls_datum_t *key)
+{
+  int ret;
+
+  if (!session || !key
+      || key->size != sizeof(struct gnutls_session_ticket_key_st))
+    {
+      gnutls_assert ();
+      return GNUTLS_E_INVALID_REQUEST;
+    }
+
+  ret = _gnutls_rnd (GNUTLS_RND_RANDOM,
+                    session->security_parameters.extensions.
+                    session_ticket_IV, IV_SIZE);
+  if (ret < 0)
+    {
+      gnutls_assert ();
+      return ret;
+    }
+
+  session->security_parameters.extensions.session_ticket_key =
+    (struct gnutls_session_ticket_key_st *)key->data;
+  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 = 0;
+  int ret;
+  struct ticket ticket;
+  uint16_t ticket_len;
+  gnutls_cipher_algorithm_t write_bulk_cipher_algorithm;
+  gnutls_mac_algorithm_t write_mac_algorithm;
+  gnutls_compression_method_t write_compression_algorithm;
+
+#define SAVE_WRITE_SECURITY_PARAMETERS                                 \
+  do                                                                   \
+    {                                                                  \
+      write_bulk_cipher_algorithm =                                    \
+       session->security_parameters.write_bulk_cipher_algorithm;       \
+      write_mac_algorithm =                                            \
+       session->security_parameters.write_mac_algorithm;               \
+      write_compression_algorithm =                                    \
+       session->security_parameters.write_compression_algorithm;       \
+    }                                                                  \
+  while (0)
+
+#define RESTORE_WRITE_SECURITY_PARAMETERS                              \
+  do                                                                   \
+    {                                                                  \
+      session->security_parameters.write_bulk_cipher_algorithm =       \
+       write_bulk_cipher_algorithm;                                    \
+      session->security_parameters.write_mac_algorithm =               \
+       write_mac_algorithm;                                            \
+      session->security_parameters.write_compression_algorithm =       \
+       write_compression_algorithm;                                    \
+    }                                                                  \
+  while (0)
+
+  if (again == 0)
+    {
+      /* XXX: Temporarily set write algorithms to be used.
+        _gnutls_write_connection_state_init() does this job, but it also
+        triggers encryption, while NewSessionTicket should not be
+        encrypted in the record layer. */
+      SAVE_WRITE_SECURITY_PARAMETERS;
+      ret = _gnutls_set_write_cipher (session,
+                                     _gnutls_cipher_suite_get_cipher_algo
+                                     (&session->security_parameters.
+                                      current_cipher_suite));
+      if (ret < 0)
+       return ret;
+      ret = _gnutls_set_write_mac (session,
+                                  _gnutls_cipher_suite_get_mac_algo
+                                  (&session->security_parameters.
+                                   current_cipher_suite));
+      if (ret < 0)
+       return ret;
+      ret = _gnutls_set_write_compression (session,
+                                          session->internals.
+                                          compression_method);
+      if (ret < 0)
+       return ret;
+
+      ret = encrypt_ticket (session, &ticket);
+      RESTORE_WRITE_SECURITY_PARAMETERS;
+      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;
+
+      data_size = p - data;
+    }
+
+  ret = _gnutls_send_handshake (session, data_size ? data : NULL, data_size,
+                               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,
+                               MANDATORY_PACKET);
+  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_realloc (session->security_parameters.extensions.session_ticket,
+                   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;
+
+  /* Discard the current session ID.  (RFC5077 3.4) */
+  ret = _gnutls_generate_session_id (session->security_parameters.session_id,
+                                    &session->security_parameters.
+                                    session_id_size);
+  if (ret < 0)
+    {
+      gnutls_assert ();
+      gnutls_free (data);
+      gnutls_free (session->security_parameters.extensions.session_ticket);
+      return GNUTLS_E_INTERNAL_ERROR;
+    }
+  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 (file)
index 0000000..5f6e29c
--- /dev/null
@@ -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
index 521e55f54210208e022474637d03bc2aed1f7f85..9afd897092deecf9277b1f244e91270a06a554ab 100644 (file)
@@ -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);
index 31769d248a02b6cd44077a403b16d447a02dea77..8cf5ed03cca1e3d7a4fe853eccc5c15f372c5fa6 100644 (file)
@@ -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
@@ -42,7 +43,18 @@ typedef struct
   const char *name;
   uint16_t type;
   gnutls_ext_parse_type_t parse_type;
+
+  /* this function must return 0 when Not Applicable
+   * size of extension data if ok
+   * < 0 on other error.
+   */
   gnutls_ext_recv_func recv_func;
+  
+  /* this function must return 0 when Not Applicable
+   * size of extension data if ok
+   * GNUTLS_E_INT_RET_0 if extension data size is zero
+   * < 0 on other error.
+   */
   gnutls_ext_send_func send_func;
 } gnutls_extension_entry;
 
@@ -197,7 +209,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)
@@ -225,8 +237,11 @@ _gnutls_gen_extensions (gnutls_session_t session, opaque * data,
        continue;
 
       size = p->send_func (session, sdata, sdata_size);
-      if (size > 0)
+      if (size > 0 || size == GNUTLS_E_INT_RET_0)
        {
+         if (size == GNUTLS_E_INT_RET_0)
+           size = 0;
+
          if (data_size < pos + (size_t) size + 4)
            {
              gnutls_assert ();
@@ -324,6 +339,16 @@ _gnutls_ext_init (void)
     return ret;
 #endif
 
+#ifdef ENABLE_SESSION_TICKET
+  ret = gnutls_ext_register (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;
 }
 
index 5ccd31746a629eaefbad164625ed5f512667e3ca..1b8512830c0015892106656c8a6ee75cab4bc0c5 100644 (file)
@@ -48,6 +48,7 @@
 #include <gnutls_record.h>
 #include <gnutls_state.h>
 #include <ext_srp.h>
+#include <ext_session_ticket.h>
 #include <gnutls_rsa_export.h> /* for gnutls_get_rsa_params() */
 #include <auth_anon.h>         /* for gnutls_anon_server_credentials_t */
 #include <auth_psk.h>          /* for gnutls_psk_server_credentials_t */
@@ -354,7 +355,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 +400,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 +469,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 +1311,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:
@@ -2686,6 +2707,23 @@ _gnutls_handshake_common (gnutls_session_t session)
       ret = _gnutls_recv_handshake_final (session, TRUE);
       IMED_RET ("recv handshake final", ret);
 
+#ifdef ENABLE_SESSION_TICKET
+      switch (STATE)
+       {
+       case STATE0:
+       case STATE40:
+         if (session->internals.session_ticket_renew)
+           {
+             ret = _gnutls_send_new_session_ticket (session, AGAIN(STATE40));
+             STATE = STATE40;
+             IMED_RET ("send handshake new session ticket", ret);
+           }
+         STATE = STATE0;
+       default:
+         break;
+       }
+#endif
+
       ret = _gnutls_send_handshake_final (session, FALSE);
       IMED_RET ("send handshake final", ret);
     }
@@ -2695,6 +2733,23 @@ _gnutls_handshake_common (gnutls_session_t session)
       ret = _gnutls_send_handshake_final (session, TRUE);
       IMED_RET ("send handshake final 2", ret);
 
+#ifdef ENABLE_SESSION_TICKET
+      switch (STATE)
+       {
+       case STATE0:
+       case STATE41:
+         if (session->internals.session_ticket_renew)
+           {
+             ret = _gnutls_recv_new_session_ticket (session);
+             STATE = STATE41;
+             IMED_RET ("recv handshake new session ticket", ret);
+           }
+         STATE = STATE0;
+       default:
+         break;
+       }
+#endif
+
       ret = _gnutls_recv_handshake_final (session, FALSE);
       IMED_RET ("recv handshake final 2", ret);
     }
index f8c8cebfab9083a663423609109b77b671447ced..d5f958ec89cf47728147ce2393a02310f15f912a 100644 (file)
@@ -86,6 +86,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.
  */
@@ -149,8 +155,8 @@ typedef enum handshake_state_t
 { STATE0 = 0, STATE1, STATE2,
   STATE3, STATE4, STATE5,
   STATE6, STATE7, STATE8, STATE9, STATE20 = 20, STATE21,
-  STATE30 = 30, STATE31, STATE50 = 50, STATE60 = 60, STATE61, STATE62,
-  STATE70, STATE71
+  STATE30 = 30, STATE31, STATE40 = 40, STATE41, STATE50 = 50,
+  STATE60 = 60, STATE61, STATE62, STATE70, STATE71
 } handshake_state_t;
 
 #include <gnutls_str.h>
@@ -170,6 +176,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;
 
@@ -281,6 +288,12 @@ typedef struct
 
 #define MAX_SERVER_NAME_EXTENSIONS 3
 
+struct gnutls_session_ticket_key_st {
+  opaque key_name[SESSION_TICKET_KEY_NAME_SIZE];
+  opaque key[SESSION_TICKET_KEY_SIZE];
+  opaque mac_secret[SESSION_TICKET_MAC_SECRET_SIZE];
+};
+
 typedef struct
 {
   server_name_st server_names[MAX_SERVER_NAME_EXTENSIONS];
@@ -303,6 +316,11 @@ typedef struct
   uint16_t oprfi_client_len;
   opaque *oprfi_server;
   uint16_t oprfi_server_len;
+
+  opaque *session_ticket;
+  uint16_t session_ticket_len;
+  struct gnutls_session_ticket_key_st *session_ticket_key;
+  opaque session_ticket_IV[SESSION_TICKET_IV_SIZE];
 } tls_ext_st;
 
 /* auth_info_t structures now MAY contain malloced 
@@ -672,6 +690,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;
index 115f17ed1d4ec7a9e64b77ae53e8841c0fb2fba0..dc56b0a431ac6acec4ed15ec5d57aaeeea1ad01d 100644 (file)
@@ -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;
 }
index d9abd556c7b7415da26217a6c0f879d3d69519c4..5b5f7a775b7ccfe1d276aef8d92f5dbeedd7365a 100644 (file)
@@ -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);
 }
index af27b8dee2ff0aa9f70e75a92766bf6124b3d068..a78fc39d412170f7415ec6059701e029200f5d31 100644 (file)
@@ -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,
@@ -554,6 +555,12 @@ extern "C" {
   const char *gnutls_supplemental_get_name
   (gnutls_supplemental_data_format_type_t type);
 
+  /* SessionTicket, RFC 5077. */
+  int gnutls_session_ticket_key_generate (gnutls_datum_t *key);
+  int gnutls_session_ticket_enable_client (gnutls_session_t session);
+  int gnutls_session_ticket_enable_server (gnutls_session_t session,
+                                          const gnutls_datum_t *key);
+
 /* functions to set priority of cipher suites 
  */
   int gnutls_cipher_set_priority (gnutls_session_t session, const int *list);
index 73dc6aa33bb4e11258950564032698522fb22297..63f349768f9c44fa206ce166bd77b71690a7557d 100644 (file)
@@ -565,6 +565,9 @@ GNUTLS_2_8
     gnutls_x509_crt_get_verify_algorithm;
     gnutls_x509_crt_set_crq_extensions;
     gnutls_x509_crt_verify_hash;
+    gnutls_session_ticket_key_generate;
+    gnutls_session_ticket_enable_client;
+    gnutls_session_ticket_enable_server;
 } GNUTLS_1_4;
 
 GNUTLS_PRIVATE {
index 95e43e22fac1ce8ae48629a2053f13c50a4bac8e..5e5e0637fdf6a8c8b9a9d6fe673b3d2456527709 100644 (file)
@@ -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 *)
index ddbb46fb00002b58cad6e907ef138a609d3ff657..36020c6307df6e24829495edb7f4ad0a6906d749 100644 (file)
--- a/src/cli.c
+++ b/src/cli.c
@@ -476,6 +476,11 @@ init_tls_session (const char *hostname)
                                info.opaque_prf_input);
 #endif
 
+#ifdef ENABLE_SESSION_TICKET
+  if (!info.noticket)
+    gnutls_session_ticket_enable_client (session);
+#endif
+
   return session;
 }
 
index d1672177ad6e4312baecbda54675ae015ece4788..14d7b3da26d63b389b785d2ef4b9fedaf0742f26 100644 (file)
@@ -17,6 +17,9 @@ option (d, debug) INT "integer" { $debug = $1 } "Enable debugging"
 #int resume;
 option (r, resume) { $resume = 1 } "Connect, establish a session. Connect again and resume this session."
 
+#int noticket;
+option (noticket) { $noticket = 1 } "Doen't accept session tickets."
+
 #int starttls;
 option (s, starttls) { $starttls = 1 } "Connect, establish a plain session and start TLS when EOF or a SIGALRM is received."
 
@@ -121,7 +124,7 @@ option (v, version) { cli_version(); exit(0); } "prints the program's version nu
 #char *rest_args;
 rest STR "hostname" { $rest_args = $1; }
 
-init { $resume=0; $port="443"; $rest_args=NULL; $ciphers=NULL;
+init { $resume=0; $noticket=0; $port="443"; $rest_args=NULL; $ciphers=NULL;
        $kx=NULL; $comp=NULL; $macs=NULL; $ctype=NULL; $nciphers=0;
        $nkx=0; $ncomp=0; $nmacs=0; $nctype = 0; $record_size=0; 
        $fingerprint=0; $pgp_keyring=NULL; $x509_crlfile = NULL;
index f05a3ac8ab78d2a3a23c2ff3886390f675508f73..ede5117b883b973bb0a76eb361429c1155c426b3 100644 (file)
@@ -62,6 +62,7 @@ static int debug;
 
 int verbose;
 static int nodb;
+static int noticket;
 int require_cert;
 int disable_client_cert;
 
@@ -79,6 +80,8 @@ char *x509_cafile;
 char *dh_params_file;
 char *x509_crlfile = NULL;
 
+gnutls_datum_t session_ticket_key;
+
 /* end of globals */
 
 /* This is a sample TCP echo server.
@@ -370,6 +373,10 @@ initialize_session (void)
       gnutls_db_set_store_function (session, wrap_db_store);
       gnutls_db_set_ptr (session, NULL);
     }
+#ifdef ENABLE_SESSION_TICKET
+  if (noticket == 0)
+    gnutls_session_ticket_enable_server (session, &session_ticket_key);
+#endif
 
   if (gnutls_priority_set_direct (session, info.priorities, &err) < 0)
     {
@@ -1060,6 +1067,11 @@ main (int argc, char **argv)
 /*      gnutls_anon_set_server_dh_params(dh_cred, dh_params); */
 #endif
 
+#ifdef ENABLE_SESSION_TICKET
+  if (noticket == 0)
+    gnutls_session_ticket_key_generate (&session_ticket_key);
+#endif
+
   if (listen_socket (name, port) < 0)
     exit (1);
 
@@ -1393,6 +1405,11 @@ main (int argc, char **argv)
   gnutls_anon_free_server_credentials (dh_cred);
 #endif
 
+#ifdef ENABLE_SESSION_TICKET
+  if (noticket == 0)
+    gnutls_free (session_ticket_key.data);
+#endif
+
   if (nodb == 0)
     wrap_db_deinit ();
   gnutls_global_deinit ();
@@ -1416,6 +1433,7 @@ gaa_parser (int argc, char **argv)
   debug = info.debug;
   verbose = info.quiet;
   nodb = info.nodb;
+  noticket = info.noticket;
 
   if (info.http == 0)
     http = 0;
index 4fe9870c6826e56b13aaf97032a4b5f602125dfd..c216e0000e7beaa020187e55d6666789ff0939de 100644 (file)
@@ -24,6 +24,9 @@ option (q, quiet) { $quiet = 1 } "Suppress some messages."
 #int nodb;
 option (nodb) { $nodb = 1 } "Does not use the resume database."
 
+#int noticket;
+option (noticket) { $noticket = 1 } "Does not issue session tickets."
+
 #int http;
 option (http) { $http = 1 } "Act as an HTTP Server."
 option (echo) { $http = 0 } "Act as an Echo Server."
@@ -121,7 +124,7 @@ INCOMP ra
 
 init { $generate=0; $port=5556; $http=0; $ciphers=NULL;
        $kx=NULL; $comp=NULL; $macs=NULL; $ctype=NULL; $nciphers=0;
-       $nkx=0; $ncomp=0; $nmacs=0; $nctype = 0; $nodb = 0;
+       $nkx=0; $ncomp=0; $nmacs=0; $nctype = 0; $nodb = 0; $noticket = 0;
        $x509_cafile = NULL; $pgp_keyfile=NULL; $pgp_certfile=NULL;
        $x509_keyfile=NULL; $x509_certfile=NULL; $x509_crlfile = NULL;
        $x509_dsakeyfile=NULL; $x509_dsacertfile=NULL; 
index e68a9bbecdf619d194abf916b9c80711c3298759..e4d7a2ab807c83f068ea079e02e81914760b5822 100644 (file)
@@ -49,6 +49,26 @@ static int wrap_db_delete (void *dbf, gnutls_datum_t key);
 
 #define TLS_SESSION_CACHE 50
 
+struct params_res
+{
+  const char *desc;
+  int enable_db;
+  int enable_session_ticket_server;
+  int enable_session_ticket_client;
+  int expect_resume;
+};
+
+struct params_res resume_tests[] =
+  {
+    {"try to resume from db", 50, 0, 0, 1},
+#ifdef ENABLE_SESSION_TICKET
+    {"try to resume from session ticket", 0, 1, 1, 1},
+    {"try to resume from session ticket (server only)", 0, 1, 0, 0},
+    {"try to resume from session ticket (client only)", 0, 0, 1, 0},
+#endif
+    {NULL, -1}
+  };
+
 /* A very basic TLS client, with anonymous authentication.
  */
 
@@ -56,7 +76,7 @@ static int wrap_db_delete (void *dbf, gnutls_datum_t key);
 #define MSG "Hello TLS"
 
 static void
-client (void)
+client (struct params_res *params)
 {
   int ret, sd, ii;
   gnutls_session_t session;
@@ -93,6 +113,11 @@ client (void)
        */
       gnutls_credentials_set (session, GNUTLS_CRD_ANON, anoncred);
 
+#ifdef ENABLE_SESSION_TICKET
+      if (params->enable_session_ticket_client)
+       gnutls_session_ticket_enable_client (session);
+#endif
+
       if (t > 0)
        {
          /* if this is not the first time we connect */
@@ -131,11 +156,17 @@ client (void)
          /* check if we actually resumed the previous session */
          if (gnutls_session_is_resumed (session) != 0)
            {
-             success ("- Previous session was resumed\n");
+             if (params->expect_resume)
+               success ("- Previous session was resumed\n");
+             else
+               fail ("- Previous session was resumed\n");
            }
          else
            {
-             success ("*** Previous session was NOT resumed\n");
+             if (params->expect_resume)
+               fail ("*** Previous session was NOT resumed\n");
+             else
+               success ("*** Previous session was NOT resumed\n");
            }
        }
 
@@ -185,9 +216,10 @@ client (void)
 
 /* These are global */
 gnutls_anon_server_credentials_t anoncred;
+gnutls_datum_t session_ticket_key;
 
 static gnutls_session_t
-initialize_tls_session (void)
+initialize_tls_session (struct params_res *params)
 {
   gnutls_session_t session;
   const int kx_prio[] = { GNUTLS_KX_ANON_DH, 0 };
@@ -204,13 +236,17 @@ initialize_tls_session (void)
 
   gnutls_dh_set_prime_bits (session, DH_BITS);
 
-  if (TLS_SESSION_CACHE != 0)
+  if (params->enable_db)
     {
       gnutls_db_set_retrieve_function (session, wrap_db_fetch);
       gnutls_db_set_remove_function (session, wrap_db_delete);
       gnutls_db_set_store_function (session, wrap_db_store);
       gnutls_db_set_ptr (session, NULL);
     }
+#ifdef ENABLE_SESSION_TICKET
+  if (params->enable_session_ticket_server)
+    gnutls_session_ticket_enable_server (session, &session_ticket_key);
+#endif
 
   return session;
 }
@@ -289,10 +325,12 @@ global_stop (void)
   gnutls_dh_params_deinit (dh_params);
 
   gnutls_global_deinit ();
+
+  shutdown (listen_sd, SHUT_RDWR);
 }
 
 static void
-server (void)
+server (struct params_res *params)
 {
   size_t t;
 
@@ -308,16 +346,20 @@ server (void)
 
   gnutls_anon_set_server_dh_params (anoncred, dh_params);
 
-  if (TLS_SESSION_CACHE != 0)
+  if (params->enable_db)
     {
       wrap_db_init ();
     }
+#ifdef ENABLE_SESSION_TICKET
+  if (params->enable_session_ticket_server)
+    gnutls_session_ticket_key_generate (&session_ticket_key);
+#endif
 
   for (t = 0; t < 2; t++)
     {
       client_len = sizeof (sa_cli);
 
-      session = initialize_tls_session ();
+      session = initialize_tls_session (params);
 
       sd = accept (listen_sd, (SA *) & sa_cli, &client_len);
 
@@ -374,7 +416,7 @@ server (void)
 
   close (listen_sd);
 
-  if (TLS_SESSION_CACHE != 0)
+  if (params->enable_db)
     {
       wrap_db_deinit ();
     }
@@ -386,30 +428,39 @@ void
 doit (void)
 {
   pid_t child;
+  int i;
 
-  global_start ();
-  if (error_count)
-    return;
-
-  child = fork ();
-  if (child < 0)
+  for (i = 0; resume_tests[i].desc; i++)
     {
-      perror ("fork");
-      fail ("fork");
-      return;
-    }
+      printf ("%s\n", resume_tests[i].desc);
 
-  if (child)
-    {
-      int status;
-      /* parent */
-      server ();
-      wait (&status);
-    }
-  else
-    client ();
+      global_start ();
+      if (error_count)
+       return;
+
+      child = fork ();
+      if (child < 0)
+       {
+         perror ("fork");
+         fail ("fork");
+         return;
+       }
 
-  global_stop ();
+      if (child)
+       {
+         int status;
+         /* parent */
+         server (&resume_tests[i]);
+         wait (&status);
+         global_stop ();
+       }
+      else
+       {
+         client (&resume_tests[i]);
+         global_stop ();
+         exit (0);
+       }
+    }
 }
 
 /* Functions and other stuff needed for session resuming.