]> git.ipfire.org Git - thirdparty/gnutls.git/commitdiff
Added support for an old version of the DTLS protocol
authorNikos Mavrogiannopoulos <nmav@gnutls.org>
Thu, 7 Jun 2012 16:12:59 +0000 (18:12 +0200)
committerNikos Mavrogiannopoulos <nmav@gnutls.org>
Thu, 7 Jun 2012 16:12:59 +0000 (18:12 +0200)
used by openconnect vpn client for compatibility with Cisco's AnyConnect
SSL VPN. It is marked as GNUTLS_DTLS0_9. Do not use it for newer protocols
as it has issues.

12 files changed:
NEWS
doc/Makefile.am
lib/algorithms.h
lib/algorithms/ciphersuites.c
lib/algorithms/protocols.c
lib/gnutls_constate.c
lib/gnutls_handshake.c
lib/gnutls_int.h
lib/gnutls_kx.c
lib/gnutls_session_pack.c
lib/includes/gnutls/gnutls.h.in
lib/libgnutls.map

diff --git a/NEWS b/NEWS
index c0406d2fffc9143719fc523cfa24f40becf7b83f..a62b9c2ca207e5e8f1a8faea621ce10e3edbb3f2 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -7,6 +7,11 @@ See the end for copying conditions.
 ** gnutls-cli: --benchmark-tls was split to --benchmark-tls-kx
 and --benchmark-tls-ciphers
 
+** libgnutls: Added support for an old version of the DTLS protocol
+used by openconnect vpn client for compatibility with Cisco's AnyConnect 
+SSL VPN. It is marked as GNUTLS_DTLS0_9. Do not use it for newer protocols
+as it has issues.
+
 ** libgnutls: requires libnettle 2.5.
 
 ** libgnutls: Use the PKCS #1 1.5 encoding provided by nettle (2.5)
@@ -48,6 +53,7 @@ gnutls_x509_trust_list_add_system_trust: Added
 gnutls_x509_trust_list_add_trust_file: Added
 gnutls_x509_trust_list_add_trust_mem: Added
 gnutls_pk_to_sign: Added
+gnutls_session_set_premaster: Added
 gnutls_pubkey_verify_hash: Deprecated (use gnutls_pubkey_verify_hash2)
 gnutls_pubkey_verify_data: Deprecated (use gnutls_pubkey_verify_data2)
 
index c7346f57d4b768a2630d5c6098a17cf286ff0c11..65141c85d836c72a2c66ba2d2248ca01b03da973 100644 (file)
@@ -686,6 +686,7 @@ FUNCS += functions/gnutls_session_set_data
 FUNCS += functions/gnutls_session_get_data
 FUNCS += functions/gnutls_session_get_data2
 FUNCS += functions/gnutls_session_get_random
+FUNCS += functions/gnutls_session_set_premaster
 FUNCS += functions/gnutls_session_get_id
 FUNCS += functions/gnutls_session_channel_binding
 FUNCS += functions/gnutls_session_is_resumed
index 9fe0272541541df895a2a36895bfc534bbbdd1c5..748629c17c7ac44fcf690fc6333a38985f8d3590 100644 (file)
@@ -69,6 +69,11 @@ gnutls_kx_algorithm_t _gnutls_cipher_suite_get_kx_algo (const uint8_t suite[2]);
 gnutls_mac_algorithm_t _gnutls_cipher_suite_get_mac_algo (const
                                                           uint8_t suite[2]);
 
+int
+_gnutls_cipher_suite_get_id (gnutls_kx_algorithm_t kx_algorithm,
+                              gnutls_cipher_algorithm_t cipher_algorithm,
+                              gnutls_mac_algorithm_t mac_algorithm, uint8_t suite[2]);
+
 /* Functions for ciphers. */
 int _gnutls_cipher_is_block (gnutls_cipher_algorithm_t algorithm);
 int _gnutls_cipher_algo_is_aead (gnutls_cipher_algorithm_t algorithm);
