Coverage Report

Created: 2026-11-12 07:00

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
1.12k
#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
829
{
52
829
  unsigned j;
53
54
1.52k
  for (j = 0; j < list_size; j++) {
55
1.30k
    if (list[j] == algo)
56
611
      return 1;
57
1.30k
  }
58
218
  return 0;
59
829
}
60
61
static int parse_cert_extension(void *_ctx, unsigned tls_id,
62
        const uint8_t *data, unsigned data_size)
63
1.47k
{
64
1.47k
  crt_req_ctx_st *ctx = _ctx;
65
1.47k
  gnutls_session_t session = ctx->session;
66
1.47k
  unsigned v;
67
1.47k
  int ret;
68
69
  /* Decide which certificate to use if the signature algorithms extension
70
   * is present.
71
   */
72
1.47k
  if (tls_id == ext_mod_sig.tls_id) {
73
226
    const version_entry_st *ver = get_version(session);
74
226
    const gnutls_sign_entry_st *se;
75
    /* signature algorithms; let's use it to decide the certificate to use */
76
226
    unsigned i;
77
78
226
    if (ctx->got_sig_algo)
79
6
      return gnutls_assert_val(
80
226
        GNUTLS_E_RECEIVED_ILLEGAL_EXTENSION);
81
82
220
    ctx->got_sig_algo = 1;
83
84
220
    if (data_size < 2)
85
5
      return gnutls_assert_val(
86
220
        GNUTLS_E_TLS_PACKET_DECODING_ERROR);
87
88
215
    v = _gnutls_read_uint16(data);
89
215
    if (v != data_size - 2)
90
34
      return gnutls_assert_val(
91
215
        GNUTLS_E_TLS_PACKET_DECODING_ERROR);
92
93
181
    data += 2;
94
181
    data_size -= 2;
95
96
181
    ret = _gnutls_sign_algorithm_parse_data(session, data,
97
181
              data_size);
98
181
    if (ret < 0)
99
7
      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
4.43k
    for (i = 0; i < (unsigned)data_size; i += 2) {
106
4.26k
      se = _gnutls_tls_aid_to_sign_entry(data[i], data[i + 1],
107
4.26k
                 ver);
108
4.26k
      if (se == NULL)
109
3.43k
        continue;
110
111
829
      gnutls_pk_algorithm_t algo;
112
829
      if (se->cert_pk != GNUTLS_PK_UNKNOWN)
113
102
        algo = se->cert_pk;
114
727
      else
115
727
        algo = se->pk;
116
117
829
      if (ctx->pk_algos_length >=
118
829
          sizeof(ctx->pk_algos) / sizeof(ctx->pk_algos[0]))
119
0
        break;
120
121
829
      if (is_algo_in_list(algo, ctx->pk_algos,
122
829
              ctx->pk_algos_length))
123
611
        continue;
124
125
218
      ctx->pk_algos[ctx->pk_algos_length++] = algo;
126
218
    }
127
174
#ifdef ENABLE_OCSP
128
1.24k
  } else if (tls_id == ext_mod_status_request.tls_id) {
129
126
    if (data_size != 0)
130
7
      return gnutls_assert_val(
131
126
        GNUTLS_E_TLS_PACKET_DECODING_ERROR);
132
133
    /* we are now allowed to send OCSP staples */
134
119
    session->internals.hsk_flags |= HSK_CLIENT_OCSP_REQUESTED;
135
119
#endif
136
1.12k
  } else if (tls_id == EXTID_CERTIFICATE_AUTHORITIES) {
137
70
    if (data_size < 3) {
138
4
      return gnutls_assert_val(
139
4
        GNUTLS_E_TLS_PACKET_DECODING_ERROR);
140
4
    }
141
142
66
    v = _gnutls_read_uint16(data);
143
66
    if (v != data_size - 2)
144
24
      return gnutls_assert_val(
145
66
        GNUTLS_E_TLS_PACKET_DECODING_ERROR);
146
147
42
    ctx->rdn = data + 2;
148
42
    ctx->rdn_size = v;
149
1.05k
  } else if (tls_id == ext_mod_compress_certificate.tls_id) {
150
106
    ret = _gnutls_compress_certificate_recv_params(session, data,
151
106
                     data_size);
152
106
    if (ret < 0) {
153
0
      return gnutls_assert_val(ret);
154
0
    }
155
106
  }
156
157
1.38k
  return 0;
158
1.47k
}
159
160
int _gnutls13_recv_certificate_request_int(gnutls_session_t session,
161
             gnutls_buffer_st *buf)
