Coverage Report

Created: 2025-07-23 06:43

/src/gnutls/lib/tls13/certificate_request.c
Line
Count
Source (jump to first uncovered line)
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.14k
#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
526
{
52
526
  unsigned j;
53
54
981
  for (j = 0; j < list_size; j++) {
55
825
    if (list[j] == algo)
56
370
      return 1;
57
825
  }
58
156
  return 0;
59
526
}
60
61
static int parse_cert_extension(void *_ctx, unsigned tls_id,
62
        const uint8_t *data, unsigned data_size)
63
1.46k
{
64
1.46k
  crt_req_ctx_st *ctx = _ctx;
65
1.46k
  gnutls_session_t session = ctx->session;
66
1.46k
  unsigned v;
67
1.46k
  int ret;
68
69
  /* Decide which certificate to use if the signature algorithms extension
70
   * is present.
71
   */
72
1.46k
  if (tls_id == ext_mod_sig.tls_id) {
73
206
    const version_entry_st *ver = get_version(session);
74
206
    const gnutls_sign_entry_st *se;
75
    /* signature algorithms; let's use it to decide the certificate to use */
76
206
    unsigned i;
77
78
206
    if (ctx->got_sig_algo)
79
5
      return gnutls_assert_val(
80
206
        GNUTLS_E_RECEIVED_ILLEGAL_EXTENSION);
81
82
201
    ctx->got_sig_algo = 1;
83
84
201
    if (data_size < 2)
85
5
      return gnutls_assert_val(
86
201
        GNUTLS_E_TLS_PACKET_DECODING_ERROR);
87
88
196
    v = _gnutls_read_uint16(data);
89
196
    if (v != data_size - 2)
90
40
      return gnutls_assert_val(
91
196
        GNUTLS_E_TLS_PACKET_DECODING_ERROR);
92
93
156
    data += 2;
94
156
    data_size -= 2;
95
96
156
    ret = _gnutls_sign_algorithm_parse_data(session, data,
97
156
              data_size);
98
156
    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
2.48k
    for (i = 0; i < (unsigned)data_size; i += 2) {
106
2.33k
      se = _gnutls_tls_aid_to_sign_entry(data[i], data[i + 1],
107
2.33k
                 ver);
108
2.33k
      if (se == NULL)
109
1.80k
        continue;
110
111
526
      if (ctx->pk_algos_length >=
112
526
          sizeof(ctx->pk_algos) / sizeof(ctx->pk_algos[0]))
113
0
        break;
114
115
526
      if (is_algo_in_list(se->pk, ctx->pk_algos,
116
526
              ctx->pk_algos_length))
117
370
        continue;
118
119
156
      ctx->pk_algos[ctx->pk_algos_length++] = se->pk;
120
156
    }
121
149
#ifdef ENABLE_OCSP
122
1.26k
  } else if (tls_id == ext_mod_status_request.tls_id) {
123
121
    if (data_size != 0)
124
13
      return gnutls_assert_val(
125
121
        GNUTLS_E_TLS_PACKET_DECODING_ERROR);
126
127
    /* we are now allowed to send OCSP staples */
128
108
    session->internals.hsk_flags |= HSK_CLIENT_OCSP_REQUESTED;
129
108
#endif
130
1.14k
  } else if (tls_id == EXTID_CERTIFICATE_AUTHORITIES) {
131
81
    if (data_size < 3) {
132
4
      return gnutls_assert_val(
133
4
        GNUTLS_E_TLS_PACKET_DECODING_ERROR);
134
4
    }
135
136
77
    v = _gnutls_read_uint16(data);
137
77
    if (v != data_size - 2)
138
37
      return gnutls_assert_val(
139
77
        GNUTLS_E_TLS_PACKET_DECODING_ERROR);
140
141
40
    ctx->rdn = data + 2;
142
40
    ctx->rdn_size = v;
143
1.06k
  } else if (tls_id == ext_mod_compress_certificate.tls_id) {
144
105
    ret = _gnutls_compress_certificate_recv_params(session, data,
145
105
                     data_size);
146
105
    if (ret < 0) {
147
0
      return gnutls_assert_val(ret);
148
0
    }
149
105
  }
150
151
1.35k
  return 0;
152
1.46k
}
153
154
int _gnutls13_recv_certificate_request_int(gnutls_session_t session,
155
             gnutls_buffer_st *buf)
