From 0eb891d521277045767f5e2d5ab33c00102e3b22 Mon Sep 17 00:00:00 2001 From: Nikos Mavrogiannopoulos Date: Tue, 3 Oct 2017 13:59:39 +0200 Subject: handshake: added support for client certificates That is, receive and parse a certificate request, certificate verify, as well as certificate in server side. That way, client certificates Signed-off-by: Nikos Mavrogiannopoulos --- lib/auth/cert.c | 19 +++----- lib/auth/cert.h | 5 +++ lib/ext/signature.c | 8 ++-- lib/gnutls_int.h | 1 + lib/tls13/certificate.c | 1 - lib/tls13/certificate_request.c | 99 +++++++++++++++++++++++++++++++++++++---- lib/tls13/certificate_verify.c | 5 ++- 7 files changed, 112 insertions(+), 26 deletions(-) diff --git a/lib/auth/cert.c b/lib/auth/cert.c index 3d463d0a76..744641ad24 100644 --- a/lib/auth/cert.c +++ b/lib/auth/cert.c @@ -513,10 +513,10 @@ call_get_cert_callback(gnutls_session_t session, * 20020128: added ability to select a certificate depending on the SIGN * algorithm (only in automatic mode). */ -static int -select_client_cert(gnutls_session_t session, - uint8_t * _data, size_t _data_size, - gnutls_pk_algorithm_t * pk_algos, int pk_algos_length) +int +_gnutls_select_client_cert(gnutls_session_t session, + uint8_t * _data, size_t _data_size, + gnutls_pk_algorithm_t * pk_algos, int pk_algos_length) { int result; int indx = -1; @@ -983,8 +983,8 @@ _gnutls_proc_cert_cert_req(gnutls_session_t session, uint8_t * data, * he wants to use. */ if ((ret = - select_client_cert(session, p, size, pk_algos, - pk_algos_length)) < 0) { + _gnutls_select_client_cert(session, p, size, pk_algos, + pk_algos_length)) < 0) { gnutls_assert(); return ret; } @@ -1217,9 +1217,6 @@ _gnutls_get_selected_cert(gnutls_session_t session, { if (session->security_parameters.entity == GNUTLS_SERVER) { - /* select_client_cert() has been called before. - */ - *apr_cert_list = session->internals.selected_cert_list; *apr_pkey = session->internals.selected_key; *apr_cert_list_length = @@ -1232,9 +1229,7 @@ _gnutls_get_selected_cert(gnutls_session_t session, } else { /* CLIENT SIDE */ - - /* we have already decided which certificate - * to send. + /* _gnutls_select_client_cert() must have been called before. */ *apr_cert_list = session->internals.selected_cert_list; *apr_cert_list_length = diff --git a/lib/auth/cert.h b/lib/auth/cert.h index 1c89ebe1a8..ab8e840c7c 100644 --- a/lib/auth/cert.h +++ b/lib/auth/cert.h @@ -130,6 +130,11 @@ int _gnutls_get_selected_cert(gnutls_session_t session, int *apr_cert_list_length, gnutls_privkey_t * apr_pkey); +int +_gnutls_select_client_cert(gnutls_session_t session, + uint8_t * _data, size_t _data_size, + gnutls_pk_algorithm_t * pk_algos, int pk_algos_length); + int _gnutls_copy_certificate_auth_info(cert_auth_info_t info, gnutls_pcert_st * certs, size_t ncerts); int diff --git a/lib/ext/signature.c b/lib/ext/signature.c index 21ffac2b85..8b4bb1ac65 100644 --- a/lib/ext/signature.c +++ b/lib/ext/signature.c @@ -291,8 +291,8 @@ _gnutls_session_get_sign_algo(gnutls_session_t session, ret = _gnutls_hello_ext_get_priv(session, - GNUTLS_EXTENSION_SIGNATURE_ALGORITHMS, - &epriv); + GNUTLS_EXTENSION_SIGNATURE_ALGORITHMS, + &epriv); priv = epriv; if (ret < 0 || !_gnutls_version_has_selectable_sighash(ver)) { @@ -463,8 +463,8 @@ gnutls_sign_algorithm_get_requested(gnutls_session_t session, ret = _gnutls_hello_ext_get_priv(session, - GNUTLS_EXTENSION_SIGNATURE_ALGORITHMS, - &epriv); + GNUTLS_EXTENSION_SIGNATURE_ALGORITHMS, + &epriv); if (ret < 0) { gnutls_assert(); return ret; diff --git a/lib/gnutls_int.h b/lib/gnutls_int.h index 4c18ec29ec..2f7fea00d7 100644 --- a/lib/gnutls_int.h +++ b/lib/gnutls_int.h @@ -1115,6 +1115,7 @@ typedef struct { #define HSK_HRR_SENT (1<<3) #define HSK_HRR_RECEIVED (1<<4) #define HSK_CRT_REQ_SENT (1<<5) +#define HSK_CRT_REQ_GOT_SIG_ALGO (1<<6) unsigned hsk_flags; /* TLS1.3 only */ unsigned crt_requested; /* 1 if client auth was requested (i.e., client cert). diff --git a/lib/tls13/certificate.c b/lib/tls13/certificate.c index 5afb2e409c..c4aadedc20 100644 --- a/lib/tls13/certificate.c +++ b/lib/tls13/certificate.c @@ -96,7 +96,6 @@ int _gnutls13_send_certificate(gnutls_session_t session, unsigned again) gnutls_buffer_st buf; unsigned pos_mark; unsigned i; - if (again == 0) { ret = _gnutls_get_selected_cert(session, &apr_cert_list, &apr_cert_list_length, &apr_pkey); 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;jsession; + 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; } diff --git a/lib/tls13/certificate_verify.c b/lib/tls13/certificate_verify.c index 813c37ba8c..995e0c6058 100644 --- a/lib/tls13/certificate_verify.c +++ b/lib/tls13/certificate_verify.c @@ -79,7 +79,10 @@ int _gnutls13_recv_certificate_verify(gnutls_session_t session) goto cleanup; } - gnutls_sign_algorithm_set_server(session, se->id); + if (session->security_parameters.entity == GNUTLS_CLIENT) + gnutls_sign_algorithm_set_server(session, se->id); + else + gnutls_sign_algorithm_set_client(session, se->id); buf.data+=2; buf.length-=2; -- cgit v1.2.1