Coverage Report

Created: 2025-03-18 06:55

/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
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
      if (ctx->pk_algos_length >=
112
0
          sizeof(ctx->pk_algos) / sizeof(ctx->pk_algos[0]))
113
0
        break;
114
115
0
      if (is_algo_in_list(se->pk, ctx->pk_algos,
116
0
              ctx->pk_algos_length))
117
0
        continue;
118
119
0
      ctx->pk_algos[ctx->pk_algos_length++] = se->pk;
120
0
    }
121
0
#ifdef ENABLE_OCSP
122
0
  } else if (tls_id == ext_mod_status_request.tls_id) {
123
0
    if (data_size != 0)
124
0
      return gnutls_assert_val(
125
0
        GNUTLS_E_TLS_PACKET_DECODING_ERROR);
126
127
    /* we are now allowed to send OCSP staples */
128
0
    session->internals.hsk_flags |= HSK_CLIENT_OCSP_REQUESTED;
129
0
#endif
130
0
  } else if (tls_id == EXTID_CERTIFICATE_AUTHORITIES) {
131
0
    if (data_size < 3) {
132
0
      return gnutls_assert_val(
133
0
        GNUTLS_E_TLS_PACKET_DECODING_ERROR);
134
0
    }
135
136
0
    v = _gnutls_read_uint16(data);
137
0
    if (v != data_size - 2)
138
0
      return gnutls_assert_val(
139
0
        GNUTLS_E_TLS_PACKET_DECODING_ERROR);
140
141
0
    ctx->rdn = data + 2;
142
0
    ctx->rdn_size = v;
143
0
  } else if (tls_id == ext_mod_compress_certificate.tls_id) {
144
0
    ret = _gnutls_compress_certificate_recv_params(session, data,
145
0
                     data_size);
146
0
    if (ret < 0) {
147
0
      return gnutls_assert_val(ret);
148
0
    }
149
0
  }
150
151
0
  return 0;
152
0
}
153
154
int _gnutls13_recv_certificate_request_int(gnutls_session_t session,
155
             gnutls_buffer_st *buf)
156
0
{
157
0
  int ret;
158
0
  crt_req_ctx_st ctx;
159
0
  gnutls_pcert_st *apr_cert_list;
160
0
  gnutls_privkey_t apr_pkey;
161
0
  int apr_cert_list_length;
162
163
0
  _gnutls_handshake_log("HSK[%p]: parsing certificate request\n",
164
0
            session);
165
166
0
  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
0
  if (!session->internals.initial_negotiation_completed) {
171
0
    if (buf->data[0] != 0) {
172
      /* The context field must be empty during handshake */
173
0
      return gnutls_assert_val(
174
0
        GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER);
175
0
    }
176
177
    /* buf->length is positive */
178
0
    buf->data++;
179
0
    buf->length--;
180
0
  } 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
0
  memset(&ctx, 0, sizeof(ctx));
196
0
  ctx.session = session;
197
198
0
  ret = _gnutls_extv_parse(&ctx, parse_cert_extension, buf->data,
199
0
         buf->length);
200
0
  if (ret < 0)
201
0
    return gnutls_assert_val(ret);
202
203
  /* The "signature_algorithms" extension MUST be specified */
204
0
  if (!ctx.got_sig_algo)
205
0
    return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_EXTENSION);
206
207
0
  session->internals.hsk_flags |= HSK_CRT_ASKED;
208
209
0
  ret = _gnutls_select_client_cert(session, ctx.rdn, ctx.rdn_size,
210
0
           ctx.pk_algos, ctx.pk_algos_length);
211
0
  if (ret < 0)
212
0
    return gnutls_assert_val(ret);
213
214
0
  ret = _gnutls_get_selected_cert(session, &apr_cert_list,
215
0
          &apr_cert_list_length, &apr_pkey);
216
0
  if (ret < 0)
217
0
    return gnutls_assert_val(ret);
218
219
0
  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
0
  return 0;
237
0
}
238
239
int _gnutls13_recv_certificate_request(gnutls_session_t session)
240
0
{
241
0
  int ret;
242
0
  gnutls_buffer_st buf;
243
244
0
  if (!session->internals.initial_negotiation_completed &&
245
0
      session->internals.hsk_flags & HSK_PSK_SELECTED)
246
0
    return 0;
247
248
0
  if (unlikely(session->security_parameters.entity != GNUTLS_CLIENT))
249
0
    return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
250
251
0
  ret = _gnutls_recv_handshake(
252
0
    session, GNUTLS_HANDSHAKE_CERTIFICATE_REQUEST, 1, &buf);
253
0
  if (ret < 0)
254
0
    return gnutls_assert_val(ret);
255
256
  /* if not received */
257
0
  if (buf.length == 0) {
258
0
    _gnutls_buffer_clear(&buf);
259
0
    return 0;
260
0
  }
261
262
0
  ret = _gnutls13_recv_certificate_request_int(session, &buf);
263
264
0
  _gnutls_buffer_clear(&buf);
265
0
  return ret;
266
0
}
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
0
{
298
0
  gnutls_certificate_credentials_t cred;
299
0
  int ret;
300
0
  mbuffer_st *bufel = NULL;
301
0
  gnutls_buffer_st buf;
302
0
  unsigned init_pos;
303
304
0
  if (again == 0) {
305
0
    unsigned char rnd[12];
306
307
0
    if (!session->internals.initial_negotiation_completed &&
308
0
        session->internals.hsk_flags & HSK_PSK_SELECTED)
309
0
      return 0;
310
311
0
    if (session->internals.send_cert_req == 0)
312
0
      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
0
}