summaryrefslogtreecommitdiffstats
path: root/lib/state.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/state.c')
-rw-r--r--lib/state.c1886
1 files changed, 1886 insertions, 0 deletions
diff --git a/lib/state.c b/lib/state.c
new file mode 100644
index 0000000..9e16d99
--- /dev/null
+++ b/lib/state.c
@@ -0,0 +1,1886 @@
+/*
+ * Copyright (C) 2002-2016 Free Software Foundation, Inc.
+ * Copyright (C) 2014-2016 Nikos Mavrogiannopoulos
+ * 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 to manipulate the session (gnutls_int.h), and some other stuff
+ * are included here. The file's name is traditionally gnutls_state even if the
+ * state has been renamed to session.
+ */
+
+#include "gnutls_int.h"
+#include "errors.h"
+#include <auth.h>
+#include <num.h>
+#include <datum.h>
+#include <db.h>
+#include <record.h>
+#include <handshake.h>
+#include <dh.h>
+#include <buffers.h>
+#include <mbuffers.h>
+#include <state.h>
+#include <constate.h>
+#include <auth/cert.h>
+#include <auth/anon.h>
+#include <auth/psk.h>
+#include <algorithms.h>
+#include <hello_ext.h>
+#include <system.h>
+#include <random.h>
+#include <fips.h>
+#include <intprops.h>
+#include <gnutls/dtls.h>
+#include "dtls.h"
+#include "tls13/session_ticket.h"
+#include "ext/cert_types.h"
+#include "locks.h"
+#include "kx.h"
+
+/* to be used by supplemental data support to disable TLS1.3
+ * when supplemental data have been globally registered */
+unsigned _gnutls_disable_tls13 = 0;
+
+/* These should really be static, but src/tests.c calls them. Make
+ them public functions? */
+void
+_gnutls_rsa_pms_set_version(gnutls_session_t session,
+ unsigned char major, unsigned char minor);
+
+/**
+ * gnutls_cipher_get:
+ * @session: is a #gnutls_session_t type.
+ *
+ * Get the currently used cipher.
+ *
+ * Returns: the currently used cipher, a #gnutls_cipher_algorithm_t
+ * type.
+ **/
+gnutls_cipher_algorithm_t gnutls_cipher_get(gnutls_session_t session)
+{
+ record_parameters_st *record_params;
+ int ret;
+
+ ret =
+ _gnutls_epoch_get(session, EPOCH_READ_CURRENT, &record_params);
+ if (ret < 0)
+ return gnutls_assert_val(GNUTLS_CIPHER_NULL);
+
+ return record_params->cipher->id;
+}
+
+/**
+ * gnutls_early_cipher_get:
+ * @session: is a #gnutls_session_t type.
+ *
+ * Get the cipher algorithm used for encrypting early data.
+ *
+ * Returns: the cipher used for early data, a
+ * #gnutls_cipher_algorithm_t type.
+ *
+ * Since: 3.7.2
+ **/
+gnutls_cipher_algorithm_t gnutls_early_cipher_get(gnutls_session_t session)
+{
+ const cipher_entry_st *ce;
+
+ if (!(session->internals.hsk_flags & HSK_EARLY_DATA_IN_FLIGHT)) {
+ return gnutls_assert_val(GNUTLS_CIPHER_UNKNOWN);
+ }
+
+ if (unlikely(session->internals.
+ resumed_security_parameters.cs == NULL)) {
+ return gnutls_assert_val(GNUTLS_CIPHER_UNKNOWN);
+ }
+
+ ce = cipher_to_entry(session->internals.
+ resumed_security_parameters.
+ cs->block_algorithm);
+ if (unlikely(ce == NULL)) {
+ return gnutls_assert_val(GNUTLS_CIPHER_UNKNOWN);
+ }
+
+ return ce->id;
+}
+
+/**
+ * gnutls_certificate_type_get:
+ * @session: is a #gnutls_session_t type.
+ *
+ * This function returns the type of the certificate that is negotiated
+ * for this side to send to the peer. The certificate type is by default
+ * X.509, unless an alternative certificate type is enabled by
+ * gnutls_init() and negotiated during the session.
+ *
+ * Resumed sessions will return the certificate type that was negotiated
+ * and used in the original session.
+ *
+ * As of version 3.6.4 it is recommended to use
+ * gnutls_certificate_type_get2() which is more fine-grained.
+ *
+ * Returns: the currently used #gnutls_certificate_type_t certificate
+ * type as negotiated for 'our' side of the connection.
+ **/
+gnutls_certificate_type_t
+gnutls_certificate_type_get(gnutls_session_t session)
+{
+ return gnutls_certificate_type_get2(session, GNUTLS_CTYPE_OURS);
+}
+
+/**
+ * gnutls_certificate_type_get2:
+ * @session: is a #gnutls_session_t type.
+ * @target: is a #gnutls_ctype_target_t type.
+ *
+ * This function returns the type of the certificate that a side
+ * is negotiated to use. The certificate type is by default X.509,
+ * unless an alternative certificate type is enabled by gnutls_init() and
+ * negotiated during the session.
+ *
+ * The @target parameter specifies whether to request the negotiated
+ * certificate type for the client (%GNUTLS_CTYPE_CLIENT),
+ * or for the server (%GNUTLS_CTYPE_SERVER). Additionally, in P2P mode
+ * connection set up where you don't know in advance who will be client
+ * and who will be server you can use the flag (%GNUTLS_CTYPE_OURS) and
+ * (%GNUTLS_CTYPE_PEERS) to retrieve the corresponding certificate types.
+ *
+ * Resumed sessions will return the certificate type that was negotiated
+ * and used in the original session. That is, this function can be used
+ * to reliably determine the type of the certificate returned by
+ * gnutls_certificate_get_peers().
+ *
+ * Returns: the currently used #gnutls_certificate_type_t certificate
+ * type for the client or the server.
+ *
+ * Since: 3.6.4
+ **/
+gnutls_certificate_type_t
+gnutls_certificate_type_get2(gnutls_session_t session,
+ gnutls_ctype_target_t target)
+{
+ /* We want to inline this function so therefore
+ * we've defined it in gnutls_int.h */
+ return get_certificate_type(session, target);
+}
+
+/**
+ * gnutls_kx_get:
+ * @session: is a #gnutls_session_t type.
+ *
+ * Get the currently used key exchange algorithm.
+ *
+ * This function will return %GNUTLS_KX_ECDHE_RSA, or %GNUTLS_KX_DHE_RSA
+ * under TLS 1.3, to indicate an elliptic curve DH key exchange or
+ * a finite field one. The precise group used is available
+ * by calling gnutls_group_get() instead.
+ *
+ * Returns: the key exchange algorithm used in the last handshake, a
+ * #gnutls_kx_algorithm_t value.
+ **/
+gnutls_kx_algorithm_t gnutls_kx_get(gnutls_session_t session)
+{
+ if (session->security_parameters.cs == 0)
+ return 0;
+
+ if (session->security_parameters.cs->kx_algorithm == 0) { /* TLS 1.3 */
+ const version_entry_st *ver = get_version(session);
+ const gnutls_group_entry_st *group = get_group(session);
+
+ if (ver->tls13_sem) {
+ if (session->internals.hsk_flags & HSK_PSK_SELECTED) {
+ if (group) {
+ if (group->pk == GNUTLS_PK_DH)
+ return GNUTLS_KX_DHE_PSK;
+ else
+ return GNUTLS_KX_ECDHE_PSK;
+ } else {
+ return GNUTLS_KX_PSK;
+ }
+ } else if (group) {
+ if (group->pk == GNUTLS_PK_DH)
+ return GNUTLS_KX_DHE_RSA;
+ else
+ return GNUTLS_KX_ECDHE_RSA;
+ }
+ }
+ }
+
+ return session->security_parameters.cs->kx_algorithm;
+}
+
+/**
+ * gnutls_mac_get:
+ * @session: is a #gnutls_session_t type.
+ *
+ * Get the currently used MAC algorithm.
+ *
+ * Returns: the currently used mac algorithm, a
+ * #gnutls_mac_algorithm_t value.
+ **/
+gnutls_mac_algorithm_t gnutls_mac_get(gnutls_session_t session)
+{
+ record_parameters_st *record_params;
+ int ret;
+
+ ret =
+ _gnutls_epoch_get(session, EPOCH_READ_CURRENT, &record_params);
+ if (ret < 0)
+ return gnutls_assert_val(GNUTLS_MAC_NULL);
+
+ return record_params->mac->id;
+}
+
+/**
+ * gnutls_compression_get:
+ * @session: is a #gnutls_session_t type.
+ *
+ * Get the currently used compression algorithm.
+ *
+ * Returns: the currently used compression method, a
+ * #gnutls_compression_method_t value.
+ **/
+gnutls_compression_method_t
+gnutls_compression_get(gnutls_session_t session)
+{
+ return GNUTLS_COMP_NULL;
+}
+
+/**
+ * gnutls_prf_hash_get:
+ * @session: is a #gnutls_session_t type.
+ *
+ * Get the currently used hash algorithm. In TLS 1.3, the hash
+ * algorithm is used for both the key derivation function and
+ * handshake message authentication code. In TLS 1.2, it matches the
+ * hash algorithm used for PRF.
+ *
+ * Returns: the currently used hash algorithm, a
+ * #gnutls_digest_algorithm_t value.
+ *
+ * Since: 3.6.13
+ **/
+gnutls_digest_algorithm_t
+gnutls_prf_hash_get(const gnutls_session_t session)
+{
+ if (session->security_parameters.prf == NULL)
+ return gnutls_assert_val(GNUTLS_DIG_UNKNOWN);
+
+ if (session->security_parameters.prf->id >= GNUTLS_MAC_AEAD)
+ return gnutls_assert_val(GNUTLS_DIG_UNKNOWN);
+
+ return (gnutls_digest_algorithm_t)session->security_parameters.prf->id;
+}
+
+/**
+ * gnutls_early_prf_hash_get:
+ * @session: is a #gnutls_session_t type.
+ *
+ * Get the hash algorithm used as a PRF to derive keys for encrypting
+ * early data in TLS 1.3.
+ *
+ * Returns: the hash algorithm used for early data, a
+ * #gnutls_digest_algorithm_t value.
+ *
+ * Since: 3.7.2
+ **/
+gnutls_digest_algorithm_t
+gnutls_early_prf_hash_get(const gnutls_session_t session)
+{
+ if (!(session->internals.hsk_flags & HSK_EARLY_DATA_IN_FLIGHT)) {
+ return gnutls_assert_val(GNUTLS_DIG_UNKNOWN);
+ }
+
+ if (unlikely(session->internals.
+ resumed_security_parameters.prf == NULL)) {
+ return gnutls_assert_val(GNUTLS_DIG_UNKNOWN);
+ }
+
+ if (unlikely(session->internals.
+ resumed_security_parameters.prf->id >= GNUTLS_MAC_AEAD)) {
+ return gnutls_assert_val(GNUTLS_DIG_UNKNOWN);
+ }
+
+ return (gnutls_digest_algorithm_t)session->internals.
+ resumed_security_parameters.prf->id;
+}
+
+/**
+ * gnutls_ciphersuite_get:
+ * @session: is a #gnutls_session_t type.
+ *
+ * Get the canonical name of negotiated TLS ciphersuite. The names
+ * returned by this function match the IANA registry, with one
+ * exception:
+ *
+ * TLS_DHE_DSS_RC4_128_SHA { 0x00, 0x66 }
+ *
+ * which is reserved for compatibility.
+ *
+ * To get a detailed description of the current ciphersuite, it is
+ * recommended to use gnutls_session_get_desc().
+ *
+ * Returns: a string that contains the canonical name of a TLS ciphersuite,
+ * or %NULL if the handshake is not completed.
+ *
+ * Since: 3.7.4
+ **/
+const char *
+gnutls_ciphersuite_get(gnutls_session_t session)
+{
+ if (unlikely(session->internals.handshake_in_progress)) {
+ return NULL;
+ }
+ return session->security_parameters.cs->canonical_name;
+}
+
+void reset_binders(gnutls_session_t session)
+{
+ _gnutls_free_temp_key_datum(&session->key.binders[0].psk);
+ _gnutls_free_temp_key_datum(&session->key.binders[1].psk);
+ memset(session->key.binders, 0, sizeof(session->key.binders));
+}
+
+/* Check whether certificate credentials of type @cert_type are set
+ * for the current session.
+ */
+static bool _gnutls_has_cert_credentials(gnutls_session_t session,
+ gnutls_certificate_type_t cert_type)
+{
+ unsigned i;
+ unsigned cert_found = 0;
+ gnutls_certificate_credentials_t cred;
+
+ /* First, check for certificate credentials. If we have no certificate
+ * credentials set then we don't support certificates at all.
+ */
+ cred = (gnutls_certificate_credentials_t)
+ _gnutls_get_cred(session, GNUTLS_CRD_CERTIFICATE);
+
+ if (cred == NULL)
+ return false;
+
+ /* There are credentials initialized. Now check whether we can find
+ * pre-set certificates of the required type, but only if we don't
+ * use the callback functions.
+ */
+ if (cred->get_cert_callback3 == NULL) {
+ for (i = 0; i < cred->ncerts; i++) {
+ if (cred->certs[i].cert_list[0].type == cert_type) {
+ cert_found = 1;
+ break;
+ }
+ }
+
+ if (cert_found == 0) {
+ /* No matching certificate found. */
+ return false;
+ }
+ }
+
+ return true; // OK
+}
+
+/* Check if the given certificate type is supported.
+ * This means that it is enabled by the priority functions,
+ * and in some cases a matching certificate exists. A check for
+ * the latter can be toggled via the parameter @check_credentials.
+ */
+bool
+_gnutls_session_is_cert_type_supported(gnutls_session_t session,
+ gnutls_certificate_type_t cert_type,
+ bool check_credentials,
+ gnutls_ctype_target_t target)
+{
+ unsigned i;
+ priority_st* ctype_priorities;
+
+ // Check whether this cert type is enabled by the application
+ if (!is_cert_type_enabled(session, cert_type))
+ return gnutls_assert_val(GNUTLS_E_UNSUPPORTED_CERTIFICATE_TYPE);
+
+ // Perform a credentials check if requested
+ if (check_credentials) {
+ if (!_gnutls_has_cert_credentials(session, cert_type))
+ return gnutls_assert_val(GNUTLS_E_UNSUPPORTED_CERTIFICATE_TYPE);
+ }
+
+ /* So far so good. We have the required credentials (if needed).
+ * Now check whether we are allowed to use them according to our
+ * priorities.
+ */
+ // Which certificate type should we query?
+ switch (target) {
+ case GNUTLS_CTYPE_CLIENT:
+ ctype_priorities =
+ &(session->internals.priorities->client_ctype);
+ break;
+ case GNUTLS_CTYPE_SERVER:
+ ctype_priorities =
+ &(session->internals.priorities->server_ctype);
+ break;
+ default:
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+ }
+
+ // No explicit priorities set, and default ctype is asked
+ if (ctype_priorities->num_priorities == 0
+ && cert_type == DEFAULT_CERT_TYPE)
+ return 0;
+
+ /* Now lets find out whether our cert type is in our priority
+ * list, i.e. set of allowed cert types.
+ */
+ for (i = 0; i < ctype_priorities->num_priorities; i++) {
+ if (ctype_priorities->priorities[i] == cert_type)
+ return 0;
+ }
+
+ return GNUTLS_E_UNSUPPORTED_CERTIFICATE_TYPE;
+}
+
+static void deinit_keys(gnutls_session_t session)
+{
+ const version_entry_st *vers = get_version(session);
+
+ if (vers == NULL)
+ return;
+
+ gnutls_pk_params_release(&session->key.kshare.ecdhx_params);
+ gnutls_pk_params_release(&session->key.kshare.ecdh_params);
+ gnutls_pk_params_release(&session->key.kshare.dh_params);
+
+ if (!vers->tls13_sem && session->key.binders[0].prf == NULL) {
+ gnutls_pk_params_release(&session->key.proto.tls12.ecdh.params);
+ gnutls_pk_params_release(&session->key.proto.tls12.dh.params);
+ zrelease_temp_mpi_key(&session->key.proto.tls12.ecdh.x);
+ zrelease_temp_mpi_key(&session->key.proto.tls12.ecdh.y);
+ _gnutls_free_temp_key_datum(&session->key.proto.tls12.ecdh.raw);
+
+ zrelease_temp_mpi_key(&session->key.proto.tls12.dh.client_Y);
+
+ /* SRP */
+ zrelease_temp_mpi_key(&session->key.proto.tls12.srp.srp_p);
+ zrelease_temp_mpi_key(&session->key.proto.tls12.srp.srp_g);
+ zrelease_temp_mpi_key(&session->key.proto.tls12.srp.srp_key);
+
+ zrelease_temp_mpi_key(&session->key.proto.tls12.srp.u);
+ zrelease_temp_mpi_key(&session->key.proto.tls12.srp.a);
+ zrelease_temp_mpi_key(&session->key.proto.tls12.srp.x);
+ zrelease_temp_mpi_key(&session->key.proto.tls12.srp.A);
+ zrelease_temp_mpi_key(&session->key.proto.tls12.srp.B);
+ zrelease_temp_mpi_key(&session->key.proto.tls12.srp.b);
+ } else {
+ gnutls_memset(session->key.proto.tls13.temp_secret, 0,
+ sizeof(session->key.proto.tls13.temp_secret));
+ }
+
+ reset_binders(session);
+ _gnutls_free_temp_key_datum(&session->key.key);
+}
+
+/* An internal version of _gnutls_handshake_internal_state_clear(),
+ * it will not attempt to deallocate, only initialize */
+static void handshake_internal_state_clear1(gnutls_session_t session)
+{
+ /* by default no selected certificate */
+ session->internals.adv_version_major = 0;
+ session->internals.adv_version_minor = 0;
+ session->internals.direction = 0;
+
+ /* use out of band data for the last
+ * handshake messages received.
+ */
+ session->internals.last_handshake_in = -1;
+ session->internals.last_handshake_out = -1;
+
+ session->internals.resumable = true;
+
+ session->internals.handshake_suspicious_loops = 0;
+ session->internals.dtls.hsk_read_seq = 0;
+ session->internals.dtls.hsk_write_seq = 0;
+
+ session->internals.cand_ec_group = 0;
+ session->internals.cand_dh_group = 0;
+
+ session->internals.hrr_cs[0] = CS_INVALID_MAJOR;
+ session->internals.hrr_cs[1] = CS_INVALID_MINOR;
+}
+
+/* This function will clear all the variables in internals
+ * structure within the session, which depend on the current handshake.
+ * This is used to allow further handshakes.
+ */
+void _gnutls_handshake_internal_state_clear(gnutls_session_t session)
+{
+ handshake_internal_state_clear1(session);
+
+ _gnutls_handshake_hash_buffers_clear(session);
+ deinit_keys(session);
+
+ _gnutls_epoch_gc(session);
+
+ session->internals.handshake_abs_timeout.tv_sec = 0;
+ session->internals.handshake_abs_timeout.tv_nsec = 0;
+ session->internals.handshake_in_progress = 0;
+
+ session->internals.tfo.connect_addrlen = 0;
+ session->internals.tfo.connect_only = 0;
+ session->internals.early_data_received = 0;
+}
+
+/**
+ * gnutls_init:
+ * @session: is a pointer to a #gnutls_session_t type.
+ * @flags: indicate if this session is to be used for server or client.
+ *
+ * This function initializes the provided session. Every
+ * session must be initialized before use, and must be deinitialized
+ * after used by calling gnutls_deinit().
+ *
+ * @flags can be any combination of flags from %gnutls_init_flags_t.
+ *
+ * Note that since version 3.1.2 this function enables some common
+ * TLS extensions such as session tickets and OCSP certificate status
+ * request in client side by default. To prevent that use the %GNUTLS_NO_EXTENSIONS
+ * flag.
+ *
+ * Returns: %GNUTLS_E_SUCCESS on success, or an error code.
+ **/
+int gnutls_init(gnutls_session_t * session, unsigned int flags)
+{
+ int ret;
+
+ FAIL_IF_LIB_ERROR;
+
+ *session = gnutls_calloc(1, sizeof(struct gnutls_session_int));
+ if (*session == NULL)
+ return GNUTLS_E_MEMORY_ERROR;
+
+ ret = gnutls_mutex_init(&(*session)->internals.post_negotiation_lock);
+ if (ret < 0) {
+ gnutls_assert();
+ gnutls_free(*session);
+ return ret;
+ }
+
+ ret = gnutls_mutex_init(&(*session)->internals.epoch_lock);
+ if (ret < 0) {
+ gnutls_assert();
+ gnutls_mutex_deinit(&(*session)->internals.post_negotiation_lock);
+ gnutls_free(*session);
+ return ret;
+ }
+
+ ret = _gnutls_epoch_setup_next(*session, 1, NULL);
+ if (ret < 0) {
+ gnutls_mutex_deinit(&(*session)->internals.post_negotiation_lock);
+ gnutls_mutex_deinit(&(*session)->internals.epoch_lock);
+ gnutls_free(*session);
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+ }
+ _gnutls_epoch_bump(*session);
+
+ (*session)->security_parameters.entity =
+ (flags & GNUTLS_SERVER ? GNUTLS_SERVER : GNUTLS_CLIENT);
+
+ /* the default certificate type for TLS */
+ (*session)->security_parameters.client_ctype = DEFAULT_CERT_TYPE;
+ (*session)->security_parameters.server_ctype = DEFAULT_CERT_TYPE;
+
+ /* Initialize buffers */
+ _gnutls_buffer_init(&(*session)->internals.handshake_hash_buffer);
+ _gnutls_buffer_init(&(*session)->internals.post_handshake_hash_buffer);
+ _gnutls_buffer_init(&(*session)->internals.hb_remote_data);
+ _gnutls_buffer_init(&(*session)->internals.hb_local_data);
+ _gnutls_buffer_init(&(*session)->internals.record_presend_buffer);
+ _gnutls_buffer_init(&(*session)->internals.record_key_update_buffer);
+ _gnutls_buffer_init(&(*session)->internals.reauth_buffer);
+
+ _mbuffer_head_init(&(*session)->internals.record_buffer);
+ _mbuffer_head_init(&(*session)->internals.record_send_buffer);
+ _mbuffer_head_init(&(*session)->internals.record_recv_buffer);
+ _mbuffer_head_init(&(*session)->internals.early_data_recv_buffer);
+ _gnutls_buffer_init(&(*session)->internals.early_data_presend_buffer);
+
+ _mbuffer_head_init(&(*session)->internals.handshake_send_buffer);
+ _gnutls_handshake_recv_buffer_init(*session);
+
+ (*session)->internals.expire_time = DEFAULT_EXPIRE_TIME;
+
+ /* Ticket key rotation - set the default X to 3 times the ticket expire time */
+ (*session)->key.totp.last_result = 0;
+
+ gnutls_handshake_set_max_packet_length((*session),
+ MAX_HANDSHAKE_PACKET_SIZE);
+
+ /* set the socket pointers to -1;
+ */
+ (*session)->internals.transport_recv_ptr =
+ (gnutls_transport_ptr_t) - 1;
+ (*session)->internals.transport_send_ptr =
+ (gnutls_transport_ptr_t) - 1;
+
+ /* set the default maximum record size for TLS
+ */
+ (*session)->security_parameters.max_record_recv_size =
+ DEFAULT_MAX_RECORD_SIZE;
+ (*session)->security_parameters.max_record_send_size =
+ DEFAULT_MAX_RECORD_SIZE;
+ (*session)->security_parameters.max_user_record_recv_size =
+ DEFAULT_MAX_RECORD_SIZE;
+ (*session)->security_parameters.max_user_record_send_size =
+ DEFAULT_MAX_RECORD_SIZE;
+
+ /* set the default early data size for TLS
+ */
+ if ((*session)->security_parameters.entity == GNUTLS_SERVER) {
+ (*session)->security_parameters.max_early_data_size =
+ DEFAULT_MAX_EARLY_DATA_SIZE;
+ } else {
+ (*session)->security_parameters.max_early_data_size =
+ UINT32_MAX;
+ }
+
+ /* Everything else not initialized here is initialized as NULL
+ * or 0. This is why calloc is used. However, we want to
+ * ensure that certain portions of data are initialized at
+ * runtime before being used. Mark such regions with a
+ * valgrind client request as undefined.
+ */
+ _gnutls_memory_mark_undefined((*session)->security_parameters.master_secret,
+ GNUTLS_MASTER_SIZE);
+ _gnutls_memory_mark_undefined((*session)->security_parameters.client_random,
+ GNUTLS_RANDOM_SIZE);
+ _gnutls_memory_mark_undefined((*session)->security_parameters.server_random,
+ GNUTLS_RANDOM_SIZE);
+ _gnutls_memory_mark_undefined((*session)->key.session_ticket_key,
+ TICKET_MASTER_KEY_SIZE);
+ _gnutls_memory_mark_undefined((*session)->key.previous_ticket_key,
+ TICKET_MASTER_KEY_SIZE);
+ _gnutls_memory_mark_undefined((*session)->key.initial_stek,
+ TICKET_MASTER_KEY_SIZE);
+
+ handshake_internal_state_clear1(*session);
+
+#ifdef MSG_NOSIGNAL
+ if (flags & GNUTLS_NO_SIGNAL)
+ gnutls_transport_set_vec_push_function(*session, system_writev_nosignal);
+ else
+#endif
+ gnutls_transport_set_vec_push_function(*session, system_writev);
+ (*session)->internals.pull_timeout_func = gnutls_system_recv_timeout;
+ (*session)->internals.pull_func = system_read;
+ (*session)->internals.errno_func = system_errno;
+
+ (*session)->internals.saved_username = NULL;
+ (*session)->internals.saved_username_size = -1;
+
+ /* heartbeat timeouts */
+ (*session)->internals.hb_retrans_timeout_ms = 1000;
+ (*session)->internals.hb_total_timeout_ms = 60000;
+
+ if (flags & GNUTLS_DATAGRAM) {
+ (*session)->internals.dtls.mtu = DTLS_DEFAULT_MTU;
+ (*session)->internals.transport = GNUTLS_DGRAM;
+
+ gnutls_dtls_set_timeouts(*session, DTLS_RETRANS_TIMEOUT, 60000);
+ } else {
+ (*session)->internals.transport = GNUTLS_STREAM;
+ }
+
+ /* Enable useful extensions */
+ if ((flags & GNUTLS_CLIENT) && !(flags & GNUTLS_NO_EXTENSIONS)) {
+#ifdef ENABLE_OCSP
+ gnutls_ocsp_status_request_enable_client(*session, NULL, 0,
+ NULL);
+#endif
+ }
+
+ /* session tickets in server side are enabled by setting a key */
+ if (flags & GNUTLS_SERVER)
+ flags |= GNUTLS_NO_TICKETS;
+
+ (*session)->internals.flags = flags;
+
+ if (_gnutls_disable_tls13 != 0)
+ (*session)->internals.flags |= INT_FLAG_NO_TLS13;
+
+ /* Install the default keylog function */
+ gnutls_session_set_keylog_function(*session, _gnutls_nss_keylog_func);
+
+ return 0;
+}
+
+
+/**
+ * gnutls_deinit:
+ * @session: is a #gnutls_session_t type.
+ *
+ * This function clears all buffers associated with the @session.
+ * This function will also remove session data from the session
+ * database if the session was terminated abnormally.
+ **/
+void gnutls_deinit(gnutls_session_t session)
+{
+ unsigned int i;
+
+ if (session == NULL)
+ return;
+
+ /* remove auth info firstly */
+ _gnutls_free_auth_info(session);
+
+ _gnutls_handshake_internal_state_clear(session);
+ _gnutls_handshake_io_buffer_clear(session);
+ _gnutls_hello_ext_priv_deinit(session);
+
+ for (i = 0; i < MAX_EPOCH_INDEX; i++)
+ if (session->record_parameters[i] != NULL) {
+ _gnutls_epoch_free(session,
+ session->record_parameters[i]);
+ session->record_parameters[i] = NULL;
+ }
+
+ _gnutls_buffer_clear(&session->internals.handshake_hash_buffer);
+ _gnutls_buffer_clear(&session->internals.post_handshake_hash_buffer);
+ _gnutls_buffer_clear(&session->internals.hb_remote_data);
+ _gnutls_buffer_clear(&session->internals.hb_local_data);
+ _gnutls_buffer_clear(&session->internals.record_presend_buffer);
+ _gnutls_buffer_clear(&session->internals.record_key_update_buffer);
+ _gnutls_buffer_clear(&session->internals.reauth_buffer);
+
+ _mbuffer_head_clear(&session->internals.record_buffer);
+ _mbuffer_head_clear(&session->internals.record_recv_buffer);
+ _mbuffer_head_clear(&session->internals.record_send_buffer);
+
+ _mbuffer_head_clear(&session->internals.early_data_recv_buffer);
+ _gnutls_buffer_clear(&session->internals.early_data_presend_buffer);
+
+ _gnutls_free_datum(&session->internals.resumption_data);
+ _gnutls_free_datum(&session->internals.dtls.dcookie);
+
+ for (i = 0; i < session->internals.rexts_size; i++)
+ gnutls_free(session->internals.rexts[i].name);
+ gnutls_free(session->internals.rexts);
+ gnutls_free(session->internals.post_handshake_cr_context.data);
+
+ gnutls_free(session->internals.saved_username);
+ gnutls_free(session->internals.rsup);
+
+ gnutls_credentials_clear(session);
+ _gnutls_selected_certs_deinit(session);
+
+ /* destroy any session ticket we may have received */
+ tls13_ticket_deinit(&session->internals.tls13_ticket);
+
+ /* we rely on priorities' internal reference counting */
+ gnutls_priority_deinit(session->internals.priorities);
+
+ /* overwrite any temp TLS1.3 keys */
+ gnutls_memset(&session->key.proto, 0, sizeof(session->key.proto));
+
+ /* clear session ticket keys */
+ _gnutls_memory_mark_defined(session->key.session_ticket_key,
+ TICKET_MASTER_KEY_SIZE);
+ gnutls_memset(&session->key.session_ticket_key, 0,
+ TICKET_MASTER_KEY_SIZE);
+ _gnutls_memory_mark_undefined(session->key.session_ticket_key,
+ TICKET_MASTER_KEY_SIZE);
+
+ _gnutls_memory_mark_defined(session->key.previous_ticket_key,
+ TICKET_MASTER_KEY_SIZE);
+ gnutls_memset(&session->key.previous_ticket_key, 0,
+ TICKET_MASTER_KEY_SIZE);
+ _gnutls_memory_mark_undefined(session->key.previous_ticket_key,
+ TICKET_MASTER_KEY_SIZE);
+
+ _gnutls_memory_mark_defined(session->key.initial_stek,
+ TICKET_MASTER_KEY_SIZE);
+ gnutls_memset(&session->key.initial_stek, 0,
+ TICKET_MASTER_KEY_SIZE);
+ _gnutls_memory_mark_undefined(session->key.initial_stek,
+ TICKET_MASTER_KEY_SIZE);
+
+ gnutls_mutex_deinit(&session->internals.post_negotiation_lock);
+ gnutls_mutex_deinit(&session->internals.epoch_lock);
+
+ gnutls_free(session);
+}
+
+int _gnutls_dh_set_peer_public(gnutls_session_t session, bigint_t public)
+{
+ dh_info_st *dh;
+ int ret;
+
+ switch (gnutls_auth_get_type(session)) {
+ case GNUTLS_CRD_ANON:
+ {
+ anon_auth_info_t info;
+ info = _gnutls_get_auth_info(session, GNUTLS_CRD_ANON);
+ if (info == NULL)
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+
+ dh = &info->dh;
+ break;
+ }
+ case GNUTLS_CRD_PSK:
+ {
+ psk_auth_info_t info;
+ info = _gnutls_get_auth_info(session, GNUTLS_CRD_PSK);
+ if (info == NULL)
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+
+ dh = &info->dh;
+ break;
+ }
+ case GNUTLS_CRD_CERTIFICATE:
+ {
+ cert_auth_info_t info;
+
+ info = _gnutls_get_auth_info(session, GNUTLS_CRD_CERTIFICATE);
+ if (info == NULL)
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+
+ dh = &info->dh;
+ break;
+ }
+ default:
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+ }
+
+ if (dh->public_key.data)
+ _gnutls_free_datum(&dh->public_key);
+
+ ret = _gnutls_mpi_dprint_lz(public, &dh->public_key);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ return 0;
+}
+
+int _gnutls_dh_set_secret_bits(gnutls_session_t session, unsigned bits)
+{
+ switch (gnutls_auth_get_type(session)) {
+ case GNUTLS_CRD_ANON:
+ {
+ anon_auth_info_t info;
+ info = _gnutls_get_auth_info(session, GNUTLS_CRD_ANON);
+ if (info == NULL)
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+ info->dh.secret_bits = bits;
+ break;
+ }
+ case GNUTLS_CRD_PSK:
+ {
+ psk_auth_info_t info;
+ info = _gnutls_get_auth_info(session, GNUTLS_CRD_PSK);
+ if (info == NULL)
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+ info->dh.secret_bits = bits;
+ break;
+ }
+ case GNUTLS_CRD_CERTIFICATE:
+ {
+ cert_auth_info_t info;
+
+ info = _gnutls_get_auth_info(session, GNUTLS_CRD_CERTIFICATE);
+ if (info == NULL)
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+
+ info->dh.secret_bits = bits;
+ break;
+ default:
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+ }
+ }
+
+ return 0;
+}
+
+/* Sets the prime and the generator in the auth info structure.
+ */
+int
+_gnutls_dh_save_group(gnutls_session_t session, bigint_t gen,
+ bigint_t prime)
+{
+ dh_info_st *dh;
+ int ret;
+
+ switch (gnutls_auth_get_type(session)) {
+ case GNUTLS_CRD_ANON:
+ {
+ anon_auth_info_t info;
+ info = _gnutls_get_auth_info(session, GNUTLS_CRD_ANON);
+ if (info == NULL)
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+
+ dh = &info->dh;
+ break;
+ }
+ case GNUTLS_CRD_PSK:
+ {
+ psk_auth_info_t info;
+ info = _gnutls_get_auth_info(session, GNUTLS_CRD_PSK);
+ if (info == NULL)
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+
+ dh = &info->dh;
+ break;
+ }
+ case GNUTLS_CRD_CERTIFICATE:
+ {
+ cert_auth_info_t info;
+
+ info = _gnutls_get_auth_info(session, GNUTLS_CRD_CERTIFICATE);
+ if (info == NULL)
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+
+ dh = &info->dh;
+ break;
+ }
+ default:
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+ }
+
+ if (dh->prime.data)
+ _gnutls_free_datum(&dh->prime);
+
+ if (dh->generator.data)
+ _gnutls_free_datum(&dh->generator);
+
+ /* prime
+ */
+ ret = _gnutls_mpi_dprint_lz(prime, &dh->prime);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ /* generator
+ */
+ ret = _gnutls_mpi_dprint_lz(gen, &dh->generator);
+ if (ret < 0) {
+ gnutls_assert();
+ _gnutls_free_datum(&dh->prime);
+ return ret;
+ }
+
+ return 0;
+}
+
+/**
+ * gnutls_certificate_send_x509_rdn_sequence:
+ * @session: a #gnutls_session_t type.
+ * @status: is 0 or 1
+ *
+ * If status is non zero, this function will order gnutls not to send
+ * the rdnSequence in the certificate request message. That is the
+ * server will not advertise its trusted CAs to the peer. If status
+ * is zero then the default behaviour will take effect, which is to
+ * advertise the server's trusted CAs.
+ *
+ * This function has no effect in clients, and in authentication
+ * methods other than certificate with X.509 certificates.
+ **/
+void
+gnutls_certificate_send_x509_rdn_sequence(gnutls_session_t session,
+ int status)
+{
+ session->internals.ignore_rdn_sequence = status;
+}
+
+/*-
+ * _gnutls_record_set_default_version - Used to set the default version for the first record packet
+ * @session: is a #gnutls_session_t type.
+ * @major: is a tls major version
+ * @minor: is a tls minor version
+ *
+ * This function sets the default version that we will use in the first
+ * record packet (client hello). This function is only useful to people
+ * that know TLS internals and want to debug other implementations.
+ -*/
+void
+_gnutls_record_set_default_version(gnutls_session_t session,
+ unsigned char major,
+ unsigned char minor)
+{
+ session->internals.default_record_version[0] = major;
+ session->internals.default_record_version[1] = minor;
+}
+
+/*-
+ * _gnutls_hello_set_default_version - Used to set the default version for the first record packet
+ * @session: is a #gnutls_session_t type.
+ * @major: is a tls major version
+ * @minor: is a tls minor version
+ *
+ * This function sets the default version that we will use in the first
+ * record packet (client hello). This function is only useful to people
+ * that know TLS internals and want to debug other implementations.
+ -*/
+void
+_gnutls_hello_set_default_version(gnutls_session_t session,
+ unsigned char major,
+ unsigned char minor)
+{
+ session->internals.default_hello_version[0] = major;
+ session->internals.default_hello_version[1] = minor;
+}
+
+/**
+ * gnutls_handshake_set_private_extensions:
+ * @session: is a #gnutls_session_t type.
+ * @allow: is an integer (0 or 1)
+ *
+ * This function will enable or disable the use of private cipher
+ * suites (the ones that start with 0xFF). By default or if @allow
+ * is 0 then these cipher suites will not be advertised nor used.
+ *
+ * Currently GnuTLS does not include such cipher-suites or
+ * compression algorithms.
+ *
+ * Enabling the private ciphersuites when talking to other than
+ * gnutls servers and clients may cause interoperability problems.
+ **/
+void
+gnutls_handshake_set_private_extensions(gnutls_session_t session,
+ int allow)
+{
+ /* we have no private extensions */
+ return;
+}
+
+
+/**
+ * gnutls_session_is_resumed:
+ * @session: is a #gnutls_session_t type.
+ *
+ * Checks whether session is resumed or not. This is functional
+ * for both server and client side.
+ *
+ * Returns: non zero if this session is resumed, or a zero if this is
+ * a new session.
+ **/
+int gnutls_session_is_resumed(gnutls_session_t session)
+{
+ if (session->security_parameters.entity == GNUTLS_CLIENT) {
+ const version_entry_st *ver = get_version(session);
+ if (ver && ver->tls13_sem) {
+ return session->internals.resumed;
+ }
+
+ if (session->security_parameters.session_id_size > 0 &&
+ session->security_parameters.session_id_size ==
+ session->internals.resumed_security_parameters.
+ session_id_size
+ && memcmp(session->security_parameters.session_id,
+ session->
+ internals.resumed_security_parameters.
+ session_id,
+ session->security_parameters.
+ session_id_size) == 0)
+ return 1;
+ } else {
+ if (session->internals.resumed)
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * gnutls_session_resumption_requested:
+ * @session: is a #gnutls_session_t type.
+ *
+ * Check whether the client has asked for session resumption.
+ * This function is valid only on server side.
+ *
+ * Returns: non zero if session resumption was asked, or a zero if not.
+ **/
+int gnutls_session_resumption_requested(gnutls_session_t session)
+{
+ if (session->security_parameters.entity == GNUTLS_CLIENT) {
+ return 0;
+ } else {
+ return session->internals.resumption_requested;
+ }
+}
+
+/*-
+ * _gnutls_session_is_psk - Used to check whether this session uses PSK kx
+ * @session: is a #gnutls_session_t type.
+ *
+ * This function will return non zero if this session uses a PSK key
+ * exchange algorithm.
+ -*/
+int _gnutls_session_is_psk(gnutls_session_t session)
+{
+ gnutls_kx_algorithm_t kx;
+
+ kx = session->security_parameters.cs->kx_algorithm;
+ if (kx == GNUTLS_KX_PSK || kx == GNUTLS_KX_DHE_PSK
+ || kx == GNUTLS_KX_RSA_PSK)
+ return 1;
+
+ return 0;
+}
+
+/*-
+ * _gnutls_session_is_ecc - Used to check whether this session uses ECC kx
+ * @session: is a #gnutls_session_t type.
+ *
+ * This function will return non zero if this session uses an elliptic
+ * curves key exchange exchange algorithm.
+ -*/
+int _gnutls_session_is_ecc(gnutls_session_t session)
+{
+ gnutls_kx_algorithm_t kx;
+
+ /* We get the key exchange algorithm through the ciphersuite because
+ * the negotiated key exchange might not have been set yet.
+ */
+ kx = session->security_parameters.cs->kx_algorithm;
+
+ return _gnutls_kx_is_ecc(kx);
+}
+
+/**
+ * gnutls_session_get_ptr:
+ * @session: is a #gnutls_session_t type.
+ *
+ * Get user pointer for session. Useful in callbacks. This is the
+ * pointer set with gnutls_session_set_ptr().
+ *
+ * Returns: the user given pointer from the session structure, or
+ * %NULL if it was never set.
+ **/
+void *gnutls_session_get_ptr(gnutls_session_t session)
+{
+ return session->internals.user_ptr;
+}
+
+/**
+ * gnutls_session_set_ptr:
+ * @session: is a #gnutls_session_t type.
+ * @ptr: is the user pointer
+ *
+ * This function will set (associate) the user given pointer @ptr to
+ * the session structure. This pointer can be accessed with
+ * gnutls_session_get_ptr().
+ **/
+void gnutls_session_set_ptr(gnutls_session_t session, void *ptr)
+{
+ session->internals.user_ptr = ptr;
+}
+
+/**
+ * gnutls_session_set_verify_function:
+ * @session: is a #gnutls_session_t type.
+ * @func: is the callback function
+ *
+ * This function sets a callback to be called when peer's certificate
+ * has been received in order to verify it on receipt rather than
+ * doing after the handshake is completed. This overrides any callback
+ * set using gnutls_certificate_set_verify_function().
+ *
+ * The callback's function prototype is:
+ * int (*callback)(gnutls_session_t);
+ *
+ * If the callback function is provided then gnutls will call it, in the
+ * handshake, just after the certificate message has been received.
+ * To verify or obtain the certificate the gnutls_certificate_verify_peers2(),
+ * gnutls_certificate_type_get(), gnutls_certificate_get_peers() functions
+ * can be used.
+ *
+ * The callback function should return 0 for the handshake to continue
+ * or non-zero to terminate.
+ *
+ * Since: 3.4.6
+ **/
+void
+ gnutls_session_set_verify_function
+ (gnutls_session_t session,
+ gnutls_certificate_verify_function * func)
+{
+ session->internals.verify_callback = func;
+}
+
+/**
+ * gnutls_record_get_direction:
+ * @session: is a #gnutls_session_t type.
+ *
+ * This function is useful to determine whether a GnuTLS function was interrupted
+ * while sending or receiving, so that select() or poll() may be called appropriately.
+ *
+ * It provides information about the internals of the record
+ * protocol and is only useful if a prior gnutls function call,
+ * e.g. gnutls_handshake(), was interrupted and returned
+ * %GNUTLS_E_INTERRUPTED or %GNUTLS_E_AGAIN. After such an interrupt
+ * applications may call select() or poll() before restoring the
+ * interrupted GnuTLS function.
+ *
+ * This function's output is unreliable if you are using the same
+ * @session in different threads for sending and receiving.
+ *
+ * Returns: 0 if interrupted while trying to read data, or 1 while trying to write data.
+ **/
+int gnutls_record_get_direction(gnutls_session_t session)
+{
+ return session->internals.direction;
+}
+
+/*-
+ * _gnutls_rsa_pms_set_version - Sets a version to be used at the RSA PMS
+ * @session: is a #gnutls_session_t type.
+ * @major: is the major version to use
+ * @minor: is the minor version to use
+ *
+ * This function will set the given version number to be used at the
+ * RSA PMS secret. This is only useful to clients, which want to
+ * test server's capabilities.
+ -*/
+void
+_gnutls_rsa_pms_set_version(gnutls_session_t session,
+ unsigned char major, unsigned char minor)
+{
+ session->internals.rsa_pms_version[0] = major;
+ session->internals.rsa_pms_version[1] = minor;
+}
+
+void _gnutls_session_client_cert_type_set(gnutls_session_t session,
+ gnutls_certificate_type_t ct)
+{
+ _gnutls_handshake_log
+ ("HSK[%p]: Selected client certificate type %s (%d)\n", session,
+ gnutls_certificate_type_get_name(ct), ct);
+ session->security_parameters.client_ctype = ct;
+}
+
+void _gnutls_session_server_cert_type_set(gnutls_session_t session,
+ gnutls_certificate_type_t ct)
+{
+ _gnutls_handshake_log
+ ("HSK[%p]: Selected server certificate type %s (%d)\n", session,
+ gnutls_certificate_type_get_name(ct), ct);
+ session->security_parameters.server_ctype = ct;
+}
+
+/**
+ * gnutls_handshake_set_post_client_hello_function:
+ * @session: is a #gnutls_session_t type.
+ * @func: is the function to be called
+ *
+ * This function will set a callback to be called after the client
+ * hello has been received (callback valid in server side only). This
+ * allows the server to adjust settings based on received extensions.
+ *
+ * Those settings could be ciphersuites, requesting certificate, or
+ * anything else except for version negotiation (this is done before
+ * the hello message is parsed).
+ *
+ * This callback must return 0 on success or a gnutls error code to
+ * terminate the handshake.
+ *
+ * Since GnuTLS 3.3.5 the callback is
+ * allowed to return %GNUTLS_E_AGAIN or %GNUTLS_E_INTERRUPTED to
+ * put the handshake on hold. In that case gnutls_handshake()
+ * will return %GNUTLS_E_INTERRUPTED and can be resumed when needed.
+ *
+ * Warning: You should not use this function to terminate the
+ * handshake based on client input unless you know what you are
+ * doing. Before the handshake is finished there is no way to know if
+ * there is a man-in-the-middle attack being performed.
+ **/
+void
+gnutls_handshake_set_post_client_hello_function(gnutls_session_t session,
+ gnutls_handshake_simple_hook_func func)
+{
+ session->internals.user_hello_func = func;
+}
+
+
+/**
+ * gnutls_session_enable_compatibility_mode:
+ * @session: is a #gnutls_session_t type.
+ *
+ * This function can be used to disable certain (security) features in
+ * TLS in order to maintain maximum compatibility with buggy
+ * clients. Because several trade-offs with security are enabled,
+ * if required they will be reported through the audit subsystem.
+ *
+ * Normally only servers that require maximum compatibility with
+ * everything out there, need to call this function.
+ *
+ * Note that this function must be called after any call to gnutls_priority
+ * functions.
+ *
+ * Since: 2.1.4
+ **/
+void gnutls_session_enable_compatibility_mode(gnutls_session_t session)
+{
+ ENABLE_COMPAT(&session->internals);
+}
+
+/**
+ * gnutls_session_channel_binding:
+ * @session: is a #gnutls_session_t type.
+ * @cbtype: an #gnutls_channel_binding_t enumeration type
+ * @cb: output buffer array with data
+ *
+ * Extract given channel binding data of the @cbtype (e.g.,
+ * %GNUTLS_CB_TLS_UNIQUE) type.
+ *
+ * Returns: %GNUTLS_E_SUCCESS on success,
+ * %GNUTLS_E_UNIMPLEMENTED_FEATURE if the @cbtype is unsupported,
+ * %GNUTLS_E_CHANNEL_BINDING_NOT_AVAILABLE if the data is not
+ * currently available, or an error code.
+ *
+ * Since: 2.12.0
+ **/
+int
+gnutls_session_channel_binding(gnutls_session_t session,
+ gnutls_channel_binding_t cbtype,
+ gnutls_datum_t * cb)
+{
+ if (!session->internals.initial_negotiation_completed)
+ return GNUTLS_E_CHANNEL_BINDING_NOT_AVAILABLE;
+
+ if (cbtype == GNUTLS_CB_TLS_UNIQUE) {
+ const version_entry_st *ver = get_version(session);
+ if (unlikely(ver == NULL || ver->tls13_sem))
+ return GNUTLS_E_CHANNEL_BINDING_NOT_AVAILABLE;
+
+ cb->size = session->internals.cb_tls_unique_len;
+ cb->data = gnutls_malloc(cb->size);
+ if (cb->data == NULL)
+ return GNUTLS_E_MEMORY_ERROR;
+
+ memcpy(cb->data, session->internals.cb_tls_unique, cb->size);
+
+ return 0;
+ }
+
+ if (cbtype == GNUTLS_CB_TLS_SERVER_END_POINT) {
+ const gnutls_datum_t *ders;
+ unsigned int num_certs = 1;
+ int ret;
+ size_t rlen;
+ gnutls_x509_crt_t cert;
+ gnutls_digest_algorithm_t algo;
+
+ /* Only X509 certificates are supported for this binding type */
+ ret = gnutls_certificate_type_get (session);
+ if (ret != GNUTLS_CRT_X509)
+ return GNUTLS_E_UNIMPLEMENTED_FEATURE;
+
+ if (session->security_parameters.entity == GNUTLS_CLIENT)
+ ders = gnutls_certificate_get_peers (session, &num_certs);
+ else
+ ders = gnutls_certificate_get_ours (session);
+
+ /* Previous check indicated we have x509 but you never know */
+ if (!ders || num_certs == 0)
+ return GNUTLS_E_UNIMPLEMENTED_FEATURE;
+
+ ret = gnutls_x509_crt_list_import (&cert, &num_certs, ders,
+ GNUTLS_X509_FMT_DER, 0);
+ /* Again, this is not supposed to happen (normally) */
+ if (ret < 0 || num_certs == 0)
+ return GNUTLS_E_CHANNEL_BINDING_NOT_AVAILABLE;
+
+ /* Obtain signature algorithm used by certificate */
+ ret = gnutls_x509_crt_get_signature_algorithm (cert);
+ if (ret < 0 || ret == GNUTLS_SIGN_UNKNOWN)
+ return GNUTLS_E_UNIMPLEMENTED_FEATURE;
+
+ /* obtain hash function from signature and normalize it */
+ algo = gnutls_sign_get_hash_algorithm (ret);
+ switch (algo) {
+ case GNUTLS_DIG_MD5:
+ case GNUTLS_DIG_SHA1:
+ algo = GNUTLS_DIG_SHA256;
+ break;
+ case GNUTLS_DIG_UNKNOWN:
+ case GNUTLS_DIG_NULL:
+ case GNUTLS_DIG_MD5_SHA1:
+ /* double hashing not supported either */
+ gnutls_x509_crt_deinit (cert);
+ return GNUTLS_E_UNIMPLEMENTED_FEATURE;
+ default:
+ break;
+ }
+
+ /* preallocate 512 bits buffer as maximum supported digest */
+ rlen = MAX_HASH_SIZE;
+ cb->data = gnutls_malloc(rlen);
+ if (cb->data == NULL) {
+ gnutls_x509_crt_deinit (cert);
+ return GNUTLS_E_MEMORY_ERROR;
+ }
+
+ ret = gnutls_x509_crt_get_fingerprint (cert, algo, cb->data,
+ &rlen);
+ if (ret == GNUTLS_E_SHORT_MEMORY_BUFFER) {
+ cb->data = gnutls_realloc_fast (cb->data, cb->size);
+ if (cb->data == NULL) {
+ gnutls_x509_crt_deinit (cert);
+ return GNUTLS_E_MEMORY_ERROR;
+ }
+ ret = gnutls_x509_crt_get_fingerprint (cert, algo,
+ cb->data, &rlen);
+ }
+ cb->size = rlen;
+ gnutls_x509_crt_deinit (cert);
+ return ret;
+ }
+
+ if (cbtype == GNUTLS_CB_TLS_EXPORTER) {
+#define RFC5705_LABEL_DATA "EXPORTER-Channel-Binding"
+#define RFC5705_LABEL_LEN 24
+#define EXPORTER_CTX_DATA ""
+#define EXPORTER_CTX_LEN 0
+
+ const version_entry_st *ver = get_version(session);
+ if (unlikely(ver == NULL)) {
+ return GNUTLS_E_CHANNEL_BINDING_NOT_AVAILABLE;
+ }
+
+ /* "tls-exporter" channel binding is defined only when
+ * the TLS handshake results in unique master secrets,
+ * i.e., either TLS 1.3, or TLS 1.2 with extended
+ * master secret negotiated.
+ */
+ if (!ver->tls13_sem &&
+ gnutls_session_ext_master_secret_status(session) == 0) {
+ return GNUTLS_E_CHANNEL_BINDING_NOT_AVAILABLE;
+ }
+
+ cb->size = 32;
+ cb->data = gnutls_malloc(cb->size);
+ if (cb->data == NULL)
+ return GNUTLS_E_MEMORY_ERROR;
+
+ return gnutls_prf_rfc5705 (session,
+ RFC5705_LABEL_LEN, RFC5705_LABEL_DATA,
+ EXPORTER_CTX_LEN, EXPORTER_CTX_DATA,
+ cb->size, (char *) cb->data);
+ }
+
+ return GNUTLS_E_UNIMPLEMENTED_FEATURE;
+}
+
+/**
+ * gnutls_ecc_curve_get:
+ * @session: is a #gnutls_session_t type.
+ *
+ * Returns the currently used elliptic curve for key exchange. Only valid
+ * when using an elliptic curve ciphersuite.
+ *
+ * Returns: the currently used curve, a #gnutls_ecc_curve_t
+ * type.
+ *
+ * Since: 3.0
+ **/
+gnutls_ecc_curve_t gnutls_ecc_curve_get(gnutls_session_t session)
+{
+ const gnutls_group_entry_st *e;
+
+ e = get_group(session);
+ if (e == NULL || e->curve == 0)
+ return 0;
+ return e->curve;
+}
+
+/**
+ * gnutls_group_get:
+ * @session: is a #gnutls_session_t type.
+ *
+ * Returns the currently used group for key exchange. Only valid
+ * when using an elliptic curve or DH ciphersuite.
+ *
+ * Returns: the currently used group, a #gnutls_group_t
+ * type.
+ *
+ * Since: 3.6.0
+ **/
+gnutls_group_t gnutls_group_get(gnutls_session_t session)
+{
+ const gnutls_group_entry_st *e;
+
+ e = get_group(session);
+ if (e == NULL)
+ return 0;
+ return e->id;
+}
+
+/**
+ * gnutls_protocol_get_version:
+ * @session: is a #gnutls_session_t type.
+ *
+ * Get TLS version, a #gnutls_protocol_t value.
+ *
+ * Returns: The version of the currently used protocol.
+ **/
+gnutls_protocol_t gnutls_protocol_get_version(gnutls_session_t session)
+{
+ return get_num_version(session);
+}
+
+/**
+ * gnutls_session_get_random:
+ * @session: is a #gnutls_session_t type.
+ * @client: the client part of the random
+ * @server: the server part of the random
+ *
+ * This function returns pointers to the client and server
+ * random fields used in the TLS handshake. The pointers are
+ * not to be modified or deallocated.
+ *
+ * If a client random value has not yet been established, the output
+ * will be garbage.
+ *
+ * Since: 3.0
+ **/
+void
+gnutls_session_get_random(gnutls_session_t session,
+ gnutls_datum_t * client, gnutls_datum_t * server)
+{
+ if (client) {
+ client->data = session->security_parameters.client_random;
+ client->size =
+ sizeof(session->security_parameters.client_random);
+ }
+
+ if (server) {
+ server->data = session->security_parameters.server_random;
+ server->size =
+ sizeof(session->security_parameters.server_random);
+ }
+}
+
+/**
+ * gnutls_session_get_master_secret:
+ * @session: is a #gnutls_session_t type.
+ * @secret: the session's master secret
+ *
+ * This function returns pointers to the master secret
+ * used in the TLS session. The pointers are not to be modified or deallocated.
+ *
+ * This function is only applicable under TLS 1.2 or earlier versions.
+ *
+ * Since: 3.5.0
+ **/
+void
+gnutls_session_get_master_secret(gnutls_session_t session, gnutls_datum_t *secret)
+{
+ secret->data = session->security_parameters.master_secret;
+ secret->size = sizeof(session->security_parameters.master_secret);
+}
+
+unsigned int timespec_sub_ms(struct timespec *a, struct timespec *b)
+{
+ time_t dsecs;
+
+ dsecs = a->tv_sec - b->tv_sec;
+ if (!INT_MULTIPLY_OVERFLOW(dsecs, 1000)) {
+ return (dsecs*1000 + (a->tv_nsec - b->tv_nsec) / (1000 * 1000));
+ } else {
+ return UINT_MAX;
+ }
+}
+
+/**
+ * gnutls_handshake_set_random:
+ * @session: is a #gnutls_session_t type.
+ * @random: a random value of 32-bytes
+ *
+ * This function will explicitly set the server or client hello
+ * random value in the subsequent TLS handshake. The random value
+ * should be a 32-byte value.
+ *
+ * Note that this function should not normally be used as gnutls
+ * will select automatically a random value for the handshake.
+ *
+ * This function should not be used when resuming a session.
+ *
+ * Returns: %GNUTLS_E_SUCCESS on success, or an error code.
+ *
+ * Since 3.1.9
+ **/
+int
+gnutls_handshake_set_random(gnutls_session_t session,
+ const gnutls_datum_t * random)
+{
+ if (random->size != GNUTLS_RANDOM_SIZE)
+ return GNUTLS_E_INVALID_REQUEST;
+
+ session->internals.sc_random_set = 1;
+ if (session->security_parameters.entity == GNUTLS_CLIENT)
+ memcpy(session->internals.resumed_security_parameters.
+ client_random, random->data, random->size);
+ else
+ memcpy(session->internals.resumed_security_parameters.
+ server_random, random->data, random->size);
+
+ return 0;
+}
+
+/**
+ * gnutls_handshake_set_hook_function:
+ * @session: is a #gnutls_session_t type
+ * @htype: the %gnutls_handshake_description_t of the message to hook at
+ * @when: %GNUTLS_HOOK_* depending on when the hook function should be called
+ * @func: is the function to be called
+ *
+ * This function will set a callback to be called after or before the specified
+ * handshake message has been received or generated. This is a
+ * generalization of gnutls_handshake_set_post_client_hello_function().
+ *
+ * To call the hook function prior to the message being generated or processed
+ * use %GNUTLS_HOOK_PRE as @when parameter, %GNUTLS_HOOK_POST to call
+ * after, and %GNUTLS_HOOK_BOTH for both cases.
+ *
+ * This callback must return 0 on success or a gnutls error code to
+ * terminate the handshake.
+ *
+ * To hook at all handshake messages use an @htype of %GNUTLS_HANDSHAKE_ANY.
+ *
+ * Warning: You should not use this function to terminate the
+ * handshake based on client input unless you know what you are
+ * doing. Before the handshake is finished there is no way to know if
+ * there is a man-in-the-middle attack being performed.
+ **/
+void
+gnutls_handshake_set_hook_function(gnutls_session_t session,
+ unsigned int htype,
+ int when,
+ gnutls_handshake_hook_func func)
+{
+ session->internals.h_hook = func;
+ session->internals.h_type = htype;
+ session->internals.h_post = when;
+}
+
+/**
+ * gnutls_handshake_set_read_function:
+ * @session: is #gnutls_session_t type
+ * @func: is the function to be called
+ *
+ * This function will set a callback to be called when a handshake
+ * message is being sent.
+ *
+ * Since: 3.7.0
+ */
+void
+gnutls_handshake_set_read_function(gnutls_session_t session,
+ gnutls_handshake_read_func func)
+{
+ session->internals.h_read_func = func;
+}
+
+/**
+ * gnutls_alert_set_read_function:
+ * @session: is #gnutls_session_t type
+ * @func: is the function to be called
+ *
+ * This function will set a callback to be called when an alert
+ * message is being sent.
+ *
+ * Since: 3.7.0
+ */
+void
+gnutls_alert_set_read_function(gnutls_session_t session,
+ gnutls_alert_read_func func)
+{
+ session->internals.alert_read_func = func;
+}
+
+/**
+ * gnutls_record_get_state:
+ * @session: is a #gnutls_session_t type
+ * @read: if non-zero the read parameters are returned, otherwise the write
+ * @mac_key: the key used for MAC (if a MAC is used)
+ * @IV: the initialization vector or nonce used
+ * @cipher_key: the cipher key
+ * @seq_number: A 64-bit sequence number
+ *
+ * This function will return the parameters of the current record state.
+ * These are only useful to be provided to an external off-loading device
+ * or subsystem. The returned values should be considered constant
+ * and valid for the lifetime of the session.
+ *
+ * In that case, to sync the state back you must call gnutls_record_set_state().
+ *
+ * Returns: %GNUTLS_E_SUCCESS on success, or an error code.
+ *
+ * Since 3.4.0
+ **/
+int
+gnutls_record_get_state(gnutls_session_t session,
+ unsigned read,
+ gnutls_datum_t *mac_key,
+ gnutls_datum_t *IV,
+ gnutls_datum_t *cipher_key,
+ unsigned char seq_number[8])
+{
+ record_parameters_st *record_params;
+ record_state_st *record_state;
+ unsigned int epoch;
+ int ret;
+
+ if (read)
+ epoch = EPOCH_READ_CURRENT;
+ else
+ epoch = EPOCH_WRITE_CURRENT;
+
+ ret = _gnutls_epoch_get(session, epoch, &record_params);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ if (!record_params->initialized)
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+
+ if (read)
+ record_state = &record_params->read;
+ else
+ record_state = &record_params->write;
+
+ if (mac_key) {
+ mac_key->data = record_state->mac_key;
+ mac_key->size = record_state->mac_key_size;
+ }
+
+ if (IV) {
+ IV->data = record_state->iv;
+ IV->size = record_state->iv_size;
+ }
+
+ if (cipher_key) {
+ cipher_key->data = record_state->key;
+ cipher_key->size = record_state->key_size;
+ }
+
+ if (seq_number)
+ _gnutls_write_uint64(record_state->sequence_number, seq_number);
+ return 0;
+}
+
+/**
+ * gnutls_record_set_state:
+ * @session: is a #gnutls_session_t type
+ * @read: if non-zero the read parameters are returned, otherwise the write
+ * @seq_number: A 64-bit sequence number
+ *
+ * This function will set the sequence number in the current record state.
+ * This function is useful if sending and receiving are offloaded from
+ * gnutls. That is, if gnutls_record_get_state() was used.
+ *
+ * Returns: %GNUTLS_E_SUCCESS on success, or an error code.
+ *
+ * Since 3.4.0
+ **/
+int
+gnutls_record_set_state(gnutls_session_t session,
+ unsigned read,
+ const unsigned char seq_number[8])
+{
+ record_parameters_st *record_params;
+ record_state_st *record_state;
+ int epoch, ret;
+
+ if (read)
+ epoch = EPOCH_READ_CURRENT;
+ else
+ epoch = EPOCH_WRITE_CURRENT;
+
+ ret = _gnutls_epoch_get(session, epoch, &record_params);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ if (!record_params->initialized)
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+
+ if (read)
+ record_state = &record_params->read;
+ else
+ record_state = &record_params->write;
+
+ record_state->sequence_number = _gnutls_read_uint64(seq_number);
+
+ if (IS_DTLS(session)) {
+ _dtls_reset_window(record_params);
+ }
+
+ return 0;
+}
+
+/**
+ * gnutls_session_get_flags:
+ * @session: is a #gnutls_session_t type.
+ *
+ * This function will return a series (ORed) of flags, applicable
+ * for the current session.
+ *
+ * This replaces individual informational functions such as
+ * gnutls_safe_renegotiation_status(), gnutls_session_ext_master_secret_status(),
+ * etc.
+ *
+ * Returns: An ORed sequence of flags (see %gnutls_session_flags_t)
+ *
+ * Since: 3.5.0
+ **/
+unsigned gnutls_session_get_flags(gnutls_session_t session)
+{
+ unsigned flags = 0;
+
+ if (gnutls_safe_renegotiation_status(session))
+ flags |= GNUTLS_SFLAGS_SAFE_RENEGOTIATION;
+ if (gnutls_session_ext_master_secret_status(session))
+ flags |= GNUTLS_SFLAGS_EXT_MASTER_SECRET;
+ if (gnutls_session_etm_status(session))
+ flags |= GNUTLS_SFLAGS_ETM;
+ if (gnutls_heartbeat_allowed(session, GNUTLS_HB_LOCAL_ALLOWED_TO_SEND))
+ flags |= GNUTLS_SFLAGS_HB_LOCAL_SEND;
+ if (gnutls_heartbeat_allowed(session, GNUTLS_HB_PEER_ALLOWED_TO_SEND))
+ flags |= GNUTLS_SFLAGS_HB_PEER_SEND;
+ if (session->internals.hsk_flags & HSK_FALSE_START_USED)
+ flags |= GNUTLS_SFLAGS_FALSE_START;
+ if ((session->internals.hsk_flags & HSK_EARLY_START_USED) &&
+ (session->internals.flags & GNUTLS_ENABLE_EARLY_START))
+ flags |= GNUTLS_SFLAGS_EARLY_START;
+ if (session->internals.hsk_flags & HSK_USED_FFDHE)
+ flags |= GNUTLS_SFLAGS_RFC7919;
+ if (session->internals.hsk_flags & HSK_TICKET_RECEIVED)
+ flags |= GNUTLS_SFLAGS_SESSION_TICKET;
+ if (session->security_parameters.post_handshake_auth)
+ flags |= GNUTLS_SFLAGS_POST_HANDSHAKE_AUTH;
+ if (session->internals.hsk_flags & HSK_EARLY_DATA_ACCEPTED)
+ flags |= GNUTLS_SFLAGS_EARLY_DATA;
+ if (session->internals.hsk_flags & HSK_OCSP_REQUESTED)
+ flags |= GNUTLS_SFLAGS_CLI_REQUESTED_OCSP;
+ if (session->internals.hsk_flags & HSK_CLIENT_OCSP_REQUESTED)
+ flags |= GNUTLS_SFLAGS_SERV_REQUESTED_OCSP;
+
+ return flags;
+}