Coverage Report

Created: 2026-05-16 07:57

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gnutls/lib/tls13/certificate_request.c
Line
Count
Source
1
/*
2
 * Copyright (C) 2017 Red Hat, Inc.
3
 *
4
 * Author: Nikos Mavrogiannopoulos
5
 *
6
 * This file is part of GnuTLS.
7
 *
8
 * The GnuTLS is free software; you can redistribute it and/or
9
 * modify it under the terms of the GNU Lesser General Public License
10
 * as published by the Free Software Foundation; either version 2.1 of
11
 * the License, or (at your option) any later version.
12
 *
13
 * This library is distributed in the hope that it will be useful, but
14
 * WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16
 * Lesser General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU Lesser General Public License
19
 * along with this program.  If not, see <https://www.gnu.org/licenses/>
20
 *
21
 */
22
23
#include "gnutls_int.h"
24
#include "errors.h"
25
#include "extv.h"
26
#include "handshake.h"
27
#include "tls13/certificate_request.h"
28
#include "ext/compress_certificate.h"
29
#include "ext/signature.h"
30
#include "ext/status_request.h"
31
#include "mbuffers.h"
32
#include "algorithms.h"
33
#include "auth/cert.h"
34
35
/* for tlist dereference */
36
#include "x509/verify-high.h"
37
38
0
#define EXTID_CERTIFICATE_AUTHORITIES 47
39
40
typedef struct crt_req_ctx_st {
41
  gnutls_session_t session;
42
  unsigned got_sig_algo;
43
  gnutls_pk_algorithm_t pk_algos[MAX_ALGOS];
44
  unsigned pk_algos_length;
45
  const uint8_t *rdn; /* pointer inside the message buffer */
46
  unsigned rdn_size;
47
} crt_req_ctx_st;
48
49
static unsigned is_algo_in_list(gnutls_pk_algorithm_t algo,
50
        gnutls_pk_algorithm_t *list, unsigned list_size)
51
0
{
52
0
  unsigned j;
53
54
0
  for (j = 0; j < list_size; j++) {
55
0
    if (list[j] == algo)
56
0
      return 1;
57
0
  }
58
0
  return 0;
59
0
}
60
61
static int parse_cert_extension(void *_ctx, unsigned tls_id,
62
        const uint8_t *data, unsigned data_size)
