]> git.ipfire.org Git - thirdparty/gnutls.git/commitdiff
handshake: send hello retry request when no key share matches
authorNikos Mavrogiannopoulos <nmav@redhat.com>
Thu, 21 Sep 2017 14:40:43 +0000 (16:40 +0200)
committerNikos Mavrogiannopoulos <nmav@redhat.com>
Mon, 19 Feb 2018 14:29:35 +0000 (15:29 +0100)
Signed-off-by: Nikos Mavrogiannopoulos <nmav@redhat.com>
12 files changed:
lib/Makefile.am
lib/alert.c
lib/constate.c
lib/ext/key_share.c
lib/gnutls_int.h
lib/handshake-tls13.c
lib/handshake.c
lib/handshake.h
lib/hello_ext.c
lib/state.c
lib/tls13/hello_retry.c [new file with mode: 0644]
lib/tls13/hello_retry.h [new file with mode: 0644]

index fe5c9236af87aa85f9e7cf845f4443461ac7c44f..81ef76832ae7270c17a3234609fd6e0ffda4e975 100644 (file)
@@ -92,6 +92,7 @@ COBJECTS += tls13/encrypted_extensions.c tls13/encrypted_extensions.h \
        tls13/certificate_verify.c tls13/certificate_verify.h \
        tls13-sig.c tls13-sig.h \
        tls13/finished.c tls13/finished.h \
+       tls13/hello_retry.c tls13/hello_retry.h \
        tls13/session_ticket.c tls13/session_ticket.h \
        tls13/certificate.c tls13/certificate.h
 
index b692ed6f9cb52f33a68eb8577ba6178651422456..aec54615cf06248bbb95ecf9b29e8c6e32026283 100644 (file)
@@ -218,6 +218,7 @@ int gnutls_error_to_alert(int err, int *level)
        case GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER:
        case GNUTLS_E_ILLEGAL_SRP_USERNAME:
        case GNUTLS_E_PK_INVALID_PUBKEY:
+       case GNUTLS_E_NO_COMMON_KEY_SHARE:
                ret = GNUTLS_A_ILLEGAL_PARAMETER;
                _level = GNUTLS_AL_FATAL;
                break;
index fe16b5456c2da3215ffa02571fc63c67932a62b3..db4aa6561afd3e98f17ff0be89a6b5696017fbd9 100644 (file)
@@ -354,18 +354,27 @@ _gnutls_set_cipher_suite2(gnutls_session_t session,
        const mac_entry_st *mac_algo;
        record_parameters_st *params;
        int ret;
+       const version_entry_st *ver = get_version(session);
 
        ret = _gnutls_epoch_get(session, EPOCH_NEXT, &params);
        if (ret < 0)
                return gnutls_assert_val(ret);
 
-       if (params->initialized
-           || params->cipher != NULL || params->mac != NULL)
-               return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
-
        cipher_algo = cipher_to_entry(cs->block_algorithm);
        mac_algo = mac_to_entry(cs->mac_algorithm);
 
+       if (ver->tls13_sem && (session->internals.hsk_flags & HSK_HRR_SENT)) {
+               if (params->initialized && (params->cipher != cipher_algo ||
+                   params->mac != mac_algo || cs != session->security_parameters.cs))
+                       return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER);
+
+               return 0;
+       } else {
+               if (params->initialized
+                   || params->cipher != NULL || params->mac != NULL)
+                       return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+       }
+
        if (_gnutls_cipher_is_ok(cipher_algo) == 0
            || _gnutls_mac_is_ok(mac_algo) == 0)
                return gnutls_assert_val(GNUTLS_E_UNWANTED_ALGORITHM);
index 321364e7277fa9136916a5cc5d833331cd771350..72bec9f55d11f31f050f224172538983892b15e6 100644 (file)
@@ -652,13 +652,30 @@ key_share_send_params(gnutls_session_t session,
                if (unlikely(ver == NULL || ver->key_shares == 0))
                        return gnutls_assert_val(0);
 
-               group = get_group(session);
-               if (unlikely(group == NULL))
-                       return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER);
+               if (_gnutls_ext_get_msg(session) == GNUTLS_EXT_FLAG_HRR) {
+                       if (session->internals.cand_ec_group != NULL)
+                               group = session->internals.cand_ec_group;
+                       else
+                               group = session->internals.cand_dh_group;
+                       if (group == NULL)
+                               return gnutls_assert_val(GNUTLS_E_NO_COMMON_KEY_SHARE);
+
+                       _gnutls_session_group_set(session, group);
+
+                       _gnutls_handshake_log("EXT[%p]: requesting retry with group %s\n", session, group->name);
+                       ret =
+                           _gnutls_buffer_append_prefix(extdata, 16, group->tls_id);
+                       if (ret < 0)
+                               return gnutls_assert_val(ret);
+               } else {
+                       group = get_group(session);
+                       if (unlikely(group == NULL))
+                               return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER);
 
