* 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;
* 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;
}
{
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 =
} 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 =
#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;
}
{
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)
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;
}