156
402
{
157
402
  int ret;
158
402
  crt_req_ctx_st ctx;
159
402
  gnutls_pcert_st *apr_cert_list;
160
402
  gnutls_privkey_t apr_pkey;
161
402
  int apr_cert_list_length;
162
163
402
  _gnutls_handshake_log("HSK[%p]: parsing certificate request\n",
164
402
            session);
165
166
402
  if (unlikely(session->security_parameters.entity == GNUTLS_SERVER))
167
0
    return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
168
169
  /* if initial negotiation is complete, this is a post-handshake auth */
170
402
  if (!session->internals.initial_negotiation_completed) {
171
402
    if (buf->data[0] != 0) {
172
      /* The context field must be empty during handshake */
173
26
      return gnutls_assert_val(
174
26
        GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER);
175
26
    }
176
177
    /* buf->length is positive */
178
376
    buf->data++;
179
376
    buf->length--;
180
376
  } else {
181
0
    gnutls_datum_t context;
182
183
0
    ret = _gnutls_buffer_pop_datum_prefix8(buf, &context);
184
0
    if (ret < 0)
185
0
      return gnutls_assert_val(ret);
186
187
0
    gnutls_free(session->internals.post_handshake_cr_context.data);
188
0
    ret = _gnutls_set_datum(
189
0
      &session->internals.post_handshake_cr_context,
190
0
      context.data, context.size);
191
0
    if (ret < 0)
192
0
      return gnutls_assert_val(ret);
193
0
  }
194
195
376
  memset(&ctx, 0, sizeof(ctx));
196
376
  ctx.session = session;
197
198
376
  ret = _gnutls_extv_parse(&ctx, parse_cert_extension, buf->data,
199
376
         buf->length);
200
376
  if (ret < 0)
201
243
    return gnutls_assert_val(ret);
202
203
  /* The "signature_algorithms" extension MUST be specified */
204
133
  if (!ctx.got_sig_algo)
205
63
    return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_EXTENSION);
206
207
70
  session->internals.hsk_flags |= HSK_CRT_ASKED;
208
209
70
  ret = _gnutls_select_client_cert(session, ctx.rdn, ctx.rdn_size,
210
70
           ctx.pk_algos, ctx.pk_algos_length);
211
70
  if (ret < 0)
212
22
    return gnutls_assert_val(ret);
213
214
48
  ret = _gnutls_get_selected_cert(session, &apr_cert_list,
215
48
          &apr_cert_list_length, &apr_pkey);
216
48
  if (ret < 0)
217
0
    return gnutls_assert_val(ret);
218
219
48
  if (apr_cert_list_length > 0) {
220
0
    gnutls_sign_algorithm_t algo;
221
222
0
    algo = _gnutls_session_get_sign_algo(session, &apr_cert_list[0],
223
0
                 apr_pkey, 0,
224
0
                 GNUTLS_KX_UNKNOWN);
225
0
    if (algo == GNUTLS_SIGN_UNKNOWN) {
226
0
      _gnutls_handshake_log(
227
0
        "HSK[%p]: rejecting client auth because of no suitable signature algorithm\n",
228
0
        session);
229
0
      _gnutls_selected_certs_deinit(session);
230
0
      return gnutls_assert_val(0);
231
0
    }
232
233
0
    gnutls_sign_algorithm_set_client(session, algo);
234
0
  }
235
236
48
  return 0;
