summaryrefslogtreecommitdiffstats
path: root/lib/handshake.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/handshake.c')
-rw-r--r--lib/handshake.c3788
1 files changed, 3788 insertions, 0 deletions
diff --git a/lib/handshake.c b/lib/handshake.c
new file mode 100644
index 0000000..21edc5e
--- /dev/null
+++ b/lib/handshake.c
@@ -0,0 +1,3788 @@
+/*
+ * Copyright (C) 2000-2016 Free Software Foundation, Inc.
+ * Copyright (C) 2015-2018 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 <https://www.gnu.org/licenses/>
+ *
+ */
+
+/* Functions that relate to the TLS handshake procedure.
+ */
+
+#include "gnutls_int.h"
+#include "errors.h"
+#include "dh.h"
+#include "debug.h"
+#include "algorithms.h"
+#include "cipher.h"
+#include "buffers.h"
+#include "mbuffers.h"
+#include "kx.h"
+#include "handshake.h"
+#include "num.h"
+#include "hash_int.h"
+#include "db.h"
+#include "hello_ext.h"
+#include "supplemental.h"
+#include "auth.h"
+#include "sslv2_compat.h"
+#include <auth/cert.h>
+#include "constate.h"
+#include <record.h>
+#include <state.h>
+#include <ext/pre_shared_key.h>
+#include <ext/srp.h>
+#include <ext/session_ticket.h>
+#include <ext/status_request.h>
+#include <ext/safe_renegotiation.h>
+#include <auth/anon.h> /* for gnutls_anon_server_credentials_t */
+#include <auth/psk.h> /* for gnutls_psk_server_credentials_t */
+#include <random.h>
+#include <dtls.h>
+#include "secrets.h"
+#include "tls13/early_data.h"
+#include "tls13/session_ticket.h"
+#include "locks.h"
+#include "system/ktls.h"
+
+
+static int check_if_null_comp_present(gnutls_session_t session,
+ uint8_t * data, int datalen);
+static int handshake_client(gnutls_session_t session);
+static int handshake_server(gnutls_session_t session);
+
+static int
+read_server_hello(gnutls_session_t session,
+ uint8_t * data, int datalen);
+
+static int
+recv_handshake_final(gnutls_session_t session, int init);
+static int
+send_handshake_final(gnutls_session_t session, int init);
+
+/* Empties but does not free the buffer
+ */
+inline static void
+handshake_hash_buffer_reset(gnutls_session_t session)
+{
+ _gnutls_buffers_log("BUF[HSK]: Emptied buffer\n");
+
+ session->internals.handshake_hash_buffer_client_hello_len = 0;
+ session->internals.handshake_hash_buffer_client_kx_len = 0;
+ session->internals.handshake_hash_buffer_server_finished_len = 0;
+ session->internals.handshake_hash_buffer_client_finished_len = 0;
+ session->internals.handshake_hash_buffer_prev_len = 0;
+ session->internals.handshake_hash_buffer.length = 0;
+ session->internals.full_client_hello.length = 0;
+ return;
+}
+
+static int
+handshake_hash_add_recvd(gnutls_session_t session,
+ gnutls_handshake_description_t recv_type,
+ uint8_t * header, uint16_t header_size,
+ uint8_t * dataptr, uint32_t datalen);
+
+static int
+handshake_hash_add_sent(gnutls_session_t session,
+ gnutls_handshake_description_t type,
+ uint8_t * dataptr, uint32_t datalen);
+
+static int
+recv_hello_verify_request(gnutls_session_t session,
+ uint8_t * data, int datalen);
+
+
+/* Clears the handshake hash buffers and handles.
+ */
+void _gnutls_handshake_hash_buffers_clear(gnutls_session_t session)
+{
+ handshake_hash_buffer_reset(session);
+ _gnutls_buffer_clear(&session->internals.handshake_hash_buffer);
+ _gnutls_buffer_clear(&session->internals.full_client_hello);
+}
+
+/* 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,
+ const mac_entry_st *prf,
+ unsigned client)
+{
+ int ret;
+ uint8_t hdata[4+MAX_HASH_SIZE];
+ size_t length;
+
+ if (client)
+ length = session->internals.handshake_hash_buffer_prev_len;
+ else
+ length = session->internals.handshake_hash_buffer.length;
+
+ /* calculate hash */
+ hdata[0] = 254;
+ _gnutls_write_uint24(prf->output_size, &hdata[1]);
+
+ ret = gnutls_hash_fast((gnutls_digest_algorithm_t)prf->id,
+ session->internals.handshake_hash_buffer.data,
+ 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, 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",
+ 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.
+ */
+static int tls12_resume_copy_required_vals(gnutls_session_t session, unsigned ticket)
+{
+ int ret;
+
+ /* get the new random values */
+ memcpy(session->internals.resumed_security_parameters.
+ server_random, session->security_parameters.server_random,
+ GNUTLS_RANDOM_SIZE);
+ memcpy(session->internals.resumed_security_parameters.
+ client_random, session->security_parameters.client_random,
+ GNUTLS_RANDOM_SIZE);
+
+ /* keep the ciphersuite and compression
+ * That is because the client must see these in our
+ * hello message.
+ */
+ ret = _gnutls_set_cipher_suite2(session,
+ session->internals.
+ resumed_security_parameters.
+ cs);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ /* or write_compression_algorithm
+ * they are the same
+ */
+
+ session->security_parameters.entity =
+ session->internals.resumed_security_parameters.entity;
+
+ if (session->internals.resumed_security_parameters.pversion ==
+ NULL)
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+
+ if (_gnutls_set_current_version(session,
+ session->internals.
+ resumed_security_parameters.pversion->
+ id) < 0)
+ return gnutls_assert_val(GNUTLS_E_UNSUPPORTED_VERSION_PACKET);
+
+ session->security_parameters.client_ctype =
+ session->internals.resumed_security_parameters.client_ctype;
+ session->security_parameters.server_ctype =
+ session->internals.resumed_security_parameters.server_ctype;
+
+ if (!ticket) {
+ memcpy(session->security_parameters.session_id,
+ session->internals.resumed_security_parameters.session_id,
+ sizeof(session->security_parameters.session_id));
+ session->security_parameters.session_id_size =
+ session->internals.resumed_security_parameters.session_id_size;
+ }
+
+ return 0;
+}
+
+void _gnutls_set_client_random(gnutls_session_t session, uint8_t * rnd)
+{
+ _gnutls_memory_mark_defined(session->security_parameters.client_random,
+ GNUTLS_RANDOM_SIZE);
+ memcpy(session->security_parameters.client_random, rnd,
+ GNUTLS_RANDOM_SIZE);
+}
+
+static
+int _gnutls_gen_client_random(gnutls_session_t session)
+{
+ int ret;
+
+ /* no random given, we generate. */
+ if (session->internals.sc_random_set != 0) {
+ _gnutls_memory_mark_defined(session->security_parameters.client_random,
+ GNUTLS_RANDOM_SIZE);
+ memcpy(session->security_parameters.client_random,
+ session->internals.
+ resumed_security_parameters.client_random,
+ GNUTLS_RANDOM_SIZE);
+ } else {
+ _gnutls_memory_mark_defined(session->security_parameters.client_random,
+ GNUTLS_RANDOM_SIZE);
+ ret = gnutls_rnd(GNUTLS_RND_NONCE,
+ session->security_parameters.client_random,
+ GNUTLS_RANDOM_SIZE);
+ if (ret < 0) {
+ _gnutls_memory_mark_undefined(session->security_parameters.client_random,
+ GNUTLS_RANDOM_SIZE);
+ return gnutls_assert_val(ret);
+ }
+ }
+
+ return 0;
+}
+
+static
+int _gnutls_set_server_random(gnutls_session_t session, const version_entry_st *vers, uint8_t * rnd)
+{
+ const version_entry_st *max;
+
+ _gnutls_memory_mark_defined(session->security_parameters.server_random,
+ GNUTLS_RANDOM_SIZE);
+ memcpy(session->security_parameters.server_random, rnd,
+ GNUTLS_RANDOM_SIZE);
+
+ /* check whether the server random value is set according to
+ * to TLS 1.3. p4.1.3 requirements */
+ if (!IS_DTLS(session) && vers->id <= GNUTLS_TLS1_2 && have_creds_for_tls13(session)) {
+
+ max = _gnutls_version_max(session);
+ if (max->id <= GNUTLS_TLS1_2)
+ return 0;
+
+ if (vers->id == GNUTLS_TLS1_2 &&
+ memcmp(&session->security_parameters.server_random[GNUTLS_RANDOM_SIZE-8],
+ "\x44\x4F\x57\x4E\x47\x52\x44\x01", 8) == 0) {
+
+ _gnutls_audit_log(session,
+ "Detected downgrade to TLS 1.2 from TLS 1.3\n");
+ return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER);
+ } else if (vers->id <= GNUTLS_TLS1_1 &&
+ memcmp(&session->security_parameters.server_random[GNUTLS_RANDOM_SIZE-8],
+ "\x44\x4F\x57\x4E\x47\x52\x44\x00", 8) == 0) {
+
+ _gnutls_audit_log(session,
+ "Detected downgrade to TLS 1.1 or earlier from TLS 1.3\n");
+ return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER);
+ }
+ }
+
+ return 0;
+}
+
+int _gnutls_gen_server_random(gnutls_session_t session, int version)
+{
+ int ret;
+ const version_entry_st *max;
+
+ if (session->internals.sc_random_set != 0) {
+ _gnutls_memory_mark_defined(session->security_parameters.server_random,
+ GNUTLS_RANDOM_SIZE);
+ memcpy(session->security_parameters.server_random,
+ session->internals.
+ resumed_security_parameters.server_random,
+ GNUTLS_RANDOM_SIZE);
+ return 0;
+ }
+
+ max = _gnutls_version_max(session);
+ if (max == NULL)
+ return gnutls_assert_val(GNUTLS_E_NO_CIPHER_SUITES);
+
+ _gnutls_memory_mark_defined(session->security_parameters.server_random,
+ GNUTLS_RANDOM_SIZE);
+
+ if (!IS_DTLS(session) && max->id >= GNUTLS_TLS1_3 &&
+ version <= GNUTLS_TLS1_2) {
+ if (version == GNUTLS_TLS1_2) {
+ memcpy(&session->security_parameters.server_random[GNUTLS_RANDOM_SIZE-8],
+ "\x44\x4F\x57\x4E\x47\x52\x44\x01", 8);
+ } else {
+ memcpy(&session->security_parameters.server_random[GNUTLS_RANDOM_SIZE-8],
+ "\x44\x4F\x57\x4E\x47\x52\x44\x00", 8);
+ }
+ ret =
+ gnutls_rnd(GNUTLS_RND_NONCE, session->security_parameters.server_random, GNUTLS_RANDOM_SIZE-8);
+
+ } else {
+ ret =
+ gnutls_rnd(GNUTLS_RND_NONCE, session->security_parameters.server_random, GNUTLS_RANDOM_SIZE);
+ }
+
+ if (ret < 0) {
+ gnutls_assert();
+ _gnutls_memory_mark_undefined(session->security_parameters.server_random,
+ GNUTLS_RANDOM_SIZE);
+ return ret;
+ }
+
+ return 0;
+}
+
+#ifdef ENABLE_SSL3
+/* Calculate The SSL3 Finished message
+ */
+#define SSL3_CLIENT_MSG "CLNT"
+#define SSL3_SERVER_MSG "SRVR"
+#define SSL_MSG_LEN 4
+static int
+_gnutls_ssl3_finished(gnutls_session_t session, int type, uint8_t * ret,
+ int sending)
+{
+ digest_hd_st td_md5;
+ digest_hd_st td_sha;
+ const char *mesg;
+ int rc, len;
+
+ if (sending)
+ len = session->internals.handshake_hash_buffer.length;
+ else
+ len = session->internals.handshake_hash_buffer_prev_len;
+
+ rc = _gnutls_hash_init(&td_sha, hash_to_entry(GNUTLS_DIG_SHA1));
+ if (rc < 0)
+ return gnutls_assert_val(rc);
+
+ rc = _gnutls_hash_init(&td_md5, hash_to_entry(GNUTLS_DIG_MD5));
+ if (rc < 0) {
+ _gnutls_hash_deinit(&td_sha, NULL);
+ return gnutls_assert_val(rc);
+ }
+
+ _gnutls_hash(&td_sha,
+ session->internals.handshake_hash_buffer.data, len);
+ _gnutls_hash(&td_md5,
+ session->internals.handshake_hash_buffer.data, len);
+
+ if (type == GNUTLS_SERVER)
+ mesg = SSL3_SERVER_MSG;
+ else
+ mesg = SSL3_CLIENT_MSG;
+
+ _gnutls_hash(&td_md5, mesg, SSL_MSG_LEN);
+ _gnutls_hash(&td_sha, mesg, SSL_MSG_LEN);
+
+ rc = _gnutls_mac_deinit_ssl3_handshake(&td_md5, ret,
+ session->security_parameters.
+ master_secret,
+ GNUTLS_MASTER_SIZE);
+ if (rc < 0) {
+ _gnutls_hash_deinit(&td_md5, NULL);
+ _gnutls_hash_deinit(&td_sha, NULL);
+ return gnutls_assert_val(rc);
+ }
+
+ rc = _gnutls_mac_deinit_ssl3_handshake(&td_sha, &ret[16],
+ session->security_parameters.
+ master_secret,
+ GNUTLS_MASTER_SIZE);
+ if (rc < 0) {
+ _gnutls_hash_deinit(&td_sha, NULL);
+ return gnutls_assert_val(rc);
+ }
+
+ return 0;
+}
+#endif
+
+/* Hash the handshake messages as required by TLS 1.0
+ */
+#define SERVER_MSG "server finished"
+#define CLIENT_MSG "client finished"
+#define TLS_MSG_LEN 15
+static int
+_gnutls_finished(gnutls_session_t session, int type, void *ret,
+ int sending)
+{
+ const int siz = TLS_MSG_LEN;
+ uint8_t concat[MAX_HASH_SIZE];
+ size_t hash_len;
+ const char *mesg;
+ int rc, len, algorithm;
+
+ if (sending)
+ len = session->internals.handshake_hash_buffer.length;
+ else
+ len = session->internals.handshake_hash_buffer_prev_len;
+
+ algorithm = session->security_parameters.prf->id;
+ rc = _gnutls_hash_fast(algorithm,
+ session->internals.
+ handshake_hash_buffer.data, len,
+ concat);
+ if (rc < 0)
+ return gnutls_assert_val(rc);
+
+ hash_len = session->security_parameters.prf->output_size;
+
+ if (type == GNUTLS_SERVER) {
+ mesg = SERVER_MSG;
+ } else {
+ mesg = CLIENT_MSG;
+ }
+
+ _gnutls_memory_mark_defined(session->security_parameters.master_secret,
+ GNUTLS_MASTER_SIZE);
+ rc = _gnutls_PRF(session,
+ session->security_parameters.master_secret,
+ GNUTLS_MASTER_SIZE, mesg, siz, concat, hash_len,
+ 12, ret);
+ if (rc < 0) {
+ _gnutls_memory_mark_undefined(session->security_parameters.master_secret,
+ GNUTLS_MASTER_SIZE);
+ }
+ return rc;
+}
+
+
+/* returns the 0 on success or a negative error code.
+ */
+int
+_gnutls_negotiate_version(gnutls_session_t session,
+ uint8_t major, uint8_t minor, unsigned allow_tls13)
+{
+ const version_entry_st *vers;
+ const version_entry_st *aversion = nversion_to_entry(major, minor);
+
+ /* if we do not support that version, unless that version is TLS 1.2;
+ * TLS 1.2 is handled separately because it is always advertized under TLS 1.3 or later */
+ if (aversion == NULL ||
+ _gnutls_nversion_is_supported(session, major, minor) == 0) {
+
+ if (aversion && aversion->id == GNUTLS_TLS1_2) {
+ vers = _gnutls_version_max(session);
+ if (unlikely(vers == NULL))
+ return gnutls_assert_val(GNUTLS_E_NO_CIPHER_SUITES);
+
+ if (vers->id >= GNUTLS_TLS1_2) {
+ session->security_parameters.pversion = aversion;
+ return 0;
+ }
+ }
+
+ /* if we get an unknown/unsupported version, then fail if the version we
+ * got is too low to be supported */
+ if (!_gnutls_version_is_too_high(session, major, minor))
+ return gnutls_assert_val(GNUTLS_E_UNSUPPORTED_VERSION_PACKET);
+
+ /* If he requested something we do not support
+ * then we send him the highest we support.
+ */
+ vers = _gnutls_legacy_version_max(session);
+ if (vers == NULL) {
+ /* this check is not really needed.
+ */
+ gnutls_assert();
+ return GNUTLS_E_UNKNOWN_CIPHER_SUITE;
+ }
+
+ session->security_parameters.pversion = vers;
+
+ return 0;
+ } else {
+ session->security_parameters.pversion = aversion;
+
+ /* we do not allow TLS1.3 negotiation using this mechanism */
+ if (aversion->tls13_sem && !allow_tls13) {
+ vers = _gnutls_legacy_version_max(session);
+ session->security_parameters.pversion = vers;
+ }
+
+ return 0;
+ }
+}
+
+/* This function returns:
+ * - zero on success
+ * - GNUTLS_E_INT_RET_0 if GNUTLS_E_AGAIN || GNUTLS_E_INTERRUPTED were returned by the callback
+ * - a negative error code on other error
+ */
+int
+_gnutls_user_hello_func(gnutls_session_t session,
+ uint8_t major, uint8_t minor)
+{
+ int ret, sret = 0;
+ const version_entry_st *vers, *old_vers;
+ const version_entry_st *new_max;
+
+ if (session->internals.user_hello_func != NULL) {
+ ret = session->internals.user_hello_func(session);
+
+ if (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED) {
+ gnutls_assert();
+ sret = GNUTLS_E_INT_RET_0;
+ } else if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ /* This callback is often used to switch the priority string of the
+ * server, and that includes switching version which we have already
+ * negotiated; note that this doesn't apply when resuming as the version
+ * negotiation is already complete. */
+ if (!session->internals.resumed) {
+ new_max = _gnutls_version_max(session);
+ old_vers = get_version(session);
+
+ if (!old_vers->tls13_sem || (new_max && !new_max->tls13_sem)) {
+#if GNUTLS_TLS_VERSION_MAX != GNUTLS_TLS1_3
+# error "Need to update the following logic"
+#endif
+ /* Here we need to renegotiate the version since the callee might
+ * have disabled some TLS versions. This logic does not cope for
+ * protocols later than TLS1.3 if they have the tls13_sem set */
+ ret = _gnutls_negotiate_version(session, major, minor, 0);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ vers = get_version(session);
+ if (old_vers != vers) {
+ /* at this point we need to regenerate the random value to
+ * avoid the peer detecting this session as a rollback
+ * attempt. */
+ ret = _gnutls_gen_server_random(session, vers->id);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+ }
+ }
+ }
+ }
+ return sret;
+}
+
+/* Associates the right credential types for the session, and
+ * performs sanity checks. */
+static int set_auth_types(gnutls_session_t session)
+{
+ const version_entry_st *ver = get_version(session);
+ gnutls_kx_algorithm_t kx;
+
+ /* sanity check:
+ * we see TLS1.3 negotiated but no key share was sent */
+ if (ver->tls13_sem) {
+ if (unlikely(!(session->internals.hsk_flags & HSK_PSK_KE_MODE_PSK) &&
+ !(session->internals.hsk_flags & HSK_KEY_SHARE_RECEIVED))) {
+ return gnutls_assert_val(GNUTLS_E_MISSING_EXTENSION);
+ }
+
+ /* Under TLS1.3 this returns a KX which matches the negotiated
+ * groups from the key shares; if we are resuming then the KX seen
+ * here doesn't match the original session. */
+ if (!session->internals.resumed)
+ kx = gnutls_kx_get(session);
+ else
+ kx = GNUTLS_KX_UNKNOWN;
+ } else {
+ /* TLS1.2 or earlier, kx is associated with ciphersuite */
+ kx = session->security_parameters.cs->kx_algorithm;
+ }
+
+ if (kx != GNUTLS_KX_UNKNOWN) {
+ session->security_parameters.server_auth_type = _gnutls_map_kx_get_cred(kx, 1);
+ session->security_parameters.client_auth_type = _gnutls_map_kx_get_cred(kx, 0);
+ } else if (unlikely(!session->internals.resumed)) {
+ /* Here we can only arrive if something we received
+ * prevented the session from completing. */
+ return gnutls_assert_val(GNUTLS_E_ILLEGAL_PARAMETER);
+ }
+
+ return 0;
+}
+
+/* Read a client hello packet.
+ * A client hello must be a known version client hello
+ * or version 2.0 client hello (only for compatibility
+ * since SSL version 2.0 is not supported).
+ */
+static int
+read_client_hello(gnutls_session_t session, uint8_t * data,
+ int datalen)
+{
+ uint8_t session_id_len;
+ int pos = 0, ret;
+ uint16_t suite_size, comp_size;
+ int ext_size;
+ int neg_version, sret = 0;
+ int len = datalen;
+ uint8_t major, minor;
+ uint8_t *suite_ptr, *comp_ptr, *session_id, *ext_ptr;
+ const version_entry_st *vers;
+
+ DECR_LEN(len, 2);
+ _gnutls_handshake_log("HSK[%p]: Client's version: %d.%d\n",
+ session, data[pos], data[pos + 1]);
+
+ major = data[pos];
+ minor = data[pos+1];
+
+ set_adv_version(session, major, minor);
+
+ ret = _gnutls_negotiate_version(session, major, minor, 0);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ vers = get_version(session);
+ if (vers == NULL)
+ return gnutls_assert_val(GNUTLS_E_UNSUPPORTED_VERSION_PACKET);
+
+ neg_version = vers->id;
+
+ pos += 2;
+
+ /* Read client random value.
+ */
+ DECR_LEN(len, GNUTLS_RANDOM_SIZE);
+ _gnutls_set_client_random(session, &data[pos]);
+
+ pos += GNUTLS_RANDOM_SIZE;
+
+ ret = _gnutls_gen_server_random(session, neg_version);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ session->security_parameters.timestamp = gnutls_time(NULL);
+
+ DECR_LEN(len, 1);
+ session_id_len = data[pos++];
+
+ /* RESUME SESSION
+ */
+ if (session_id_len > GNUTLS_MAX_SESSION_ID_SIZE) {
+ gnutls_assert();
+ return GNUTLS_E_UNEXPECTED_PACKET_LENGTH;
+ }
+ DECR_LEN(len, session_id_len);
+ session_id = &data[pos];
+ pos += session_id_len;
+
+ if (IS_DTLS(session)) {
+ int cookie_size;
+
+ DECR_LEN(len, 1);
+ cookie_size = data[pos++];
+ DECR_LEN(len, cookie_size);
+ pos += cookie_size;
+ }
+
+ /* move forward to extensions and store other vals */
+ DECR_LEN(len, 2);
+ suite_size = _gnutls_read_uint16(&data[pos]);
+ pos += 2;
+
+ if (suite_size == 0 || (suite_size % 2) != 0)
+ return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
+
+ suite_ptr = &data[pos];
+ DECR_LEN(len, suite_size);
+ pos += suite_size;
+
+ DECR_LEN(len, 1);
+ comp_size = data[pos++]; /* the number of compression methods */
+
+ if (comp_size == 0)
+ return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
+
+ comp_ptr = &data[pos];
+ DECR_LEN(len, comp_size);
+ pos += comp_size;
+
+ ext_ptr = &data[pos];
+ ext_size = len;
+
+ /* Parse only the mandatory to read extensions for resumption
+ * and version negotiation. We don't want to parse any other
+ * extensions since we don't want new extension values to override
+ * the resumed ones. */
+ ret =
+ _gnutls_parse_hello_extensions(session, GNUTLS_EXT_FLAG_CLIENT_HELLO,
+ GNUTLS_EXT_VERSION_NEG,
+ ext_ptr, ext_size);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ ret =
+ _gnutls_parse_hello_extensions(session, GNUTLS_EXT_FLAG_CLIENT_HELLO,
+ GNUTLS_EXT_MANDATORY,
+ ext_ptr, ext_size);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ vers = get_version(session);
+ if (unlikely(vers == NULL))
+ return gnutls_assert_val(GNUTLS_E_UNSUPPORTED_VERSION_PACKET);
+
+ if (!vers->tls13_sem) {
+ ret =
+ _gnutls_server_restore_session(session, session_id,
+ session_id_len);
+
+ if (session_id_len > 0)
+ session->internals.resumption_requested = 1;
+
+ if (ret == 0) { /* resumed using default TLS resumption! */
+ ret = _gnutls_server_select_suite(session, suite_ptr, suite_size, 1);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ ret = tls12_resume_copy_required_vals(session, 0);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ session->internals.resumed = true;
+
+ return _gnutls_user_hello_func(session, major, minor);
+ } else {
+ ret = _gnutls_generate_session_id(session->security_parameters.
+ session_id,
+ &session->security_parameters.
+ session_id_size);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ session->internals.resumed = false;
+ }
+ } else { /* TLS1.3 */
+ /* we echo client's session ID - length was checked previously */
+ assert(session_id_len <= GNUTLS_MAX_SESSION_ID_SIZE);
+ if (session_id_len > 0)
+ memcpy(session->security_parameters.session_id, session_id, session_id_len);
+ session->security_parameters.session_id_size = session_id_len;
+ }
+
+ /* Parse the extensions (if any)
+ *
+ * Unconditionally try to parse extensions; safe renegotiation uses them in
+ * sslv3 and higher, even though sslv3 doesn't officially support them.
+ */
+ ret = _gnutls_parse_hello_extensions(session, GNUTLS_EXT_FLAG_CLIENT_HELLO,
+ GNUTLS_EXT_APPLICATION,
+ ext_ptr, ext_size);
+ /* len is the rest of the parsed length */
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ /* we cache this error code */
+ sret = _gnutls_user_hello_func(session, major, minor);
+ if (sret < 0 && sret != GNUTLS_E_INT_RET_0) {
+ gnutls_assert();
+ return sret;
+ }
+
+ /* Session tickets are parsed in this point */
+ ret =
+ _gnutls_parse_hello_extensions(session, GNUTLS_EXT_FLAG_CLIENT_HELLO,
+ GNUTLS_EXT_TLS, ext_ptr, ext_size);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ if (session->internals.hsk_flags & HSK_EARLY_DATA_ACCEPTED) {
+ const cipher_entry_st *ce;
+ const mac_entry_st *me;
+ record_parameters_st *params;
+
+ ce = cipher_to_entry(session->internals.
+ resumed_security_parameters.
+ cs->block_algorithm);
+ me = mac_to_entry(session->internals.
+ resumed_security_parameters.
+ cs->mac_algorithm);
+
+ ret = _gnutls_epoch_get(session, EPOCH_NEXT, &params);
+ if (ret < 0) {
+ return gnutls_assert_val(ret);
+ }
+
+ params->cipher = ce;
+ params->mac = me;
+
+ ret = _tls13_read_connection_state_init(session, STAGE_EARLY);
+ if (ret < 0) {
+ return gnutls_assert_val(ret);
+ }
+
+ _gnutls_epoch_bump(session);
+ ret = _gnutls_epoch_dup(session, EPOCH_READ_CURRENT);
+ if (ret < 0) {
+ return gnutls_assert_val(ret);
+ }
+ }
+
+ /* resumed by session_ticket extension */
+ if (!vers->tls13_sem && session->internals.resumed) {
+ 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;
+
+ ret = _gnutls_server_select_suite(session, suite_ptr, suite_size, 1);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ ret = tls12_resume_copy_required_vals(session, 1);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ /* to indicate to the client that the current session is resumed */
+ memcpy(session->security_parameters.session_id, session_id, session_id_len);
+ session->security_parameters.session_id_size = session_id_len;
+
+ return 0;
+ }
+
+ /* select an appropriate cipher suite (as well as certificate)
+ */
+ ret = _gnutls_server_select_suite(session, suite_ptr, suite_size, 0);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ /* Only at this point we know the version we are actually going to use
+ * ("supported_versions" extension is parsed, user_hello_func is called,
+ * legacy version negotiation is done). */
+ vers = get_version(session);
+ if (unlikely(vers == NULL))
+ return gnutls_assert_val(GNUTLS_E_UNSUPPORTED_VERSION_PACKET);
+
+ if (_gnutls_version_priority(session, vers->id) < 0)
+ return gnutls_assert_val(GNUTLS_E_UNSUPPORTED_VERSION_PACKET);
+
+ _gnutls_handshake_log("HSK[%p]: Selected version %s\n", session, vers->name);
+
+ /* select appropriate compression method */
+ ret =
+ check_if_null_comp_present(session, comp_ptr,
+ comp_size);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ /* call extensions that are intended to be parsed after the ciphersuite/cert
+ * are known. */
+ ret =
+ _gnutls_parse_hello_extensions(session, GNUTLS_EXT_FLAG_CLIENT_HELLO,
+ _GNUTLS_EXT_TLS_POST_CS, ext_ptr, ext_size);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ /* Calculate TLS 1.3 Early Secret */
+ if (session->security_parameters.pversion->tls13_sem &&
+ !(session->internals.hsk_flags & HSK_PSK_SELECTED)) {
+ ret = _tls13_init_secret(session, NULL, 0);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+ }
+
+ ret = set_auth_types(session);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ return sret;
+}
+
+/* This is to be called after sending CHANGE CIPHER SPEC packet
+ * and initializing encryption. This is the first encrypted message
+ * we send.
+ */
+int _gnutls_send_finished(gnutls_session_t session, int again)
+{
+ mbuffer_st *bufel;
+ uint8_t *data;
+ int ret;
+ size_t vdata_size = 0;
+ const version_entry_st *vers;
+
+ if (again == 0) {
+ bufel =
+ _gnutls_handshake_alloc(session,
+ MAX_VERIFY_DATA_SIZE);
+ if (bufel == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_MEMORY_ERROR;
+ }
+ data = _mbuffer_get_udata_ptr(bufel);
+
+ vers = get_version(session);
+ if (unlikely(vers == NULL))
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+
+#ifdef ENABLE_SSL3
+ if (vers->id == GNUTLS_SSL3) {
+ ret =
+ _gnutls_ssl3_finished(session,
+ session->
+ security_parameters.
+ entity, data, 1);
+ _mbuffer_set_udata_size(bufel, 36);
+ } else { /* TLS 1.0+ */
+#endif
+ ret = _gnutls_finished(session,
+ session->
+ security_parameters.entity,
+ data, 1);
+ _mbuffer_set_udata_size(bufel, 12);
+#ifdef ENABLE_SSL3
+ }
+#endif
+
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ vdata_size = _mbuffer_get_udata_size(bufel);
+
+ ret =
+ _gnutls_ext_sr_finished(session, data, vdata_size, 0);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ if ((!session->internals.resumed
+ && session->security_parameters.entity ==
+ GNUTLS_CLIENT)
+ || (session->internals.resumed
+ && session->security_parameters.entity ==
+ GNUTLS_SERVER)) {
+ /* if we are a client not resuming - or we are a server resuming */
+ _gnutls_handshake_log
+ ("HSK[%p]: recording tls-unique CB (send)\n",
+ session);
+ memcpy(session->internals.cb_tls_unique, data,
+ vdata_size);
+ session->internals.cb_tls_unique_len = vdata_size;
+ }
+
+ ret =
+ _gnutls_send_handshake(session, bufel,
+ GNUTLS_HANDSHAKE_FINISHED);
+ } else {
+ ret =
+ _gnutls_send_handshake(session, NULL,
+ GNUTLS_HANDSHAKE_FINISHED);
+ }
+
+ return ret;
+}
+
+/* This is to be called after sending our finished message. If everything
+ * went fine we have negotiated a secure connection
+ */
+int _gnutls_recv_finished(gnutls_session_t session)
+{
+ uint8_t data[MAX_VERIFY_DATA_SIZE], *vrfy;
+ gnutls_buffer_st buf;
+ int data_size;
+ int ret;
+ int vrfy_size;
+ const version_entry_st *vers = get_version(session);
+
+ if (unlikely(vers == NULL))
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+
+ ret =
+ _gnutls_recv_handshake(session, GNUTLS_HANDSHAKE_FINISHED,
+ 0, &buf);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ vrfy = buf.data;
+ vrfy_size = buf.length;
+
+#ifdef ENABLE_SSL3
+ if (vers->id == GNUTLS_SSL3)
+ data_size = 36;
+ else
+#endif
+ data_size = 12;
+
+ if (vrfy_size != data_size) {
+ gnutls_assert();
+ ret = GNUTLS_E_ERROR_IN_FINISHED_PACKET;
+ goto cleanup;
+ }
+
+#ifdef ENABLE_SSL3
+ if (vers->id == GNUTLS_SSL3) {
+ ret =
+ _gnutls_ssl3_finished(session,
+ (session->security_parameters.
+ entity + 1) % 2, data, 0);
+ } else /* TLS 1.0+ */
+#endif
+ ret =
+ _gnutls_finished(session,
+ (session->security_parameters.entity +
+ 1) % 2, data, 0);
+
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+#if defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION)
+ /* When fuzzying allow to proceed without verifying the handshake
+ * consistency */
+ (void) vrfy;
+# warning This is unsafe for production builds
+
+#else
+ if (memcmp(vrfy, data, data_size) != 0) {
+ gnutls_assert();
+ ret = GNUTLS_E_ERROR_IN_FINISHED_PACKET;
+ goto cleanup;
+ }
+#endif
+
+ ret = _gnutls_ext_sr_finished(session, data, data_size, 1);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ if ((session->internals.resumed
+ && session->security_parameters.entity == GNUTLS_CLIENT)
+ || (!session->internals.resumed
+ && session->security_parameters.entity == GNUTLS_SERVER)) {
+ /* if we are a client resuming - or we are a server not resuming */
+ _gnutls_handshake_log
+ ("HSK[%p]: recording tls-unique CB (recv)\n", session);
+ memcpy(session->internals.cb_tls_unique, data, data_size);
+ session->internals.cb_tls_unique_len = data_size;
+ }
+
+
+ cleanup:
+ _gnutls_buffer_clear(&buf);
+
+ return ret;
+}
+
+/* This selects the best supported ciphersuite from the given ones. Then
+ * it adds the suite to the session and performs some checks.
+ *
+ * When @scsv_only is non-zero only the available SCSVs are parsed
+ * and acted upon.
+ */
+int
+_gnutls_server_select_suite(gnutls_session_t session, uint8_t * data,
+ unsigned int datalen, unsigned scsv_only)
+{
+ int ret;
+ unsigned int i;
+ ciphersuite_list_st peer_clist;
+ const gnutls_cipher_suite_entry_st *selected;
+ gnutls_kx_algorithm_t kx;
+ int retval;
+ const version_entry_st *vers = get_version(session);
+
+ peer_clist.size = 0;
+
+ for (i = 0; i < datalen; i += 2) {
+ /* we support the TLS renegotiation SCSV, even if we are
+ * not under SSL 3.0, because openssl sends this SCSV
+ * on resumption unconditionally. */
+ /* TLS_RENEGO_PROTECTION_REQUEST = { 0x00, 0xff } */
+ if (session->internals.priorities->sr != SR_DISABLED &&
+ data[i] == GNUTLS_RENEGO_PROTECTION_REQUEST_MAJOR &&
+ data[i + 1] == GNUTLS_RENEGO_PROTECTION_REQUEST_MINOR) {
+ _gnutls_handshake_log
+ ("HSK[%p]: Received safe renegotiation CS\n",
+ session);
+ retval = _gnutls_ext_sr_recv_cs(session);
+ if (retval < 0) {
+ gnutls_assert();
+ return retval;
+ }
+ }
+
+ /* TLS_FALLBACK_SCSV */
+ if (data[i] == GNUTLS_FALLBACK_SCSV_MAJOR &&
+ data[i + 1] == GNUTLS_FALLBACK_SCSV_MINOR) {
+ const version_entry_st *max = _gnutls_version_max(session);
+
+ _gnutls_handshake_log
+ ("HSK[%p]: Received fallback CS\n",
+ session);
+
+ if (vers != max)
+ return gnutls_assert_val(GNUTLS_E_INAPPROPRIATE_FALLBACK);
+ } else if (!scsv_only) {
+ if (peer_clist.size < MAX_CIPHERSUITE_SIZE) {
+ peer_clist.entry[peer_clist.size] = ciphersuite_to_entry(&data[i]);
+ if (peer_clist.entry[peer_clist.size] != NULL)
+ peer_clist.size++;
+ }
+ }
+ }
+
+ if (scsv_only)
+ return 0;
+
+ ret = _gnutls_figure_common_ciphersuite(session, &peer_clist, &selected);
+ if (ret < 0) {
+ return gnutls_assert_val(ret);
+ }
+
+ _gnutls_handshake_log
+ ("HSK[%p]: Selected cipher suite: %s\n", session, selected->name);
+
+ ret = _gnutls_set_cipher_suite2(session, selected);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ if (!vers->tls13_sem) {
+ /* check if the credentials (username, public key etc.) are ok
+ */
+ kx = selected->kx_algorithm;
+ if (_gnutls_get_kx_cred(session, kx) == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INSUFFICIENT_CREDENTIALS;
+ }
+
+ /* set the mod_auth_st to the appropriate struct
+ * according to the KX algorithm. This is needed since all the
+ * handshake functions are read from there;
+ */
+ session->internals.auth_struct = _gnutls_kx_auth_struct(kx);
+ if (session->internals.auth_struct == NULL) {
+ _gnutls_handshake_log
+ ("HSK[%p]: Cannot find the appropriate handler for the KX algorithm\n",
+ session);
+ gnutls_assert();
+ return GNUTLS_E_INTERNAL_ERROR;
+ }
+ }
+
+ return 0;
+
+}
+
+
+/* This checks whether the null compression method is present.
+ */
+static int
+check_if_null_comp_present(gnutls_session_t session,
+ uint8_t * data, int datalen)
+{
+ int j;
+
+ for (j = 0; j < datalen; j++) {
+ if (data[j] == 0)
+ return 0;
+ }
+
+ /* we were not able to find a the NULL compression
+ * algorithm
+ */
+ gnutls_assert();
+ return GNUTLS_E_UNKNOWN_COMPRESSION_ALGORITHM;
+
+}
+
+/* This function sends an empty handshake packet. (like hello request).
+ * If the previous _gnutls_send_empty_handshake() returned
+ * GNUTLS_E_AGAIN or GNUTLS_E_INTERRUPTED, then it must be called again
+ * (until it returns ok), with NULL parameters.
+ */
+static int
+_gnutls_send_empty_handshake(gnutls_session_t session,
+ gnutls_handshake_description_t type,
+ int again)
+{
+ mbuffer_st *bufel;
+
+ if (again == 0) {
+ bufel = _gnutls_handshake_alloc(session, 0);
+ if (bufel == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_MEMORY_ERROR;
+ }
+ } else
+ bufel = NULL;
+
+ return _gnutls_send_handshake(session, bufel, type);
+}
+
+int _gnutls_call_hook_func(gnutls_session_t session,
+ gnutls_handshake_description_t type,
+ int post, unsigned incoming,
+ const uint8_t *data, unsigned data_size)
+{
+ gnutls_datum_t msg = {(void*)data, data_size};
+
+ if (session->internals.h_hook != NULL) {
+ if ((session->internals.h_type == type
+ || session->internals.h_type == GNUTLS_HANDSHAKE_ANY)
+ && (session->internals.h_post == post
+ || session->internals.h_post == GNUTLS_HOOK_BOTH)) {
+
+ /* internal API for testing: when we are expected to
+ * wait for GNUTLS_HANDSHAKE_CHANGE_CIPHER_SPEC, we
+ * do so, but not when doing for all messages. The
+ * reason is that change cipher specs are not handshake
+ * messages, and we don't support waiting for them
+ * consistently (only sending is tracked, not receiving).
+ */
+ if (type == GNUTLS_HANDSHAKE_CHANGE_CIPHER_SPEC &&
+ session->internals.h_type != GNUTLS_HANDSHAKE_CHANGE_CIPHER_SPEC)
+ return 0;
+
+ return session->internals.h_hook(session, type,
+ post, incoming, &msg);
+ }
+ }
+ return 0;
+}
+
+/* Note that the "New session ticket" handshake packet behaves differently under
+ * TLS 1.2 or 1.3. In 1.2 it is included in the handshake process, while in 1.3
+ * it is sent asynchronously */
+#define IS_ASYNC(t, v) \
+ (t == GNUTLS_HANDSHAKE_HELLO_REQUEST || t == GNUTLS_HANDSHAKE_KEY_UPDATE || \
+ (t == GNUTLS_HANDSHAKE_NEW_SESSION_TICKET && v->tls13_sem))
+
+int
+_gnutls_send_handshake(gnutls_session_t session, mbuffer_st * bufel,
+ gnutls_handshake_description_t type)
+{
+ return _gnutls_send_handshake2(session, bufel, type, 0);
+}
+
+/* This function sends a handshake message of type 'type' containing the
+ * data specified here. If the previous _gnutls_send_handshake() returned
+ * GNUTLS_E_AGAIN or GNUTLS_E_INTERRUPTED, then it must be called again
+ * (until it returns ok), with NULL parameters.
+ */
+int
+_gnutls_send_handshake2(gnutls_session_t session, mbuffer_st * bufel,
+ gnutls_handshake_description_t type, unsigned queue_only)
+{
+ int ret;
+ uint8_t *data;
+ uint32_t datasize, i_datasize;
+ int pos = 0;
+ const version_entry_st *vers = get_version(session);
+
+ if (bufel == NULL) {
+ /* we are resuming a previously interrupted
+ * send.
+ */
+ ret = _gnutls_handshake_io_write_flush(session);
+ return ret;
+
+ }
+
+ /* first run */
+ data = _mbuffer_get_uhead_ptr(bufel);
+ i_datasize = _mbuffer_get_udata_size(bufel);
+ datasize = i_datasize + _mbuffer_get_uhead_size(bufel);
+
+ data[pos++] = (uint8_t) REAL_HSK_TYPE(type);
+ _gnutls_write_uint24(_mbuffer_get_udata_size(bufel), &data[pos]);
+ pos += 3;
+
+ /* Add DTLS handshake fragment headers. The message will be
+ * fragmented later by the fragmentation sub-layer. All fields must
+ * be set properly for HMAC. The HMAC requires we pretend that the
+ * message was sent in a single fragment. */
+ if (IS_DTLS(session)) {
+ _gnutls_write_uint16(session->internals.dtls.
+ hsk_write_seq++, &data[pos]);
+ pos += 2;
+
+ /* Fragment offset */
+ _gnutls_write_uint24(0, &data[pos]);
+ pos += 3;
+
+ /* Fragment length */
+ _gnutls_write_uint24(i_datasize, &data[pos]);
+ /* pos += 3; */
+ }
+
+ _gnutls_handshake_log("HSK[%p]: %s was queued [%ld bytes]\n",
+ session, _gnutls_handshake2str(type),
+ (long) datasize);
+
+ /* Here we keep the handshake messages in order to hash them...
+ */
+ if (!IS_ASYNC(type, vers)) {
+ if ((ret =
+ handshake_hash_add_sent(session, type, data,
+ datasize)) < 0) {
+ gnutls_assert();
+ _mbuffer_xfree(&bufel);
+ return ret;
+ }
+ /* If we are sending a PSK, generate early secrets here.
+ * This cannot be done in pre_shared_key.c, because it
+ * relies on transcript hash of a Client Hello. */
+ if (type == GNUTLS_HANDSHAKE_CLIENT_HELLO &&
+ session->key.binders[0].prf != NULL) {
+ ret = _gnutls_generate_early_secrets_for_psk(session);
+ if (ret < 0) {
+ gnutls_assert();
+ _mbuffer_xfree(&bufel);
+ return ret;
+ }
+ }
+ }
+
+ ret = _gnutls_call_hook_func(session, type, GNUTLS_HOOK_PRE, 0,
+ _mbuffer_get_udata_ptr(bufel), _mbuffer_get_udata_size(bufel));
+ if (ret < 0) {
+ gnutls_assert();
+ _mbuffer_xfree(&bufel);
+ return ret;
+ }
+
+ session->internals.last_handshake_out = type;
+
+ ret = _gnutls_handshake_io_cache_int(session, type, bufel);
+ if (ret < 0) {
+ _mbuffer_xfree(&bufel);
+ gnutls_assert();
+ return ret;
+ }
+
+ ret = _gnutls_call_hook_func(session, type, GNUTLS_HOOK_POST, 0,
+ _mbuffer_get_udata_ptr(bufel), _mbuffer_get_udata_size(bufel));
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ if (queue_only)
+ return 0;
+
+ /* Decide when to cache and when to send */
+ if (vers && vers->tls13_sem) {
+
+ if (session->internals.initial_negotiation_completed) {
+ /* we are under TLS1.3 in a re-authentication phase.
+ * we don't attempt to cache any messages */
+ goto force_send;
+ }
+
+ /* The messages which are followed by another are not sent by default
+ * but are cached instead */
+ switch (type) {
+ case GNUTLS_HANDSHAKE_SERVER_HELLO: /* always followed by something */
+ case GNUTLS_HANDSHAKE_ENCRYPTED_EXTENSIONS: /* followed by finished or cert */
+ case GNUTLS_HANDSHAKE_CERTIFICATE_REQUEST: /* followed by certificate */
+ case GNUTLS_HANDSHAKE_CERTIFICATE_PKT: /* this one is followed by cert verify */
+ case GNUTLS_HANDSHAKE_COMPRESSED_CERTIFICATE_PKT: /* as above */
+ case GNUTLS_HANDSHAKE_CERTIFICATE_VERIFY: /* followed by finished */
+ ret = 0; /* cache */
+ break;
+ default:
+ /* send this and any cached messages */
+ goto force_send;
+ }
+ } else {
+ /* The messages which are followed by another are not sent by default
+ * but are cached instead */
+ switch (type) {
+ case GNUTLS_HANDSHAKE_CERTIFICATE_PKT: /* this one is followed by ServerHelloDone
+ * or ClientKeyExchange always.
+ */
+ case GNUTLS_HANDSHAKE_COMPRESSED_CERTIFICATE_PKT: /* as above */
+ case GNUTLS_HANDSHAKE_CERTIFICATE_STATUS:
+ case GNUTLS_HANDSHAKE_SERVER_KEY_EXCHANGE: /* as above */
+ case GNUTLS_HANDSHAKE_SERVER_HELLO: /* as above */
+ case GNUTLS_HANDSHAKE_CERTIFICATE_REQUEST: /* as above */
+ case GNUTLS_HANDSHAKE_NEW_SESSION_TICKET: /* followed by ChangeCipherSpec */
+
+ /* now for client Certificate, ClientKeyExchange and
+ * CertificateVerify are always followed by ChangeCipherSpec
+ */
+ case GNUTLS_HANDSHAKE_CERTIFICATE_VERIFY:
+ case GNUTLS_HANDSHAKE_CLIENT_KEY_EXCHANGE:
+ ret = 0;
+ break;
+ default:
+ /* send this and any cached messages */
+ goto force_send;
+ }
+ }
+
+ return ret;
+
+ force_send:
+ return _gnutls_handshake_io_write_flush(session);
+}
+
+#define CHECK_SIZE(ll) \
+ if ((session->internals.max_handshake_data_buffer_size > 0) && \
+ (((ll) + session->internals.handshake_hash_buffer.length) > \
+ session->internals.max_handshake_data_buffer_size)) { \
+ _gnutls_debug_log("Handshake buffer length is %u (max: %u)\n", (unsigned)((ll) + session->internals.handshake_hash_buffer.length), (unsigned)session->internals.max_handshake_data_buffer_size); \
+ return gnutls_assert_val(GNUTLS_E_HANDSHAKE_TOO_LARGE); \
+ }
+
+
+/* This function add the handshake headers and the
+ * handshake data to the handshake hash buffers. Needed
+ * for the finished messages calculations.
+ */
+static int
+handshake_hash_add_recvd(gnutls_session_t session,
+ gnutls_handshake_description_t recv_type,
+ uint8_t * header, uint16_t header_size,
+ uint8_t * dataptr, uint32_t datalen)
+{
+ int ret;
+ const version_entry_st *vers = get_version(session);
+
+ if (unlikely(vers == NULL))
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+
+ if ((vers->id != GNUTLS_DTLS0_9 &&
+ recv_type == GNUTLS_HANDSHAKE_HELLO_VERIFY_REQUEST) ||
+ IS_ASYNC(recv_type, vers))
+ return 0;
+
+ CHECK_SIZE(header_size + datalen);
+
+ session->internals.handshake_hash_buffer_prev_len =
+ session->internals.handshake_hash_buffer.length;
+
+ if (vers->id != 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,
+ dataptr, datalen);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+ }
+
+ /* save the size until client KX. That is because the TLS
+ * session hash is calculated up to this message.
+ */
+ if (recv_type == GNUTLS_HANDSHAKE_CLIENT_HELLO)
+ session->internals.handshake_hash_buffer_client_hello_len =
+ session->internals.handshake_hash_buffer.length;
+ if (recv_type == GNUTLS_HANDSHAKE_CLIENT_KEY_EXCHANGE)
+ session->internals.handshake_hash_buffer_client_kx_len =
+ session->internals.handshake_hash_buffer.length;
+ if (recv_type == GNUTLS_HANDSHAKE_FINISHED && session->security_parameters.entity == GNUTLS_CLIENT)
+ session->internals.handshake_hash_buffer_server_finished_len =
+ session->internals.handshake_hash_buffer.length;
+ if (recv_type == GNUTLS_HANDSHAKE_FINISHED && session->security_parameters.entity == GNUTLS_SERVER)
+ session->internals.handshake_hash_buffer_client_finished_len =
+ session->internals.handshake_hash_buffer.length;
+
+ return 0;
+}
+
+/* This function will store the handshake message we sent.
+ */
+static int
+handshake_hash_add_sent(gnutls_session_t session,
+ gnutls_handshake_description_t type,
+ uint8_t * dataptr, uint32_t datalen)
+{
+ int ret;
+ const version_entry_st *vers = get_version(session);
+
+ if (unlikely(vers == NULL))
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+
+ if (IS_ASYNC(type, vers))
+ return 0;
+
+ CHECK_SIZE(datalen);
+
+ if (vers->id == GNUTLS_DTLS0_9) {
+ /* Old DTLS doesn't include the header in the MAC */
+ if (datalen < 12) {
+ gnutls_assert();
+ return GNUTLS_E_INTERNAL_ERROR;
+ }
+ dataptr += 12;
+ datalen -= 12;
+
+ if (datalen == 0)
+ return 0;
+ }
+
+ ret =
+ _gnutls_buffer_append_data(&session->internals.
+ handshake_hash_buffer,
+ dataptr, datalen);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ if (type == GNUTLS_HANDSHAKE_CLIENT_HELLO)
+ session->internals.handshake_hash_buffer_client_hello_len =
+ session->internals.handshake_hash_buffer.length;
+ if (type == GNUTLS_HANDSHAKE_CLIENT_KEY_EXCHANGE)
+ session->internals.handshake_hash_buffer_client_kx_len =
+ session->internals.handshake_hash_buffer.length;
+ if (type == GNUTLS_HANDSHAKE_FINISHED && session->security_parameters.entity == GNUTLS_SERVER)
+ session->internals.handshake_hash_buffer_server_finished_len =
+ session->internals.handshake_hash_buffer.length;
+ if (type == GNUTLS_HANDSHAKE_FINISHED && session->security_parameters.entity == GNUTLS_CLIENT)
+ session->internals.handshake_hash_buffer_client_finished_len =
+ session->internals.handshake_hash_buffer.length;
+
+ return 0;
+}
+
+/* This function will receive handshake messages of the given types,
+ * and will pass the message to the right place in order to be processed.
+ * E.g. for the SERVER_HELLO message (if it is expected), it will be
+ * passed to _gnutls_recv_hello().
+ */
+int
+_gnutls_recv_handshake(gnutls_session_t session,
+ gnutls_handshake_description_t type,
+ unsigned int optional, gnutls_buffer_st * buf)
+{
+ int ret, ret2;
+ handshake_buffer_st hsk;
+
+ ret = _gnutls_handshake_io_recv_int(session, type, &hsk, optional);
+ if (ret < 0) {
+ if (optional != 0
+ && ret == GNUTLS_E_UNEXPECTED_HANDSHAKE_PACKET) {
+ if (buf)
+ _gnutls_buffer_init(buf);
+ return 0;
+ }
+
+ return gnutls_assert_val_fatal(ret);
+ }
+ session->internals.last_handshake_in = hsk.htype;
+
+ ret = _gnutls_call_hook_func(session, hsk.htype, GNUTLS_HOOK_PRE, 1, hsk.data.data, hsk.data.length);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = handshake_hash_add_recvd(session, hsk.rtype,
+ hsk.header, hsk.header_size,
+ hsk.data.data,
+ hsk.data.length);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ switch (hsk.htype) {
+ case GNUTLS_HANDSHAKE_CLIENT_HELLO_V2:
+ case GNUTLS_HANDSHAKE_CLIENT_HELLO:
+ if (!(IS_SERVER(session))) {
+ ret = gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET);
+ goto cleanup;
+ }
+
+#ifdef ENABLE_SSL2
+ if (hsk.htype == GNUTLS_HANDSHAKE_CLIENT_HELLO_V2)
+ ret =
+ _gnutls_read_client_hello_v2(session,
+ hsk.data.data,
+ hsk.data.length);
+ else
+#endif
+ {
+ /* Reference the full ClientHello in case an extension needs it */
+ ret = _gnutls_ext_set_full_client_hello(session, &hsk);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ ret = read_client_hello(session, hsk.data.data,
+ hsk.data.length);
+ }
+
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ break;
+
+ case GNUTLS_HANDSHAKE_SERVER_HELLO:
+ if (IS_SERVER(session)) {
+ ret = gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET);
+ goto cleanup;
+ }
+
+ ret = read_server_hello(session, hsk.data.data,
+ hsk.data.length);
+
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ break;
+ case GNUTLS_HANDSHAKE_HELLO_VERIFY_REQUEST:
+ if (IS_SERVER(session)) {
+ ret = gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET);
+ goto cleanup;
+ }
+
+ ret =
+ recv_hello_verify_request(session,
+ hsk.data.data,
+ hsk.data.length);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ } else {
+ /* Signal our caller we have received a verification cookie
+ and ClientHello needs to be sent again. */
+ ret = 1;
+ }
+
+ break;
+ case GNUTLS_HANDSHAKE_HELLO_RETRY_REQUEST: {
+ /* hash buffer synth message is generated during hello retry parsing */
+ gnutls_datum_t hrr = {hsk.data.data, hsk.data.length};
+
+ if (IS_SERVER(session)) {
+ ret = gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET);
+ goto cleanup;
+ }
+
+ ret =
+ _gnutls13_recv_hello_retry_request(session,
+ &hsk.data);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ } else {
+ /* during hello retry parsing, we reset handshake hash buffer,
+ * re-add this message */
+ ret = handshake_hash_add_recvd(session, hsk.htype,
+ hsk.header, hsk.header_size,
+ hrr.data,
+ hrr.size);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ /* Signal our caller we have received a retry request
+ and ClientHello needs to be sent again. */
+ ret = 1;
+ }
+
+ break;
+ }
+ case GNUTLS_HANDSHAKE_SERVER_HELLO_DONE:
+ if (hsk.data.length == 0)
+ ret = 0;
+ else {
+ gnutls_assert();
+ ret = GNUTLS_E_UNEXPECTED_PACKET_LENGTH;
+ goto cleanup;
+ }
+ break;
+ case GNUTLS_HANDSHAKE_CERTIFICATE_PKT:
+ case GNUTLS_HANDSHAKE_COMPRESSED_CERTIFICATE_PKT:
+ case GNUTLS_HANDSHAKE_CERTIFICATE_STATUS:
+ case GNUTLS_HANDSHAKE_FINISHED:
+ case GNUTLS_HANDSHAKE_ENCRYPTED_EXTENSIONS:
+ case GNUTLS_HANDSHAKE_SERVER_KEY_EXCHANGE:
+ case GNUTLS_HANDSHAKE_CLIENT_KEY_EXCHANGE:
+ case GNUTLS_HANDSHAKE_CERTIFICATE_REQUEST:
+ case GNUTLS_HANDSHAKE_CERTIFICATE_VERIFY:
+ case GNUTLS_HANDSHAKE_SUPPLEMENTAL:
+ case GNUTLS_HANDSHAKE_NEW_SESSION_TICKET:
+ case GNUTLS_HANDSHAKE_END_OF_EARLY_DATA:
+ ret = hsk.data.length;
+ break;
+ default:
+ gnutls_assert();
+ /* we shouldn't actually arrive here in any case .
+ * unexpected messages should be caught after _gnutls_handshake_io_recv_int()
+ */
+ ret = GNUTLS_E_UNEXPECTED_HANDSHAKE_PACKET;
+ goto cleanup;
+ }
+
+ ret2 = _gnutls_call_hook_func(session, hsk.htype, GNUTLS_HOOK_POST, 1, hsk.data.data, hsk.data.length);
+ if (ret2 < 0) {
+ ret = ret2;
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ if (buf) {
+ *buf = hsk.data;
+ return ret;
+ }
+
+ cleanup:
+ _gnutls_handshake_buffer_clear(&hsk);
+ return ret;
+}
+
+/* This function checks if the given cipher suite is supported, and sets it
+ * to the session;
+ */
+static int
+set_client_ciphersuite(gnutls_session_t session, uint8_t suite[2])
+{
+ unsigned j;
+ int ret;
+ const gnutls_cipher_suite_entry_st *selected = NULL;
+ const version_entry_st *vers = get_version(session);
+ gnutls_kx_algorithm_t kx;
+
+ for (j = 0; j < session->internals.priorities->cs.size; j++) {
+ if (suite[0] == session->internals.priorities->cs.entry[j]->id[0] &&
+ suite[1] == session->internals.priorities->cs.entry[j]->id[1]) {
+ selected = session->internals.priorities->cs.entry[j];
+ break;
+ }
+ }
+
+ if (!selected) {
+ gnutls_assert();
+ _gnutls_handshake_log
+ ("HSK[%p]: unsupported cipher suite %.2X.%.2X was negotiated\n",
+ session, (unsigned int) suite[0],
+ (unsigned int) suite[1]);
+ return GNUTLS_E_UNKNOWN_CIPHER_SUITE;
+ }
+
+ ret = _gnutls_set_cipher_suite2(session, selected);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ _gnutls_handshake_log("HSK[%p]: Selected cipher suite: %s\n",
+ session,
+ selected->name);
+
+ /* check if the credentials (username, public key etc.) are ok.
+ * Actually checks if they exist.
+ */
+ if (!vers->tls13_sem) {
+ kx = selected->kx_algorithm;
+
+ if (!session->internals.premaster_set &&
+ _gnutls_get_kx_cred
+ (session, kx) == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INSUFFICIENT_CREDENTIALS;
+ }
+
+ /* set the mod_auth_st to the appropriate struct
+ * according to the KX algorithm. This is needed since all the
+ * handshake functions are read from there;
+ */
+ session->internals.auth_struct =
+ _gnutls_kx_auth_struct(kx);
+
+ if (session->internals.auth_struct == NULL) {
+ _gnutls_handshake_log
+ ("HSK[%p]: Cannot find the appropriate handler for the KX algorithm\n",
+ session);
+ gnutls_assert();
+ return GNUTLS_E_INTERNAL_ERROR;
+ }
+ } else {
+ if (session->internals.hsk_flags & HSK_PSK_SELECTED) {
+ if (session->key.binders[0].prf->id != selected->prf) {
+ _gnutls_handshake_log
+ ("HSK[%p]: PRF of ciphersuite differs with the PSK identity (cs: %s, id: %s)\n",
+ session, selected->name, session->key.binders[0].prf->name);
+ gnutls_assert();
+ return GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/* This function returns 0 if we are resuming a session or -1 otherwise.
+ * This also sets the variables in the session. Used only while reading a server
+ * hello. Only applicable to TLS1.2 or earlier.
+ */
+static int
+client_check_if_resuming(gnutls_session_t session,
+ uint8_t * session_id, int session_id_len)
+{
+ char buf[2 * GNUTLS_MAX_SESSION_ID_SIZE + 1];
+ int ret;
+
+ _gnutls_handshake_log("HSK[%p]: SessionID length: %d\n", session,
+ session_id_len);
+ _gnutls_handshake_log("HSK[%p]: SessionID: %s\n", session,
+ _gnutls_bin2hex(session_id, session_id_len,
+ buf, sizeof(buf), NULL));
+
+ if ((session->internals.resumption_requested != 0 ||
+ session->internals.premaster_set != 0) &&
+ session_id_len > 0 &&
+ session->internals.resumed_security_parameters.
+ session_id_size == session_id_len
+ && memcmp(session_id,
+ session->internals.resumed_security_parameters.
+ session_id, session_id_len) == 0) {
+ /* resume session */
+ memcpy(session->internals.resumed_security_parameters.
+ server_random,
+ session->security_parameters.server_random,
+ GNUTLS_RANDOM_SIZE);
+ memcpy(session->internals.resumed_security_parameters.
+ client_random,
+ session->security_parameters.client_random,
+ GNUTLS_RANDOM_SIZE);
+
+ ret = _gnutls_set_cipher_suite2
+ (session,
+ session->internals.resumed_security_parameters.
+ cs);
+ if (ret < 0) {
+ gnutls_assert();
+ goto no_resume;
+ }
+
+ session->internals.resumed = true; /* we are resuming */
+
+ return 0;
+ } else {
+no_resume:
+ /* keep the new session id */
+ session->internals.resumed = false; /* we are not resuming */
+ return -1;
+ }
+}
+
+
+/* This function reads and parses the server hello handshake message.
+ * This function also restores resumed parameters if we are resuming a
+ * session.
+ */
+static int
+read_server_hello(gnutls_session_t session,
+ uint8_t * data, int datalen)
+{
+ uint8_t session_id_len = 0;
+ uint8_t *session_id;
+ uint8_t *cs_pos, *comp_pos, *srandom_pos;
+ uint8_t major, minor;
+ int pos = 0;
+ int ret;
+ int len = datalen;
+ unsigned ext_parse_flag = 0;
+ const version_entry_st *vers, *saved_vers;
+
+ if (datalen < GNUTLS_RANDOM_SIZE+2) {
+ gnutls_assert();
+ return GNUTLS_E_UNEXPECTED_PACKET_LENGTH;
+ }
+
+ _gnutls_handshake_log("HSK[%p]: Server's version: %d.%d\n",
+ session, data[pos], data[pos + 1]);
+
+ DECR_LEN(len, 2);
+ major = data[pos];
+ minor = data[pos+1];
+
+ saved_vers = get_version(session); /* will be non-null if HRR has been received */
+
+ vers = nversion_to_entry(major, minor);
+ if (unlikely(vers == NULL))
+ return gnutls_assert_val(GNUTLS_E_UNSUPPORTED_VERSION_PACKET);
+
+ if (vers->tls13_sem) /* that shouldn't have been negotiated here */
+ return gnutls_assert_val(GNUTLS_E_UNSUPPORTED_VERSION_PACKET);
+
+ if (_gnutls_set_current_version(session, vers->id) < 0)
+ return gnutls_assert_val(GNUTLS_E_UNSUPPORTED_VERSION_PACKET);
+
+ pos += 2;
+
+ DECR_LEN(len, GNUTLS_RANDOM_SIZE);
+ srandom_pos = &data[pos];
+ pos += GNUTLS_RANDOM_SIZE;
+
+ /* Read session ID
+ */
+ DECR_LEN(len, 1);
+ session_id_len = data[pos++];
+
+ if (len < session_id_len || session_id_len > GNUTLS_MAX_SESSION_ID_SIZE) {
+ gnutls_assert();
+ return GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER;
+ }
+ DECR_LEN(len, session_id_len);
+ session_id = &data[pos];
+ pos += session_id_len;
+
+ DECR_LEN(len, 2);
+ cs_pos = &data[pos];
+ pos += 2;
+
+ /* move to compression
+ */
+ DECR_LEN(len, 1);
+ comp_pos = &data[pos];
+ pos++;
+
+ /* parse extensions to figure version */
+ ret =
+ _gnutls_parse_hello_extensions(session, GNUTLS_EXT_FLAG_TLS12_SERVER_HELLO|
+ GNUTLS_EXT_FLAG_TLS13_SERVER_HELLO,
+ GNUTLS_EXT_VERSION_NEG,
+ &data[pos], len);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ vers = get_version(session);
+ if (unlikely(vers == NULL))
+ return gnutls_assert_val(GNUTLS_E_UNSUPPORTED_VERSION_PACKET);
+ if (vers->tls13_sem) {
+ if (major != 0x03 || minor != 0x03)
+ return gnutls_assert_val(GNUTLS_E_UNSUPPORTED_VERSION_PACKET);
+ }
+
+ if (_gnutls_nversion_is_supported(session, vers->major, vers->minor) == 0)
+ return gnutls_assert_val(GNUTLS_E_UNSUPPORTED_VERSION_PACKET);
+
+ /* set server random - done after final version is selected */
+ ret = _gnutls_set_server_random(session, vers, srandom_pos);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ /* reset keys and binders if we are not using TLS 1.3 */
+ if (!vers->tls13_sem) {
+ gnutls_memset(&session->key.proto.tls13, 0,
+ sizeof(session->key.proto.tls13));
+ reset_binders(session);
+ }
+
+ /* check if we are resuming and set the appropriate
+ * values;
+ */
+ if (!vers->tls13_sem &&
+ client_check_if_resuming(session, session_id, session_id_len) == 0) {
+ ret =
+ _gnutls_parse_hello_extensions(session, GNUTLS_EXT_FLAG_TLS12_SERVER_HELLO,
+ GNUTLS_EXT_MANDATORY,
+ &data[pos], len);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ return 0;
+ } else {
+ session->security_parameters.session_id_size = session_id_len;
+ if (session_id_len > 0)
+ memcpy(session->security_parameters.session_id, session_id,
+ session_id_len);
+ }
+
+ /* Check if the given cipher suite is supported and copy
+ * it to the session.
+ */
+ ret = set_client_ciphersuite(session, cs_pos);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ if (session->internals.hsk_flags & HSK_HRR_RECEIVED) {
+ /* check if ciphersuite matches */
+ if (memcmp(cs_pos, session->internals.hrr_cs, 2) != 0)
+ return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER);
+
+ /* check if HRR version matches this version */
+ if (vers != saved_vers)
+ return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER);
+ }
+
+ if (*comp_pos != 0)
+ return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER);
+
+ if (vers->tls13_sem)
+ ext_parse_flag |= GNUTLS_EXT_FLAG_TLS13_SERVER_HELLO;
+ else
+ ext_parse_flag |= GNUTLS_EXT_FLAG_TLS12_SERVER_HELLO;
+
+ /* Parse extensions in order.
+ */
+ ret =
+ _gnutls_parse_hello_extensions(session,
+ ext_parse_flag,
+ GNUTLS_EXT_MANDATORY,
+ &data[pos], len);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ /* check if EtM is required */
+ if (!vers->tls13_sem && session->internals.priorities->force_etm && !session->security_parameters.etm) {
+ const cipher_entry_st *cipher = cipher_to_entry(session->security_parameters.cs->block_algorithm);
+ if (_gnutls_cipher_type(cipher) == CIPHER_BLOCK)
+ return gnutls_assert_val(GNUTLS_E_UNWANTED_ALGORITHM);
+ }
+
+
+ ret =
+ _gnutls_parse_hello_extensions(session,
+ ext_parse_flag,
+ GNUTLS_EXT_APPLICATION,
+ &data[pos], len);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ ret =
+ _gnutls_parse_hello_extensions(session,
+ ext_parse_flag,
+ GNUTLS_EXT_TLS,
+ &data[pos], len);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ ret =
+ _gnutls_parse_hello_extensions(session,
+ ext_parse_flag,
+ _GNUTLS_EXT_TLS_POST_CS,
+ &data[pos], len);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ /* Calculate TLS 1.3 Early Secret */
+ if (vers->tls13_sem &&
+ !(session->internals.hsk_flags & HSK_PSK_SELECTED)) {
+ ret = _tls13_init_secret(session, NULL, 0);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+ }
+
+ ret = set_auth_types(session);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ session->internals.hsk_flags |= HSK_SERVER_HELLO_RECEIVED;
+
+ return 0;
+}
+
+/* This function copies the appropriate compression methods, to a locally allocated buffer
+ * Needed in hello messages. Returns the new data length.
+ */
+static int
+append_null_comp(gnutls_session_t session,
+ gnutls_buffer_st * cdata)
+{
+ uint8_t compression_methods[2] = {0x01, 0x00};
+ size_t init_length = cdata->length;
+ int ret;
+
+ ret =
+ _gnutls_buffer_append_data(cdata, compression_methods, 2);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ ret = cdata->length - init_length;
+
+ return ret;
+}
+
+/* This function sends the client hello handshake message.
+ */
+static int send_client_hello(gnutls_session_t session, int again)
+{
+ mbuffer_st *bufel = NULL;
+ int type;
+ int ret = 0;
+ const version_entry_st *hver, *min_ver, *max_ver;
+ uint8_t tver[2];
+ gnutls_buffer_st extdata;
+ bool rehandshake = false;
+ bool resuming = false;
+ unsigned add_sr_scsv = 0;
+ uint8_t *session_id =
+ session->internals.resumed_security_parameters.session_id;
+ uint8_t session_id_len =
+ session->internals.resumed_security_parameters.session_id_size;
+
+ if (again == 0) {
+ /* note that rehandshake is different than resuming
+ */
+ if (session->internals.initial_negotiation_completed)
+ rehandshake = true;
+
+ ret = _gnutls_buffer_init_handshake_mbuffer(&extdata);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ /* if we are resuming a session then we set the
+ * version number to the previously established.
+ */
+ if (session->internals.resumption_requested == 0 &&
+ session->internals.premaster_set == 0) {
+ if (rehandshake) /* already negotiated version thus version_max == negotiated version */
+ hver = get_version(session);
+ else /* new handshake. just get the max */
+ hver = _gnutls_legacy_version_max(session);
+ } else {
+ /* we are resuming a session */
+ resuming = true;
+
+ hver =
+ session->internals.resumed_security_parameters.
+ pversion;
+
+ if (hver && hver->tls13_sem)
+ hver = _gnutls_legacy_version_max(session);
+ }
+
+ if (hver == NULL) {
+ gnutls_assert();
+ if (session->internals.flags & INT_FLAG_NO_TLS13)
+ ret = GNUTLS_E_INSUFFICIENT_CREDENTIALS;
+ else
+ ret = GNUTLS_E_NO_PRIORITIES_WERE_SET;
+ goto cleanup;
+ }
+
+ if (unlikely(session->internals.default_hello_version[0] != 0)) {
+ tver[0] = session->internals.default_hello_version[0];
+ tver[1] = session->internals.default_hello_version[1];
+ } else {
+ tver[0] = hver->major;
+ tver[1] = hver->minor;
+ }
+ ret = _gnutls_buffer_append_data(&extdata, tver, 2);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ _gnutls_handshake_log("HSK[%p]: Adv. version: %u.%u\n", session,
+ (unsigned)tver[0], (unsigned)tver[1]);
+
+ min_ver = _gnutls_version_lowest(session);
+ max_ver = _gnutls_version_max(session);
+ if (min_ver == NULL || max_ver == NULL) {
+ gnutls_assert();
+ ret = GNUTLS_E_NO_PRIORITIES_WERE_SET;
+ goto cleanup;
+ }
+
+ /* if we are replying to an HRR the version is already negotiated */
+ if (!(session->internals.hsk_flags & HSK_HRR_RECEIVED) || !get_version(session)) {
+ /* Set the version we advertized as maximum
+ * (RSA uses it). */
+ set_adv_version(session, hver->major, hver->minor);
+ if (_gnutls_set_current_version(session, hver->id) < 0) {
+ ret = gnutls_assert_val(GNUTLS_E_UNSUPPORTED_VERSION_PACKET);
+ goto cleanup;
+ }
+ }
+
+ if (session->internals.priorities->min_record_version != 0) {
+ /* Advertise the lowest supported (SSL 3.0) record packet
+ * version in record packets during the handshake.
+ * That is to avoid confusing implementations
+ * that do not support TLS 1.2 and don't know
+ * how 3,3 version of record packets look like.
+ */
+ set_default_version(session, min_ver);
+ } else {
+ set_default_version(session, hver);
+ }
+
+ /* In order to know when this session was initiated.
+ */
+ session->security_parameters.timestamp = gnutls_time(NULL);
+
+ /* Generate random data
+ */
+ if (!(session->internals.hsk_flags & HSK_HRR_RECEIVED) &&
+ !(IS_DTLS(session) && session->internals.dtls.hsk_hello_verify_requests != 0)) {
+ ret = _gnutls_gen_client_random(session);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ }
+
+ ret = _gnutls_buffer_append_data(&extdata,
+ session->security_parameters.client_random,
+ GNUTLS_RANDOM_SIZE);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+#ifdef TLS13_APPENDIX_D4
+ if (max_ver->tls13_sem &&
+ session->internals.priorities->tls13_compat_mode &&
+ !resuming) {
+ if (!(session->internals.hsk_flags & HSK_HRR_RECEIVED)) {
+ /* Under TLS1.3 we generate a random session ID to make
+ * the TLS1.3 session look like a resumed TLS1.2 session */
+ ret = _gnutls_generate_session_id(session->security_parameters.
+ session_id,
+ &session->security_parameters.
+ session_id_size);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ }
+ session_id = session->security_parameters.session_id;
+ session_id_len = session->security_parameters.session_id_size;
+ }
+#endif
+
+ /* Copy the Session ID - if any
+ */
+ ret = _gnutls_buffer_append_data_prefix(&extdata, 8,
+ session_id,
+ session_id_len);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ /* Copy the DTLS cookie
+ */
+ if (IS_DTLS(session)) {
+ ret = _gnutls_buffer_append_data_prefix(&extdata, 8,
+ session->internals.dtls.dcookie.data,
+ session->internals.dtls.dcookie.size);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ _gnutls_free_datum(&session->internals.dtls.dcookie);
+ }
+
+ /* Copy the ciphersuites.
+ */
+#ifdef ENABLE_SSL3
+ /* If using SSLv3 Send TLS_RENEGO_PROTECTION_REQUEST SCSV for MITM
+ * prevention on initial negotiation (but not renegotiation; that's
+ * handled with the RI extension below).
+ */
+ if (!session->internals.initial_negotiation_completed &&
+ session->security_parameters.entity == GNUTLS_CLIENT &&
+ (hver->id == GNUTLS_SSL3 &&
+ session->internals.priorities->no_extensions != 0)) {
+ add_sr_scsv = 1;
+ }
+#endif
+ ret = _gnutls_get_client_ciphersuites(session, &extdata, min_ver, add_sr_scsv);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ /* Copy the compression methods.
+ */
+ ret = append_null_comp(session, &extdata);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ /* Generate and copy TLS extensions.
+ */
+ if (session->internals.priorities->no_extensions == 0) {
+ if (_gnutls_version_has_extensions(hver)) {
+ type = GNUTLS_EXT_ANY;
+ } else {
+ type = GNUTLS_EXT_MANDATORY;
+ }
+
+ ret =
+ _gnutls_gen_hello_extensions(session, &extdata,
+ GNUTLS_EXT_FLAG_CLIENT_HELLO,
+ type);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ }
+
+ bufel = _gnutls_buffer_to_mbuffer(&extdata);
+ }
+
+ ret = _gnutls_send_handshake(session, bufel,
+ GNUTLS_HANDSHAKE_CLIENT_HELLO);
+
+ if (session->internals.hsk_flags & HSK_EARLY_DATA_IN_FLIGHT) {
+ const cipher_entry_st *ce;
+ const mac_entry_st *me;
+ record_parameters_st *params;
+
+ ce = cipher_to_entry(session->internals.
+ resumed_security_parameters.
+ cs->block_algorithm);
+ me = mac_to_entry(session->internals.
+ resumed_security_parameters.
+ cs->mac_algorithm);
+
+ ret = _gnutls_epoch_get(session, EPOCH_NEXT, &params);
+ if (ret < 0) {
+ return gnutls_assert_val(ret);
+ }
+
+ params->cipher = ce;
+ params->mac = me;
+
+ ret = _tls13_write_connection_state_init(session, STAGE_EARLY);
+ if (ret < 0) {
+ return gnutls_assert_val(ret);
+ }
+
+ _gnutls_epoch_bump(session);
+ ret = _gnutls_epoch_dup(session, EPOCH_WRITE_CURRENT);
+ if (ret < 0) {
+ return gnutls_assert_val(ret);
+ }
+
+ ret = _gnutls13_send_early_data(session);
+ if (ret < 0) {
+ return gnutls_assert_val(ret);
+ }
+ }
+
+ return ret;
+
+ cleanup:
+ _gnutls_buffer_clear(&extdata);
+ return ret;
+}
+
+int _gnutls_send_server_hello(gnutls_session_t session, int again)
+{
+ mbuffer_st *bufel = NULL;
+ gnutls_buffer_st buf;
+ int ret;
+ uint8_t session_id_len =
+ session->security_parameters.session_id_size;
+ char tmpbuf[2 * GNUTLS_MAX_SESSION_ID_SIZE + 1];
+ const version_entry_st *vers;
+ uint8_t vbytes[2];
+ unsigned extflag = 0;
+ gnutls_ext_parse_type_t etype;
+
+ _gnutls_buffer_init(&buf);
+
+ if (again == 0) {
+ vers = get_version(session);
+ if (unlikely(vers == NULL || session->security_parameters.cs == NULL))
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+
+ if (vers->tls13_sem) {
+ vbytes[0] = 0x03; /* TLS1.2 */
+ vbytes[1] = 0x03;
+ extflag |= GNUTLS_EXT_FLAG_TLS13_SERVER_HELLO;
+ } else {
+ vbytes[0] = vers->major;
+ vbytes[1] = vers->minor;
+ extflag |= GNUTLS_EXT_FLAG_TLS12_SERVER_HELLO;
+ }
+
+ ret = _gnutls_buffer_init_handshake_mbuffer(&buf);
+ if (ret < 0) {
+ gnutls_assert();
+ goto fail;
+ }
+
+ ret = _gnutls_buffer_append_data(&buf, vbytes, 2);
+ if (ret < 0) {
+ gnutls_assert();
+ goto fail;
+ }
+
+ ret = _gnutls_buffer_append_data(&buf,
+ session->security_parameters.server_random,
+ GNUTLS_RANDOM_SIZE);
+ if (ret < 0) {
+ gnutls_assert();
+ goto fail;
+ }
+
+ ret = _gnutls_buffer_append_data_prefix(&buf, 8,
+ session->security_parameters.session_id,
+ session_id_len);
+ if (ret < 0) {
+ gnutls_assert();
+ goto fail;
+ }
+
+ _gnutls_handshake_log("HSK[%p]: SessionID: %s\n", session,
+ _gnutls_bin2hex(session->
+ security_parameters.session_id,
+ session_id_len, tmpbuf,
+ sizeof(tmpbuf), NULL));
+
+ ret = _gnutls_buffer_append_data(&buf,
+ session->security_parameters.cs->id,
+ 2);
+ if (ret < 0) {
+ gnutls_assert();
+ goto fail;
+ }
+
+ /* compression */
+ ret = _gnutls_buffer_append_prefix(&buf, 8, 0);
+ if (ret < 0) {
+ gnutls_assert();
+ goto fail;
+ }
+
+ if (!vers->tls13_sem && session->internals.resumed)
+ etype = GNUTLS_EXT_MANDATORY;
+ else
+ etype = GNUTLS_EXT_ANY;
+ ret =
+ _gnutls_gen_hello_extensions(session, &buf, extflag, etype);
+ if (ret < 0) {
+ gnutls_assert();
+ goto fail;
+ }
+
+ if (vers->tls13_sem) {
+ /* Under TLS1.3, the session ID is used for different purposes than
+ * the TLS1.0 session ID. Ensure that there is an internally set
+ * value which the server will see on the original and resumed sessions */
+ ret = _gnutls_generate_session_id(session->security_parameters.
+ session_id,
+ &session->security_parameters.
+ session_id_size);
+ if (ret < 0) {
+ gnutls_assert();
+ goto fail;
+ }
+ }
+
+ bufel = _gnutls_buffer_to_mbuffer(&buf);
+ }
+
+ ret =
+ _gnutls_send_handshake(session, bufel,
+ GNUTLS_HANDSHAKE_SERVER_HELLO);
+
+fail:
+ _gnutls_buffer_clear(&buf);
+ return ret;
+}
+
+static int
+recv_hello_verify_request(gnutls_session_t session,
+ uint8_t * data, int datalen)
+{
+ ssize_t len = datalen;
+ size_t pos = 0;
+ uint8_t cookie_len;
+ unsigned int nb_verifs;
+ int ret;
+
+ if (!IS_DTLS(session)) {
+ gnutls_assert();
+ return GNUTLS_E_UNEXPECTED_PACKET;
+ }
+
+ nb_verifs = ++session->internals.dtls.hsk_hello_verify_requests;
+ if (nb_verifs >= MAX_HANDSHAKE_HELLO_VERIFY_REQUESTS) {
+ /* The server is either buggy, malicious or changing cookie
+ secrets _way_ too fast. */
+ gnutls_assert();
+ return GNUTLS_E_UNEXPECTED_PACKET;
+ }
+
+ DECR_LEN(len, 2);
+ pos += 2;
+
+ DECR_LEN(len, 1);
+ cookie_len = data[pos];
+ pos++;
+
+ if (cookie_len > DTLS_MAX_COOKIE_SIZE) {
+ gnutls_assert();
+ return GNUTLS_E_UNEXPECTED_PACKET_LENGTH;
+ }
+
+ DECR_LEN(len, cookie_len);
+
+ gnutls_free(session->internals.dtls.dcookie.data);
+ ret = _gnutls_set_datum(&session->internals.dtls.dcookie, &data[pos], cookie_len);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ if (len != 0) {
+ gnutls_assert();
+ return GNUTLS_E_UNEXPECTED_PACKET_LENGTH;
+ }
+
+ /* reset handshake hash buffers */
+ handshake_hash_buffer_reset(session);
+ /* reset extensions used in previous hello */
+ session->internals.used_exts = 0;
+
+ return 0;
+}
+
+/* The packets in gnutls_handshake (it's more broad than original TLS handshake)
+ *
+ * Client Server
+ *
+ * ClientHello -------->
+ * <-------- ServerHello
+ *
+ * Certificate*
+ * ServerKeyExchange*
+ * <-------- CertificateRequest*
+ *
+ * <-------- ServerHelloDone
+ * Certificate*
+ * ClientKeyExchange
+ * CertificateVerify*
+ * [ChangeCipherSpec]
+ * Finished -------->
+ * NewSessionTicket
+ * [ChangeCipherSpec]
+ * <-------- Finished
+ *
+ * (*): means optional packet.
+ */
+
+/* Handshake when resumming session:
+ * Client Server
+ *
+ * ClientHello -------->
+ * ServerHello
+ * [ChangeCipherSpec]
+ * <-------- Finished
+ * [ChangeCipherSpec]
+ * Finished -------->
+ *
+ */
+
+/**
+ * gnutls_rehandshake:
+ * @session: is a #gnutls_session_t type.
+ *
+ * This function can only be called in server side, and
+ * instructs a TLS 1.2 or earlier client to renegotiate
+ * parameters (perform a handshake), by sending a
+ * hello request message.
+ *
+ * If this function succeeds, the calling application
+ * should call gnutls_record_recv() until %GNUTLS_E_REHANDSHAKE
+ * is returned to clear any pending data. If the %GNUTLS_E_REHANDSHAKE
+ * error code is not seen, then the handshake request was
+ * not followed by the peer (the TLS protocol does not require
+ * the client to do, and such compliance should be handled
+ * by the application protocol).
+ *
+ * Once the %GNUTLS_E_REHANDSHAKE error code is seen, the
+ * calling application should proceed to calling
+ * gnutls_handshake() to negotiate the new
+ * parameters.
+ *
+ * If the client does not wish to renegotiate parameters he
+ * may reply with an alert message, and in that case the return code seen
+ * by subsequent gnutls_record_recv() will be
+ * %GNUTLS_E_WARNING_ALERT_RECEIVED with the specific alert being
+ * %GNUTLS_A_NO_RENEGOTIATION. A client may also choose to ignore
+ * this request.
+ *
+ * Under TLS 1.3 this function is equivalent to gnutls_session_key_update()
+ * with the %GNUTLS_KU_PEER flag. In that case subsequent calls to
+ * gnutls_record_recv() will not return %GNUTLS_E_REHANDSHAKE, and
+ * calls to gnutls_handshake() in server side are a no-op.
+ *
+ * This function always fails with %GNUTLS_E_INVALID_REQUEST when
+ * called in client side.
+ *
+ * Returns: %GNUTLS_E_SUCCESS on success, otherwise a negative error code.
+ **/
+int gnutls_rehandshake(gnutls_session_t session)
+{
+ int ret;
+ const version_entry_st *vers = get_version(session);
+
+ /* only server sends that handshake packet */
+ if (session->security_parameters.entity == GNUTLS_CLIENT)
+ return GNUTLS_E_INVALID_REQUEST;
+
+ if (vers->tls13_sem) {
+ return gnutls_session_key_update(session, GNUTLS_KU_PEER);
+ }
+
+ _dtls_async_timer_delete(session);
+
+ ret =
+ _gnutls_send_empty_handshake(session,
+ GNUTLS_HANDSHAKE_HELLO_REQUEST,
+ AGAIN(STATE50));
+ STATE = STATE50;
+
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+ STATE = STATE0;
+
+ return 0;
+}
+
+/* This function checks whether the error code should be treated fatal
+ * or not, and also does the necessary state transition. In
+ * particular, in the case of a rehandshake abort it resets the
+ * handshake's internal state.
+ */
+inline static int
+_gnutls_abort_handshake(gnutls_session_t session, int ret)
+{
+ switch (ret) {
+ case GNUTLS_E_WARNING_ALERT_RECEIVED:
+ if (gnutls_alert_get(session) == GNUTLS_A_NO_RENEGOTIATION) {
+ /* The server always toleretes a "no_renegotiation" alert. */
+ if (session->security_parameters.entity == GNUTLS_SERVER) {
+ STATE = STATE0;
+ return ret;
+ }
+
+ /* The client should tolerete a "no_renegotiation" alert only if:
+ * - the initial handshake has completed, or
+ * - a Server Hello is not yet received
+ */
+ if (session->internals.initial_negotiation_completed ||
+ !(session->internals.hsk_flags & HSK_SERVER_HELLO_RECEIVED)) {
+ STATE = STATE0;
+ return ret;
+ }
+
+ return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET);
+ }
+ return ret;
+ case GNUTLS_E_GOT_APPLICATION_DATA:
+ STATE = STATE0;
+ return ret;
+ default:
+ return ret;
+ }
+}
+
+
+static int _gnutls_send_supplemental(gnutls_session_t session, int again)
+{
+ mbuffer_st *bufel = NULL;
+ int ret = 0;
+
+ _gnutls_debug_log("EXT[%p]: Sending supplemental data\n", session);
+
+ if (!again) {
+ gnutls_buffer_st buf;
+ ret = _gnutls_buffer_init_handshake_mbuffer(&buf);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ ret = _gnutls_gen_supplemental(session, &buf);
+ if (ret < 0) {
+ gnutls_assert();
+ _gnutls_buffer_clear(&buf);
+ return ret;
+ }
+
+ bufel = _gnutls_buffer_to_mbuffer(&buf);
+ }
+
+ return _gnutls_send_handshake(session, bufel,
+ GNUTLS_HANDSHAKE_SUPPLEMENTAL);
+}
+
+static int _gnutls_recv_supplemental(gnutls_session_t session)
+{
+ gnutls_buffer_st buf;
+ int ret;
+
+ _gnutls_debug_log("EXT[%p]: Expecting supplemental data\n",
+ session);
+
+ ret =
+ _gnutls_recv_handshake(session, GNUTLS_HANDSHAKE_SUPPLEMENTAL,
+ 1, &buf);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ ret = _gnutls_parse_supplemental(session, buf.data, buf.length);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ cleanup:
+ _gnutls_buffer_clear(&buf);
+
+ return ret;
+}
+
+/**
+ * gnutls_handshake:
+ * @session: is a #gnutls_session_t type.
+ *
+ * This function performs the handshake of the TLS/SSL protocol, and
+ * initializes the TLS session parameters.
+ *
+ * The non-fatal errors expected by this function are:
+ * %GNUTLS_E_INTERRUPTED, %GNUTLS_E_AGAIN,
+ * %GNUTLS_E_WARNING_ALERT_RECEIVED. When this function is called
+ * for re-handshake under TLS 1.2 or earlier, the non-fatal error code
+ * %GNUTLS_E_GOT_APPLICATION_DATA may also be returned.
+ *
+ * The former two interrupt the handshake procedure due to the transport
+ * layer being interrupted, and the latter because of a "warning" alert that
+ * was sent by the peer (it is always a good idea to check any
+ * received alerts). On these non-fatal errors call this function again,
+ * until it returns 0; cf. gnutls_record_get_direction() and
+ * gnutls_error_is_fatal(). In DTLS sessions the non-fatal error
+ * %GNUTLS_E_LARGE_PACKET is also possible, and indicates that
+ * the MTU should be adjusted.
+ *
+ * When this function is called by a server after a rehandshake request
+ * under TLS 1.2 or earlier the %GNUTLS_E_GOT_APPLICATION_DATA error code indicates
+ * that some data were pending prior to peer initiating the handshake.
+ * Under TLS 1.3 this function when called after a successful handshake, is a no-op
+ * and always succeeds in server side; in client side this function is
+ * equivalent to gnutls_session_key_update() with %GNUTLS_KU_PEER flag.
+ *
+ * This function handles both full and abbreviated TLS handshakes (resumption).
+ * For abbreviated handshakes, in client side, the gnutls_session_set_data()
+ * should be called prior to this function to set parameters from a previous session.
+ * In server side, resumption is handled by either setting a DB back-end, or setting
+ * up keys for session tickets.
+ *
+ * Returns: %GNUTLS_E_SUCCESS on a successful handshake, otherwise a negative error code.
+ **/
+int gnutls_handshake(gnutls_session_t session)
+{
+ const version_entry_st *vers = get_version(session);
+ int ret;
+
+ if (unlikely(session->internals.initial_negotiation_completed)) {
+ if (vers->tls13_sem) {
+ if (session->security_parameters.entity == GNUTLS_CLIENT) {
+ return gnutls_session_key_update(session, GNUTLS_KU_PEER);
+ } else {
+ /* This is a no-op for a server under TLS 1.3, as
+ * a server has already called gnutls_rehandshake()
+ * which performed a key update.
+ */
+ return 0;
+ }
+ }
+ }
+
+ if (STATE == STATE0) {
+ unsigned int tmo_ms;
+ struct timespec *end;
+ struct timespec *start;
+
+ /* first call */
+ if (session->internals.priorities == NULL ||
+ session->internals.priorities->cs.size == 0)
+ return gnutls_assert_val(GNUTLS_E_NO_PRIORITIES_WERE_SET);
+
+ ret =
+ _gnutls_epoch_setup_next(session, 0, NULL);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ session->internals.used_exts = 0;
+ session->internals.hsk_flags = 0;
+ session->internals.handshake_in_progress = 1;
+ session->internals.vc_status = -1;
+ gnutls_gettime(&session->internals.handshake_start_time);
+
+ tmo_ms = session->internals.handshake_timeout_ms;
+ end = &session->internals.handshake_abs_timeout;
+ start = &session->internals.handshake_start_time;
+
+ if (tmo_ms && end->tv_sec == 0 && end->tv_nsec == 0) {
+ end->tv_sec =
+ start->tv_sec + (start->tv_nsec + tmo_ms * 1000000LL) / 1000000000LL;
+ end->tv_nsec =
+ (start->tv_nsec + tmo_ms * 1000000LL) % 1000000000LL;
+ }
+
+#ifdef ENABLE_KTLS
+ if (_gnutls_config_is_ktls_enabled()) {
+ if ((session->internals.pull_func &&
+ session->internals.pull_func != system_read) ||
+ session->internals.push_func) {
+ _gnutls_audit_log(session,
+ "Not enabling KTLS with "
+ "custom pull/push function\n");
+ } else {
+ _gnutls_ktls_enable(session);
+ }
+ }
+#endif
+ }
+
+ if (session->internals.recv_state == RECV_STATE_FALSE_START) {
+ session_invalidate(session);
+ return gnutls_assert_val(GNUTLS_E_HANDSHAKE_DURING_FALSE_START);
+ }
+
+ if (session->security_parameters.entity == GNUTLS_CLIENT) {
+ do {
+ ret = handshake_client(session);
+ } while (ret == 1);
+ } else {
+ ret = handshake_server(session);
+ }
+
+ if (ret < 0) {
+ return _gnutls_abort_handshake(session, ret);
+ }
+
+ /* clear handshake buffer */
+ if (session->internals.recv_state != RECV_STATE_FALSE_START &&
+ session->internals.recv_state != RECV_STATE_EARLY_START) {
+
+ _gnutls_handshake_hash_buffers_clear(session);
+
+ if (IS_DTLS(session) == 0) {
+ _gnutls_handshake_io_buffer_clear(session);
+ } else {
+ _dtls_async_timer_init(session);
+ }
+
+ _gnutls_handshake_internal_state_clear(session);
+
+ _gnutls_buffer_clear(&session->internals.record_presend_buffer);
+
+ _gnutls_epoch_bump(session);
+ }
+
+ /* Give an estimation of the round-trip under TLS1.3, used by gnutls_session_get_data2() */
+ if (!IS_SERVER(session) && vers->tls13_sem) {
+ struct timespec handshake_finish_time;
+ gnutls_gettime(&handshake_finish_time);
+
+ if (!(session->internals.hsk_flags & HSK_HRR_RECEIVED)) {
+ session->internals.ertt = timespec_sub_ms(&handshake_finish_time, &session->internals.handshake_start_time)/2;
+ } else {
+ session->internals.ertt = timespec_sub_ms(&handshake_finish_time, &session->internals.handshake_start_time)/4;
+ }
+ }
+
+#ifdef ENABLE_KTLS
+ if (IS_KTLS_ENABLED(session, GNUTLS_KTLS_DUPLEX)) {
+ _gnutls_ktls_set_keys(session);
+ }
+#endif
+
+ return 0;
+}
+
+/**
+ * gnutls_handshake_set_timeout:
+ * @session: is a #gnutls_session_t type.
+ * @ms: is a timeout value in milliseconds
+ *
+ * This function sets the timeout for the TLS handshake process
+ * to the provided value. Use an @ms value of zero to disable
+ * timeout, or %GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT for a reasonable
+ * default value. For the DTLS protocol, the more detailed
+ * gnutls_dtls_set_timeouts() is provided.
+ *
+ * This function requires to set a pull timeout callback. See
+ * gnutls_transport_set_pull_timeout_function().
+ *
+ * Since: 3.1.0
+ **/
+void
+gnutls_handshake_set_timeout(gnutls_session_t session, unsigned int ms)
+{
+ if (ms == GNUTLS_INDEFINITE_TIMEOUT) {
+ session->internals.handshake_timeout_ms = 0;
+ return;
+ }
+
+ if (ms == GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT)
+ ms = DEFAULT_HANDSHAKE_TIMEOUT_MS;
+
+ if (IS_DTLS(session)) {
+ gnutls_dtls_set_timeouts(session, DTLS_RETRANS_TIMEOUT, ms);
+ return;
+ }
+
+ session->internals.handshake_timeout_ms = ms;
+}
+
+/* Runs the certificate verification callback.
+ * side is the side that we verify the certificate
+ * from (either GNUTLS_CLIENT or GNUTLS_SERVER).
+ */
+int _gnutls_run_verify_callback(gnutls_session_t session, unsigned int side)
+{
+ gnutls_certificate_credentials_t cred;
+ int ret, type;
+
+ if (session->internals.hsk_flags & HSK_PSK_SELECTED)
+ return 0;
+
+ cred =
+ (gnutls_certificate_credentials_t) _gnutls_get_cred(session,
+ GNUTLS_CRD_CERTIFICATE);
+
+ if (side == GNUTLS_CLIENT)
+ type = gnutls_auth_server_get_type(session);
+ else
+ type = gnutls_auth_client_get_type(session);
+
+ if (type != GNUTLS_CRD_CERTIFICATE)
+ return 0;
+
+ /* verify whether the certificate of the peer remained the same
+ * as with any previous handshakes */
+ if (cred != NULL) {
+ ret = _gnutls_check_if_cert_hash_is_same(session, cred);
+ if (ret < 0) {
+ return gnutls_assert_val(ret);
+ }
+ }
+
+ if (cred != NULL &&
+ (cred->verify_callback != NULL || session->internals.verify_callback != NULL) &&
+ (session->security_parameters.entity == GNUTLS_CLIENT ||
+ session->internals.send_cert_req != GNUTLS_CERT_IGNORE)) {
+ if (session->internals.verify_callback)
+ ret = session->internals.verify_callback(session);
+ else
+ ret = cred->verify_callback(session);
+ if (ret < -1)
+ return gnutls_assert_val(ret);
+ else if (ret != 0)
+ return gnutls_assert_val(GNUTLS_E_CERTIFICATE_ERROR);
+ }
+
+ return 0;
+}
+
+static bool can_send_false_start(gnutls_session_t session)
+{
+ const version_entry_st *vers;
+
+ vers = get_version(session);
+ if (unlikely(vers == NULL || !vers->false_start))
+ return 0;
+
+ if (session->internals.selected_cert_list != NULL)
+ return 0;
+
+ if (!_gnutls_kx_allows_false_start(session))
+ return 0;
+
+ return 1;
+}
+
+/*
+ * handshake_client
+ * This function performs the client side of the handshake of the TLS/SSL protocol.
+ */
+static int handshake_client(gnutls_session_t session)
+{
+ int ret = 0;
+ const version_entry_st *ver;
+
+ reset:
+ if (STATE >= STATE99)
+ return _gnutls13_handshake_client(session);
+
+ switch (STATE) {
+ case STATE0:
+ case STATE1:
+ ret = send_client_hello(session, AGAIN(STATE1));
+ STATE = STATE1;
+ IMED_RET("send hello", ret, 1);
+ FALLTHROUGH;
+ case STATE2:
+ if (IS_DTLS(session)) {
+ ret =
+ _gnutls_recv_handshake(session,
+ GNUTLS_HANDSHAKE_HELLO_VERIFY_REQUEST,
+ 1, NULL);
+ STATE = STATE2;
+ IMED_RET("recv hello verify", ret, 1);
+
+ if (ret == 1) {
+ STATE = STATE0;
+ return 1;
+ }
+ } else {
+ ret =
+ _gnutls_recv_handshake(session,
+ GNUTLS_HANDSHAKE_HELLO_RETRY_REQUEST,
+ 1, NULL);
+ STATE = STATE2;
+ IMED_RET("recv hello retry", ret, 1);
+
+ if (ret == 1) {
+ STATE = STATE0;
+ return 1;
+ }
+ }
+ FALLTHROUGH;
+ case STATE3:
+ /* receive the server hello */
+ ret =
+ _gnutls_recv_handshake(session,
+ GNUTLS_HANDSHAKE_SERVER_HELLO,
+ 0, NULL);
+ STATE = STATE3;
+ IMED_RET("recv hello", ret, 1);
+ FALLTHROUGH;
+ case STATE4:
+ ver = get_version(session);
+ if (ver->tls13_sem) { /* TLS 1.3 state machine */
+ STATE = STATE99;
+ goto reset;
+ }
+
+ ret = _gnutls_ext_sr_verify(session);
+ STATE = STATE4;
+ IMED_RET_FATAL("recv hello", ret, 0);
+ FALLTHROUGH;
+ case STATE5:
+ if (session->security_parameters.do_recv_supplemental) {
+ ret = _gnutls_recv_supplemental(session);
+ STATE = STATE5;
+ IMED_RET("recv supplemental", ret, 1);
+ }
+ FALLTHROUGH;
+ case STATE6:
+ /* RECV CERTIFICATE */
+ if (!session->internals.resumed) /* if we are not resuming */
+ ret = _gnutls_recv_server_certificate(session);
+ STATE = STATE6;
+ IMED_RET("recv server certificate", ret, 1);
+ FALLTHROUGH;
+ case STATE7:
+#ifdef ENABLE_OCSP
+ /* RECV CERTIFICATE STATUS */
+ if (!session->internals.resumed) /* if we are not resuming */
+ ret =
+ _gnutls_recv_server_certificate_status
+ (session);
+ STATE = STATE7;
+ IMED_RET("recv server certificate", ret, 1);
+#endif
+ FALLTHROUGH;
+ case STATE8:
+ ret = _gnutls_run_verify_callback(session, GNUTLS_CLIENT);
+ STATE = STATE8;
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ FALLTHROUGH;
+ case STATE9:
+ /* receive the server key exchange */
+ if (!session->internals.resumed) /* if we are not resuming */
+ ret = _gnutls_recv_server_kx_message(session);
+ STATE = STATE9;
+ IMED_RET("recv server kx message", ret, 1);
+ FALLTHROUGH;
+ case STATE10:
+ /* receive the server certificate request - if any
+ */
+
+ if (!session->internals.resumed) /* if we are not resuming */
+ ret = _gnutls_recv_server_crt_request(session);
+ STATE = STATE10;
+ IMED_RET("recv server certificate request message", ret,
+ 1);
+ FALLTHROUGH;
+ case STATE11:
+ /* receive the server hello done */
+ if (!session->internals.resumed) /* if we are not resuming */
+ ret =
+ _gnutls_recv_handshake(session,
+ GNUTLS_HANDSHAKE_SERVER_HELLO_DONE,
+ 0, NULL);
+ STATE = STATE11;
+ IMED_RET("recv server hello done", ret, 1);
+ FALLTHROUGH;
+ case STATE12:
+ if (session->security_parameters.do_send_supplemental) {
+ ret =
+ _gnutls_send_supplemental(session,
+ AGAIN(STATE12));
+ STATE = STATE12;
+ IMED_RET("send supplemental", ret, 0);
+ }
+ FALLTHROUGH;
+ case STATE13:
+ /* send our certificate - if any and if requested
+ */
+ if (!session->internals.resumed) /* if we are not resuming */
+ ret =
+ _gnutls_send_client_certificate(session,
+ AGAIN
+ (STATE13));
+ STATE = STATE13;
+ IMED_RET("send client certificate", ret, 0);
+ FALLTHROUGH;
+ case STATE14:
+ if (!session->internals.resumed) /* if we are not resuming */
+ ret =
+ _gnutls_send_client_kx_message(session,
+ AGAIN(STATE14));
+ STATE = STATE14;
+ IMED_RET("send client kx", ret, 0);
+ FALLTHROUGH;
+ case STATE15:
+ /* send client certificate verify */
+ if (!session->internals.resumed) /* if we are not resuming */
+ ret =
+ _gnutls_send_client_certificate_verify(session,
+ AGAIN
+ (STATE15));
+ STATE = STATE15;
+ IMED_RET("send client certificate verify", ret, 1);
+ FALLTHROUGH;
+ case STATE16:
+ STATE = STATE16;
+ if (!session->internals.resumed) {
+ ret = send_handshake_final(session, true);
+ IMED_RET("send handshake final 2", ret, 1);
+ } else {
+ ret = _gnutls_recv_new_session_ticket(session);
+ IMED_RET("recv handshake new session ticket", ret,
+ 1);
+ }
+ FALLTHROUGH;
+ case STATE17:
+ STATE = STATE17;
+ if (!session->internals.resumed && (session->internals.flags & GNUTLS_ENABLE_FALSE_START) && can_send_false_start(session)) {
+ session->internals.hsk_flags |= HSK_FALSE_START_USED;
+ session->internals.recv_state = RECV_STATE_FALSE_START;
+ /* complete this phase of the handshake. We
+ * should be called again by gnutls_record_recv()
+ */
+ STATE = STATE18;
+ gnutls_assert();
+
+ return 0;
+ }
+ FALLTHROUGH;
+ case STATE18:
+ STATE = STATE18;
+
+ if (!session->internals.resumed) {
+ ret = _gnutls_recv_new_session_ticket(session);
+ IMED_RET("recv handshake new session ticket", ret,
+ 1);
+ } else {
+ ret = recv_handshake_final(session, true);
+ IMED_RET("recv handshake final", ret, 1);
+ }
+ FALLTHROUGH;
+ case STATE19:
+ STATE = STATE19;
+ if (!session->internals.resumed) {
+ ret = recv_handshake_final(session, false);
+ IMED_RET("recv handshake final 2", ret, 1);
+ } else {
+ ret = send_handshake_final(session, false);
+ IMED_RET("send handshake final", ret, 1);
+ }
+
+ STATE = STATE0;
+ FALLTHROUGH;
+ default:
+ break;
+ }
+
+ /* explicitly reset any false start flags */
+ gnutls_mutex_lock(&session->internals.post_negotiation_lock);
+ session->internals.initial_negotiation_completed = 1;
+ session->internals.recv_state = RECV_STATE_0;
+ gnutls_mutex_unlock(&session->internals.post_negotiation_lock);
+
+ return 0;
+}
+
+
+
+/* This function is to be called if the handshake was successfully
+ * completed. This sends a Change Cipher Spec packet to the peer.
+ */
+ssize_t _gnutls_send_change_cipher_spec(gnutls_session_t session, int again)
+{
+ uint8_t *data;
+ mbuffer_st *bufel;
+ int ret;
+ const version_entry_st *vers;
+
+ if (again == 0) {
+ bufel = _gnutls_handshake_alloc(session, 3); /* max for DTLS0.9 */
+ if (bufel == NULL)
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+
+ vers = get_version(session);
+ if (unlikely(vers == NULL))
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+
+ if (vers->id == 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 (vers->id == GNUTLS_DTLS0_9) {
+ _gnutls_write_uint16(session->internals.dtls.
+ hsk_write_seq, &data[1]);
+ session->internals.dtls.hsk_write_seq++;
+ }
+
+ ret = _gnutls_call_hook_func(session, GNUTLS_HANDSHAKE_CHANGE_CIPHER_SPEC, GNUTLS_HOOK_PRE, 0,
+ data, 1);
+ if (ret < 0) {
+ _mbuffer_xfree(&bufel);
+ return gnutls_assert_val(ret);
+ }
+
+ ret =
+ _gnutls_handshake_io_cache_int(session,
+ GNUTLS_HANDSHAKE_CHANGE_CIPHER_SPEC,
+ bufel);
+ if (ret < 0) {
+ _mbuffer_xfree(&bufel);
+ return gnutls_assert_val(ret);
+ }
+
+ ret = _gnutls_call_hook_func(session, GNUTLS_HANDSHAKE_CHANGE_CIPHER_SPEC, GNUTLS_HOOK_POST, 0,
+ data, 1);
+ if (ret < 0) {
+ return gnutls_assert_val(ret);
+ }
+
+ /* under TLS 1.3, CCS may be immediately followed by
+ * receiving ClientHello thus cannot be cached */
+ if (vers->tls13_sem) {
+ ret = _gnutls_handshake_io_write_flush(session);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+ }
+
+ _gnutls_handshake_log("REC[%p]: Sent ChangeCipherSpec\n",
+ session);
+ }
+
+ return 0;
+}
+
+/* This function sends the final handshake packets and initializes connection
+ */
+static int send_handshake_final(gnutls_session_t session, int init)
+{
+ int ret = 0;
+
+ /* Send the CHANGE CIPHER SPEC PACKET */
+
+ switch (FINAL_STATE) {
+ case STATE0:
+ case STATE1:
+ ret = _gnutls_send_change_cipher_spec(session, FAGAIN(STATE1));
+ FINAL_STATE = STATE0;
+
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+ /* Initialize the connection session (start encryption) - in case of client
+ */
+ if (init) {
+ ret = _gnutls_connection_state_init(session);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+ }
+
+ ret = _gnutls_write_connection_state_init(session);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ FALLTHROUGH;
+ case STATE2:
+ /* send the finished message */
+ ret = _gnutls_send_finished(session, FAGAIN(STATE2));
+ FINAL_STATE = STATE2;
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ FINAL_STATE = STATE0;
+ FALLTHROUGH;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+/* This function receives the final handshake packets
+ * And executes the appropriate function to initialize the
+ * read session.
+ */
+static int recv_handshake_final(gnutls_session_t session, int init)
+{
+ int ret = 0;
+ uint8_t ccs[3];
+ unsigned int ccs_len = 1;
+ unsigned int tleft;
+ const version_entry_st *vers;
+
+ ret = handshake_remaining_time(session);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+ tleft = ret;
+
+ switch (FINAL_STATE) {
+ case STATE0:
+ case STATE30:
+ FINAL_STATE = STATE30;
+
+ /* This is the last flight and peer cannot be sure
+ * we have received it unless we notify him. So we
+ * wait for a message and retransmit if needed. */
+ if (IS_DTLS(session) && !_dtls_is_async(session) &&
+ (gnutls_record_check_pending(session) +
+ record_check_unprocessed(session)) == 0) {
+ ret = _dtls_wait_and_retransmit(session);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+ }
+
+ vers = get_version(session);
+ if (unlikely(vers == NULL))
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+
+ if (vers->id == GNUTLS_DTLS0_9)
+ ccs_len = 3;
+
+ ret =
+ _gnutls_recv_int(session, GNUTLS_CHANGE_CIPHER_SPEC,
+ ccs, ccs_len, NULL, tleft);
+ if (ret <= 0) {
+ gnutls_assert();
+ return (ret<0)?ret:GNUTLS_E_UNEXPECTED_PACKET;
+ }
+
+ if (vers->id == GNUTLS_DTLS0_9)
+ session->internals.dtls.hsk_read_seq++;
+
+ /* Initialize the connection session (start encryption) - in case of server */
+ if (init) {
+ ret = _gnutls_connection_state_init(session);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+ }
+
+ ret = _gnutls_read_connection_state_init(session);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+ FALLTHROUGH;
+ case STATE31:
+ FINAL_STATE = STATE31;
+
+ if (IS_DTLS(session) && !_dtls_is_async(session) &&
+ (gnutls_record_check_pending(session) +
+ record_check_unprocessed(session)) == 0) {
+ ret = _dtls_wait_and_retransmit(session);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+ }
+
+ ret = _gnutls_recv_finished(session);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+ FINAL_STATE = STATE0;
+ FALLTHROUGH;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+/*
+ * handshake_server
+ * This function does the server stuff of the handshake protocol.
+ */
+static int handshake_server(gnutls_session_t session)
+{
+ int ret = 0;
+ const version_entry_st *ver;
+
+ reset:
+
+ if (STATE >= STATE90)
+ return _gnutls13_handshake_server(session);
+
+ switch (STATE) {
+ case STATE0:
+ case STATE1:
+ ret =
+ _gnutls_recv_handshake(session,
+ GNUTLS_HANDSHAKE_CLIENT_HELLO,
+ 0, NULL);
+ 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 = STATE2; /* hello already parsed -> move on */
+ } 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);
+ FALLTHROUGH;
+ case STATE2:
+
+ ret = _gnutls_ext_sr_verify(session);
+ STATE = STATE2;
+ IMED_RET_FATAL("recv hello", ret, 0);
+ FALLTHROUGH;
+ case STATE3:
+ ret = _gnutls_send_server_hello(session, AGAIN(STATE3));
+ STATE = STATE3;
+ IMED_RET("send hello", ret, 1);
+
+ ver = get_version(session);
+ if (ver->tls13_sem) { /* TLS 1.3 state machine */
+ STATE = STATE99;
+ goto reset;
+ }
+
+ FALLTHROUGH;
+ case STATE4:
+ if (session->security_parameters.do_send_supplemental) {
+ ret =
+ _gnutls_send_supplemental(session,
+ AGAIN(STATE4));
+ STATE = STATE4;
+ IMED_RET("send supplemental data", ret, 0);
+ }
+ /* SEND CERTIFICATE + KEYEXCHANGE + CERTIFICATE_REQUEST */
+ FALLTHROUGH;
+ case STATE5:
+ /* NOTE: these should not be send if we are resuming */
+
+ if (!session->internals.resumed)
+ ret =
+ _gnutls_send_server_certificate(session,
+ AGAIN(STATE5));
+ STATE = STATE5;
+ IMED_RET("send server certificate", ret, 0);
+ FALLTHROUGH;
+ case STATE6:
+#ifdef ENABLE_OCSP
+ if (!session->internals.resumed)
+ ret =
+ _gnutls_send_server_certificate_status(session,
+ AGAIN
+ (STATE6));
+ STATE = STATE6;
+ IMED_RET("send server certificate status", ret, 0);
+#endif
+ FALLTHROUGH;
+ case STATE7:
+ /* send server key exchange (A) */
+ if (!session->internals.resumed)
+ ret =
+ _gnutls_send_server_kx_message(session,
+ AGAIN(STATE7));
+ STATE = STATE7;
+ IMED_RET("send server kx", ret, 0);
+ FALLTHROUGH;
+ case STATE8:
+ /* Send certificate request - if requested to */
+ if (!session->internals.resumed)
+ ret =
+ _gnutls_send_server_crt_request(session,
+ AGAIN(STATE8));
+ STATE = STATE8;
+ IMED_RET("send server cert request", ret, 0);
+ FALLTHROUGH;
+ case STATE9:
+ /* send the server hello done */
+ if (!session->internals.resumed) /* if we are not resuming */
+ ret =
+ _gnutls_send_empty_handshake(session,
+ GNUTLS_HANDSHAKE_SERVER_HELLO_DONE,
+ AGAIN(STATE9));
+ STATE = STATE9;
+ IMED_RET("send server hello done", ret, 1);
+ FALLTHROUGH;
+ case STATE10:
+ if (session->security_parameters.do_recv_supplemental) {
+ ret = _gnutls_recv_supplemental(session);
+ STATE = STATE10;
+ IMED_RET("recv client supplemental", ret, 1);
+ }
+ /* RECV CERTIFICATE + KEYEXCHANGE + CERTIFICATE_VERIFY */
+ FALLTHROUGH;
+ case STATE11:
+ /* receive the client certificate message */
+ if (!session->internals.resumed) /* if we are not resuming */
+ ret = _gnutls_recv_client_certificate(session);
+ STATE = STATE11;
+ IMED_RET("recv client certificate", ret, 1);
+ FALLTHROUGH;
+ case STATE12:
+ ret = _gnutls_run_verify_callback(session, GNUTLS_SERVER);
+ STATE = STATE12;
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+ FALLTHROUGH;
+ case STATE13:
+ /* receive the client key exchange message */
+ if (!session->internals.resumed) /* if we are not resuming */
+ ret = _gnutls_recv_client_kx_message(session);
+ STATE = STATE13;
+ IMED_RET("recv client kx", ret, 1);
+ FALLTHROUGH;
+ case STATE14:
+ /* receive the client certificate verify message */
+ if (!session->internals.resumed) /* if we are not resuming */
+ ret =
+ _gnutls_recv_client_certificate_verify_message
+ (session);
+ STATE = STATE14;
+ IMED_RET("recv client certificate verify", ret, 1);
+ FALLTHROUGH;
+ case STATE15:
+ STATE = STATE15;
+ if (!session->internals.resumed) { /* if we are not resuming */
+ ret = recv_handshake_final(session, true);
+ IMED_RET("recv handshake final", ret, 1);
+ } else {
+ ret = send_handshake_final(session, true);
+ IMED_RET("send handshake final 2", ret, 1);
+ }
+ FALLTHROUGH;
+ case STATE16:
+ ret =
+ _gnutls_send_new_session_ticket(session,
+ AGAIN(STATE16));
+ STATE = STATE16;
+ IMED_RET("send handshake new session ticket", ret, 0);
+ FALLTHROUGH;
+ case STATE17:
+ STATE = STATE17;
+ if (!session->internals.resumed) { /* if we are not resuming */
+ ret = send_handshake_final(session, false);
+ IMED_RET("send handshake final", ret, 1);
+
+ if (session->security_parameters.entity ==
+ GNUTLS_SERVER
+ && !(session->internals.hsk_flags & HSK_TLS12_TICKET_SENT)) {
+ /* if no ticket, save session data */
+ _gnutls_server_register_current_session
+ (session);
+ }
+ } else {
+ ret = recv_handshake_final(session, false);
+ IMED_RET("recv handshake final 2", ret, 1);
+ }
+
+ STATE = STATE0;
+ FALLTHROUGH;
+ default:
+ break;
+ }
+
+ /* no lock of post_negotiation_lock is required here as this is not run
+ * after handshake */
+ session->internals.initial_negotiation_completed = 1;
+
+ return _gnutls_check_id_for_change(session);
+}
+
+int _gnutls_generate_session_id(uint8_t * session_id, uint8_t *len)
+{
+ int ret;
+
+ *len = GNUTLS_DEF_SESSION_ID_SIZE;
+
+ ret =
+ gnutls_rnd(GNUTLS_RND_NONCE, session_id,
+ GNUTLS_DEF_SESSION_ID_SIZE);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ return 0;
+}
+
+
+/**
+ * gnutls_handshake_set_max_packet_length:
+ * @session: is a #gnutls_session_t type.
+ * @max: is the maximum number.
+ *
+ * This function will set the maximum size of all handshake messages.
+ * Handshakes over this size are rejected with
+ * %GNUTLS_E_HANDSHAKE_TOO_LARGE error code. The default value is
+ * 128kb which is typically large enough. Set this to 0 if you do not
+ * want to set an upper limit.
+ *
+ * The reason for restricting the handshake message sizes are to
+ * limit Denial of Service attacks.
+ *
+ * Note that the maximum handshake size was increased to 128kb
+ * from 48kb in GnuTLS 3.5.5.
+ **/
+void
+gnutls_handshake_set_max_packet_length(gnutls_session_t session,
+ size_t max)
+{
+ session->internals.max_handshake_data_buffer_size = max;
+}
+
+/**
+ * gnutls_handshake_get_last_in:
+ * @session: is a #gnutls_session_t type.
+ *
+ * This function is only useful to check where the last performed
+ * handshake failed. If the previous handshake succeed or was not
+ * performed at all then no meaningful value will be returned.
+ *
+ * Check %gnutls_handshake_description_t in gnutls.h for the
+ * available handshake descriptions.
+ *
+ * Returns: the last handshake message type received, a
+ * %gnutls_handshake_description_t.
+ **/
+gnutls_handshake_description_t
+gnutls_handshake_get_last_in(gnutls_session_t session)
+{
+ return session->internals.last_handshake_in;
+}
+
+/**
+ * gnutls_handshake_get_last_out:
+ * @session: is a #gnutls_session_t type.
+ *
+ * This function is only useful to check where the last performed
+ * handshake failed. If the previous handshake succeed or was not
+ * performed at all then no meaningful value will be returned.
+ *
+ * Check %gnutls_handshake_description_t in gnutls.h for the
+ * available handshake descriptions.
+ *
+ * Returns: the last handshake message type sent, a
+ * %gnutls_handshake_description_t.
+ **/
+gnutls_handshake_description_t
+gnutls_handshake_get_last_out(gnutls_session_t session)
+{
+ return session->internals.last_handshake_out;
+}
+
+/* This returns the session hash as in draft-ietf-tls-session-hash-02.
+ */
+int _gnutls_handshake_get_session_hash(gnutls_session_t session, gnutls_datum_t *shash)
+{
+ const version_entry_st *ver = get_version(session);
+ int ret;
+ uint8_t concat[2*MAX_HASH_SIZE];
+
+ if (unlikely(ver == NULL))
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+
+ if (session->internals.handshake_hash_buffer_client_kx_len == 0 ||
+ (session->internals.handshake_hash_buffer.length <
+ session->internals.handshake_hash_buffer_client_kx_len)) {
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+ }
+
+ ret =
+ _gnutls_hash_fast((gnutls_digest_algorithm_t)session->security_parameters.prf->id,
+ session->internals.handshake_hash_buffer.
+ data,
+ session->internals.handshake_hash_buffer_client_kx_len,
+ concat);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ return _gnutls_set_datum(shash, concat, session->security_parameters.prf->output_size);
+}