63
0
{
64
0
  crt_req_ctx_st *ctx = _ctx;
65
0
  gnutls_session_t session = ctx->session;
66
0
  unsigned v;
67
0
  int ret;
68
69
  /* Decide which certificate to use if the signature algorithms extension
70
   * is present.
71
   */
72
0
  if (tls_id == ext_mod_sig.tls_id) {
73
0
    const version_entry_st *ver = get_version(session);
74
0
    const gnutls_sign_entry_st *se;
75
    /* signature algorithms; let's use it to decide the certificate to use */
76
0
    unsigned i;
77
78
0
    if (ctx->got_sig_algo)
79
0
      return gnutls_assert_val(
80
0
        GNUTLS_E_RECEIVED_ILLEGAL_EXTENSION);
81
82
0
    ctx->got_sig_algo = 1;
83
84
0
    if (data_size < 2)
85
0
      return gnutls_assert_val(
86
0
        GNUTLS_E_TLS_PACKET_DECODING_ERROR);
87
88
0
    v = _gnutls_read_uint16(data);
89
0
    if (v != data_size - 2)
90
0
      return gnutls_assert_val(
91
0
        GNUTLS_E_TLS_PACKET_DECODING_ERROR);
92
93
0
    data += 2;
94
0
    data_size -= 2;
95
96
0
    ret = _gnutls_sign_algorithm_parse_data(session, data,
97
0
              data_size);
98
0
    if (ret < 0)
99
0
      return gnutls_assert_val(ret);
100
101
    /* The APIs to retrieve a client certificate accept the public
102
     * key algorithms instead of signatures. Get the public key algorithms
103
     * from the signatures.
104
     */
105
0
    for (i = 0; i < (unsigned)data_size; i += 2) {
106
0
      se = _gnutls_tls_aid_to_sign_entry(data[i], data[i + 1],
107
0
                 ver);
108
0
      if (se == NULL)
109
0
        continue;
110
111
0
      gnutls_pk_algorithm_t algo;
112
0
      if (se->cert_pk != GNUTLS_PK_UNKNOWN)
113
0
        algo = se->cert_pk;
114
0
      else
115
0
        algo = se->pk;
116
117
0
      if (ctx->pk_algos_length >=
118
0
          sizeof(ctx->pk_algos) / sizeof(ctx->pk_algos[0]))
119
0
        break;
120
121
0
      if (is_algo_in_list(algo, ctx->pk_algos,
122
0
              ctx->pk_algos_length))
123
0
        continue;
124
125
0
      ctx->pk_algos[ctx->pk_algos_length++] = algo;
126
0
    }
127
0
#ifdef ENABLE_OCSP
128
0
  } else if (tls_id == ext_mod_status_request.tls_id) {
129
0
    if (data_size != 0)
130
0
      return gnutls_assert_val(
131
0
        GNUTLS_E_TLS_PACKET_DECODING_ERROR);
132
133
    /* we are now allowed to send OCSP staples */
134
0
    session->internals.hsk_flags |= HSK_CLIENT_OCSP_REQUESTED;
135
0
#endif
136
0
  } else if (tls_id == EXTID_CERTIFICATE_AUTHORITIES) {
137
0
    if (data_size < 3) {
138
0
      return gnutls_assert_val(
139
0
        GNUTLS_E_TLS_PACKET_DECODING_ERROR);
140
0
    }
141
142
0
    v = _gnutls_read_uint16(data);
143
0
    if (v != data_size - 2)
144
0
      return gnutls_assert_val(
145
0
        GNUTLS_E_TLS_PACKET_DECODING_ERROR);
146
147
0
    ctx->rdn = data + 2;
148
0
    ctx->rdn_size = v;
149
0
  } else if (tls_id == ext_mod_compress_certificate.tls_id) {
150
0
    ret = _gnutls_compress_certificate_recv_params(session, data,
151
0
                     data_size);
152
0
    if (ret < 0) {
153
0
      return gnutls_assert_val(ret);
154
0
    }
155
0
  }
156
157
0
  return 0;
158
0
}
159
160
int _gnutls13_recv_certificate_request_int(gnutls_session_t session,
161
             gnutls_buffer_st *buf)
162
0
{
163
0
  int ret;
164
0
  crt_req_ctx_st ctx;
165
0
  gnutls_pcert_st *apr_cert_list;
166
0
  gnutls_privkey_t apr_pkey;
167
0
  int apr_cert_list_length;
168
169
0
  _gnutls_handshake_log("HSK[%p]: parsing certificate request\n",
170
0
            session);
171
172
0
  if (unlikely(session->security_parameters.entity == GNUTLS_SERVER))
173
0
    return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
174
175
  /* if initial negotiation is complete, this is a post-handshake auth */
176
0
  if (!session->internals.initial_negotiation_completed) {
177
0
    if (buf->data[0] != 0) {
178
      /* The context field must be empty during handshake */
179
0
      return gnutls_assert_val(
180
0
        GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER);
181
0
    }
182
183
    /* buf->length is positive */
184
0
    buf->data++;
185
0
    buf->length--;
186
0
  } else {
187
0
    gnutls_datum_t context;
188
189
0
    ret = _gnutls_buffer_pop_datum_prefix8(buf, &context);
190
0
    if (ret < 0)
191
0
      return gnutls_assert_val(ret);
192
193
0
    gnutls_free(session->internals.post_handshake_cr_context.data);
194
0
    ret = _gnutls_set_datum(
195
0
      &session->internals.post_handshake_cr_context,
196
0
      context.data, context.size);
197
0
    if (ret < 0)
198
0
      return gnutls_assert_val(ret);
199
0
  }
200
201
0
  memset(&ctx, 0, sizeof(ctx));
202
0
  ctx.session = session;
203
204
0
  ret = _gnutls_extv_parse(&ctx, parse_cert_extension, buf->data,
205
0
         buf->length);
206
0
  if (ret < 0)
207
0
    return gnutls_assert_val(ret);
208
209
  /* The "signature_algorithms" extension MUST be specified */
210
0
  if (!ctx.got_sig_algo)
211
0
    return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_EXTENSION);
