summaryrefslogtreecommitdiff
path: root/lib/tls13/certificate_request.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/tls13/certificate_request.c')
-rw-r--r--lib/tls13/certificate_request.c99
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;
}