/*
* Copyright (C) 2000-2012 Free Software Foundation, 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
*
*/
/* Functions that relate to the TLS handshake procedure.
*/
#include "gnutls_int.h"
#include "gnutls_errors.h"
#include "gnutls_dh.h"
#include "debug.h"
#include "algorithms.h"
#include "gnutls_compress.h"
#include "gnutls_cipher.h"
#include "gnutls_buffers.h"
#include "gnutls_mbuffers.h"
#include "gnutls_kx.h"
#include "gnutls_handshake.h"
#include "gnutls_num.h"
#include "gnutls_hash_int.h"
#include "gnutls_db.h"
#include "gnutls_extensions.h"
#include "gnutls_supplemental.h"
#include "gnutls_auth.h"
#include "gnutls_v2_compat.h"
#include
#include "gnutls_constate.h"
#include
#include
#include
#include
#include
#include
#include /* for gnutls_get_rsa_params() */
#include /* for gnutls_anon_server_credentials_t */
#include /* for gnutls_psk_server_credentials_t */
#include
#include
#ifdef HANDSHAKE_DEBUG
#define ERR(x, y) _gnutls_handshake_log("HSK[%p]: %s (%d)\n", session, x,y)
#else
#define ERR(x, y)
#endif
#define TRUE 1
#define FALSE 0
static int _gnutls_server_select_comp_method (gnutls_session_t session,
uint8_t * data, int datalen);
static int
_gnutls_remove_unwanted_ciphersuites (gnutls_session_t session,
uint8_t * cipher_suites,
int cipher_suites_size,
gnutls_pk_algorithm_t *pk_algos,
size_t pk_algos_size);
static int _gnutls_handshake_client (gnutls_session_t session);
static int _gnutls_handshake_server (gnutls_session_t session);
static int
_gnutls_recv_handshake_final (gnutls_session_t session, int init);
static int
_gnutls_send_handshake_final (gnutls_session_t session, int init);
/* Empties but does not free the buffer
*/
static inline void
_gnutls_handshake_hash_buffer_empty (gnutls_session_t session)
{
_gnutls_buffers_log ("BUF[HSK]: Emptied buffer\n");
session->internals.handshake_hash_buffer_prev_len = 0;
session->internals.handshake_hash_buffer.length = 0;
return;
}
static int
_gnutls_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
_gnutls_handshake_hash_add_sent (gnutls_session_t session,
gnutls_handshake_description_t type,
uint8_t * dataptr, uint32_t datalen);
static int
_gnutls_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)
{
session->internals.handshake_hash_buffer_prev_len = 0;
_gnutls_buffer_clear(&session->internals.handshake_hash_buffer);
}
/* this will copy the required values for resuming to
* internals, and to security_parameters.
* this will keep as less data to security_parameters.
*/
static void
resume_copy_required_values (gnutls_session_t session)
{
/* 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.
*/
memcpy (session->security_parameters.cipher_suite,
session->internals.resumed_security_parameters.cipher_suite, 2);
session->security_parameters.compression_method = session->internals.resumed_security_parameters.compression_method;
_gnutls_epoch_set_cipher_suite (session, EPOCH_NEXT,
session->
internals.resumed_security_parameters.cipher_suite);
_gnutls_epoch_set_compression (session, EPOCH_NEXT,
session->
internals.resumed_security_parameters.compression_method);
/* or write_compression_algorithm
* they are the same
*/
session->security_parameters.entity =
session->internals.resumed_security_parameters.entity;
_gnutls_set_current_version (session,
session->internals.resumed_security_parameters.
version);
session->security_parameters.cert_type =
session->internals.resumed_security_parameters.cert_type;
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;
}
/* this function will produce GNUTLS_RANDOM_SIZE==32 bytes of random data
* and put it to dst.
*/
static int
_gnutls_tls_create_random (uint8_t * dst)
{
uint32_t tim;
int ret;
/* Use weak random numbers for the most of the
* buffer except for the first 4 that are the
* system's time.
*/
tim = gnutls_time (NULL);
/* generate server random value */
_gnutls_write_uint32 (tim, dst);
ret = _gnutls_rnd (GNUTLS_RND_NONCE, &dst[4], GNUTLS_RANDOM_SIZE - 4);
if (ret < 0)
{
gnutls_assert ();
return ret;
}
return 0;
}
int
_gnutls_set_client_random (gnutls_session_t session, uint8_t * rnd)
{
int ret;
if (rnd != NULL)
memcpy (session->security_parameters.client_random, rnd,
GNUTLS_RANDOM_SIZE);
else
{
/* no random given, we generate. */
if (session->internals.sc_random_set != 0)
{
memcpy (session->security_parameters.client_random,
session->internals.resumed_security_parameters.client_random,
GNUTLS_RANDOM_SIZE);
}
else
{
ret = _gnutls_tls_create_random (session->security_parameters.client_random);
if (ret < 0)
return gnutls_assert_val(ret);
}
}
return 0;
}
int
_gnutls_set_server_random (gnutls_session_t session, uint8_t * rnd)
{
int ret;
if (rnd != NULL)
memcpy (session->security_parameters.server_random, rnd,
GNUTLS_RANDOM_SIZE);
else
{
/* no random given, we generate. */
if (session->internals.sc_random_set != 0)
{
memcpy (session->security_parameters.server_random,
session->internals.resumed_security_parameters.server_random,
GNUTLS_RANDOM_SIZE);
}
else
{
ret = _gnutls_tls_create_random (session->security_parameters.server_random);
if (ret < 0)
return gnutls_assert_val(ret);
}
}
return 0;
}
/* 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, GNUTLS_DIG_SHA1);
if (rc < 0)
return gnutls_assert_val(rc);
rc = _gnutls_hash_init (&td_md5, 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;
}
/* 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 + 16 /*MD5 */ ];
size_t hash_len;
const char *mesg;
int rc, len;
if (sending)
len = session->internals.handshake_hash_buffer.length;
else
len = session->internals.handshake_hash_buffer_prev_len;
if (!_gnutls_version_has_selectable_prf (gnutls_protocol_get_version(session)))
{
rc = _gnutls_hash_fast( GNUTLS_DIG_SHA1, session->internals.handshake_hash_buffer.data, len, &concat[16]);
if (rc < 0)
return gnutls_assert_val(rc);
rc = _gnutls_hash_fast( GNUTLS_DIG_MD5, session->internals.handshake_hash_buffer.data, len, concat);
if (rc < 0)
return gnutls_assert_val(rc);
hash_len = 20 + 16;
}
else
{
int algorithm = _gnutls_cipher_suite_get_prf(session->security_parameters.cipher_suite);
rc = _gnutls_hash_fast( algorithm, session->internals.handshake_hash_buffer.data, len, concat);
if (rc < 0)
return gnutls_assert_val(rc);
hash_len = _gnutls_hash_get_algo_len (algorithm);
}
if (type == GNUTLS_SERVER)
{
mesg = SERVER_MSG;
}
else
{
mesg = CLIENT_MSG;
}
return _gnutls_PRF (session, session->security_parameters.master_secret,
GNUTLS_MASTER_SIZE, mesg, siz, concat, hash_len, 12, ret);
}
/* returns the 0 on success or a negative error code.
*/
int
_gnutls_negotiate_version (gnutls_session_t session,
gnutls_protocol_t adv_version)
{
int ret;
/* if we do not support that version */
if (_gnutls_version_is_supported (session, adv_version) == 0)
{
/* If he requested something we do not support
* then we send him the highest we support.
*/
ret = _gnutls_version_max (session);
if (ret == GNUTLS_VERSION_UNKNOWN)
{
/* this check is not really needed.
*/
gnutls_assert ();
return GNUTLS_E_UNKNOWN_CIPHER_SUITE;
}
}
else
{
ret = adv_version;
}
_gnutls_set_current_version (session, ret);
return ret;
}
int
_gnutls_user_hello_func (gnutls_session_t session,
gnutls_protocol_t adv_version)
{
int ret;
if (session->internals.user_hello_func != NULL)
{
ret = session->internals.user_hello_func (session);
if (ret < 0)
{
gnutls_assert ();
return ret;
}
/* Here we need to renegotiate the version since the callee might
* have disabled some TLS versions.
*/
ret = _gnutls_negotiate_version (session, adv_version);
if (ret < 0)
{
gnutls_assert ();
return ret;
}
}
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
_gnutls_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;
gnutls_protocol_t adv_version;
int neg_version;
int len = datalen;
uint8_t *suite_ptr, *comp_ptr, *session_id;
DECR_LEN (len, 2);
_gnutls_handshake_log ("HSK[%p]: Client's version: %d.%d\n", session,
data[pos], data[pos + 1]);
adv_version = _gnutls_version_get (data[pos], data[pos + 1]);
set_adv_version (session, data[pos], data[pos + 1]);
pos += 2;
neg_version = _gnutls_negotiate_version (session, adv_version);
if (neg_version < 0)
{
gnutls_assert ();
return neg_version;
}
/* Read client random value.
*/
DECR_LEN (len, GNUTLS_RANDOM_SIZE);
ret = _gnutls_set_client_random (session, &data[pos]);
if (ret < 0)
return gnutls_assert_val(ret);
pos += GNUTLS_RANDOM_SIZE;
ret = _gnutls_set_server_random (session, NULL);
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 > TLS_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;
}
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! */
/* Parse only the safe renegotiation extension
* We don't want to parse any other extensions since
* we don't want new extension values to overwrite the
* resumed ones.
*/
/* move forward to extensions */
DECR_LEN (len, 2);
suite_size = _gnutls_read_uint16 (&data[pos]);
pos += 2;
DECR_LEN (len, suite_size);
pos += suite_size;
DECR_LEN (len, 1);
comp_size = data[pos++]; /* z is the number of compression methods */
DECR_LEN (len, comp_size);
pos += comp_size;
ret = _gnutls_parse_extensions (session, GNUTLS_EXT_MANDATORY,
&data[pos], len);
if (ret < 0)
{
gnutls_assert ();
return ret;
}
resume_copy_required_values (session);
session->internals.resumed = RESUME_TRUE;
return _gnutls_user_hello_func (session, adv_version);
}
else
{
_gnutls_generate_session_id (session->security_parameters.session_id,
&session->
security_parameters.session_id_size);
session->internals.resumed = RESUME_FALSE;
}
/* Remember ciphersuites for later
*/
DECR_LEN (len, 2);
suite_size = _gnutls_read_uint16 (&data[pos]);
pos += 2;
DECR_LEN (len, suite_size);
suite_ptr = &data[pos];
pos += suite_size;
/* Point to the compression methods
*/
DECR_LEN (len, 1);
comp_size = data[pos++]; /* z is the number of compression methods */
DECR_LEN (len, comp_size);
comp_ptr = &data[pos];
pos += comp_size;
/* 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_extensions (session, GNUTLS_EXT_APPLICATION,
&data[pos], len);
/* len is the rest of the parsed length */
if (ret < 0)
{
gnutls_assert ();
return ret;
}
ret = _gnutls_user_hello_func (session, adv_version);
if (ret < 0)
{
gnutls_assert ();
return ret;
}
ret = _gnutls_parse_extensions (session, GNUTLS_EXT_MANDATORY,
&data[pos], len);
if (ret < 0)
{
gnutls_assert ();
return ret;
}
ret = _gnutls_parse_extensions (session, GNUTLS_EXT_TLS, &data[pos], len);
if (ret < 0)
{
gnutls_assert ();
return ret;
}
/* resumed by session_ticket extension */
if (session->internals.resumed != RESUME_FALSE)
{
/* to indicate the client that the current session is resumed */
memcpy (session->internals.resumed_security_parameters.session_id,
session_id, session_id_len);
session->internals.resumed_security_parameters.session_id_size =
session_id_len;
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;
resume_copy_required_values (session);
return _gnutls_user_hello_func (session, adv_version);
}
/* select an appropriate cipher suite
*/
ret = _gnutls_server_select_suite (session, suite_ptr, suite_size);
if (ret < 0)
{
gnutls_assert ();
return ret;
}
/* select appropriate compression method */
ret = _gnutls_server_select_comp_method (session, comp_ptr, comp_size);
if (ret < 0)
{
gnutls_assert ();
return ret;
}
return 0;
}
/* This is to be called after sending CHANGE CIPHER SPEC packet
* and initializing encryption. This is the first encrypted message
* we send.
*/
static int
_gnutls_send_finished (gnutls_session_t session, int again)
{
mbuffer_st *bufel;
uint8_t *data;
int ret;
size_t vdata_size = 0;
if (again == 0)
{
bufel = _gnutls_handshake_alloc (session, MAX_VERIFY_DATA_SIZE, MAX_VERIFY_DATA_SIZE);
if (bufel == NULL)
{
gnutls_assert ();
return GNUTLS_E_MEMORY_ERROR;
}
data = _mbuffer_get_udata_ptr (bufel);
if (gnutls_protocol_get_version (session) == GNUTLS_SSL3)
{
ret =
_gnutls_ssl3_finished (session,
session->security_parameters.entity, data, 1);
_mbuffer_set_udata_size (bufel, 36);
}
else
{ /* TLS 1.0+ */
ret = _gnutls_finished (session,
session->security_parameters.entity, data, 1);
_mbuffer_set_udata_size (bufel, 12);
}
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 == RESUME_FALSE
&& session->security_parameters.entity == GNUTLS_CLIENT)
|| (session->internals.resumed != RESUME_FALSE
&& 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
*/
static 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;
ret =
_gnutls_recv_handshake (session, GNUTLS_HANDSHAKE_FINISHED,
0, &buf);
if (ret < 0)
{
ERR ("recv finished int", ret);
gnutls_assert ();
return ret;
}
vrfy = buf.data;
vrfy_size = buf.length;
if (gnutls_protocol_get_version (session) == GNUTLS_SSL3)
{
data_size = 36;
}
else
{
data_size = 12;
}
if (vrfy_size != data_size)
{
gnutls_assert ();
ret = GNUTLS_E_ERROR_IN_FINISHED_PACKET;
goto cleanup;
}
if (gnutls_protocol_get_version (session) == GNUTLS_SSL3)
{
ret =
_gnutls_ssl3_finished (session,
(session->security_parameters.entity + 1) % 2,
data, 0);
}
else
{ /* TLS 1.0 */
ret =
_gnutls_finished (session,
(session->security_parameters.entity +
1) % 2, data, 0);
}
if (ret < 0)
{
gnutls_assert ();
goto cleanup;
}
if (memcmp (vrfy, data, data_size) != 0)
{
gnutls_assert ();
ret = GNUTLS_E_ERROR_IN_FINISHED_PACKET;
goto cleanup;
}
ret = _gnutls_ext_sr_finished (session, data, data_size, 1);
if (ret < 0)
{
gnutls_assert ();
goto cleanup;
}
if ((session->internals.resumed != RESUME_FALSE
&& session->security_parameters.entity == GNUTLS_CLIENT)
|| (session->internals.resumed == RESUME_FALSE
&& 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;
}
session->internals.initial_negotiation_completed = 1;
cleanup:
_gnutls_buffer_clear(&buf);
return ret;
}
/* returns PK_RSA if the given cipher suite list only supports,
* RSA algorithms, PK_DSA if DSS, and PK_ANY for both or PK_NONE for none.
*/
static int
server_find_pk_algos_in_ciphersuites (const uint8_t *
data, unsigned int datalen,
gnutls_pk_algorithm_t * algos,
size_t* algos_size)
{
unsigned int j;
gnutls_kx_algorithm_t kx;
unsigned int max = *algos_size;
if (datalen % 2 != 0)
{
gnutls_assert ();
return GNUTLS_E_UNEXPECTED_PACKET_LENGTH;
}
*algos_size = 0;
for (j = 0; j < datalen; j += 2)
{
kx = _gnutls_cipher_suite_get_kx_algo (&data[j]);
if (_gnutls_map_kx_get_cred (kx, 1) == GNUTLS_CRD_CERTIFICATE)
{
algos[(*algos_size)++] = _gnutls_map_pk_get_pk (kx);
if ((*algos_size) >= max)
return 0;
}
}
return 0;
}
/* This selects the best supported ciphersuite from the given ones. Then
* it adds the suite to the session and performs some checks.
*/
int
_gnutls_server_select_suite (gnutls_session_t session, uint8_t * data,
unsigned int datalen)
{
int ret;
unsigned int i, j, cipher_suites_size;
size_t pk_algos_size;
uint8_t cipher_suites[MAX_CIPHERSUITE_SIZE];
int retval, err;
gnutls_pk_algorithm_t pk_algos[MAX_ALGOS]; /* will hold the pk algorithms
* supported by the peer.
*/
/* First, check for safe renegotiation SCSV.
*/
if (session->internals.priorities.sr != SR_DISABLED)
{
unsigned int offset;
for (offset = 0; offset < datalen; offset += 2)
{
/* TLS_RENEGO_PROTECTION_REQUEST = { 0x00, 0xff } */
if (data[offset] == GNUTLS_RENEGO_PROTECTION_REQUEST_MAJOR &&
data[offset + 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;
}
break;
}
}
}
pk_algos_size = MAX_ALGOS;
ret = server_find_pk_algos_in_ciphersuites (data, datalen, pk_algos, &pk_algos_size);
if (ret < 0)
return gnutls_assert_val(ret);
ret = _gnutls_supported_ciphersuites (session, cipher_suites, sizeof(cipher_suites));
if (ret < 0)
return gnutls_assert_val(ret);
cipher_suites_size = ret;
/* Here we remove any ciphersuite that does not conform
* the certificate requested, or to the
* authentication requested (e.g. SRP).
*/
ret = _gnutls_remove_unwanted_ciphersuites (session, cipher_suites, cipher_suites_size, pk_algos, pk_algos_size);
if (ret <= 0)
{
gnutls_assert ();
if (ret < 0)
return ret;
else
return GNUTLS_E_UNKNOWN_CIPHER_SUITE;
}
cipher_suites_size = ret;
/* Data length should be zero mod 2 since
* every ciphersuite is 2 bytes. (this check is needed
* see below).
*/
if (datalen % 2 != 0)
{
gnutls_assert ();
return GNUTLS_E_UNEXPECTED_PACKET_LENGTH;
}
memset (session->security_parameters.cipher_suite, 0, 2);
retval = GNUTLS_E_UNKNOWN_CIPHER_SUITE;
_gnutls_handshake_log ("HSK[%p]: Requested cipher suites[size: %d]: \n", session, (int)datalen);
if (session->internals.priorities.server_precedence == 0)
{
for (j = 0; j < datalen; j += 2)
{
_gnutls_handshake_log ("\t0x%.2x, 0x%.2x %s\n", data[j], data[j+1], _gnutls_cipher_suite_get_name (&data[j]));
for (i = 0; i < cipher_suites_size; i+=2)
{
if (memcmp (&cipher_suites[i], &data[j], 2) == 0)
{
_gnutls_handshake_log
("HSK[%p]: Selected cipher suite: %s\n", session,
_gnutls_cipher_suite_get_name (&data[j]));
memcpy (session->security_parameters.cipher_suite,
&cipher_suites[i], 2);
_gnutls_epoch_set_cipher_suite (session, EPOCH_NEXT,
session->
security_parameters.cipher_suite);
retval = 0;
goto finish;
}
}
}
}
else /* server selects */
{
for (i = 0; i < cipher_suites_size; i+=2)
{
for (j = 0; j < datalen; j += 2)
{
if (memcmp (&cipher_suites[i], &data[j], 2) == 0)
{
_gnutls_handshake_log
("HSK[%p]: Selected cipher suite: %s\n", session,
_gnutls_cipher_suite_get_name (&data[j]));
memcpy (session->security_parameters.cipher_suite,
&cipher_suites[i], 2);
_gnutls_epoch_set_cipher_suite (session, EPOCH_NEXT,
session->
security_parameters.cipher_suite);
retval = 0;
goto finish;
}
}
}
}
finish:
if (retval != 0)
{
gnutls_assert ();
return retval;
}
/* check if the credentials (username, public key etc.) are ok
*/
if (_gnutls_get_kx_cred
(session,
_gnutls_cipher_suite_get_kx_algo (session->
security_parameters.cipher_suite),
&err) == NULL && err != 0)
{
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 (_gnutls_cipher_suite_get_kx_algo
(session->
security_parameters.cipher_suite));
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 selects the best supported compression method from the ones provided
*/
static int
_gnutls_server_select_comp_method (gnutls_session_t session,
uint8_t * data, int datalen)
{
int x, i, j;
uint8_t comps[MAX_ALGOS];
x = _gnutls_supported_compression_methods (session, comps, MAX_ALGOS);
if (x < 0)
{
gnutls_assert ();
return x;
}
if (session->internals.priorities.server_precedence == 0)
{
for (j = 0; j < datalen; j++)
{
for (i = 0; i < x; i++)
{
if (comps[i] == data[j])
{
gnutls_compression_method_t method =
_gnutls_compression_get_id (comps[i]);
_gnutls_epoch_set_compression (session, EPOCH_NEXT, method);
session->security_parameters.compression_method = method;
_gnutls_handshake_log
("HSK[%p]: Selected Compression Method: %s\n", session,
gnutls_compression_get_name (method));
return 0;
}
}
}
}
else
{
for (i = 0; i < x; i++)
{
for (j = 0; j < datalen; j++)
{
if (comps[i] == data[j])
{
gnutls_compression_method_t method =
_gnutls_compression_get_id (comps[i]);
_gnutls_epoch_set_compression (session, EPOCH_NEXT, method);
session->security_parameters.compression_method = method;
_gnutls_handshake_log
("HSK[%p]: Selected Compression Method: %s\n", session,
gnutls_compression_get_name (method));
return 0;
}
}
}
}
/* we were not able to find a compatible 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, 0);
if (bufel == NULL)
{
gnutls_assert ();
return GNUTLS_E_MEMORY_ERROR;
}
}
else
bufel = NULL;
return _gnutls_send_handshake (session, bufel, type);
}
/* 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_handshake (gnutls_session_t session, mbuffer_st * bufel,
gnutls_handshake_description_t type)
{
int ret;
uint8_t *data;
uint32_t datasize, i_datasize;
int pos = 0;
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) 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 (type != GNUTLS_HANDSHAKE_HELLO_REQUEST)
if ((ret =
_gnutls_handshake_hash_add_sent (session, type, data, datasize)) < 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;
}
switch (type)
{
case GNUTLS_HANDSHAKE_CERTIFICATE_PKT: /* this one is followed by ServerHelloDone
* or ClientKeyExchange always.
*/
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 cached messages */
ret = _gnutls_handshake_io_write_flush (session);
break;
}
return ret;
}
#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)) \
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
_gnutls_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;
if ((gnutls_protocol_get_version (session) != GNUTLS_DTLS0_9 &&
recv_type == GNUTLS_HANDSHAKE_HELLO_VERIFY_REQUEST) ||
recv_type == GNUTLS_HANDSHAKE_HELLO_REQUEST)
return 0;
CHECK_SIZE(header_size + datalen);
session->internals.handshake_hash_buffer_prev_len = session->internals.handshake_hash_buffer.length;
if (gnutls_protocol_get_version (session) != GNUTLS_DTLS0_9)
{
ret = _gnutls_buffer_append_data(&session->internals.handshake_hash_buffer,
header, header_size);
if (ret < 0)
return gnutls_assert_val(ret);
}
if (datalen > 0)
{
ret = _gnutls_buffer_append_data(&session->internals.handshake_hash_buffer,
dataptr, datalen);
if (ret < 0)
return gnutls_assert_val(ret);
}
return 0;
}
/* This function will store the handshake message we sent.
*/
static int
_gnutls_handshake_hash_add_sent (gnutls_session_t session,
gnutls_handshake_description_t type,
uint8_t * dataptr, uint32_t datalen)
{
int ret;
/* We don't check for GNUTLS_HANDSHAKE_HELLO_VERIFY_REQUEST because it
* is not sent via that channel.
*/
if (type != GNUTLS_HANDSHAKE_HELLO_REQUEST)
{
CHECK_SIZE(datalen);
if (gnutls_protocol_get_version (session) == GNUTLS_DTLS0_9)
{
/* Old DTLS doesn't include the header in the MAC */
if (datalen < 12)
{
gnutls_assert ();
return GNUTLS_E_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);
return 0;
}
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;
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);
}
ret = _gnutls_handshake_hash_add_recvd (session, hsk.htype,
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:
case GNUTLS_HANDSHAKE_SERVER_HELLO:
if (hsk.htype == GNUTLS_HANDSHAKE_CLIENT_HELLO_V2)
ret = _gnutls_read_client_hello_v2 (session, hsk.data.data, hsk.data.length);
else
ret = _gnutls_recv_hello (session, hsk.data.data, hsk.data.length);
if (ret < 0)
{
gnutls_assert();
goto cleanup;
}
goto cleanup; /* caller doesn't need dataptr */
break;
case GNUTLS_HANDSHAKE_HELLO_VERIFY_REQUEST:
ret = _gnutls_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;
goto cleanup; /* caller doesn't need dataptr */
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_CERTIFICATE_STATUS:
case GNUTLS_HANDSHAKE_FINISHED:
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:
ret = hsk.data.length;
break;
default:
gnutls_assert ();
/* we shouldn't actually arrive here in any case .
* unexpected messages should be catched after _gnutls_handshake_io_recv_int()
*/
ret = GNUTLS_E_UNEXPECTED_HANDSHAKE_PACKET;
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
_gnutls_client_set_ciphersuite (gnutls_session_t session, uint8_t suite[2])
{
uint8_t z;
uint8_t cipher_suites[MAX_CIPHERSUITE_SIZE];
int cipher_suite_size;
int i, err;
z = 1;
cipher_suite_size = _gnutls_supported_ciphersuites (session, cipher_suites, sizeof(cipher_suites));
if (cipher_suite_size < 0)
{
gnutls_assert ();
return cipher_suite_size;
}
for (i = 0; i < cipher_suite_size; i+=2)
{
if (memcmp (&cipher_suites[i], suite, 2) == 0)
{
z = 0;
break;
}
}
if (z != 0)
{
gnutls_assert ();
_gnutls_handshake_log("HSK[%p]: unsupported cipher suite %.2X.%.2X\n", session,
(unsigned int)suite[0], (unsigned int)suite[1]);
return GNUTLS_E_UNKNOWN_CIPHER_SUITE;
}
memcpy (session->security_parameters.cipher_suite, suite, 2);
_gnutls_epoch_set_cipher_suite (session, EPOCH_NEXT,
session->
security_parameters.cipher_suite);
_gnutls_handshake_log ("HSK[%p]: Selected cipher suite: %s\n", session,
_gnutls_cipher_suite_get_name
(session->
security_parameters.cipher_suite));
/* check if the credentials (username, public key etc.) are ok.
* Actually checks if they exist.
*/
if (!session->internals.premaster_set &&
_gnutls_get_kx_cred
(session,
_gnutls_cipher_suite_get_kx_algo
(session->security_parameters.cipher_suite), &err) == NULL
&& err != 0)
{
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 (_gnutls_cipher_suite_get_kx_algo
(session->
security_parameters.cipher_suite));
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 function sets the given comp method to the session.
*/
static int
_gnutls_client_set_comp_method (gnutls_session_t session, uint8_t comp_method)
{
int comp_methods_num;
uint8_t compression_methods[MAX_ALGOS];
int id = _gnutls_compression_get_id(comp_method);
int i;
_gnutls_handshake_log ("HSK[%p]: Selected compression method: %s (%d)\n", session,
gnutls_compression_get_name(id), (int)comp_method);
comp_methods_num = _gnutls_supported_compression_methods (session,
compression_methods, MAX_ALGOS);
if (comp_methods_num < 0)
{
gnutls_assert ();
return comp_methods_num;
}
for (i = 0; i < comp_methods_num; i++)
{
if (compression_methods[i] == comp_method)
{
comp_methods_num = 0;
break;
}
}
if (comp_methods_num != 0)
{
gnutls_assert ();
return GNUTLS_E_UNKNOWN_COMPRESSION_ALGORITHM;
}
session->security_parameters.compression_method = id;
_gnutls_epoch_set_compression (session, EPOCH_NEXT, id);
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.
*/
static int
_gnutls_client_check_if_resuming (gnutls_session_t session,
uint8_t * session_id, int session_id_len)
{
char buf[2 * TLS_MAX_SESSION_ID_SIZE + 1];
_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_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);
_gnutls_epoch_set_cipher_suite
(session, EPOCH_NEXT,
session->internals.
resumed_security_parameters.cipher_suite);
_gnutls_epoch_set_compression (session, EPOCH_NEXT,
session->
internals.resumed_security_parameters.compression_method);
session->internals.resumed = RESUME_TRUE; /* we are resuming */
return 0;
}
else
{
/* keep the new session id */
session->internals.resumed = RESUME_FALSE; /* we are not resuming */
session->security_parameters.session_id_size = session_id_len;
memcpy (session->security_parameters.session_id,
session_id, session_id_len);
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
_gnutls_read_server_hello (gnutls_session_t session,
uint8_t * data, int datalen)
{
uint8_t session_id_len = 0;
int pos = 0;
int ret = 0;
gnutls_protocol_t version;
int len = datalen;
if (datalen < 38)
{
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);
version = _gnutls_version_get (data[pos], data[pos + 1]);
if (_gnutls_version_is_supported (session, version) == 0)
{
gnutls_assert ();
return GNUTLS_E_UNSUPPORTED_VERSION_PACKET;
}
else
{
_gnutls_set_current_version (session, version);
}
pos += 2;
DECR_LEN (len, GNUTLS_RANDOM_SIZE);
ret = _gnutls_set_server_random (session, &data[pos]);
if (ret < 0)
return gnutls_assert_val(ret);
pos += GNUTLS_RANDOM_SIZE;
/* Read session ID
*/
DECR_LEN (len, 1);
session_id_len = data[pos++];
if (len < session_id_len)
{
gnutls_assert ();
return GNUTLS_E_UNSUPPORTED_VERSION_PACKET;
}
DECR_LEN (len, session_id_len);
/* check if we are resuming and set the appropriate
* values;
*/
if (_gnutls_client_check_if_resuming
(session, &data[pos], session_id_len) == 0)
{
pos += session_id_len + 2 + 1;
DECR_LEN (len, 2 + 1);
ret = _gnutls_parse_extensions (session, GNUTLS_EXT_MANDATORY,
&data[pos], len);
if (ret < 0)
{
gnutls_assert ();
return ret;
}
return 0;
}
pos += session_id_len;
/* Check if the given cipher suite is supported and copy
* it to the session.
*/
DECR_LEN (len, 2);
ret = _gnutls_client_set_ciphersuite (session, &data[pos]);
if (ret < 0)
{
gnutls_assert ();
return ret;
}
pos += 2;
/* move to compression
*/
DECR_LEN (len, 1);
ret = _gnutls_client_set_comp_method (session, data[pos++]);
if (ret < 0)
{
gnutls_assert ();
return GNUTLS_E_UNKNOWN_COMPRESSION_ALGORITHM;
}
/* Parse extensions.
*/
ret = _gnutls_parse_extensions (session, GNUTLS_EXT_ANY, &data[pos], len);
if (ret < 0)
{
gnutls_assert ();
return ret;
}
return ret;
}
/* This function copies the appropriate ciphersuites to a locally allocated buffer
* Needed in client hello messages. Returns the new data length. If add_scsv is
* true, add the special safe renegotiation CS.
*/
static int
_gnutls_copy_ciphersuites (gnutls_session_t session,
gnutls_buffer_st * cdata,
int add_scsv)
{
int ret;
uint8_t cipher_suites[MAX_CIPHERSUITE_SIZE+2];
int cipher_suites_size;
size_t init_length = cdata->length;
ret = _gnutls_supported_ciphersuites (session, cipher_suites, sizeof(cipher_suites)-2);
if (ret < 0)
return gnutls_assert_val(ret);
/* Here we remove any ciphersuite that does not conform
* the certificate requested, or to the
* authentication requested (eg SRP).
*/
ret =
_gnutls_remove_unwanted_ciphersuites (session, cipher_suites, ret, NULL, 0);
if (ret < 0)
return gnutls_assert_val(ret);
/* If no cipher suites were enabled.
*/
if (ret == 0)
return gnutls_assert_val(GNUTLS_E_INSUFFICIENT_CREDENTIALS);
cipher_suites_size = ret;
if (add_scsv)
{
cipher_suites[cipher_suites_size] = 0x00;
cipher_suites[cipher_suites_size+1] = 0xff;
cipher_suites_size += 2;
ret = _gnutls_ext_sr_send_cs (session);
if (ret < 0)
return gnutls_assert_val(ret);
}
ret = _gnutls_buffer_append_data_prefix(cdata, 16, cipher_suites, cipher_suites_size);
if (ret < 0)
return gnutls_assert_val(ret);
ret = cdata->length - init_length;
return ret;
}
/* This function copies the appropriate compression methods, to a locally allocated buffer
* Needed in hello messages. Returns the new data length.
*/
static int
_gnutls_copy_comp_methods (gnutls_session_t session,
gnutls_buffer_st * cdata)
{
int ret;
uint8_t compression_methods[MAX_ALGOS], comp_num;
size_t init_length = cdata->length;
ret = _gnutls_supported_compression_methods (session, compression_methods, MAX_ALGOS);
if (ret < 0)
return gnutls_assert_val(ret);
comp_num = ret;
/* put the number of compression methods */
ret = _gnutls_buffer_append_prefix(cdata, 8, comp_num);
if (ret < 0)
return gnutls_assert_val(ret);
ret = _gnutls_buffer_append_data(cdata, compression_methods, comp_num);
if (ret < 0)
return gnutls_assert_val(ret);
ret = cdata->length - init_length;
return ret;
}
/* This should be sufficient by now. It should hold all the extensions
* plus the headers in a hello message.
*/
#define MAX_EXT_DATA_LENGTH 32*1024
/* This function sends the client hello handshake message.
*/
static int
_gnutls_send_client_hello (gnutls_session_t session, int again)
{
mbuffer_st *bufel = NULL;
uint8_t *data = NULL;
int pos = 0, type;
int datalen = 0, ret = 0;
gnutls_protocol_t hver;
gnutls_buffer_st extdata;
int rehandshake = 0;
uint8_t session_id_len =
session->internals.resumed_security_parameters.session_id_size;
uint8_t cookie_len;
_gnutls_buffer_init(&extdata);
/* note that rehandshake is different than resuming
*/
if (session->security_parameters.session_id_size)
rehandshake = 1;
if (again == 0)
{
if(IS_DTLS(session))
{
cookie_len = session->internals.dtls.cookie_len + 1;
}
else
{
cookie_len = 0;
}
datalen = 2 + (session_id_len + 1) + GNUTLS_RANDOM_SIZE + cookie_len;
/* 2 for version, (4 for unix time + 28 for random bytes==GNUTLS_RANDOM_SIZE)
*/
bufel = _gnutls_handshake_alloc (session, datalen, datalen+MAX_EXT_DATA_LENGTH);
if (bufel == NULL)
{
gnutls_assert ();
return GNUTLS_E_MEMORY_ERROR;
}
data = _mbuffer_get_udata_ptr (bufel);
/* if we are resuming a session then we set the
* version number to the previously established.
*/
if (session_id_len == 0)
{
if (rehandshake) /* already negotiated version thus version_max == negotiated version */
hver = session->security_parameters.version;
else /* new handshake. just get the max */
hver = _gnutls_version_max (session);
}
else
{
/* we are resuming a session */
hver = session->internals.resumed_security_parameters.version;
}
if (hver == GNUTLS_VERSION_UNKNOWN || hver == 0)
{
gnutls_assert ();
gnutls_free (bufel);
return GNUTLS_E_INTERNAL_ERROR;
}
data[pos++] = _gnutls_version_get_major (hver);
data[pos++] = _gnutls_version_get_minor (hver);
/* Set the version we advertized as maximum
* (RSA uses it).
*/
_gnutls_set_adv_version (session, hver);
_gnutls_set_current_version (session, hver);
if (session->internals.priorities.ssl3_record_version != 0)
{
/* Advertize the 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.
*/
if (!IS_DTLS(session))
_gnutls_record_set_default_version (session, 3, 0);
else if (gnutls_protocol_get_version (session) == GNUTLS_DTLS0_9)
_gnutls_record_set_default_version (session, 1, 0);
else
_gnutls_record_set_default_version (session, 254, 255);
}
/* In order to know when this session was initiated.
*/
session->security_parameters.timestamp = gnutls_time (NULL);
/* Generate random data
*/
if (!IS_DTLS (session)
|| session->internals.dtls.hsk_hello_verify_requests == 0)
{
ret = _gnutls_set_client_random (session, NULL);
if (ret < 0)
return gnutls_assert_val(ret);
memcpy (&data[pos], session->security_parameters.client_random, GNUTLS_RANDOM_SIZE);
}
else
memcpy (&data[pos], session->security_parameters.client_random, GNUTLS_RANDOM_SIZE);
pos += GNUTLS_RANDOM_SIZE;
/* Copy the Session ID
*/
data[pos++] = session_id_len;
if (session_id_len > 0)
{
memcpy (&data[pos],
session->internals.resumed_security_parameters.session_id,
session_id_len);
pos += session_id_len;
}
/* Copy the DTLS cookie
*/
if (IS_DTLS(session))
{
data[pos++] = session->internals.dtls.cookie_len;
memcpy(&data[pos], &session->internals.dtls.cookie, session->internals.dtls.cookie_len);
/* pos += session->internals.dtls.cookie_len; */
}
/* Copy the ciphersuites.
*
* 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 &&
(gnutls_protocol_get_version (session) == GNUTLS_SSL3 ||
session->internals.priorities.no_extensions != 0))
{
ret =
_gnutls_copy_ciphersuites (session, &extdata, TRUE);
_gnutls_extension_list_add (session,
GNUTLS_EXTENSION_SAFE_RENEGOTIATION);
}
else
ret = _gnutls_copy_ciphersuites (session, &extdata, FALSE);
if (ret < 0)
{
gnutls_assert();
goto cleanup;
}
/* Copy the compression methods.
*/
ret = _gnutls_copy_comp_methods (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
{
if (session->internals.initial_negotiation_completed != 0)
type = GNUTLS_EXT_MANDATORY;
else
type = GNUTLS_EXT_NONE;
}
ret = _gnutls_gen_extensions (session, &extdata, type);
if (ret < 0)
{
gnutls_assert();
goto cleanup;
}
}
ret = _mbuffer_append_data (bufel, extdata.data, extdata.length);
if (ret < 0)
{
gnutls_assert ();
goto cleanup;
}
}
_gnutls_buffer_clear(&extdata);
return
_gnutls_send_handshake (session, bufel, GNUTLS_HANDSHAKE_CLIENT_HELLO);
cleanup:
_mbuffer_xfree(&bufel);
_gnutls_buffer_clear(&extdata);
return ret;
}
static int
_gnutls_send_server_hello (gnutls_session_t session, int again)
{
mbuffer_st *bufel = NULL;
uint8_t *data = NULL;
gnutls_buffer_st extdata;
int pos = 0;
int datalen, ret = 0;
uint8_t comp;
uint8_t session_id_len = session->security_parameters.session_id_size;
char buf[2 * TLS_MAX_SESSION_ID_SIZE + 1];
_gnutls_buffer_init(&extdata);
if (again == 0)
{
datalen = 2 + session_id_len + 1 + GNUTLS_RANDOM_SIZE + 3;
ret =
_gnutls_gen_extensions (session, &extdata, GNUTLS_EXT_ANY);
if (ret < 0)
{
gnutls_assert ();
goto fail;
}
bufel = _gnutls_handshake_alloc (session, datalen + extdata.length, datalen + extdata.length);
if (bufel == NULL)
{
gnutls_assert ();
ret = GNUTLS_E_MEMORY_ERROR;
goto fail;
}
data = _mbuffer_get_udata_ptr (bufel);
data[pos++] =
_gnutls_version_get_major (session->security_parameters.version);
data[pos++] =
_gnutls_version_get_minor (session->security_parameters.version);
memcpy (&data[pos],
session->security_parameters.server_random, GNUTLS_RANDOM_SIZE);
pos += GNUTLS_RANDOM_SIZE;
data[pos++] = session_id_len;
if (session_id_len > 0)
{
memcpy (&data[pos], session->security_parameters.session_id,
session_id_len);
}
pos += session_id_len;
_gnutls_handshake_log ("HSK[%p]: SessionID: %s\n", session,
_gnutls_bin2hex (session->security_parameters.
session_id, session_id_len, buf,
sizeof (buf), NULL));
memcpy (&data[pos],
session->security_parameters.cipher_suite, 2);
pos += 2;
comp = _gnutls_compression_get_num ( session->security_parameters.compression_method);
data[pos++] = comp;
if (extdata.length > 0)
{
memcpy (&data[pos], extdata.data, extdata.length);
}
}
ret =
_gnutls_send_handshake (session, bufel, GNUTLS_HANDSHAKE_SERVER_HELLO);
fail:
_gnutls_buffer_clear(&extdata);
return ret;
}
int
_gnutls_send_hello (gnutls_session_t session, int again)
{
int ret;
if (session->security_parameters.entity == GNUTLS_CLIENT)
{
ret = _gnutls_send_client_hello (session, again);
}
else
{ /* SERVER */
ret = _gnutls_send_server_hello (session, again);
}
return ret;
}
/* RECEIVE A HELLO MESSAGE. This should be called from gnutls_recv_handshake_int only if a
* hello message is expected. It uses the security_parameters.cipher_suite
* and internals.compression_method.
*/
int
_gnutls_recv_hello (gnutls_session_t session, uint8_t * data, int datalen)
{
int ret;
if (session->security_parameters.entity == GNUTLS_CLIENT)
{
ret = _gnutls_read_server_hello (session, data, datalen);
if (ret < 0)
{
gnutls_assert ();
return ret;
}
}
else
{ /* Server side reading a client hello */
ret = _gnutls_read_client_hello (session, data, datalen);
if (ret < 0)
{
gnutls_assert ();
return ret;
}
}
ret = _gnutls_ext_sr_verify (session);
if (ret < 0)
{
gnutls_assert ();
return ret;
}
return 0;
}
static int
_gnutls_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;
if (!IS_DTLS (session)
|| session->security_parameters.entity == GNUTLS_SERVER)
{
gnutls_assert ();
return GNUTLS_E_INTERNAL_ERROR;
}
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;
}
/* TODO: determine if we need to do anything with the server version field */
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);
session->internals.dtls.cookie_len = cookie_len;
memcpy (session->internals.dtls.cookie, &data[pos], cookie_len);
if (len != 0)
{
gnutls_assert ();
return GNUTLS_E_UNEXPECTED_PACKET_LENGTH;
}
/* reset handshake hash buffers */
_gnutls_handshake_hash_buffer_empty (session);
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 structure.
*
* This function will renegotiate security parameters with the
* client. This should only be called in case of a server.
*
* This message informs the peer that we want to renegotiate
* parameters (perform a handshake).
*
* If this function succeeds (returns 0), you must call the
* gnutls_handshake() function in order to negotiate the new
* parameters.
*
* Since TLS is full duplex some application data might have been
* sent during peer's processing of this message. In that case
* one should call gnutls_record_recv() until GNUTLS_E_REHANDSHAKE
* is returned to clear any pending data. Care must be taken if
* rehandshake is mandatory to terminate if it does not start after
* some threshold.
*
* If the client does not wish to renegotiate parameters he will
* should with an alert message, thus the return code will be
* %GNUTLS_E_WARNING_ALERT_RECEIVED and the alert will be
* %GNUTLS_A_NO_RENEGOTIATION. A client may also choose to ignore
* this message.
*
* Returns: %GNUTLS_E_SUCCESS on success, otherwise a negative error code.
**/
int
gnutls_rehandshake (gnutls_session_t session)
{
int ret;
/* only server sends that handshake packet */
if (session->security_parameters.entity == GNUTLS_CLIENT)
return GNUTLS_E_INVALID_REQUEST;
_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;
}
inline static int
_gnutls_abort_handshake (gnutls_session_t session, int ret)
{
if (((ret == GNUTLS_E_WARNING_ALERT_RECEIVED) &&
(gnutls_alert_get (session) == GNUTLS_A_NO_RENEGOTIATION))
|| ret == GNUTLS_E_GOT_APPLICATION_DATA)
return 0;
/* this doesn't matter */
return GNUTLS_E_INTERNAL_ERROR;
}
static int
_gnutls_send_supplemental (gnutls_session_t session, int again)
{
mbuffer_st *bufel;
int ret = 0;
_gnutls_debug_log ("EXT[%p]: Sending supplemental data\n", session);
if (again)
ret =
_gnutls_send_handshake (session, NULL, GNUTLS_HANDSHAKE_SUPPLEMENTAL);
else
{
gnutls_buffer_st buf;
_gnutls_buffer_init (&buf);
ret = _gnutls_gen_supplemental (session, &buf);
if (ret < 0)
{
gnutls_assert ();
return ret;
}
bufel = _gnutls_handshake_alloc(session, buf.length, buf.length);
if (bufel == NULL)
{
gnutls_assert ();
return GNUTLS_E_MEMORY_ERROR;
}
_mbuffer_set_udata (bufel, buf.data, buf.length);
_gnutls_buffer_clear (&buf);
ret = _gnutls_send_handshake (session, bufel,
GNUTLS_HANDSHAKE_SUPPLEMENTAL);
}
return ret;
}
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 structure.
*
* This function does the handshake of the TLS/SSL protocol, and
* initializes the TLS connection.
*
* This function will fail if any problem is encountered, and will
* return a negative error code. In case of a client, if the client
* has asked to resume a session, but the server couldn't, then a
* full handshake will be performed.
*
* The non-fatal errors such as %GNUTLS_E_AGAIN and
* %GNUTLS_E_INTERRUPTED interrupt the handshake procedure, which
* should be resumed later. Call this function again, until it
* returns 0; cf. gnutls_record_get_direction() and
* gnutls_error_is_fatal().
*
* If this function is called by a server after a rehandshake request
* then %GNUTLS_E_GOT_APPLICATION_DATA or
* %GNUTLS_E_WARNING_ALERT_RECEIVED may be returned. Note that these
* are non fatal errors, only in the specific case of a rehandshake.
* Their meaning is that the client rejected the rehandshake request or
* in the case of %GNUTLS_E_GOT_APPLICATION_DATA it might also mean that
* some data were pending.
*
* Returns: %GNUTLS_E_SUCCESS on success, otherwise a negative error code.
**/
int
gnutls_handshake (gnutls_session_t session)
{
int ret;
record_parameters_st *params;
/* sanity check. Verify that there are priorities setup.
*/
if (session->internals.priorities.protocol.algorithms == 0)
return gnutls_assert_val(GNUTLS_E_NO_PRIORITIES_WERE_SET);
if (session->internals.handshake_timeout_ms &&
session->internals.handshake_endtime == 0)
session->internals.handshake_endtime = gnutls_time(0) +
session->internals.handshake_timeout_ms / 1000;
ret = _gnutls_epoch_get (session, session->security_parameters.epoch_next,
¶ms);
if (ret < 0)
{
/* We assume the epoch is not allocated if _gnutls_epoch_get fails. */
ret =
_gnutls_epoch_alloc (session, session->security_parameters.epoch_next,
NULL);
if (ret < 0)
return gnutls_assert_val(ret);
}
if (session->security_parameters.entity == GNUTLS_CLIENT)
{
do
{
ret = _gnutls_handshake_client (session);
} while (ret == 1);
}
else
{
ret = _gnutls_handshake_server (session);
}
if (ret < 0)
{
/* In the case of a rehandshake abort
* we should reset the handshake's internal state.
*/
if (_gnutls_abort_handshake (session, ret) == 0)
STATE = STATE0;
return ret;
}
/* clear handshake buffer */
_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);
session->security_parameters.epoch_next++;
ret = _gnutls_ext_after_handshake(session);
if (ret < 0)
return gnutls_assert_val(ret);
return 0;
}
/**
* gnutls_handshake_set_timeout:
* @session: is a #gnutls_session_t structure.
* @ms: is a timeout value in milliseconds
*
* This function sets the timeout for the handshake process
* to the provided value. Use an @ms value of zero to disable
* timeout.
*
* Note that in order for the timeout to be enforced
* gnutls_transport_set_pull_timeout_function() must be set
* (it is set by default in most systems).
*
**/
void
gnutls_handshake_set_timeout (gnutls_session_t session, unsigned int ms)
{
if (ms == GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT)
ms = 40*1000;
session->internals.handshake_timeout_ms = ms;
}
#define IMED_RET( str, ret, allow_alert) do { \
if (ret < 0) { \
/* EAGAIN and INTERRUPTED are always non-fatal */ \
if (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED) \
return ret; \
/* a warning alert might interrupt handshake */ \
if (allow_alert != 0 && ret==GNUTLS_E_WARNING_ALERT_RECEIVED) return ret; \
gnutls_assert(); \
ERR( str, ret); \
_gnutls_handshake_hash_buffers_clear(session); \
return ret; \
} } while (0)
/* Runs the certificate verification callback.
* side is either GNUTLS_CLIENT or GNUTLS_SERVER.
*/
static int run_verify_callback(gnutls_session_t session, unsigned int side)
{
gnutls_certificate_credentials_t cred;
int ret, type;
cred =
(gnutls_certificate_credentials_t) _gnutls_get_cred (session,
GNUTLS_CRD_CERTIFICATE,
NULL);
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;
if (cred != NULL && cred->verify_callback != NULL &&
(session->security_parameters.entity == GNUTLS_CLIENT ||
session->internals.send_cert_req != GNUTLS_CERT_IGNORE))
{
ret = cred->verify_callback (session);
if (ret < -1)
return ret;
else if (ret != 0)
return GNUTLS_E_CERTIFICATE_ERROR;
}
return 0;
}
/*
* _gnutls_handshake_client
* This function performs the client side of the handshake of the TLS/SSL protocol.
*/
static int
_gnutls_handshake_client (gnutls_session_t session)
{
int ret = 0;
#ifdef HANDSHAKE_DEBUG
char buf[64];
if (session->internals.resumed_security_parameters.session_id_size > 0)
_gnutls_handshake_log ("HSK[%p]: Ask to resume: %s\n", session,
_gnutls_bin2hex (session->
internals.resumed_security_parameters.session_id,
session->
internals.resumed_security_parameters.session_id_size,
buf, sizeof (buf), NULL));
#endif
switch (STATE)
{
case STATE0:
case STATE1:
ret = _gnutls_send_hello (session, AGAIN (STATE1));
STATE = STATE1;
IMED_RET ("send hello", ret, 1);
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;
}
}
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);
case STATE4:
if (session->security_parameters.do_recv_supplemental)
{
ret = _gnutls_recv_supplemental (session);
STATE = STATE4;
IMED_RET ("recv supplemental", ret, 1);
}
case STATE5:
/* RECV CERTIFICATE */
if (session->internals.resumed == RESUME_FALSE) /* if we are not resuming */
ret = _gnutls_recv_server_certificate (session);
STATE = STATE5;
IMED_RET ("recv server certificate", ret, 1);
case STATE6:
/* RECV CERTIFICATE STATUS */
if (session->internals.resumed == RESUME_FALSE) /* if we are not resuming */
ret = _gnutls_recv_server_certificate_status (session);
STATE = STATE6;
IMED_RET ("recv server certificate", ret, 1);
case STATE7:
ret = run_verify_callback(session, GNUTLS_CLIENT);
STATE = STATE7;
if (ret < 0)
return gnutls_assert_val(ret);
case STATE8:
/* receive the server key exchange */
if (session->internals.resumed == RESUME_FALSE) /* if we are not resuming */
ret = _gnutls_recv_server_kx_message (session);
STATE = STATE8;
IMED_RET ("recv server kx message", ret, 1);
case STATE9:
/* receive the server certificate request - if any
*/
if (session->internals.resumed == RESUME_FALSE) /* if we are not resuming */
ret = _gnutls_recv_server_crt_request (session);
STATE = STATE9;
IMED_RET ("recv server certificate request message", ret, 1);
case STATE10:
/* receive the server hello done */
if (session->internals.resumed == RESUME_FALSE) /* if we are not resuming */
ret =
_gnutls_recv_handshake (session,
GNUTLS_HANDSHAKE_SERVER_HELLO_DONE,
0, NULL);
STATE = STATE10;
IMED_RET ("recv server hello done", ret, 1);
case STATE11:
if (session->security_parameters.do_send_supplemental)
{
ret = _gnutls_send_supplemental (session, AGAIN (STATE11));
STATE = STATE11;
IMED_RET ("send supplemental", ret, 0);
}
case STATE12:
/* send our certificate - if any and if requested
*/
if (session->internals.resumed == RESUME_FALSE) /* if we are not resuming */
ret = _gnutls_send_client_certificate (session, AGAIN (STATE12));
STATE = STATE12;
IMED_RET ("send client certificate", ret, 0);
case STATE13:
if (session->internals.resumed == RESUME_FALSE) /* if we are not resuming */
ret = _gnutls_send_client_kx_message (session, AGAIN (STATE13));
STATE = STATE13;
IMED_RET ("send client kx", ret, 0);
case STATE14:
/* send client certificate verify */
if (session->internals.resumed == RESUME_FALSE) /* if we are not resuming */
ret =
_gnutls_send_client_certificate_verify (session, AGAIN (STATE14));
STATE = STATE14;
IMED_RET ("send client certificate verify", ret, 1);
case STATE15:
STATE = STATE15;
if (session->internals.resumed == RESUME_FALSE)
{
ret = _gnutls_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);
}
case STATE16:
STATE = STATE16;
if (session->internals.resumed == RESUME_FALSE)
{
ret = _gnutls_recv_new_session_ticket (session);
IMED_RET ("recv handshake new session ticket", ret, 1);
}
else
{
ret = _gnutls_recv_handshake_final (session, TRUE);
IMED_RET ("recv handshake final", ret, 1);
}
case STATE17:
STATE = STATE17;
if (session->internals.resumed == RESUME_FALSE)
{
ret = _gnutls_recv_handshake_final (session, FALSE);
IMED_RET ("recv handshake final 2", ret, 1);
}
else
{
ret = _gnutls_send_handshake_final (session, FALSE);
IMED_RET ("send handshake final", ret, 1);
}
STATE = STATE0;
default:
break;
}
return 0;
}
/* This function is to be called if the handshake was successfully
* completed. This sends a Change Cipher Spec packet to the peer.
*/
static ssize_t
send_change_cipher_spec (gnutls_session_t session, int again)
{
uint8_t* data;
mbuffer_st * bufel;
int ret;
if (again == 0)
{
bufel = _gnutls_handshake_alloc (session, 1, 1);
if (bufel == NULL)
return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
if (gnutls_protocol_get_version (session) == GNUTLS_DTLS0_9)
_mbuffer_set_uhead_size(bufel, 3);
else
_mbuffer_set_uhead_size(bufel, 1);
_mbuffer_set_udata_size(bufel, 0);
data = _mbuffer_get_uhead_ptr (bufel);
data[0] = 1;
if (gnutls_protocol_get_version (session) == GNUTLS_DTLS0_9)
{
_gnutls_write_uint16 (session->internals.dtls.hsk_write_seq, &data[1]);
session->internals.dtls.hsk_write_seq++;
}
ret = _gnutls_handshake_io_cache_int (session, GNUTLS_HANDSHAKE_CHANGE_CIPHER_SPEC, bufel);
if (ret < 0)
{
_mbuffer_xfree(&bufel);
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
_gnutls_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 = send_change_cipher_spec (session, FAGAIN (STATE1));
FINAL_STATE = STATE0;
if (ret < 0)
{
ERR ("send ChangeCipherSpec", ret);
gnutls_assert ();
return ret;
}
/* Initialize the connection session (start encryption) - in case of client
*/
if (init == TRUE)
{
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;
}
case STATE2:
/* send the finished message */
ret = _gnutls_send_finished (session, FAGAIN (STATE2));
FINAL_STATE = STATE2;
if (ret < 0)
{
ERR ("send Finished", ret);
gnutls_assert ();
return ret;
}
FINAL_STATE = STATE0;
default:
break;
}
return 0;
}
/* This function receives the final handshake packets
* And executes the appropriate function to initialize the
* read session.
*/
static int
_gnutls_recv_handshake_final (gnutls_session_t session, int init)
{
int ret = 0;
uint8_t ch;
unsigned int ccs_len = 1;
unsigned int tleft;
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);
}
if (gnutls_protocol_get_version (session) == GNUTLS_DTLS0_9)
ccs_len = 3;
ret = _gnutls_recv_int (session, GNUTLS_CHANGE_CIPHER_SPEC, -1, &ch, ccs_len, NULL,
tleft);
if (ret <= 0)
{
ERR ("recv ChangeCipherSpec", ret);
gnutls_assert ();
return (ret < 0) ? ret : GNUTLS_E_UNEXPECTED_PACKET_LENGTH;
}
if (gnutls_protocol_get_version (session) == GNUTLS_DTLS0_9)
session->internals.dtls.hsk_read_seq++;
/* Initialize the connection session (start encryption) - in case of server */
if (init == TRUE)
{
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;
}
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)
{
ERR ("recv finished", ret);
gnutls_assert ();
return ret;
}
FINAL_STATE = STATE0;
default:
break;
}
return 0;
}
/*
* _gnutls_handshake_server
* This function does the server stuff of the handshake protocol.
*/
static int
_gnutls_handshake_server (gnutls_session_t session)
{
int ret = 0;
switch (STATE)
{
case STATE0:
case STATE1:
ret =
_gnutls_recv_handshake (session,
GNUTLS_HANDSHAKE_CLIENT_HELLO,
0, NULL);
STATE = STATE1;
IMED_RET ("recv hello", ret, 1);
case STATE2:
ret = _gnutls_send_hello (session, AGAIN (STATE2));
STATE = STATE2;
IMED_RET ("send hello", ret, 1);
case STATE70:
if (session->security_parameters.do_send_supplemental)
{
ret = _gnutls_send_supplemental (session, AGAIN (STATE70));
STATE = STATE70;
IMED_RET ("send supplemental data", ret, 0);
}
/* SEND CERTIFICATE + KEYEXCHANGE + CERTIFICATE_REQUEST */
case STATE3:
/* NOTE: these should not be send if we are resuming */
if (session->internals.resumed == RESUME_FALSE)
ret = _gnutls_send_server_certificate (session, AGAIN (STATE3));
STATE = STATE3;
IMED_RET ("send server certificate", ret, 0);
case STATE4:
if (session->internals.resumed == RESUME_FALSE)
ret = _gnutls_send_server_certificate_status (session, AGAIN (STATE4));
STATE = STATE4;
IMED_RET ("send server certificate status", ret, 0);
case STATE5:
/* send server key exchange (A) */
if (session->internals.resumed == RESUME_FALSE)
ret = _gnutls_send_server_kx_message (session, AGAIN (STATE5));
STATE = STATE5;
IMED_RET ("send server kx", ret, 0);
case STATE6:
/* Send certificate request - if requested to */
if (session->internals.resumed == RESUME_FALSE)
ret =
_gnutls_send_server_crt_request (session, AGAIN (STATE6));
STATE = STATE6;
IMED_RET ("send server cert request", ret, 0);
case STATE7:
/* send the server hello done */
if (session->internals.resumed == RESUME_FALSE) /* if we are not resuming */
ret =
_gnutls_send_empty_handshake (session,
GNUTLS_HANDSHAKE_SERVER_HELLO_DONE,
AGAIN (STATE7));
STATE = STATE7;
IMED_RET ("send server hello done", ret, 1);
case STATE71:
if (session->security_parameters.do_recv_supplemental)
{
ret = _gnutls_recv_supplemental (session);
STATE = STATE71;
IMED_RET ("recv client supplemental", ret, 1);
}
/* RECV CERTIFICATE + KEYEXCHANGE + CERTIFICATE_VERIFY */
case STATE8:
/* receive the client certificate message */
if (session->internals.resumed == RESUME_FALSE) /* if we are not resuming */
ret = _gnutls_recv_client_certificate (session);
STATE = STATE8;
IMED_RET ("recv client certificate", ret, 1);
case STATE9:
ret = run_verify_callback(session, GNUTLS_SERVER);
STATE = STATE9;
if (ret < 0)
return gnutls_assert_val(ret);
case STATE10:
/* receive the client key exchange message */
if (session->internals.resumed == RESUME_FALSE) /* if we are not resuming */
ret = _gnutls_recv_client_kx_message (session);
STATE = STATE10;
IMED_RET ("recv client kx", ret, 1);
case STATE11:
/* receive the client certificate verify message */
if (session->internals.resumed == RESUME_FALSE) /* if we are not resuming */
ret = _gnutls_recv_client_certificate_verify_message (session);
STATE = STATE11;
IMED_RET ("recv client certificate verify", ret, 1);
case STATE12:
STATE = STATE12;
if (session->internals.resumed == RESUME_FALSE) /* if we are not resuming */
{
ret = _gnutls_recv_handshake_final (session, TRUE);
IMED_RET ("recv handshake final", ret, 1);
}
else
{
ret = _gnutls_send_handshake_final (session, TRUE);
IMED_RET ("send handshake final 2", ret, 1);
}
case STATE13:
ret = _gnutls_send_new_session_ticket (session, AGAIN (STATE13));
STATE = STATE13;
IMED_RET ("send handshake new session ticket", ret, 0);
case STATE14:
STATE = STATE14;
if (session->internals.resumed == RESUME_FALSE) /* if we are not resuming */
{
ret = _gnutls_send_handshake_final (session, FALSE);
IMED_RET ("send handshake final", ret, 1);
}
else
{
ret = _gnutls_recv_handshake_final (session, FALSE);
IMED_RET ("recv handshake final 2", ret, 1);
}
if (session->security_parameters.entity == GNUTLS_SERVER && session->internals.ticket_sent == 0)
{
/* if no ticket, save session data */
_gnutls_server_register_current_session (session);
}
STATE = STATE0;
default:
break;
}
return 0;
}
int
_gnutls_generate_session_id (uint8_t * session_id, uint8_t * len)
{
int ret;
*len = TLS_MAX_SESSION_ID_SIZE;
ret = _gnutls_rnd (GNUTLS_RND_NONCE, session_id, *len);
if (ret < 0)
{
gnutls_assert ();
return ret;
}
return 0;
}
int
_gnutls_recv_hello_request (gnutls_session_t session, void *data,
uint32_t data_size)
{
uint8_t type;
if (session->security_parameters.entity == GNUTLS_SERVER)
{
gnutls_assert ();
return GNUTLS_E_UNEXPECTED_PACKET;
}
if (data_size < 1)
{
gnutls_assert ();
return GNUTLS_E_UNEXPECTED_PACKET_LENGTH;
}
type = ((uint8_t *) data)[0];
if (type == GNUTLS_HANDSHAKE_HELLO_REQUEST)
{
if (IS_DTLS(session))
session->internals.dtls.hsk_read_seq++;
return GNUTLS_E_REHANDSHAKE;
}
else
{
gnutls_assert ();
return GNUTLS_E_UNEXPECTED_PACKET;
}
}
/* Returns 1 if the given KX has not the corresponding parameters
* (DH or RSA) set up. Otherwise returns 0.
*/
inline static int
check_server_params (gnutls_session_t session,
gnutls_kx_algorithm_t kx,
gnutls_kx_algorithm_t * alg, int alg_size)
{
int cred_type;
gnutls_dh_params_t dh_params = NULL;
gnutls_rsa_params_t rsa_params = NULL;
int j;
cred_type = _gnutls_map_kx_get_cred (kx, 1);
/* Read the Diffie-Hellman parameters, if any.
*/
if (cred_type == GNUTLS_CRD_CERTIFICATE)
{
int delete;
gnutls_certificate_credentials_t x509_cred =
(gnutls_certificate_credentials_t) _gnutls_get_cred (session,
cred_type, NULL);
if (x509_cred != NULL)
{
dh_params =
_gnutls_get_dh_params (x509_cred->dh_params,
x509_cred->params_func, session);
rsa_params =
_gnutls_certificate_get_rsa_params (x509_cred->rsa_params,
x509_cred->params_func,
session);
}
/* Check also if the certificate supports the
* KX method.
*/
delete = 1;
for (j = 0; j < alg_size; j++)
{
if (alg[j] == kx)
{
delete = 0;
break;
}
}
if (delete == 1)
return 1;
#ifdef ENABLE_ANON
}
else if (cred_type == GNUTLS_CRD_ANON)
{
gnutls_anon_server_credentials_t anon_cred =
(gnutls_anon_server_credentials_t) _gnutls_get_cred (session,
cred_type, NULL);
if (anon_cred != NULL)
{
dh_params =
_gnutls_get_dh_params (anon_cred->dh_params,
anon_cred->params_func, session);
}
#endif
#ifdef ENABLE_PSK
}
else if (cred_type == GNUTLS_CRD_PSK)
{
gnutls_psk_server_credentials_t psk_cred =
(gnutls_psk_server_credentials_t) _gnutls_get_cred (session,
cred_type, NULL);
if (psk_cred != NULL)
{
dh_params =
_gnutls_get_dh_params (psk_cred->dh_params, psk_cred->params_func,
session);
}
#endif
}
else
return 0; /* no need for params */
/* If the key exchange method needs RSA or DH params,
* but they are not set then remove it.
*/
if (_gnutls_kx_needs_rsa_params (kx) != 0)
{
/* needs rsa params. */
if (_gnutls_rsa_params_to_mpi (rsa_params) == NULL)
{
gnutls_assert ();
return 1;
}
}
if (_gnutls_kx_needs_dh_params (kx) != 0)
{
/* needs DH params. */
if (_gnutls_dh_params_to_mpi (dh_params) == NULL)
{
gnutls_assert ();
return 1;
}
}
return 0;
}
/* This function will remove algorithms that are not supported by
* the requested authentication method. We remove an algorithm if
* we have a certificate with keyUsage bits set.
*
* This does a more high level check than gnutls_supported_ciphersuites(),
* by checking certificates etc.
*/
static int
_gnutls_remove_unwanted_ciphersuites (gnutls_session_t session,
uint8_t * cipher_suites,
int cipher_suites_size,
gnutls_pk_algorithm_t *pk_algos,
size_t pk_algos_size)
{
int ret = 0;
int i, new_suites_size;
gnutls_certificate_credentials_t cert_cred;
gnutls_kx_algorithm_t kx;
int server = session->security_parameters.entity == GNUTLS_SERVER ? 1 : 0;
gnutls_kx_algorithm_t alg[MAX_ALGOS];
int alg_size = MAX_ALGOS;
/* if we should use a specific certificate,
* we should remove all algorithms that are not supported
* by that certificate and are on the same authentication
* method (CERTIFICATE).
*/
cert_cred =
(gnutls_certificate_credentials_t) _gnutls_get_cred (session,
GNUTLS_CRD_CERTIFICATE,
NULL);
/* If there are certificate credentials, find an appropriate certificate
* or disable them;
*/
if (session->security_parameters.entity == GNUTLS_SERVER
&& cert_cred != NULL && pk_algos_size > 0)
{
ret = _gnutls_server_select_cert (session, pk_algos, pk_algos_size);
if (ret < 0)
{
gnutls_assert ();
_gnutls_debug_log ("Could not find an appropriate certificate: %s\n",
gnutls_strerror (ret));
}
}
/* get all the key exchange algorithms that are
* supported by the X509 certificate parameters.
*/
if ((ret =
_gnutls_selected_cert_supported_kx (session, alg, &alg_size)) < 0)
{
gnutls_assert ();
return ret;
}
new_suites_size = 0;
/* now removes ciphersuites based on the KX algorithm
*/
for (i = 0; i < cipher_suites_size; i+=2)
{
int delete = 0;
/* finds the key exchange algorithm in
* the ciphersuite
*/
kx = _gnutls_cipher_suite_get_kx_algo (&cipher_suites[i]);
/* if it is defined but had no credentials
*/
if (!session->internals.premaster_set &&
_gnutls_get_kx_cred (session, kx, NULL) == NULL)
{
delete = 1;
}
else
{
delete = 0;
if (server)
delete = check_server_params (session, kx, alg, alg_size);
}
/* If we have not agreed to a common curve with the peer don't bother
* negotiating ECDH.
*/
if (server != 0 && _gnutls_kx_is_ecc(kx))
{
if (_gnutls_session_ecc_curve_get(session) == GNUTLS_ECC_CURVE_INVALID)
{
delete = 1;
}
}
/* These two SRP kx's are marked to require a CRD_CERTIFICATE,
(see cred_mappings in gnutls_algorithms.c), but it also
requires a SRP credential. Don't use SRP kx unless we have a
SRP credential too. */
if (kx == GNUTLS_KX_SRP_RSA || kx == GNUTLS_KX_SRP_DSS)
{
if (!_gnutls_get_cred (session, GNUTLS_CRD_SRP, NULL))
{
delete = 1;
}
}
if (delete == 0)
{
_gnutls_handshake_log ("HSK[%p]: Keeping ciphersuite: %s (%.2X.%.2X)\n",
session,
_gnutls_cipher_suite_get_name (&cipher_suites[i]),
cipher_suites[i], cipher_suites[i+1]);
if (i != new_suites_size)
memmove( &cipher_suites[new_suites_size], &cipher_suites[i], 2);
new_suites_size+=2;
}
else
{
_gnutls_handshake_log ("HSK[%p]: Removing ciphersuite: %s\n",
session,
_gnutls_cipher_suite_get_name (&cipher_suites[i]));
}
}
ret = new_suites_size;
return ret;
}
/**
* gnutls_handshake_set_max_packet_length:
* @session: is a #gnutls_session_t structure.
* @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
* 48kb 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.
**/
void
gnutls_handshake_set_max_packet_length (gnutls_session_t session, size_t max)
{
session->internals.max_handshake_data_buffer_size = max;
}
void
_gnutls_set_adv_version (gnutls_session_t session, gnutls_protocol_t ver)
{
set_adv_version (session, _gnutls_version_get_major (ver),
_gnutls_version_get_minor (ver));
}
gnutls_protocol_t
_gnutls_get_adv_version (gnutls_session_t session)
{
return _gnutls_version_get (_gnutls_get_adv_version_major (session),
_gnutls_get_adv_version_minor (session));
}
/**
* gnutls_handshake_get_last_in:
* @session: is a #gnutls_session_t structure.
*
* 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 structure.
*
* 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;
}