diff options
author | Nikos Mavrogiannopoulos <nmav@redhat.com> | 2017-09-21 16:40:43 +0200 |
---|---|---|
committer | Nikos Mavrogiannopoulos <nmav@redhat.com> | 2018-02-19 15:29:35 +0100 |
commit | aeb3353b48dfe08c851dc53bdc1914d3173d5909 (patch) | |
tree | 364ebfe0c65c9dd96cba7da2c9ed2789556c1d8c | |
parent | 98ffb853e3ed9244b82f6954fbb638da6799796f (diff) | |
download | gnutls-aeb3353b48dfe08c851dc53bdc1914d3173d5909.tar.gz |
handshake: send hello retry request when no key share matches
Signed-off-by: Nikos Mavrogiannopoulos <nmav@redhat.com>
-rw-r--r-- | lib/Makefile.am | 1 | ||||
-rw-r--r-- | lib/alert.c | 1 | ||||
-rw-r--r-- | lib/constate.c | 17 | ||||
-rw-r--r-- | lib/ext/key_share.c | 29 | ||||
-rw-r--r-- | lib/gnutls_int.h | 2 | ||||
-rw-r--r-- | lib/handshake-tls13.c | 36 | ||||
-rw-r--r-- | lib/handshake.c | 63 | ||||
-rw-r--r-- | lib/handshake.h | 4 | ||||
-rw-r--r-- | lib/hello_ext.c | 4 | ||||
-rw-r--r-- | lib/state.c | 2 | ||||
-rw-r--r-- | lib/tls13/hello_retry.c | 83 | ||||
-rw-r--r-- | lib/tls13/hello_retry.h | 23 |
12 files changed, 243 insertions, 22 deletions
diff --git a/lib/Makefile.am b/lib/Makefile.am index fe5c9236af..81ef76832a 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -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 diff --git a/lib/alert.c b/lib/alert.c index b692ed6f9c..aec54615cf 100644 --- a/lib/alert.c +++ b/lib/alert.c @@ -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; diff --git a/lib/constate.c b/lib/constate.c index fe16b5456c..db4aa6561a 100644 --- a/lib/constate.c +++ b/lib/constate.c @@ -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, ¶ms); 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); diff --git a/lib/ext/key_share.c b/lib/ext/key_share.c index 321364e727..72bec9f55d 100644 --- a/lib/ext/key_share.c +++ b/lib/ext/key_share.c @@ -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; diff --git a/lib/gnutls_int.h b/lib/gnutls_int.h index 03297bfa1b..ccbdce8f1c 100644 --- a/lib/gnutls_int.h +++ b/lib/gnutls_int.h @@ -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). diff --git a/lib/handshake-tls13.c b/lib/handshake-tls13.c index 9b45d6f729..87a8766389 100644 --- a/lib/handshake-tls13.c +++ b/lib/handshake-tls13.c @@ -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); diff --git a/lib/handshake.c b/lib/handshake.c index da0f41cc05..bf2b029db3 100644 --- a/lib/handshake.c +++ b/lib/handshake.c @@ -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); diff --git a/lib/handshake.h b/lib/handshake.h index 79ac19eb7a..1af8fbc19c 100644 --- a/lib/handshake.h +++ b/lib/handshake.h @@ -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 diff --git a/lib/hello_ext.c b/lib/hello_ext.c index 55bfd6df64..e22f72fca8 100644 --- a/lib/hello_ext.c +++ b/lib/hello_ext.c @@ -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 diff --git a/lib/state.c b/lib/state.c index 65468cdca6..3d45f4f566 100644 --- a/lib/state.c +++ b/lib/state.c @@ -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 index 0000000000..30d0a2a8e1 --- /dev/null +++ b/lib/tls13/hello_retry.c @@ -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 index 0000000000..1f21f8b089 --- /dev/null +++ b/lib/tls13/hello_retry.h @@ -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); |