162
395
{
163
395
  int ret;
164
395
  crt_req_ctx_st ctx;
165
395
  gnutls_pcert_st *apr_cert_list;
166
395
  gnutls_privkey_t apr_pkey;
167
395
  int apr_cert_list_length;
168
169
395
  _gnutls_handshake_log("HSK[%p]: parsing certificate request\n",
170
395
            session);
171
172
395
  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
395
  if (!session->internals.initial_negotiation_completed) {
177
395
    if (buf->data[0] != 0) {
178
      /* The context field must be empty during handshake */
179
25
      return gnutls_assert_val(
180
25
        GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER);
181
25
    }
182
183
    /* buf->length is positive */
184
370
    buf->data++;
185
370
    buf->length--;
186
370
  } 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
370
  memset(&ctx, 0, sizeof(ctx));
202
370
  ctx.session = session;
203
204
370
  ret = _gnutls_extv_parse(&ctx, parse_cert_extension, buf->data,
205
370
         buf->length);
206
370
  if (ret < 0)
207
238
    return gnutls_assert_val(ret);
208
209
  /* The "signature_algorithms" extension MUST be specified */
210
132
  if (!ctx.got_sig_algo)
211
57
    return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_EXTENSION);
212
213
75
  session->internals.hsk_flags |= HSK_CRT_ASKED;
214
215
75
  ret = _gnutls_select_client_cert(session, ctx.rdn, ctx.rdn_size,
216
75
           ctx.pk_algos, ctx.pk_algos_length);
217
75
  if (ret < 0)
218
25
    return gnutls_assert_val(ret);
219
220
50
  ret = _gnutls_get_selected_cert(session, &apr_cert_list,
221
50
          &apr_cert_list_length, &apr_pkey);
222
50
  if (ret < 0)
223
0
    return gnutls_assert_val(ret);
224
225
50
  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
50
  return 0;
243
50
}
244
245
int _gnutls13_recv_certificate_request(gnutls_session_t session)
246
8.79k
{
247
8.79k
  int ret;
248
8.79k
  gnutls_buffer_st buf;
249
250
8.79k
  if (!session->internals.initial_negotiation_completed &&
251
8.79k
      session->internals.hsk_flags & HSK_PSK_SELECTED)
252
22
    return 0;
253
254
8.77k
  if (unlikely(session->security_parameters.entity != GNUTLS_CLIENT))
255
0
    return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
256
257
8.77k
  ret = _gnutls_recv_handshake(
258
8.77k
    session, GNUTLS_HANDSHAKE_CERTIFICATE_REQUEST, 1, &buf);
259
8.77k
  if (ret < 0)
260
4.19k
    return gnutls_assert_val(ret);
261
262
  /* if not received */
263
4.57k
  if (buf.length == 0) {
264
4.17k
    _gnutls_buffer_clear(&buf);
265
4.17k
    return 0;
266
4.17k
  }
267
268
395
  ret = _gnutls13_recv_certificate_request_int(session, &buf);
269
270
395
  _gnutls_buffer_clear(&buf);
271
395
  return ret;
272
4.57k
}
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_prefix16(
293
0
    buf, 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
1.28k
{
304
1.28k
  gnutls_certificate_credentials_t cred;
305
1.28k
  int ret;
306
1.28k
  mbuffer_st *bufel = NULL;
307
1.28k
  gnutls_buffer_st buf;
308
1.28k
  unsigned init_pos;
309
310
1.28k
  if (again == 0) {
311
1.28k
    unsigned char rnd[12];
312
313
1.28k
    if (!session->internals.initial_negotiation_completed &&
314
1.28k
        session->internals.hsk_flags & HSK_PSK_SELECTED)
315
0
      return 0;
316
317
1.28k
    if (session->internals.send_cert_req == 0)
318
1.28k
      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_prefix8(
349
0
        &buf,
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_uint8(&buf, 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
1.28k
}