237
48
}
238
239
int _gnutls13_recv_certificate_request(gnutls_session_t session)
240
9.69k
{
241
9.69k
  int ret;
242
9.69k
  gnutls_buffer_st buf;
243
244
9.69k
  if (!session->internals.initial_negotiation_completed &&
245
9.69k
      session->internals.hsk_flags & HSK_PSK_SELECTED)
246
12
    return 0;
247
248
9.67k
  if (unlikely(session->security_parameters.entity != GNUTLS_CLIENT))
249
0
    return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
250
251
9.67k
  ret = _gnutls_recv_handshake(
252
9.67k
    session, GNUTLS_HANDSHAKE_CERTIFICATE_REQUEST, 1, &buf);
253
9.67k
  if (ret < 0)
254
4.61k
    return gnutls_assert_val(ret);
255
256
  /* if not received */
257
5.06k
  if (buf.length == 0) {
258
4.65k
    _gnutls_buffer_clear(&buf);
259
4.65k
    return 0;
260
4.65k
  }
261
262
402
  ret = _gnutls13_recv_certificate_request_int(session, &buf);
263
264
402
  _gnutls_buffer_clear(&buf);
265
402
  return ret;
266
5.06k
}
267
268
static int write_certificate_authorities(void *ctx, gnutls_buffer_st *buf)
269
0
{
270
0
  gnutls_session_t session = ctx;
271
0
  gnutls_certificate_credentials_t cred;
272
273
0
  if (session->internals.ignore_rdn_sequence != 0)
274
0
    return 0;
275
276
0
  cred = (gnutls_certificate_credentials_t)_gnutls_get_cred(
277
0
    session, GNUTLS_CRD_CERTIFICATE);
278
0
  if (cred == NULL) {
279
0
    gnutls_assert();
280
0
    return GNUTLS_E_INSUFFICIENT_CREDENTIALS;
281
0
  }
282
283
0
  if (cred->tlist->x509_rdn_sequence.size == 0)
284
0
    return 0;
285
286
0
  return _gnutls_buffer_append_data_prefix(
287
0
    buf, 16, cred->tlist->x509_rdn_sequence.data,
288
0
    cred->tlist->x509_rdn_sequence.size);
289
0
}
290
291
static int append_empty_ext(void *ctx, gnutls_buffer_st *buf)
292
0
{
293
0
  return GNUTLS_E_INT_RET_0;
294
0
}
295
296
int _gnutls13_send_certificate_request(gnutls_session_t session, unsigned again)
297
1.50k
{
298
1.50k
  gnutls_certificate_credentials_t cred;
299
1.50k
  int ret;
300
1.50k
  mbuffer_st *bufel = NULL;
301
1.50k
  gnutls_buffer_st buf;
302
1.50k
  unsigned init_pos;
303
304
1.50k
  if (again == 0) {
305
1.50k
    unsigned char rnd[12];
306
307
1.50k
    if (!session->internals.initial_negotiation_completed &&
308
1.50k
        session->internals.hsk_flags & HSK_PSK_SELECTED)
309
0
      return 0;
310
311
1.50k
    if (session->internals.send_cert_req == 0)
312
1.50k
      return 0;
313
314
0
    cred = (gnutls_certificate_credentials_t)_gnutls_get_cred(
315
0
      session, GNUTLS_CRD_CERTIFICATE);
316
0
    if (cred == NULL)
317
0
      return gnutls_assert_val(
318
0
        GNUTLS_E_INSUFFICIENT_CREDENTIALS);
319
320
0
    ret = _gnutls_buffer_init_handshake_mbuffer(&buf);
321
0
    if (ret < 0)
322
0
      return gnutls_assert_val(ret);
323
324
0
    if (session->internals
325
0
          .initial_negotiation_completed) { /* reauth */
326
0
      ret = gnutls_rnd(GNUTLS_RND_NONCE, rnd, sizeof(rnd));
327
0
      if (ret < 0) {
328
0
        gnutls_assert();
329
0
        goto cleanup;
330
0
      }
331
332
0
      gnutls_free(session->internals.post_handshake_cr_context
333
0
              .data);
334
0
      ret = _gnutls_set_datum(
335
0
        &session->internals.post_handshake_cr_context,
336
0
        rnd, sizeof(rnd));
337
0
      if (ret < 0) {
338
0
        gnutls_assert();
339
0
        goto cleanup;
340
0
      }
341
342
0
      ret = _gnutls_buffer_append_data_prefix(
343
0
        &buf, 8,
344
0
        session->internals.post_handshake_cr_context
345
0
          .data,
346
0
        session->internals.post_handshake_cr_context
347
0
          .size);
348
0
    } else {
349
0
      ret = _gnutls_buffer_append_prefix(&buf, 8, 0);
350
0
    }
351
352
0
    if (ret < 0) {
353
0
      gnutls_assert();
354
0
      goto cleanup;
355
0
    }
356
357
0
    ret = _gnutls_extv_append_init(&buf);
358
0
    if (ret < 0) {
359
0
      gnutls_assert();
360
0
      goto cleanup;
361
0
    }
362
0
    init_pos = ret;
363
364
0
    ret = _gnutls_extv_append(
365
0
      &buf, ext_mod_sig.tls_id, session,
366
0
      (extv_append_func)_gnutls_sign_algorithm_write_params);
367
0
    if (ret < 0) {
368
0
      gnutls_assert();
369
0
      goto cleanup;
370
0
    }
371
372
0
    ret = _gnutls_extv_append(&buf, EXTID_CERTIFICATE_AUTHORITIES,
373
0
            session,
374
0
            write_certificate_authorities);
375
0
    if (ret < 0) {
376
0
      gnutls_assert();
377
0
      goto cleanup;
378
0
    }
379
0
#ifdef ENABLE_OCSP
380
    /* We always advertise our support for OCSP stapling */
381
0
    ret = _gnutls_extv_append(&buf, ext_mod_status_request.tls_id,
382
0
            session, append_empty_ext);
383
0
    if (ret < 0) {
384
0
      gnutls_assert();
385
0
      goto cleanup;
386
0
    }
387
0
    session->internals.hsk_flags |= HSK_CLIENT_OCSP_REQUESTED;
388
0
#endif
389
390
0
    ret = _gnutls_extv_append(
391
0
      &buf, ext_mod_compress_certificate.tls_id, session,
392
0
      (extv_append_func)
393
0
        _gnutls_compress_certificate_send_params);
394
0
    if (ret < 0) {
395
0
      gnutls_assert();
396
0
      goto cleanup;
397
0
    }
398
399
0
    ret = _gnutls_extv_append_final(&buf, init_pos, 0);
400
0
    if (ret < 0) {
401
0
      gnutls_assert();
402
0
      goto cleanup;
403
0
    }
404
405
0
    bufel = _gnutls_buffer_to_mbuffer(&buf);
406
407
0
    session->internals.hsk_flags |= HSK_CRT_REQ_SENT;
408
0
  }
409
410
0
  return _gnutls_send_handshake(session, bufel,
411
0
              GNUTLS_HANDSHAKE_CERTIFICATE_REQUEST);
412
413
0
cleanup:
414
0
  _gnutls_buffer_clear(&buf);
415
0
  return ret;
416
1.50k
}