index 3c5a79d06d91ec683817ddaf4d441519e4997974..678812e6dc074f0af9991d8268ce17f4d012ad2d 100644 (file)
@@ -726,6 +726,35 @@ const gnutls_cipher_suite_entry * ce;
     return ce->name + sizeof ("GNUTLS_") - 1;
 }
 
+/*-
+ * _gnutls_cipher_suite_get_id:
+ * @kx_algorithm: is a Key exchange algorithm
+ * @cipher_algorithm: is a cipher algorithm
+ * @mac_algorithm: is a MAC algorithm
+ * @suite: The id to be returned
+ *
+ * It fills @suite with the ID of the ciphersuite of the provided parameters.
+ *
+ * Returns: 0 on success or a negative error code otherwise.
+ -*/
+int
+_gnutls_cipher_suite_get_id (gnutls_kx_algorithm_t kx_algorithm,
+                              gnutls_cipher_algorithm_t cipher_algorithm,
+                              gnutls_mac_algorithm_t mac_algorithm, uint8_t suite[2])
+{
+const gnutls_cipher_suite_entry * ce;
+
+  ce = cipher_suite_get (kx_algorithm, cipher_algorithm, mac_algorithm);
+  if (ce == NULL)
+    return GNUTLS_E_INVALID_REQUEST;
+  else
+    {
+      suite[0] = ce->id[0];
+      suite[1] = ce->id[1];
+    }
+  return 0;
+}
+
 /**
  * gnutls_cipher_suite_info:
  * @idx: index of cipher suite to get information about, starts on 0.
index a152901a5dec008b5558f53e7d19cec829c030bd..01203d8903f36d52711e25f3b771f2307429f0ef 100644 (file)
@@ -43,6 +43,7 @@ static const gnutls_version_entry sup_versions[] = {
   {"TLS1.0", GNUTLS_TLS1, 3, 1, GNUTLS_STREAM, 1},
   {"TLS1.1", GNUTLS_TLS1_1, 3, 2, GNUTLS_STREAM, 1},
   {"TLS1.2", GNUTLS_TLS1_2, 3, 3, GNUTLS_STREAM, 1},
+  {"DTLS0.9", GNUTLS_DTLS0_9, 1, 0, GNUTLS_DGRAM, 1}, /* Cisco AnyConnect (based on about OpenSSL 0.9.8e) */
   {"DTLS1.0", GNUTLS_DTLS1_0, 254, 255, GNUTLS_DGRAM, 1}, /* 1.1 over datagram */
   {0, 0, 0, 0, 0}
 };