212
213
0
  session->internals.hsk_flags |= HSK_CRT_ASKED;
214
215
0
  ret = _gnutls_select_client_cert(session, ctx.rdn, ctx.rdn_size,
216
0
           ctx.pk_algos, ctx.pk_algos_length);
217
0
  if (ret < 0)
218
0
    return gnutls_assert_val(ret);
219
220
0
  ret = _gnutls_get_selected_cert(session, &apr_cert_list,
221
0
          &apr_cert_list_length, &apr_pkey);
222
0
  if (ret < 0)
223
0
    return gnutls_assert_val(ret);
224
225
0
  if (apr_cert_list_length > 0) {
226
0
    gnutls_sign_algorithm_t algo;
227
228
0
    algo = _gnutls_session_get_sign_algo(session, &apr_cert_list[0],
229
0
                 apr_pkey, 0,
230
0
                 GNUTLS_KX_UNKNOWN);
231
0
    if (algo == GNUTLS_SIGN_UNKNOWN) {
232
0
      _gnutls_handshake_log(
233
0
        "HSK[%p]: rejecting client auth because of no suitable signature algorithm\n",
234
0
        session);
235
0
      _gnutls_selected_certs_deinit(session);
236
0
      return gnutls_assert_val(0);
237
0
    }
238
239
0
    gnutls_sign_algorithm_set_client(session, algo);
240
0
  }
241
242
0
  return 0;
