diff options
Diffstat (limited to 'lib/kx.c')
-rw-r--r-- | lib/kx.c | 834 |
1 files changed, 834 insertions, 0 deletions
diff --git a/lib/kx.c b/lib/kx.c new file mode 100644 index 0000000..b016779 --- /dev/null +++ b/lib/kx.c @@ -0,0 +1,834 @@ +/* + * 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 <https://www.gnu.org/licenses/> + * + */ + +/* This file contains functions which are wrappers for the key exchange + * part of TLS. They are called by the handshake functions (gnutls_handshake) + */ + +#include "gnutls_int.h" +#include "handshake.h" +#include "kx.h" +#include "dh.h" +#include "errors.h" +#include "algorithms.h" +#include "debug.h" +#include "locks.h" +#include "mpi.h" +#include <state.h> +#include <datum.h> +#include <mbuffers.h> + +/* This file contains important thing for the TLS handshake procedure. + */ + +#define MASTER_SECRET "master secret" +#define MASTER_SECRET_SIZE (sizeof(MASTER_SECRET)-1) + +#define EXT_MASTER_SECRET "extended master secret" +#define EXT_MASTER_SECRET_SIZE (sizeof(EXT_MASTER_SECRET)-1) + +GNUTLS_STATIC_MUTEX(keylog_mutex); +static FILE *keylog; + +static int generate_normal_master(gnutls_session_t session, + gnutls_datum_t *, int); + +int _gnutls_generate_master(gnutls_session_t session, int keep_premaster) +{ + if (!session->internals.resumed) + return generate_normal_master(session, &session->key.key, + keep_premaster); + else if (session->internals.premaster_set) { + gnutls_datum_t premaster; + premaster.size = + sizeof(session->internals.resumed_security_parameters. + master_secret); + premaster.data = + session->internals.resumed_security_parameters. + master_secret; + return generate_normal_master(session, &premaster, 1); + } + return 0; +} + +/** + * gnutls_session_get_keylog_function: + * @session: is #gnutls_session_t type + * + * This function will return the callback function set using + * gnutls_session_set_keylog_function(). + * + * Returns: The function set or %NULL otherwise. + * + * Since: 3.6.13 + */ +gnutls_keylog_func +gnutls_session_get_keylog_function(const gnutls_session_t session) +{ + return session->internals.keylog_func; +} + +/** + * gnutls_session_set_keylog_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 new secret is + * derived and installed during handshake. + * + * Since: 3.6.13 + */ +void +gnutls_session_set_keylog_function(gnutls_session_t session, + gnutls_keylog_func func) +{ + session->internals.keylog_func = func; +} + +int +_gnutls_call_keylog_func(gnutls_session_t session, + const char *label, + const uint8_t *data, + unsigned size) +{ + if (session->internals.keylog_func) { + gnutls_datum_t secret = {(void*)data, size}; + return session->internals.keylog_func(session, label, &secret); + } + return 0; +} + +int +_gnutls_nss_keylog_func(gnutls_session_t session, + const char *label, + const gnutls_datum_t *secret) +{ + /* ignore subsequent traffic secrets that are calculated from + * the previous traffic secret + */ + if (!session->internals.handshake_in_progress) + return 0; + + _gnutls_nss_keylog_write(session, label, secret->data, secret->size); + return 0; +} + +/* GCC analyzer doesn't like static FILE pointer */ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wanalyzer-file-leak" + +GNUTLS_ONCE(keylog_once); + +static void +keylog_once_init(void) +{ + const char *keylogfile; + + keylogfile = secure_getenv("SSLKEYLOGFILE"); + if (keylogfile != NULL && *keylogfile != '\0') { + keylog = fopen(keylogfile, "ae"); + _gnutls_debug_log("unable to open keylog file %s\n", + keylogfile); + } +} + +void _gnutls_nss_keylog_write(gnutls_session_t session, + const char *label, + const uint8_t *secret, size_t secret_size) +{ + (void)gnutls_once(&keylog_once, keylog_once_init); + + if (keylog) { + char client_random_hex[2*GNUTLS_RANDOM_SIZE+1]; + char secret_hex[2*MAX_HASH_SIZE+1]; + + if (gnutls_static_mutex_lock(&keylog_mutex) < 0) { + return; + } + fprintf(keylog, "%s %s %s\n", + label, + _gnutls_bin2hex(session->security_parameters. + client_random, GNUTLS_RANDOM_SIZE, + client_random_hex, + sizeof(client_random_hex), NULL), + _gnutls_bin2hex(secret, secret_size, + secret_hex, sizeof(secret_hex), NULL)); + fflush(keylog); + (void)gnutls_static_mutex_unlock(&keylog_mutex); + } +} + +void _gnutls_nss_keylog_deinit(void) +{ + if (keylog) { + fclose(keylog); + keylog = NULL; + } +} + +#pragma GCC diagnostic pop + +/* here we generate the TLS Master secret. + */ +static int +generate_normal_master(gnutls_session_t session, + gnutls_datum_t * premaster, int keep_premaster) +{ + int ret = 0; + char buf[512]; + + _gnutls_hard_log("INT: PREMASTER SECRET[%d]: %s\n", + premaster->size, _gnutls_bin2hex(premaster->data, + premaster->size, + buf, sizeof(buf), + NULL)); + _gnutls_hard_log("INT: CLIENT RANDOM[%d]: %s\n", 32, + _gnutls_bin2hex(session->security_parameters. + client_random, 32, buf, + sizeof(buf), NULL)); + _gnutls_hard_log("INT: SERVER RANDOM[%d]: %s\n", 32, + _gnutls_bin2hex(session->security_parameters. + server_random, 32, buf, + sizeof(buf), NULL)); + + if (session->security_parameters.ext_master_secret == 0) { + uint8_t rnd[2 * GNUTLS_RANDOM_SIZE + 1]; + memcpy(rnd, session->security_parameters.client_random, + GNUTLS_RANDOM_SIZE); + memcpy(&rnd[GNUTLS_RANDOM_SIZE], + session->security_parameters.server_random, + GNUTLS_RANDOM_SIZE); + + _gnutls_memory_mark_defined(session->security_parameters.master_secret, + GNUTLS_MASTER_SIZE); +#ifdef ENABLE_SSL3 + if (get_num_version(session) == GNUTLS_SSL3) { + ret = + _gnutls_ssl3_generate_random(premaster->data, + premaster->size, rnd, + 2 * GNUTLS_RANDOM_SIZE, + GNUTLS_MASTER_SIZE, + session->security_parameters. + master_secret); + } else +#endif + ret = + _gnutls_PRF(session, premaster->data, premaster->size, + MASTER_SECRET, MASTER_SECRET_SIZE, + rnd, 2 * GNUTLS_RANDOM_SIZE, + GNUTLS_MASTER_SIZE, + session->security_parameters. + master_secret); + if (ret < 0) + _gnutls_memory_mark_undefined(session->security_parameters.master_secret, + GNUTLS_MASTER_SIZE); + } else { + gnutls_datum_t shash = {NULL, 0}; + + /* draft-ietf-tls-session-hash-02 */ + ret = _gnutls_handshake_get_session_hash(session, &shash); + if (ret < 0) + return gnutls_assert_val(ret); +#ifdef ENABLE_SSL3 + if (get_num_version(session) == GNUTLS_SSL3) + return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); +#endif + + _gnutls_memory_mark_defined(session->security_parameters.master_secret, + GNUTLS_MASTER_SIZE); + ret = + _gnutls_PRF(session, premaster->data, premaster->size, + EXT_MASTER_SECRET, EXT_MASTER_SECRET_SIZE, + shash.data, shash.size, + GNUTLS_MASTER_SIZE, + session->security_parameters. + master_secret); + if (ret < 0) + _gnutls_memory_mark_undefined(session->security_parameters.master_secret, + GNUTLS_MASTER_SIZE); + + gnutls_free(shash.data); + } + + if (!keep_premaster) + _gnutls_free_temp_key_datum(premaster); + + if (ret < 0) + return ret; + + ret = _gnutls_call_keylog_func(session, "CLIENT_RANDOM", + session->security_parameters.master_secret, + GNUTLS_MASTER_SIZE); + if (ret < 0) + return gnutls_assert_val(ret); + + _gnutls_hard_log("INT: MASTER SECRET[%d]: %s\n", + GNUTLS_MASTER_SIZE, + _gnutls_bin2hex(session->security_parameters. + master_secret, GNUTLS_MASTER_SIZE, + buf, sizeof(buf), NULL)); + + return ret; +} + +/* This is called when we want to receive the key exchange message of the + * server. It does nothing if this type of message is not required + * by the selected ciphersuite. + */ +int _gnutls_send_server_kx_message(gnutls_session_t session, int again) +{ + gnutls_buffer_st buf; + int ret = 0; + mbuffer_st *bufel = NULL; + + if (session->internals.auth_struct->gnutls_generate_server_kx == + NULL) + return 0; + + + if (again == 0) { + ret = _gnutls_buffer_init_handshake_mbuffer(&buf); + if (ret < 0) + return gnutls_assert_val(ret); + + ret = + session->internals.auth_struct-> + gnutls_generate_server_kx(session, &buf); + + if (ret == GNUTLS_E_INT_RET_0) { + gnutls_assert(); + ret = 0; + goto cleanup; + } + + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + bufel = _gnutls_buffer_to_mbuffer(&buf); + } + + return _gnutls_send_handshake(session, bufel, GNUTLS_HANDSHAKE_SERVER_KEY_EXCHANGE); + + cleanup: + _gnutls_buffer_clear(&buf); + return ret; +} + +/* This function sends a certificate request message to the + * client. + */ +int _gnutls_send_server_crt_request(gnutls_session_t session, int again) +{ + gnutls_buffer_st buf; + int ret = 0; + mbuffer_st *bufel = NULL; + + if (session->internals.auth_struct-> + gnutls_generate_server_crt_request == NULL) + return 0; + + if (session->internals.send_cert_req <= 0) + return 0; + + + if (again == 0) { + ret = _gnutls_buffer_init_handshake_mbuffer(&buf); + if (ret < 0) + return gnutls_assert_val(ret); + + ret = + session->internals.auth_struct-> + gnutls_generate_server_crt_request(session, &buf); + + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + bufel = _gnutls_buffer_to_mbuffer(&buf); + } + + return _gnutls_send_handshake(session, bufel, GNUTLS_HANDSHAKE_CERTIFICATE_REQUEST); + + cleanup: + _gnutls_buffer_clear(&buf); + return ret; +} + + +/* This is the function for the client to send the key + * exchange message + */ +int _gnutls_send_client_kx_message(gnutls_session_t session, int again) +{ + gnutls_buffer_st buf; + int ret = 0; + mbuffer_st *bufel = NULL; + + if (session->internals.auth_struct->gnutls_generate_client_kx == + NULL) + return 0; + + if (again == 0) { + ret = _gnutls_buffer_init_handshake_mbuffer(&buf); + if (ret < 0) + return gnutls_assert_val(ret); + + ret = + session->internals.auth_struct-> + gnutls_generate_client_kx(session, &buf); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + bufel = _gnutls_buffer_to_mbuffer(&buf); + } + + return _gnutls_send_handshake(session, bufel, GNUTLS_HANDSHAKE_CLIENT_KEY_EXCHANGE); + + cleanup: + _gnutls_buffer_clear(&buf); + return ret; +} + + +/* This is the function for the client to send the certificate + * verify message + */ +int +_gnutls_send_client_certificate_verify(gnutls_session_t session, int again) +{ + gnutls_buffer_st buf; + int ret = 0; + mbuffer_st *bufel = NULL; + + /* This is a packet that is only sent by the client + */ + if (session->security_parameters.entity == GNUTLS_SERVER) + return 0; + + /* if certificate verify is not needed just exit + */ + if (!(session->internals.hsk_flags & HSK_CRT_ASKED)) + return 0; + + + if (session->internals.auth_struct-> + gnutls_generate_client_crt_vrfy == NULL) { + gnutls_assert(); + return 0; /* this algorithm does not support cli_crt_vrfy + */ + } + + if (again == 0) { + ret = _gnutls_buffer_init_handshake_mbuffer(&buf); + if (ret < 0) + return gnutls_assert_val(ret); + + ret = + session->internals.auth_struct-> + gnutls_generate_client_crt_vrfy(session, &buf); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + if (ret == 0) + goto cleanup; + + + bufel = _gnutls_buffer_to_mbuffer(&buf); + } + + return _gnutls_send_handshake(session, bufel, GNUTLS_HANDSHAKE_CERTIFICATE_VERIFY); + + cleanup: + _gnutls_buffer_clear(&buf); + return ret; +} + +/* This is called when we want send our certificate + */ +int _gnutls_send_client_certificate(gnutls_session_t session, int again) +{ + gnutls_buffer_st buf; + int ret = 0; + mbuffer_st *bufel = NULL; + + if (!(session->internals.hsk_flags & HSK_CRT_ASKED)) + return 0; + + if (session->internals.auth_struct-> + gnutls_generate_client_certificate == NULL) + return 0; + + if (again == 0) { + ret = _gnutls_buffer_init_handshake_mbuffer(&buf); + if (ret < 0) + return gnutls_assert_val(ret); + +#ifdef ENABLE_SSL3 + if (get_num_version(session) != GNUTLS_SSL3 || + session->internals.selected_cert_list_length > 0) +#endif + { + /* TLS 1.x or SSL 3.0 with a valid certificate + */ + ret = + session->internals.auth_struct-> + gnutls_generate_client_certificate(session, + &buf); + + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + } + + bufel = _gnutls_buffer_to_mbuffer(&buf); + } + +#ifdef ENABLE_SSL3 + /* In the SSL 3.0 protocol we need to send a + * no certificate alert instead of an + * empty certificate. + */ + if (get_num_version(session) == GNUTLS_SSL3 && + session->internals.selected_cert_list_length == 0) { + _mbuffer_xfree(&bufel); + return + gnutls_alert_send(session, GNUTLS_AL_WARNING, + GNUTLS_A_SSL3_NO_CERTIFICATE); + + } else /* TLS 1.0 or SSL 3.0 with a valid certificate + */ +#endif + return _gnutls_send_handshake(session, bufel, GNUTLS_HANDSHAKE_CERTIFICATE_PKT); + + cleanup: + _gnutls_buffer_clear(&buf); + return ret; +} + + +/* This is called when we want send our certificate + */ +int _gnutls_send_server_certificate(gnutls_session_t session, int again) +{ + gnutls_buffer_st buf; + int ret = 0; + mbuffer_st *bufel = NULL; + + if (session->internals.auth_struct-> + gnutls_generate_server_certificate == NULL) + return 0; + + if (again == 0) { + ret = _gnutls_buffer_init_handshake_mbuffer(&buf); + if (ret < 0) + return gnutls_assert_val(ret); + + ret = + session->internals.auth_struct-> + gnutls_generate_server_certificate(session, &buf); + + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + bufel = _gnutls_buffer_to_mbuffer(&buf); + } + + return _gnutls_send_handshake(session, bufel, GNUTLS_HANDSHAKE_CERTIFICATE_PKT); + + cleanup: + _gnutls_buffer_clear(&buf); + return ret; +} + + +int _gnutls_recv_server_kx_message(gnutls_session_t session) +{ + gnutls_buffer_st buf; + int ret = 0; + unsigned int optflag = 0; + + if (session->internals.auth_struct->gnutls_process_server_kx != + NULL) { + /* Server key exchange packet is optional for PSK. */ + if (_gnutls_session_is_psk(session)) + optflag = 1; + + ret = + _gnutls_recv_handshake(session, + GNUTLS_HANDSHAKE_SERVER_KEY_EXCHANGE, + optflag, &buf); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + ret = + session->internals.auth_struct-> + gnutls_process_server_kx(session, buf.data, + buf.length); + _gnutls_buffer_clear(&buf); + + if (ret < 0) { + gnutls_assert(); + return ret; + } + + } + return ret; +} + +int _gnutls_recv_server_crt_request(gnutls_session_t session) +{ + gnutls_buffer_st buf; + int ret = 0; + + if (session->internals.auth_struct-> + gnutls_process_server_crt_request != NULL) { + + ret = + _gnutls_recv_handshake(session, + GNUTLS_HANDSHAKE_CERTIFICATE_REQUEST, + 1, &buf); + if (ret < 0) + return ret; + + if (ret == 0 && buf.length == 0) { + _gnutls_buffer_clear(&buf); + return 0; /* ignored */ + } + + ret = + session->internals.auth_struct-> + gnutls_process_server_crt_request(session, buf.data, + buf.length); + _gnutls_buffer_clear(&buf); + if (ret < 0) + return ret; + + } + return ret; +} + +int _gnutls_recv_client_kx_message(gnutls_session_t session) +{ + gnutls_buffer_st buf; + int ret = 0; + + + /* Do key exchange only if the algorithm permits it */ + if (session->internals.auth_struct->gnutls_process_client_kx != + NULL) { + + ret = + _gnutls_recv_handshake(session, + GNUTLS_HANDSHAKE_CLIENT_KEY_EXCHANGE, + 0, &buf); + if (ret < 0) + return ret; + + ret = + session->internals.auth_struct-> + gnutls_process_client_kx(session, buf.data, + buf.length); + _gnutls_buffer_clear(&buf); + if (ret < 0) + return ret; + + } + + return ret; +} + + +int _gnutls_recv_client_certificate(gnutls_session_t session) +{ + gnutls_buffer_st buf; + int ret = 0; + int optional; + + if (session->internals.auth_struct-> + gnutls_process_client_certificate == NULL) + return 0; + + /* if we have not requested a certificate then just return + */ + if (session->internals.send_cert_req == 0) { + return 0; + } + + if (session->internals.send_cert_req == GNUTLS_CERT_REQUIRE) + optional = 0; + else + optional = 1; + + ret = + _gnutls_recv_handshake(session, + GNUTLS_HANDSHAKE_CERTIFICATE_PKT, + optional, &buf); + + if (ret < 0) { + /* Handle the case of old SSL3 clients who send + * a warning alert instead of an empty certificate to indicate + * no certificate. + */ +#ifdef ENABLE_SSL3 + if (optional != 0 && + ret == GNUTLS_E_WARNING_ALERT_RECEIVED && + get_num_version(session) == GNUTLS_SSL3 && + gnutls_alert_get(session) == + GNUTLS_A_SSL3_NO_CERTIFICATE) { + + /* SSL3 does not send an empty certificate, + * but this alert. So we just ignore it. + */ + gnutls_assert(); + return 0; + } +#endif + + /* certificate was required + */ + if ((ret == GNUTLS_E_WARNING_ALERT_RECEIVED + || ret == GNUTLS_E_FATAL_ALERT_RECEIVED) + && optional == 0) { + gnutls_assert(); + return GNUTLS_E_NO_CERTIFICATE_FOUND; + } + + return ret; + } + + if (ret == 0 && buf.length == 0 && optional != 0) { + /* Client has not sent the certificate message. + * well I'm not sure we should accept this + * behaviour. + */ + gnutls_assert(); + ret = 0; + goto cleanup; + } + ret = + session->internals.auth_struct-> + gnutls_process_client_certificate(session, buf.data, + buf.length); + + if (ret < 0 && ret != GNUTLS_E_NO_CERTIFICATE_FOUND) { + gnutls_assert(); + goto cleanup; + } + + /* ok we should expect a certificate verify message now + */ + if (ret == GNUTLS_E_NO_CERTIFICATE_FOUND && optional != 0) + ret = 0; + else + session->internals.hsk_flags |= HSK_CRT_VRFY_EXPECTED; + + cleanup: + _gnutls_buffer_clear(&buf); + return ret; +} + +int _gnutls_recv_server_certificate(gnutls_session_t session) +{ + gnutls_buffer_st buf; + int ret = 0; + + if (session->internals.auth_struct-> + gnutls_process_server_certificate != NULL) { + + ret = + _gnutls_recv_handshake(session, + GNUTLS_HANDSHAKE_CERTIFICATE_PKT, + 0, &buf); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + ret = + session->internals.auth_struct-> + gnutls_process_server_certificate(session, buf.data, + buf.length); + _gnutls_buffer_clear(&buf); + if (ret < 0) { + gnutls_assert(); + return ret; + } + } + + return ret; +} + + +/* Recv the client certificate verify. This packet may not + * arrive if the peer did not send us a certificate. + */ +int +_gnutls_recv_client_certificate_verify_message(gnutls_session_t session) +{ + gnutls_buffer_st buf; + int ret = 0; + + + if (session->internals.auth_struct-> + gnutls_process_client_crt_vrfy == NULL) + return 0; + + if (session->internals.send_cert_req == 0 || + (!(session->internals.hsk_flags & HSK_CRT_VRFY_EXPECTED))) { + return 0; + } + + ret = + _gnutls_recv_handshake(session, + GNUTLS_HANDSHAKE_CERTIFICATE_VERIFY, + 1, &buf); + if (ret < 0) + return ret; + + if (ret == 0 && buf.length == 0 + && session->internals.send_cert_req == GNUTLS_CERT_REQUIRE) { + /* certificate was required */ + gnutls_assert(); + ret = GNUTLS_E_NO_CERTIFICATE_FOUND; + goto cleanup; + } + + ret = + session->internals.auth_struct-> + gnutls_process_client_crt_vrfy(session, buf.data, buf.length); + + cleanup: + _gnutls_buffer_clear(&buf); + return ret; +} |