-               ret = server_gen_key_share(session, group, extdata);
-               if (ret < 0)
-                       return gnutls_assert_val(ret);
+                       ret = server_gen_key_share(session, group, extdata);
+                       if (ret < 0)
+                               return gnutls_assert_val(ret);
+               }
        }
 
        return 0;
index 03297bfa1b01b910712ea4cb389be2ed5dbb0a63..ccbdce8f1c17edc3f35319ec5ed205b25b2e4219 100644 (file)
@@ -247,6 +247,7 @@ typedef enum handshake_state_t { STATE0 = 0, STATE1, STATE2,
        STATE15, STATE16, STATE17, STATE18, STATE19,
        STATE20 = 20, STATE21, STATE22,
        STATE30 = 30, STATE31, STATE40 = 40, STATE41, STATE50 = 50,
+       STATE90=90, STATE91, STATE92, STATE93,
        STATE100=100, STATE101, STATE102, STATE103, STATE104,
        STATE105, STATE106, STATE107, STATE108, STATE109, STATE110
 } handshake_state_t;
@@ -1109,6 +1110,7 @@ typedef struct {
 #define HSK_CRT_VRFY_EXPECTED 1
 #define HSK_CRT_SENT (1<<1)
 #define HSK_CRT_ASKED (1<<2)
+#define HSK_HRR_SENT (1<<3)
        unsigned hsk_flags; /* TLS1.3 only */
 
        unsigned crt_requested; /* 1 if client auth was requested (i.e., client cert).
index 9b45d6f729ae05649f5385f1e39f33ac4cbf01b8..87a87663897b8f1fd1399f220b8c276a8011e1da 100644 (file)
@@ -47,6 +47,7 @@
 #include <random.h>
 #include <dtls.h>
 #include "secrets.h"
+#include "tls13/hello_retry.h"
 #include "tls13/encrypted_extensions.h"
 #include "tls13/certificate_request.h"
 #include "tls13/certificate_verify.h"
@@ -168,7 +169,7 @@ static int generate_hs_traffic_keys(gnutls_session_t session)
 {
        int ret;
 
-       if (unlikely(session->key.key.size == 0))
+       if (unlikely(session->key.key.size == 0 || session->key.temp_secret_size == 0))
                return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
 
        ret = _tls13_update_secret(session, session->key.key.data, session->key.key.size);
@@ -195,6 +196,39 @@ int _gnutls13_handshake_server(gnutls_session_t session)
        int ret = 0;
 
        switch (STATE) {
+       case STATE90:
+               ret = _gnutls13_handshake_hash_buffers_synth(session);
+               STATE = STATE90;
+               IMED_RET("reset handshake buffers", ret, 0);
+               /* fall through */
+       case STATE91:
+               ret = _gnutls13_send_hello_retry_request(session, AGAIN(STATE91));
+               STATE = STATE91;
+               IMED_RET("send hello retry request", ret, 0);
+               /* fall through */
+       case STATE92:
+               ret =
+                   _gnutls_recv_handshake(session,
+                                          GNUTLS_HANDSHAKE_CLIENT_HELLO,
+                                          0, NULL);
+               STATE = STATE92;
+
+               if (ret == GNUTLS_E_INT_RET_0) {
+                       /* this is triggered by post_client_hello, and instructs the
+                        * handshake to proceed but be put on hold */
+                       ret = GNUTLS_E_INTERRUPTED;
+                       STATE = STATE100; /* hello already parsed -> move on */
+               } else {
+                       STATE = STATE92;
+               }
+
+               IMED_RET("recv client hello", ret, 0);
+               /* fall through */
+       case STATE93:
+               ret = _gnutls_send_server_hello(session, AGAIN(STATE93));
+               STATE = STATE93;
+               IMED_RET("send hello", ret, 0);
+               /* fall through */
        case STATE100:
                ret =
                    generate_hs_traffic_keys(session);
index da0f41cc059581176e3c2d72a18fe118f0652d2e..bf2b029db3f167481cdf6f883c7fab956e8aa025 100644 (file)
@@ -74,13 +74,13 @@ send_handshake_final(gnutls_session_t session, int init);
 
 /* Empties but does not free the buffer
  */
-static inline void
-handshake_hash_buffer_empty(gnutls_session_t session)
+inline static void
+handshake_hash_buffer_reset(gnutls_session_t session)
 {
-
        _gnutls_buffers_log("BUF[HSK]: Emptied buffer\n");
 
-       session->internals.used_exts = 0;
+       session->internals.handshake_hash_buffer_client_kx_len = 0;
+       session->internals.handshake_hash_buffer_server_finished_len = 0;
        session->internals.handshake_hash_buffer_prev_len = 0;
        session->internals.handshake_hash_buffer.length = 0;
        return;
@@ -106,12 +106,43 @@ recv_hello_verify_request(gnutls_session_t session,
  */
 void _gnutls_handshake_hash_buffers_clear(gnutls_session_t session)
 {
-       session->internals.handshake_hash_buffer_prev_len = 0;
-       session->internals.handshake_hash_buffer_client_kx_len = 0;
-       session->internals.handshake_hash_buffer_server_finished_len = 0;
+       handshake_hash_buffer_reset(session);
        _gnutls_buffer_clear(&session->internals.handshake_hash_buffer);
 }
 
+/* Replace handshake message buffer, with the special synthetic message
+ * needed by TLS1.3 when HRR is sent. */
+int _gnutls13_handshake_hash_buffers_synth(gnutls_session_t session)
+{
+       int ret;
+       uint8_t hdata[4+MAX_HASH_SIZE];
+
+       /* calculate hash */
+       hdata[0] = 254;
+       _gnutls_write_uint24(session->security_parameters.prf->output_size, &hdata[1]);
+
+       ret = gnutls_hash_fast((gnutls_digest_algorithm_t)session->security_parameters.prf->id,
+                              session->internals.handshake_hash_buffer.data,
+                              session->internals.handshake_hash_buffer.length,
+                              hdata+4);
+       if (ret < 0)
+               return gnutls_assert_val(ret);
+
+       handshake_hash_buffer_reset(session);
+
+       ret =
+           _gnutls_buffer_append_data(&session->internals.
+                                      handshake_hash_buffer,
+                                      hdata, session->security_parameters.prf->output_size+4);
+       if (ret < 0)
+               return gnutls_assert_val(ret);
+
+       _gnutls_buffers_log("BUF[HSK]: Replaced handshake buffer with synth message (%d bytes)\n",
+                           session->security_parameters.prf->output_size+4);
+
+       return 0;
+}
+
 /* this will copy the required values for resuming to
  * internals, and to security_parameters.
  * this will keep as less data to security_parameters.
@@ -1883,7 +1914,7 @@ static int send_client_hello(gnutls_session_t session, int again)
        return ret;
 }
 
-static int send_server_hello(gnutls_session_t session, int again)
+int _gnutls_send_server_hello(gnutls_session_t session, int again)
 {
        mbuffer_st *bufel = NULL;
        gnutls_buffer_st buf;
@@ -2046,7 +2077,9 @@ recv_hello_verify_request(gnutls_session_t session,
        }
 
        /* reset handshake hash buffers */
-       handshake_hash_buffer_empty(session);
+       handshake_hash_buffer_reset(session);
+       /* reset extensions used in previous hello */
+       session->internals.used_exts = 0;
 
        return 0;
 }
@@ -2841,7 +2874,8 @@ static int handshake_server(gnutls_session_t session)
        const version_entry_st *ver;
 
  reset:
-       if (STATE >= STATE100)
+
+       if (STATE >= STATE90)
                return _gnutls13_handshake_server(session);
 
        switch (STATE) {
@@ -2859,6 +2893,13 @@ static int handshake_server(gnutls_session_t session)
                } else {
                        STATE = STATE1;
                }
+
+               if (ret == GNUTLS_E_NO_COMMON_KEY_SHARE) {
+                       STATE = STATE90;
+                       session->internals.hsk_flags |= HSK_HRR_SENT;
+                       goto reset;
+               }
+
                IMED_RET("recv hello", ret, 1);
                /* fall through */
        case STATE2:
@@ -2868,7 +2909,7 @@ static int handshake_server(gnutls_session_t session)
                IMED_RET("recv hello", ret, 0);
                /* fall through */
        case STATE3:
-               ret = send_server_hello(session, AGAIN(STATE3));
+               ret = _gnutls_send_server_hello(session, AGAIN(STATE3));
                STATE = STATE3;
                IMED_RET("send hello", ret, 1);
 
index 79ac19eb7aa4a95e1c793357d2085a075e181dd1..1af8fbc19c12d0c470da673a31e9d76ebcbb6515 100644 (file)
@@ -64,6 +64,8 @@ int _gnutls_generate_session_id(uint8_t * session_id, uint8_t * len);
 int _gnutls_gen_server_random(gnutls_session_t session, int version);
 void _gnutls_set_client_random(gnutls_session_t session, uint8_t * rnd);
 
+int _gnutls_send_server_hello(gnutls_session_t session, int again);
+
 int _gnutls_find_pk_algos_in_ciphersuites(uint8_t * data, int datalen);
 int _gnutls_server_select_suite(gnutls_session_t session, uint8_t * data,
                                unsigned int datalen, unsigned int scsv_only);
@@ -75,6 +77,8 @@ int _gnutls_user_hello_func(gnutls_session_t session,
 
 void _gnutls_handshake_hash_buffers_clear(gnutls_session_t session);
 
+int _gnutls13_handshake_hash_buffers_synth(gnutls_session_t session);
+
 #define STATE session->internals.handshake_state
 #define FINAL_STATE session->internals.handshake_final_state
 /* This returns true if we have got there
index 55bfd6df64186e4bdcd4bcc724709a58f793ab8c..e22f72fca8f4ecb97df784cfc25d0578d3c3b887 100644 (file)
@@ -266,6 +266,10 @@ int hello_ext_send(void *_ctx, gnutls_buffer_st *buf)
                                  p->name, (int)p->tls_id,
                                  ext_msg_validity_to_str(ctx->msg));
                return 0;
+       } else {
+               _gnutls_handshake_log("EXT[%p]: Preparing extension (%s/%d) for '%s'\n", session,
+                                 p->name, (int)p->tls_id,
+                                 ext_msg_validity_to_str(ctx->msg));
        }
 
        /* ensure we don't send something twice (i.e, overriden extensions in
index 65468cdca66f24d5d00c2a292ed0aafce26739bd..3d45f4f5665f6ca2b188a9cadb125982e31392cc 100644 (file)
@@ -222,6 +222,8 @@ static void handshake_internal_state_clear1(gnutls_session_t session)
        session->internals.have_ffdhe = 0;
        session->internals.cand_ec_group = 0;
        session->internals.cand_dh_group = 0;
+
+       session->internals.hsk_flags = 0;
 }
 
 /* This function will clear all the variables in internals
diff --git a/lib/tls13/hello_retry.c b/lib/tls13/hello_retry.c
new file mode 100644 (file)
index 0000000..30d0a2a
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2017 Red Hat, Inc.
+ *
+ * Author: Nikos Mavrogiannopoulos
+ *
+ * This file is part of GnuTLS.
+ *
+ * The GnuTLS 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 program.  If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#include "gnutls_int.h"
+#include "errors.h"
+#include "hello_ext.h"
+#include "handshake.h"
+#include "tls13/hello_retry.h"
+#include "auth/cert.h"
+#include "mbuffers.h"
+
+int _gnutls13_send_hello_retry_request(gnutls_session_t session, unsigned again)
+{
+       int ret;
+       mbuffer_st *bufel = NULL;
+       gnutls_buffer_st buf;
+       const version_entry_st *ver;
+
+       if (again == 0) {
+               ver = get_version(session);
+               if (unlikely(ver == NULL || session->security_parameters.cs == NULL))
+                       return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+
+               ret = _gnutls_buffer_init_handshake_mbuffer(&buf);
+               if (ret < 0)
+                       return gnutls_assert_val(ret);
+
+               ret = _gnutls_buffer_append_data(&buf, &ver->major, 1);
+               if (ret < 0)
+                       return gnutls_assert_val(ret);
+
+               ret = _gnutls_buffer_append_data(&buf, &ver->minor, 1);
+               if (ret < 0) {
+                       gnutls_assert();
+                       goto cleanup;
+               }
+
+               ret = _gnutls_buffer_append_data(&buf, session->security_parameters.cs->id, 2);
+               if (ret < 0) {
+                       gnutls_assert();
+                       goto cleanup;
+               }
+
+               ret = _gnutls_gen_hello_extensions(session, &buf,
+                                                  GNUTLS_EXT_FLAG_HRR,
+                                                  GNUTLS_EXT_ANY);
+               if (ret < 0) {
+                       gnutls_assert();
+                       goto cleanup;
+               }
+
+               /* reset extensions sent by this session to allow re-sending them */
+               session->internals.used_exts = 0;
+
+               bufel = _gnutls_buffer_to_mbuffer(&buf);
+       }
+
+       return _gnutls_send_handshake(session, bufel, GNUTLS_HANDSHAKE_HELLO_RETRY_REQUEST);
+
+ cleanup:
+       _gnutls_buffer_clear(&buf);
+       return ret;
+}
+
diff --git a/lib/tls13/hello_retry.h b/lib/tls13/hello_retry.h
new file mode 100644 (file)
index 0000000..1f21f8b
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2017 Red Hat, Inc.
+ *
+ * Author: Nikos Mavrogiannopoulos
+ *
+ * This file is part of GnuTLS.
+ *
+ * The GnuTLS 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 program.  If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+int _gnutls13_send_hello_retry_request(gnutls_session_t session, unsigned again);