@@ -242,6 +243,7 @@ _gnutls_version_has_selectable_prf (gnutls_protocol_t version)
 {
   switch (version)
     {
+    case GNUTLS_DTLS0_9:
     case GNUTLS_DTLS1_0:
     case GNUTLS_TLS1_1:
     case GNUTLS_TLS1_0:
@@ -259,6 +261,7 @@ _gnutls_version_has_selectable_sighash (gnutls_protocol_t version)
 {
   switch (version)
     {
+    case GNUTLS_DTLS0_9:
     case GNUTLS_DTLS1_0:
     case GNUTLS_TLS1_1:
     case GNUTLS_TLS1_0:
index 5ef0c74f536a0826dc09ac563bad610c06834f24..c4aa4bca21bb26572da9cdf070dfe5438da90085 100644 (file)
@@ -466,7 +466,7 @@ _gnutls_epoch_set_keys (gnutls_session_t session, uint16_t epoch)
        dst->timestamp = src->timestamp; \
        dst->max_record_recv_size = src->max_record_recv_size; \
        dst->max_record_send_size = src->max_record_send_size; \
-       dst->version = src->version
+       dst->version = src->version;
 
 static void
 _gnutls_set_resumed_parameters (gnutls_session_t session)
index 25520c40b6c432fd0f53d3580317222477a47934..8256e141d03f016c3a94fa59d740aa6ee06d3d19 100644 (file)
@@ -1191,7 +1191,8 @@ _gnutls_handshake_hash_add_recvd (gnutls_session_t session,
 {
   int ret;
 
-  if (recv_type == GNUTLS_HANDSHAKE_HELLO_VERIFY_REQUEST ||
+  if ((gnutls_protocol_get_version (session) != GNUTLS_DTLS0_9 &&
+       recv_type == GNUTLS_HANDSHAKE_HELLO_VERIFY_REQUEST) ||
       recv_type == GNUTLS_HANDSHAKE_HELLO_REQUEST)
     return 0;
 
@@ -1199,11 +1200,13 @@ _gnutls_handshake_hash_add_recvd (gnutls_session_t session,
 
   session->internals.handshake_hash_buffer_prev_len = session->internals.handshake_hash_buffer.length;
 
-  ret = _gnutls_buffer_append_data(&session->internals.handshake_hash_buffer,
-    header, header_size);
-  if (ret < 0)
-    return gnutls_assert_val(ret);
-
+  if (gnutls_protocol_get_version (session) != GNUTLS_DTLS0_9)
+    {
+      ret = _gnutls_buffer_append_data(&session->internals.handshake_hash_buffer,
+        header, header_size);
+      if (ret < 0)
+        return gnutls_assert_val(ret);
+  }
   if (datalen > 0)
     {
       ret = _gnutls_buffer_append_data(&session->internals.handshake_hash_buffer,
@@ -1231,6 +1234,18 @@ _gnutls_handshake_hash_add_sent (gnutls_session_t session,
     {
       CHECK_SIZE(datalen);
 
+      if (gnutls_protocol_get_version (session) == GNUTLS_DTLS0_9) 
+        {
+         /* Old DTLS doesn't include the header in the MAC */
+         if (datalen <= 12) 
+           {
+             gnutls_assert ();
+             return GNUTLS_E_INVALID_REQUEST;
+           }
+         dataptr += 12;
+         datalen -= 12;
+        }
+
       ret = _gnutls_buffer_append_data(&session->internals.handshake_hash_buffer,
                                        dataptr, datalen);
       if (ret < 0)
@@ -1402,7 +1417,8 @@ _gnutls_client_set_ciphersuite (gnutls_session_t session, uint8_t suite[2])
   /* check if the credentials (username, public key etc.) are ok.
    * Actually checks if they exist.
    */
-  if (_gnutls_get_kx_cred
+  if (!session->internals.premaster_set &&
+      _gnutls_get_kx_cred
       (session,
        _gnutls_cipher_suite_get_kx_algo
        (session->security_parameters.cipher_suite), &err) == NULL
@@ -1823,6 +1839,8 @@ _gnutls_send_client_hello (gnutls_session_t session, int again)
            */
           if (!IS_DTLS(session))
             _gnutls_record_set_default_version (session, 3, 0);
+          else if (gnutls_protocol_get_version (session) == GNUTLS_DTLS0_9)
+            _gnutls_record_set_default_version (session, 1, 0);
           else
             _gnutls_record_set_default_version (session, 254, 255);
         }
@@ -2580,12 +2598,20 @@ send_change_cipher_spec (gnutls_session_t session, int again)
       if (bufel == NULL)
         return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
 
-      _mbuffer_set_uhead_size(bufel, 1);
+      if (gnutls_protocol_get_version (session) == GNUTLS_DTLS0_9)
+        _mbuffer_set_uhead_size(bufel, 3);
+      else
+        _mbuffer_set_uhead_size(bufel, 1);
       _mbuffer_set_udata_size(bufel, 0);
 
       data = _mbuffer_get_uhead_ptr (bufel);
       
       data[0] = 1;
+      if (gnutls_protocol_get_version (session) == GNUTLS_DTLS0_9)
+        {
+          _gnutls_write_uint16 (session->internals.dtls.hsk_write_seq, &data[1]);
+          session->internals.dtls.hsk_write_seq++;
+        }
 
       ret = _gnutls_handshake_io_cache_int (session, GNUTLS_HANDSHAKE_CHANGE_CIPHER_SPEC, bufel);
       if (ret < 0)
@@ -2669,7 +2695,7 @@ _gnutls_recv_handshake_final (gnutls_session_t session, int init)
 {
   int ret = 0;
   uint8_t ch;
-
+  unsigned int ccs_len = 1;
 
   switch (STATE)
     {
@@ -2689,7 +2715,10 @@ _gnutls_recv_handshake_final (gnutls_session_t session, int init)
             return gnutls_assert_val(ret);
         }
 
-      ret = _gnutls_recv_int (session, GNUTLS_CHANGE_CIPHER_SPEC, -1, &ch, 1, NULL);
+      if (gnutls_protocol_get_version (session) == GNUTLS_DTLS0_9)
+        ccs_len = 3;
+
+      ret = _gnutls_recv_int (session, GNUTLS_CHANGE_CIPHER_SPEC, -1, &ch, ccs_len, NULL);
       if (ret <= 0)
         {
           ERR ("recv ChangeCipherSpec", ret);
@@ -2697,6 +2726,9 @@ _gnutls_recv_handshake_final (gnutls_session_t session, int init)
           return (ret < 0) ? ret : GNUTLS_E_UNEXPECTED_PACKET_LENGTH;
         }
 
+      if (gnutls_protocol_get_version (session) == GNUTLS_DTLS0_9)
+        session->internals.dtls.hsk_read_seq++;
+
       /* Initialize the connection session (start encryption) - in case of server */
       if (init == TRUE)
         {
@@ -2714,7 +2746,7 @@ _gnutls_recv_handshake_final (gnutls_session_t session, int init)
           gnutls_assert ();
           return ret;
         }
-        
+
     case STATE31:
       STATE = STATE31;
 
@@ -3149,7 +3181,8 @@ _gnutls_remove_unwanted_ciphersuites (gnutls_session_t session,
 
       /* if it is defined but had no credentials 
        */
-      if (_gnutls_get_kx_cred (session, kx, NULL) == NULL)
+      if (!session->internals.premaster_set &&
+          _gnutls_get_kx_cred (session, kx, NULL) == NULL)
         {
           delete = 1;
         }
index 4a60ae0d8f947929eb738005a2d334800a655a65..02c4f9524cfd4f77704122d6548ea71920e3a8db 100644 (file)
@@ -863,6 +863,10 @@ typedef struct
 
   /* DTLS session state */
   dtls_st dtls;
+  
+  /* if set it means that the master key was set using
+   * gnutls_session_set_master() rather than being negotiated. */
+  unsigned int premaster_set:1;
 
   unsigned int cb_tls_unique_len;
   unsigned char cb_tls_unique[MAX_VERIFY_DATA_SIZE];
index c38de186f211f71d5996e59e821566d9c65331a3..0cebd385f23b35d9fc5ceb9203bf0a437d18b6e9 100644 (file)
@@ -73,27 +73,34 @@ send_handshake (gnutls_session_t session, uint8_t * data, size_t size,
 
 #define MASTER_SECRET "master secret"
 #define MASTER_SECRET_SIZE (sizeof(MASTER_SECRET)-1)
-static int generate_normal_master (gnutls_session_t session, int);
+static int generate_normal_master (gnutls_session_t session, gnutls_datum_t*, int);
 
 int
 _gnutls_generate_master (gnutls_session_t session, int keep_premaster)
 {
   if (session->internals.resumed == RESUME_FALSE)
-    return generate_normal_master (session, keep_premaster);
+    return generate_normal_master (session, &session->key->key, keep_premaster);
+  else if (session->internals.premaster_set)  
+    {
+      gnutls_datum_t premaster;
+      premaster.size = sizeof(session->internals.resumed_security_parameters.master_secret);
+      premaster.data = session->internals.resumed_security_parameters.master_secret;
+      return generate_normal_master(session, &premaster, 1);
+    }
   return 0;
 }
 
 /* here we generate the TLS Master secret.
  */
-#define PREMASTER session->key->key
 static int
-generate_normal_master (gnutls_session_t session, int keep_premaster)
+generate_normal_master (gnutls_session_t session, gnutls_datum_t *premaster,
+                       int keep_premaster)
 {
   int ret = 0;
   char buf[512];
 
-  _gnutls_hard_log ("INT: PREMASTER SECRET[%d]: %s\n", PREMASTER.size,
-                    _gnutls_bin2hex (PREMASTER.data, PREMASTER.size, buf,
+  _gnutls_hard_log ("INT: PREMASTER SECRET[%d]: %s\n", premaster->size,
+                    _gnutls_bin2hex (premaster->data, premaster->size, buf,
                                      sizeof (buf), NULL));
   _gnutls_hard_log ("INT: CLIENT RANDOM[%d]: %s\n", 32,
                     _gnutls_bin2hex (session->
@@ -114,7 +121,7 @@ generate_normal_master (gnutls_session_t session, int keep_premaster)
               session->security_parameters.server_random, GNUTLS_RANDOM_SIZE);
 
       ret =
-        _gnutls_ssl3_generate_random (PREMASTER.data, PREMASTER.size,
+        _gnutls_ssl3_generate_random (premaster->data, premaster->size,
                                       rnd, 2 * GNUTLS_RANDOM_SIZE,
                                       GNUTLS_MASTER_SIZE,
                                       session->
@@ -131,14 +138,14 @@ generate_normal_master (gnutls_session_t session, int keep_premaster)
               session->security_parameters.server_random, GNUTLS_RANDOM_SIZE);
 
       ret =
-        _gnutls_PRF (session, PREMASTER.data, PREMASTER.size,
+        _gnutls_PRF (session, premaster->data, premaster->size,
                      MASTER_SECRET, MASTER_SECRET_SIZE,
                      rnd, 2 * GNUTLS_RANDOM_SIZE, GNUTLS_MASTER_SIZE,
                      session->security_parameters.master_secret);
     }
 
   if (!keep_premaster)
-    _gnutls_free_datum (&PREMASTER);
+    _gnutls_free_datum (premaster);
 
   if (ret < 0)
     return ret;
index 3e89b981e86d5cbc193a0cf08af9956c4945bb27..258887345311d3deb45d51558dfc5a0b332918c1 100644 (file)
@@ -40,6 +40,7 @@
 #include <gnutls_num.h>
 #include <gnutls_extensions.h>
 #include <gnutls_constate.h>
+#include <algorithms.h>
 
 static int pack_certificate_auth_info (gnutls_session_t,
                                        gnutls_buffer_st * packed_session);
@@ -860,3 +861,72 @@ unpack_security_parameters (gnutls_session_t session, gnutls_buffer_st * ps)
 error:
   return ret;
 }
+
+/**
+ * gnutls_session_set_premaster:
+ * @session: is a #gnutls_session_t structure.
+ * @entity: GNUTLS_SERVER or GNUTLS_CLIENT
+ * @version: the TLS protocol version
+ * @kx: the key exchange method
+ * @cipher: the cipher
+ * @mac: the MAC algorithm
+ * @comp: the compression method
+ * @master: the master key to use
+ * @session_id: the session identifier
+ *
+ * This function sets the premaster secret in a session. This is
+ * a function intended for exceptional uses. Do not use this
+ * function unless you are implementing a legacy protocol.
+ * Use gnutls_session_set_data() instead.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise
+ *   an error code is returned.
+ **/
+int
+gnutls_session_set_premaster (gnutls_session_t session, unsigned int entity,
+                              gnutls_protocol_t version, 
+                              gnutls_kx_algorithm_t kx, 
+                              gnutls_cipher_algorithm_t cipher,
+                              gnutls_mac_algorithm_t mac,
+                              gnutls_compression_method_t comp,
+                              const gnutls_datum_t* master,
+                              const gnutls_datum_t * session_id)
+{
+  int ret;
+
+  memset (&session->internals.resumed_security_parameters, 0,
+          sizeof (session->internals.resumed_security_parameters));
+  
+  session->internals.resumed_security_parameters.entity = entity;
+  session->internals.resumed_security_parameters.kx_algorithm = kx;
+  
+  ret = _gnutls_cipher_suite_get_id(kx, cipher, mac, session->internals.resumed_security_parameters.cipher_suite);
+  if (ret < 0)
+    return gnutls_assert_val(ret);
+
+  session->internals.resumed_security_parameters.compression_method = comp;
+  session->internals.resumed_security_parameters.cert_type = GNUTLS_CRT_UNKNOWN;
+  session->internals.resumed_security_parameters.version = version;
+
+  if (master->size != GNUTLS_MASTER_SIZE)
+    return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+
+  memcpy(session->internals.resumed_security_parameters.master_secret, master->data, master->size);
+
+  if (session_id->size > GNUTLS_MAX_SESSION_ID)
+    return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+
+  session->internals.resumed_security_parameters.session_id_size = session_id->size;
+  memcpy(session->internals.resumed_security_parameters.session_id, session_id->data, session_id->size);
+
+  session->internals.resumed_security_parameters.max_record_send_size = 
+  session->internals.resumed_security_parameters.max_record_recv_size = DEFAULT_MAX_RECORD_SIZE;
+
+  session->internals.resumed_security_parameters.timestamp = time(0);
+
+  session->internals.resumed_security_parameters.ecc_curve = GNUTLS_ECC_CURVE_INVALID;
+
+  session->internals.premaster_set = 1;
+
+  return 0;
+}
index 4bdf64bef69a82e92d7fa98ca716f55553c74ae9..3885e565760321f7a318c107fd4d92172e2fa451 100644 (file)
@@ -505,6 +505,7 @@ extern "C"
  * @GNUTLS_TLS1_1: TLS version 1.1.
  * @GNUTLS_TLS1_2: TLS version 1.2.
  * @GNUTLS_DTLS1_0: DTLS version 1.0.
+ * @GNUTLS_DTLS0_9: DTLS version 0.9 (Cisco AnyConnect / OpenSSL 0.9.8e).
  * @GNUTLS_VERSION_MAX: Maps to the highest supported TLS version.
  * @GNUTLS_VERSION_UNKNOWN: Unknown SSL/TLS version.
  *
@@ -518,7 +519,8 @@ extern "C"
     GNUTLS_TLS1_1 = 3,
     GNUTLS_TLS1_2 = 4,
     GNUTLS_DTLS1_0 = 5,
-    GNUTLS_VERSION_MAX = GNUTLS_DTLS1_0,
+    GNUTLS_DTLS0_9 = 6,
+    GNUTLS_VERSION_MAX = GNUTLS_DTLS0_9,
     GNUTLS_VERSION_UNKNOWN = 0xff
   } gnutls_protocol_t;
 
@@ -969,6 +971,15 @@ gnutls_ecc_curve_t gnutls_ecc_curve_get(gnutls_session_t session);
   void gnutls_session_get_random (gnutls_session_t session, gnutls_datum_t* client, 
                                   gnutls_datum_t* server);
 
+  int gnutls_session_set_premaster (gnutls_session_t session, unsigned int entity,
+                           gnutls_protocol_t version, 
+                           gnutls_kx_algorithm_t kx, 
+                           gnutls_cipher_algorithm_t cipher,
+                           gnutls_mac_algorithm_t mac,
+                           gnutls_compression_method_t comp,
+                           const gnutls_datum_t* master,
+                           const gnutls_datum_t * session_id);
+
 /* returns the session ID */
 #define GNUTLS_MAX_SESSION_ID 32
   int gnutls_session_get_id (gnutls_session_t session, void *session_id,
index 23dc1a15b2b47117ff9106a59a3f9a36af1fc7f8..9faa8ccce950cecb0d87e8488bbce3d22d2d84e9 100644 (file)
@@ -787,16 +787,17 @@ GNUTLS_3_0_0 {
        gnutls_session_get_random;
        gnutls_pubkey_verify_hash2;
        gnutls_pk_to_sign;
+       gnutls_certificate_set_x509_system_trust;
 } GNUTLS_2_12;
 
 GNUTLS_3_1_0 {
   global:
-       gnutls_certificate_set_x509_system_trust;
        gnutls_pkcs11_obj_list_import_url2;
        gnutls_x509_trust_list_add_system_trust;
        gnutls_x509_trust_list_add_trust_file;
        gnutls_x509_trust_list_add_trust_mem;
        gnutls_pkcs12_simple_parse;
+       gnutls_session_set_premaster;
 } GNUTLS_3_0_0;
 
 GNUTLS_PRIVATE {