diff options
Diffstat (limited to 'lib/tls13/certificate_request.c')
-rw-r--r-- | lib/tls13/certificate_request.c | 99 |
1 files changed, 91 insertions, 8 deletions
diff --git a/lib/tls13/certificate_request.c b/lib/tls13/certificate_request.c index 290b2d5f47..428f03df57 100644 --- a/lib/tls13/certificate_request.c +++ b/lib/tls13/certificate_request.c @@ -27,11 +27,72 @@ #include "tls13/certificate_request.h" #include "ext/signature.h" #include "mbuffers.h" +#include "algorithms.h" +#include "auth/cert.h" + +typedef struct crt_req_ctx_st { + gnutls_session_t session; + gnutls_pk_algorithm_t pk_algos[MAX_ALGOS]; + unsigned pk_algos_length; + uint8_t *rdn; + 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, uint16_t tls_id, const uint8_t *data, int data_size) +int parse_cert_extension(void *_ctx, uint16_t tls_id, const uint8_t *data, int data_size) { - /* ignore all exts */ + crt_req_ctx_st *ctx = _ctx; + gnutls_session_t session = ctx->session; + 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 (session->internals.hsk_flags & HSK_CRT_REQ_GOT_SIG_ALGO) + return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_EXTENSION); + + session->internals.hsk_flags |= HSK_CRT_REQ_GOT_SIG_ALGO; + + 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; + } + } + return 0; } @@ -39,6 +100,10 @@ int _gnutls13_recv_certificate_request(gnutls_session_t session) { int ret; gnutls_buffer_st buf; + crt_req_ctx_st ctx; + + 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) @@ -54,23 +119,41 @@ int _gnutls13_recv_certificate_request(gnutls_session_t session) if (buf.data[0] != 0) { /* The context field must be empty during handshake */ + ret = GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER; gnutls_assert(); - return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER); + goto cleanup; } /* buf.length is positive */ buf.data++; buf.length--; - ret = _gnutls_extv_parse(NULL, parse_cert_extension, buf.data, buf.length); - _gnutls_buffer_clear(&buf); + memset(&ctx, 0, sizeof(ctx)); + ctx.session = session; - if (ret < 0) - return gnutls_assert_val(ret); + ret = _gnutls_extv_parse(&ctx, parse_cert_extension, buf.data, buf.length); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + session->internals.crt_requested = 1; + + ret = _gnutls_select_client_cert(session, ctx.rdn, ctx.rdn_size, + ctx.pk_algos, ctx.pk_algos_length); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } session->internals.hsk_flags |= HSK_CRT_ASKED; - return 0; + ret = 0; + + cleanup: + _gnutls_buffer_clear(&buf); + gnutls_free(ctx.rdn); + return ret; } |