summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNikos Mavrogiannopoulos <nmav@redhat.com>2017-10-03 13:59:39 +0200
committerNikos Mavrogiannopoulos <nmav@redhat.com>2017-11-16 14:24:35 +0100
commit0eb891d521277045767f5e2d5ab33c00102e3b22 (patch)
treefa9861289eece803319e1f46face7280ca2053c6
parent53fdb0602e71a6024ba0c5c8ed2a6e9d506c1fa4 (diff)
downloadgnutls-0eb891d521277045767f5e2d5ab33c00102e3b22.tar.gz
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 <nmav@redhat.com>
-rw-r--r--lib/auth/cert.c19
-rw-r--r--lib/auth/cert.h5
-rw-r--r--lib/ext/signature.c8
-rw-r--r--lib/gnutls_int.h1
-rw-r--r--lib/tls13/certificate.c1
-rw-r--r--lib/tls13/certificate_request.c99
-rw-r--r--lib/tls13/certificate_verify.c5
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;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;
}
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;