|
Packit |
aea12f |
/*
|
|
Packit |
aea12f |
* Copyright (C) 2017 Red Hat, Inc.
|
|
Packit |
aea12f |
*
|
|
Packit |
aea12f |
* Author: Nikos Mavrogiannopoulos
|
|
Packit |
aea12f |
*
|
|
Packit |
aea12f |
* This file is part of GnuTLS.
|
|
Packit |
aea12f |
*
|
|
Packit |
aea12f |
* The GnuTLS is free software; you can redistribute it and/or
|
|
Packit |
aea12f |
* modify it under the terms of the GNU Lesser General Public License
|
|
Packit |
aea12f |
* as published by the Free Software Foundation; either version 2.1 of
|
|
Packit |
aea12f |
* the License, or (at your option) any later version.
|
|
Packit |
aea12f |
*
|
|
Packit |
aea12f |
* This library is distributed in the hope that it will be useful, but
|
|
Packit |
aea12f |
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
aea12f |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Packit |
aea12f |
* Lesser General Public License for more details.
|
|
Packit |
aea12f |
*
|
|
Packit |
aea12f |
* You should have received a copy of the GNU Lesser General Public License
|
|
Packit |
aea12f |
* along with this program. If not, see <https://www.gnu.org/licenses/>
|
|
Packit |
aea12f |
*
|
|
Packit |
aea12f |
*/
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
#include "gnutls_int.h"
|
|
Packit |
aea12f |
#include "errors.h"
|
|
Packit |
aea12f |
#include "extv.h"
|
|
Packit |
aea12f |
#include "handshake.h"
|
|
Packit |
aea12f |
#include "tls13/certificate_request.h"
|
|
Packit |
aea12f |
#include "ext/signature.h"
|
|
Packit Service |
991b93 |
#include "ext/status_request.h"
|
|
Packit |
aea12f |
#include "mbuffers.h"
|
|
Packit |
aea12f |
#include "algorithms.h"
|
|
Packit |
aea12f |
#include "auth/cert.h"
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
/* for tlist dereference */
|
|
Packit |
aea12f |
#include "x509/verify-high.h"
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
#define EXTID_CERTIFICATE_AUTHORITIES 47
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
typedef struct crt_req_ctx_st {
|
|
Packit |
aea12f |
gnutls_session_t session;
|
|
Packit |
aea12f |
unsigned got_sig_algo;
|
|
Packit |
aea12f |
gnutls_pk_algorithm_t pk_algos[MAX_ALGOS];
|
|
Packit |
aea12f |
unsigned pk_algos_length;
|
|
Packit |
aea12f |
const uint8_t *rdn; /* pointer inside the message buffer */
|
|
Packit |
aea12f |
unsigned rdn_size;
|
|
Packit |
aea12f |
} crt_req_ctx_st;
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
static unsigned is_algo_in_list(gnutls_pk_algorithm_t algo, gnutls_pk_algorithm_t *list, unsigned list_size)
|
|
Packit |
aea12f |
{
|
|
Packit |
aea12f |
unsigned j;
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
for (j=0;j
|
|
Packit |
aea12f |
if (list[j] == algo)
|
|
Packit |
aea12f |
return 1;
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
return 0;
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
static
|
|
Packit |
aea12f |
int parse_cert_extension(void *_ctx, unsigned tls_id, const uint8_t *data, unsigned data_size)
|
|
Packit |
aea12f |
{
|
|
Packit |
aea12f |
crt_req_ctx_st *ctx = _ctx;
|
|
Packit |
aea12f |
gnutls_session_t session = ctx->session;
|
|
Packit |
aea12f |
unsigned v;
|
|
Packit |
aea12f |
int ret;
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
/* Decide which certificate to use if the signature algorithms extension
|
|
Packit |
aea12f |
* is present.
|
|
Packit |
aea12f |
*/
|
|
Packit |
aea12f |
if (tls_id == ext_mod_sig.tls_id) {
|
|
Packit |
aea12f |
const version_entry_st *ver = get_version(session);
|
|
Packit |
aea12f |
const gnutls_sign_entry_st *se;
|
|
Packit |
aea12f |
/* signature algorithms; let's use it to decide the certificate to use */
|
|
Packit |
aea12f |
unsigned i;
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
if (ctx->got_sig_algo)
|
|
Packit |
aea12f |
return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_EXTENSION);
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
ctx->got_sig_algo = 1;
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
if (data_size < 2)
|
|
Packit |
aea12f |
return gnutls_assert_val(GNUTLS_E_TLS_PACKET_DECODING_ERROR);
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
v = _gnutls_read_uint16(data);
|
|
Packit |
aea12f |
if (v != data_size-2)
|
|
Packit |
aea12f |
return gnutls_assert_val(GNUTLS_E_TLS_PACKET_DECODING_ERROR);
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
data += 2;
|
|
Packit |
aea12f |
data_size -= 2;
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
ret = _gnutls_sign_algorithm_parse_data(session, data, data_size);
|
|
Packit |
aea12f |
if (ret < 0)
|
|
Packit |
aea12f |
return gnutls_assert_val(ret);
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
/* The APIs to retrieve a client certificate accept the public
|
|
Packit |
aea12f |
* key algorithms instead of signatures. Get the public key algorithms
|
|
Packit |
aea12f |
* from the signatures.
|
|
Packit |
aea12f |
*/
|
|
Packit |
aea12f |
for (i=0;i<(unsigned)data_size;i+=2) {
|
|
Packit |
aea12f |
se = _gnutls_tls_aid_to_sign_entry(data[i], data[i+1], ver);
|
|
Packit |
aea12f |
if (se == NULL)
|
|
Packit |
aea12f |
continue;
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
if (ctx->pk_algos_length >= sizeof(ctx->pk_algos)/sizeof(ctx->pk_algos[0]))
|
|
Packit |
aea12f |
break;
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
if (is_algo_in_list(se->pk, ctx->pk_algos, ctx->pk_algos_length))
|
|
Packit |
aea12f |
continue;
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
ctx->pk_algos[ctx->pk_algos_length++] = se->pk;
|
|
Packit |
aea12f |
}
|
|
Packit Service |
991b93 |
#ifdef ENABLE_OCSP
|
|
Packit Service |
991b93 |
} else if (tls_id == ext_mod_status_request.tls_id) {
|
|
Packit Service |
991b93 |
if (data_size != 0)
|
|
Packit Service |
991b93 |
return gnutls_assert_val(GNUTLS_E_TLS_PACKET_DECODING_ERROR);
|
|
Packit Service |
991b93 |
|
|
Packit Service |
991b93 |
/* we are now allowed to send OCSP staples */
|
|
Packit Service |
991b93 |
session->internals.hsk_flags |= HSK_CLIENT_OCSP_REQUESTED;
|
|
Packit Service |
991b93 |
#endif
|
|
Packit |
aea12f |
} else if (tls_id == EXTID_CERTIFICATE_AUTHORITIES) {
|
|
Packit |
aea12f |
if (data_size < 3) {
|
|
Packit |
aea12f |
return gnutls_assert_val(GNUTLS_E_TLS_PACKET_DECODING_ERROR);
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
v = _gnutls_read_uint16(data);
|
|
Packit |
aea12f |
if (v != data_size-2)
|
|
Packit |
aea12f |
return gnutls_assert_val(GNUTLS_E_TLS_PACKET_DECODING_ERROR);
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
ctx->rdn = data+2;
|
|
Packit |
aea12f |
ctx->rdn_size = v;
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
return 0;
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
int _gnutls13_recv_certificate_request_int(gnutls_session_t session, gnutls_buffer_st *buf)
|
|
Packit |
aea12f |
{
|
|
Packit |
aea12f |
int ret;
|
|
Packit |
aea12f |
crt_req_ctx_st ctx;
|
|
Packit |
aea12f |
gnutls_pcert_st *apr_cert_list;
|
|
Packit |
aea12f |
gnutls_privkey_t apr_pkey;
|
|
Packit |
aea12f |
int apr_cert_list_length;
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
_gnutls_handshake_log("HSK[%p]: parsing certificate request\n", session);
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
if (unlikely(session->security_parameters.entity == GNUTLS_SERVER))
|
|
Packit |
aea12f |
return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
/* if initial negotiation is complete, this is a post-handshake auth */
|
|
Packit |
aea12f |
if (!session->internals.initial_negotiation_completed) {
|
|
Packit |
aea12f |
if (buf->data[0] != 0) {
|
|
Packit |
aea12f |
/* The context field must be empty during handshake */
|
|
Packit |
aea12f |
return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER);
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
/* buf->length is positive */
|
|
Packit |
aea12f |
buf->data++;
|
|
Packit |
aea12f |
buf->length--;
|
|
Packit |
aea12f |
} else {
|
|
Packit |
aea12f |
gnutls_datum_t context;
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
ret = _gnutls_buffer_pop_datum_prefix8(buf, &context);
|
|
Packit |
aea12f |
if (ret < 0)
|
|
Packit |
aea12f |
return gnutls_assert_val(ret);
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
gnutls_free(session->internals.post_handshake_cr_context.data);
|
|
Packit |
aea12f |
ret = _gnutls_set_datum(&session->internals.post_handshake_cr_context,
|
|
Packit |
aea12f |
context.data, context.size);
|
|
Packit |
aea12f |
if (ret < 0)
|
|
Packit |
aea12f |
return gnutls_assert_val(ret);
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
memset(&ctx, 0, sizeof(ctx));
|
|
Packit |
aea12f |
ctx.session = session;
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
ret = _gnutls_extv_parse(&ctx, parse_cert_extension, buf->data, buf->length);
|
|
Packit |
aea12f |
if (ret < 0)
|
|
Packit |
aea12f |
return gnutls_assert_val(ret);
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
/* The "signature_algorithms" extension MUST be specified */
|
|
Packit |
aea12f |
if (!ctx.got_sig_algo)
|
|
Packit |
aea12f |
return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_EXTENSION);
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
session->internals.hsk_flags |= HSK_CRT_ASKED;
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
ret = _gnutls_select_client_cert(session, ctx.rdn, ctx.rdn_size,
|
|
Packit |
aea12f |
ctx.pk_algos, ctx.pk_algos_length);
|
|
Packit |
aea12f |
if (ret < 0)
|
|
Packit |
aea12f |
return gnutls_assert_val(ret);
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
ret = _gnutls_get_selected_cert(session, &apr_cert_list,
|
|
Packit |
aea12f |
&apr_cert_list_length, &apr_pkey);
|
|
Packit |
aea12f |
if (ret < 0)
|
|
Packit |
aea12f |
return gnutls_assert_val(ret);
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
if (apr_cert_list_length > 0) {
|
|
Packit |
aea12f |
gnutls_sign_algorithm_t algo;
|
|
Packit |
aea12f |
|
|
Packit Service |
991b93 |
algo = _gnutls_session_get_sign_algo(session, &apr_cert_list[0], apr_pkey, 0, GNUTLS_KX_UNKNOWN);
|
|
Packit |
aea12f |
if (algo == GNUTLS_SIGN_UNKNOWN) {
|
|
Packit |
aea12f |
_gnutls_handshake_log("HSK[%p]: rejecting client auth because of no suitable signature algorithm\n", session);
|
|
Packit |
aea12f |
_gnutls_selected_certs_deinit(session);
|
|
Packit |
aea12f |
return gnutls_assert_val(0);
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
gnutls_sign_algorithm_set_client(session, algo);
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
return 0;
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
int _gnutls13_recv_certificate_request(gnutls_session_t session)
|
|
Packit |
aea12f |
{
|
|
Packit |
aea12f |
int ret;
|
|
Packit |
aea12f |
gnutls_buffer_st buf;
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
if (!session->internals.initial_negotiation_completed &&
|
|
Packit |
aea12f |
session->internals.hsk_flags & HSK_PSK_SELECTED)
|
|
Packit |
aea12f |
return 0;
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
if (unlikely(session->security_parameters.entity != GNUTLS_CLIENT))
|
|
Packit |
aea12f |
return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
ret = _gnutls_recv_handshake(session, GNUTLS_HANDSHAKE_CERTIFICATE_REQUEST, 1, &buf;;
|
|
Packit |
aea12f |
if (ret < 0)
|
|
Packit |
aea12f |
return gnutls_assert_val(ret);
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
/* if not received */
|
|
Packit |
aea12f |
if (buf.length == 0) {
|
|
Packit |
aea12f |
_gnutls_buffer_clear(&buf;;
|
|
Packit |
aea12f |
return 0;
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
ret = _gnutls13_recv_certificate_request_int(session, &buf;;
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
_gnutls_buffer_clear(&buf;;
|
|
Packit |
aea12f |
return ret;
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
static
|
|
Packit |
aea12f |
int write_certificate_authorities(void *ctx, gnutls_buffer_st *buf)
|
|
Packit |
aea12f |
{
|
|
Packit |
aea12f |
gnutls_session_t session = ctx;
|
|
Packit |
aea12f |
gnutls_certificate_credentials_t cred;
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
if (session->internals.ignore_rdn_sequence != 0)
|
|
Packit |
aea12f |
return 0;
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
cred = (gnutls_certificate_credentials_t)
|
|
Packit |
aea12f |
_gnutls_get_cred(session, GNUTLS_CRD_CERTIFICATE);
|
|
Packit |
aea12f |
if (cred == NULL) {
|
|
Packit |
aea12f |
gnutls_assert();
|
|
Packit |
aea12f |
return GNUTLS_E_INSUFFICIENT_CREDENTIALS;
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
if (cred->tlist->x509_rdn_sequence.size == 0)
|
|
Packit |
aea12f |
return 0;
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
return
|
|
Packit |
aea12f |
_gnutls_buffer_append_data_prefix(buf, 16,
|
|
Packit |
aea12f |
cred->
|
|
Packit |
aea12f |
tlist->x509_rdn_sequence.
|
|
Packit |
aea12f |
data,
|
|
Packit |
aea12f |
cred->
|
|
Packit |
aea12f |
tlist->x509_rdn_sequence.
|
|
Packit |
aea12f |
size);
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
|
|
Packit Service |
991b93 |
static int append_empty_ext(void *ctx, gnutls_buffer_st *buf)
|
|
Packit Service |
991b93 |
{
|
|
Packit Service |
991b93 |
return GNUTLS_E_INT_RET_0;
|
|
Packit Service |
991b93 |
}
|
|
Packit Service |
991b93 |
|
|
Packit |
aea12f |
int _gnutls13_send_certificate_request(gnutls_session_t session, unsigned again)
|
|
Packit |
aea12f |
{
|
|
Packit |
aea12f |
gnutls_certificate_credentials_t cred;
|
|
Packit |
aea12f |
int ret;
|
|
Packit |
aea12f |
mbuffer_st *bufel = NULL;
|
|
Packit |
aea12f |
gnutls_buffer_st buf;
|
|
Packit |
aea12f |
unsigned init_pos;
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
if (again == 0) {
|
|
Packit |
aea12f |
unsigned char rnd[12];
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
if (!session->internals.initial_negotiation_completed &&
|
|
Packit |
aea12f |
session->internals.hsk_flags & HSK_PSK_SELECTED)
|
|
Packit |
aea12f |
return 0;
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
if (session->internals.send_cert_req == 0)
|
|
Packit |
aea12f |
return 0;
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
cred = (gnutls_certificate_credentials_t)
|
|
Packit |
aea12f |
_gnutls_get_cred(session, GNUTLS_CRD_CERTIFICATE);
|
|
Packit |
aea12f |
if (cred == NULL)
|
|
Packit |
aea12f |
return gnutls_assert_val(GNUTLS_E_INSUFFICIENT_CREDENTIALS);
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
ret = _gnutls_buffer_init_handshake_mbuffer(&buf;;
|
|
Packit |
aea12f |
if (ret < 0)
|
|
Packit |
aea12f |
return gnutls_assert_val(ret);
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
if (session->internals.initial_negotiation_completed) { /* reauth */
|
|
Packit |
aea12f |
ret = gnutls_rnd(GNUTLS_RND_NONCE, rnd, sizeof(rnd));
|
|
Packit |
aea12f |
if (ret < 0) {
|
|
Packit |
aea12f |
gnutls_assert();
|
|
Packit |
aea12f |
goto cleanup;
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
gnutls_free(session->internals.post_handshake_cr_context.data);
|
|
Packit |
aea12f |
ret = _gnutls_set_datum(&session->internals.post_handshake_cr_context,
|
|
Packit |
aea12f |
rnd, sizeof(rnd));
|
|
Packit |
aea12f |
if (ret < 0) {
|
|
Packit |
aea12f |
gnutls_assert();
|
|
Packit |
aea12f |
goto cleanup;
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
ret = _gnutls_buffer_append_data_prefix(&buf, 8,
|
|
Packit |
aea12f |
session->internals.post_handshake_cr_context.data,
|
|
Packit |
aea12f |
session->internals.post_handshake_cr_context.size);
|
|
Packit |
aea12f |
} else {
|
|
Packit |
aea12f |
ret = _gnutls_buffer_append_prefix(&buf, 8, 0);
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
if (ret < 0) {
|
|
Packit |
aea12f |
gnutls_assert();
|
|
Packit |
aea12f |
goto cleanup;
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
ret = _gnutls_extv_append_init(&buf;;
|
|
Packit |
aea12f |
if (ret < 0) {
|
|
Packit |
aea12f |
gnutls_assert();
|
|
Packit |
aea12f |
goto cleanup;
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
init_pos = ret;
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
ret = _gnutls_extv_append(&buf, ext_mod_sig.tls_id, session,
|
|
Packit |
aea12f |
(extv_append_func)_gnutls_sign_algorithm_write_params);
|
|
Packit |
aea12f |
if (ret < 0) {
|
|
Packit |
aea12f |
gnutls_assert();
|
|
Packit |
aea12f |
goto cleanup;
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
ret = _gnutls_extv_append(&buf, EXTID_CERTIFICATE_AUTHORITIES, session,
|
|
Packit |
aea12f |
write_certificate_authorities);
|
|
Packit |
aea12f |
if (ret < 0) {
|
|
Packit |
aea12f |
gnutls_assert();
|
|
Packit |
aea12f |
goto cleanup;
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
|
|
Packit Service |
991b93 |
#ifdef ENABLE_OCSP
|
|
Packit Service |
991b93 |
/* We always advertise our support for OCSP stapling */
|
|
Packit Service |
991b93 |
ret = _gnutls_extv_append(&buf, ext_mod_status_request.tls_id, session,
|
|
Packit Service |
991b93 |
append_empty_ext);
|
|
Packit Service |
991b93 |
if (ret < 0) {
|
|
Packit Service |
991b93 |
gnutls_assert();
|
|
Packit Service |
991b93 |
goto cleanup;
|
|
Packit Service |
991b93 |
}
|
|
Packit Service |
991b93 |
session->internals.hsk_flags |= HSK_CLIENT_OCSP_REQUESTED;
|
|
Packit Service |
991b93 |
#endif
|
|
Packit Service |
991b93 |
|
|
Packit |
aea12f |
ret = _gnutls_extv_append_final(&buf, init_pos, 0);
|
|
Packit |
aea12f |
if (ret < 0) {
|
|
Packit |
aea12f |
gnutls_assert();
|
|
Packit |
aea12f |
goto cleanup;
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
bufel = _gnutls_buffer_to_mbuffer(&buf;;
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
session->internals.hsk_flags |= HSK_CRT_REQ_SENT;
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
return _gnutls_send_handshake(session, bufel, GNUTLS_HANDSHAKE_CERTIFICATE_REQUEST);
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
cleanup:
|
|
Packit |
aea12f |
_gnutls_buffer_clear(&buf;;
|
|
Packit |
aea12f |
return ret;
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
|