summaryrefslogtreecommitdiffstats
path: root/lib/kx.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--lib/kx.c834
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;
+}