]> git.ipfire.org Git - thirdparty/gnutls.git/commitdiff
handshake: added support for client certificates
authorNikos Mavrogiannopoulos <nmav@redhat.com>
Tue, 3 Oct 2017 11:59:39 +0000 (13:59 +0200)
committerNikos Mavrogiannopoulos <nmav@redhat.com>
Mon, 19 Feb 2018 14:29:35 +0000 (15:29 +0100)
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>
lib/auth/cert.c
lib/auth/cert.h
lib/ext/signature.c
lib/gnutls_int.h
lib/tls13/certificate.c
lib/tls13/certificate_request.c
lib/tls13/certificate_verify.c

index 3d463d0a767b8f070b13b93247aa7990781c44fc..744641ad243ca76fb9fd11ecbca852bb82cce510 100644 (file)
@@ -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 =
index 1c89ebe1a8cd9173455790feae55e4d9d6c90fe6..ab8e840c7c0b2df5f8451538d2fc1ff22b4e463e 100644 (file)
@@ -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
index 21ffac2b85521c394a0395fbd2a4d5176c6d5cc0..8b4bb1ac6574b23837ff6136e2ed2a1413da1e09 100644 (file)
@@ -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;
index b5eac7d5ef3da6e5e743b4ed0ebca1a36b3385d7..605d3a3fb2b75cc23d537d4f44b26384945ddcbf 100644 (file)
@@ -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).
index 5afb2e409c970ac10e496167c95718b1ee856366..c4aadedc206f75bc443f997067507bebee4b8111 100644 (file)
@@ -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);
index 290b2d5f475180cd9d1716ef23f6c73b0d96d903..428f03df5733792f2819154a96b4c0f58f059d5b 100644 (file)
 #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;
 }
 
 
index 813c37ba8c7a419490aa5eebaeaca81d554f9e3a..995e0c60586410b55422a084a5d1cf1b65579d51 100644 (file)
@@ -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;