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