243
0
}
244
245
int _gnutls13_recv_certificate_request(gnutls_session_t session)
246
0
{
247
0
  int ret;
248
0
  gnutls_buffer_st buf;
249
250
0
  if (!session->internals.initial_negotiation_completed &&
251
0
      session->internals.hsk_flags & HSK_PSK_SELECTED)
252
0
    return 0;
253
254
0
  if (unlikely(session->security_parameters.entity != GNUTLS_CLIENT))
255
0
    return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
256
257
0
  ret = _gnutls_recv_handshake(
258
0
    session, GNUTLS_HANDSHAKE_CERTIFICATE_REQUEST, 1, &buf);
259
0
  if (ret < 0)
260
0
    return gnutls_assert_val(ret);
261
262
  /* if not received */
263
0
  if (buf.length == 0) {
264
0
    _gnutls_buffer_clear(&buf);
265
0
    return 0;
266
0
  }
267
268
0
  ret = _gnutls13_recv_certificate_request_int(session, &buf);
269
270
0
  _gnutls_buffer_clear(&buf);
271
0
  return ret;
272
0
}
273
274
static int write_certificate_authorities(void *ctx, gnutls_buffer_st *buf)
275
0
{
276
0
  gnutls_session_t session = ctx;
277
0
  gnutls_certificate_credentials_t cred;
278
279
0
  if (session->internals.ignore_rdn_sequence != 0)
280
0
    return 0;
281
282
0
  cred = (gnutls_certificate_credentials_t)_gnutls_get_cred(
283
0
    session, GNUTLS_CRD_CERTIFICATE);
284
0
  if (cred == NULL) {
285
0
    gnutls_assert();
286
0
    return GNUTLS_E_INSUFFICIENT_CREDENTIALS;
287
0
  }
288
289
0
  if (cred->tlist->x509_rdn_sequence.size == 0)
290
0
    return 0;
291
292
0
  return _gnutls_buffer_append_data_prefix(
293
0
    buf, 16, cred->tlist->x509_rdn_sequence.data,
294
0
    cred->tlist->x509_rdn_sequence.size);
295
0
}
296
297
static int append_empty_ext(void *ctx, gnutls_buffer_st *buf)
298
0
{
299
0
  return GNUTLS_E_INT_RET_0;
300
0
}
301
302
int _gnutls13_send_certificate_request(gnutls_session_t session, unsigned again)
303
0
{
304
0
  gnutls_certificate_credentials_t cred;
305
0
  int ret;
306
0
  mbuffer_st *bufel = NULL;
307
0
  gnutls_buffer_st buf;
308
0
  unsigned init_pos;
309
310
0
  if (again == 0) {
311
0
    unsigned char rnd[12];
312
313
0
    if (!session->internals.initial_negotiation_completed &&
314
0
        session->internals.hsk_flags & HSK_PSK_SELECTED)
315
0
      return 0;
316
317
0
    if (session->internals.send_cert_req == 0)
318
0
      return 0;
319
320
0
    cred = (gnutls_certificate_credentials_t)_gnutls_get_cred(
321
0
      session, GNUTLS_CRD_CERTIFICATE);
322
0
    if (cred == NULL)
323
0
      return gnutls_assert_val(
324
0
        GNUTLS_E_INSUFFICIENT_CREDENTIALS);
325
326
0
    ret = _gnutls_buffer_init_handshake_mbuffer(&buf);
327
0
    if (ret < 0)
328
0
      return gnutls_assert_val(ret);
329
330
0
    if (session->internals
331
0
          .initial_negotiation_completed) { /* reauth */
332
0
      ret = gnutls_rnd(GNUTLS_RND_NONCE, rnd, sizeof(rnd));
333
0
      if (ret < 0) {
334
0
        gnutls_assert();
335
0
        goto cleanup;
336
0
      }
337
338
0
      gnutls_free(session->internals.post_handshake_cr_context
339
0
              .data);
340
0
      ret = _gnutls_set_datum(
341
0
        &session->internals.post_handshake_cr_context,
342
0
        rnd, sizeof(rnd));
343
0
      if (ret < 0) {
344
0
        gnutls_assert();
345
0
        goto cleanup;
346
0
      }
347
348
0
      ret = _gnutls_buffer_append_data_prefix(
349
0
        &buf, 8,
350
0
        session->internals.post_handshake_cr_context
351
0
          .data,
352
0
        session->internals.post_handshake_cr_context
353
0
          .size);
354
0
    } else {
355
0
      ret = _gnutls_buffer_append_prefix(&buf, 8, 0);
356
0
    }
357
358
0
    if (ret < 0) {
359
0
      gnutls_assert();
360
0
      goto cleanup;
361
0
    }
362
363
0
    ret = _gnutls_extv_append_init(&buf);
364
0
    if (ret < 0) {
365
0
      gnutls_assert();
366
0
      goto cleanup;
367
0
    }
368
0
    init_pos = ret;
369
370
0
    ret = _gnutls_extv_append(
371
0
      &buf, ext_mod_sig.tls_id, session,
372
0
      (extv_append_func)_gnutls_sign_algorithm_write_params);
373
0
    if (ret < 0) {
374
0
      gnutls_assert();
375
0
      goto cleanup;
376
0
    }
377
378
0
    ret = _gnutls_extv_append(&buf, EXTID_CERTIFICATE_AUTHORITIES,
379
0
            session,
380
0
            write_certificate_authorities);
381
0
    if (ret < 0) {
382
0
      gnutls_assert();
383
0
      goto cleanup;
384
0
    }
385
0
#ifdef ENABLE_OCSP
386
    /* We always advertise our support for OCSP stapling */
387
0
    ret = _gnutls_extv_append(&buf, ext_mod_status_request.tls_id,
388
0
            session, append_empty_ext);
389
0
    if (ret < 0) {
390
0
      gnutls_assert();
391
0
      goto cleanup;
392
0
    }
393
0
    session->internals.hsk_flags |= HSK_CLIENT_OCSP_REQUESTED;
394
0
#endif
395
396
0
    ret = _gnutls_extv_append(
397
0
      &buf, ext_mod_compress_certificate.tls_id, session,
398
0
      (extv_append_func)
399
0
        _gnutls_compress_certificate_send_params);
400
0
    if (ret < 0) {
401
0
      gnutls_assert();
402
0
      goto cleanup;
403
0
    }
404
405
0
    ret = _gnutls_extv_append_final(&buf, init_pos, 0);
406
0
    if (ret < 0) {
407
0
      gnutls_assert();
408
0
      goto cleanup;
409
0
    }
410
411
0
    bufel = _gnutls_buffer_to_mbuffer(&buf);
412
413
0
    session->internals.hsk_flags |= HSK_CRT_REQ_SENT;
414
0
  }
415
416
0
  return _gnutls_send_handshake(session, bufel,
417
0
              GNUTLS_HANDSHAKE_CERTIFICATE_REQUEST);
418
419
0
cleanup:
420
0
  _gnutls_buffer_clear(&buf);
421
0
  return ret;
422
0
}