Coverage Report

Created: 2026-02-14 06:49

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gnutls/lib/ext/status_request.c
Line
Count
Source
1
/*
2
 * Copyright (C) 2012-2017 Free Software Foundation, Inc.
3
 * Copyright (C) 2017 Red Hat, Inc.
4
 *
5
 * Author: Simon Josefsson, Nikos Mavrogiannopoulos
6
 *
7
 * This file is part of GnuTLS.
8
 *
9
 * The GnuTLS is free software; you can redistribute it and/or
10
 * modify it under the terms of the GNU Lesser General Public License
11
 * as published by the Free Software Foundation; either version 2.1 of
12
 * the License, or (at your option) any later version.
13
 *
14
 * This library is distributed in the hope that it will be useful, but
15
 * WITHOUT ANY WARRANTY; without even the implied warranty of
16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17
 * Lesser General Public License for more details.
18
 *
19
 * You should have received a copy of the GNU Lesser General Public License
20
 * along with this program.  If not, see <https://www.gnu.org/licenses/>
21
 *
22
 */
23
24
/*
25
  Status Request (OCSP) TLS extension.  See RFC 6066 section 8:
26
  https://tools.ietf.org/html/rfc6066#section-8
27
*/
28
29
#include "gnutls_int.h"
30
#include "errors.h"
31
#include "hello_ext.h"
32
#include "ext/status_request.h"
33
#include "mbuffers.h"
34
#include "auth.h"
35
#include "auth/cert.h"
36
#include "handshake.h"
37
38
#ifdef ENABLE_OCSP
39
40
typedef struct {
41
  /* server response */
42
  gnutls_datum_t sresp;
43
44
  unsigned int expect_cstatus;
45
} status_request_ext_st;
46
47
/*
48
  From RFC 6066.  Client sends:
49
50
      struct {
51
    CertificateStatusType status_type;
52
    select (status_type) {
53
        case ocsp: OCSPStatusRequest;
54
    } request;
55
      } CertificateStatusRequest;
56
57
      enum { ocsp(1), (255) } CertificateStatusType;
58
59
      struct {
60
    ResponderID responder_id_list<0..2^16-1>;
61
    Extensions  request_extensions;
62
      } OCSPStatusRequest;
63
64
      opaque ResponderID<1..2^16-1>;
65
      opaque Extensions<0..2^16-1>;
66
*/
67
68
static int client_send(gnutls_session_t session, gnutls_buffer_st *extdata,
69
           status_request_ext_st *priv)
70
20.9k
{
71
20.9k
  const uint8_t data[5] = { 0x01, 0x00, 0x00, 0x00, 0x00 };
72
20.9k
  const int len = 5;
73
20.9k
  int ret;
74
75
  /* We do not support setting either ResponderID or Extensions */
76
77
20.9k
  ret = _gnutls_buffer_append_data(extdata, data, len);
78
20.9k
  if (ret < 0)
79
0
    return gnutls_assert_val(ret);
80
81
20.9k
  session->internals.hsk_flags |= HSK_OCSP_REQUESTED;
82
83
20.9k
  return len;
84
20.9k
}
85
86
static int server_recv(gnutls_session_t session, const uint8_t *data,
87
           size_t data_size)
88
1.42k
{
89
1.42k
  unsigned rid_bytes = 0;
90
91
  /* minimum message is type (1) + responder_id_list (2) +
92
     request_extension (2) = 5 */
93
1.42k
  if (data_size < 5)
94
9
    return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
95
96
  /* We ignore non-ocsp CertificateStatusType.  The spec is unclear
97
     what should be done. */
98
1.41k
  if (data[0] != 0x01) {
99
628
    gnutls_assert();
100
628
    _gnutls_handshake_log("EXT[%p]: unknown status_type %d\n",
101
628
              session, data[0]);
102
628
    return 0;
103
628
  }
104
790
  DECR_LEN(data_size, 1);
105
790
  data++;
106
107
790
  rid_bytes = _gnutls_read_uint16(data);
108
109
790
  DECR_LEN(data_size, 2);
110
  /*data += 2; */
111
112
  /* sanity check only, we don't use any of the data below */
113
114
790
  if (data_size < rid_bytes)
115
35
    return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER);
116
117
755
  _gnutls_handshake_log("EXT[%p]: OCSP status was requested\n", session);
118
755
  session->internals.hsk_flags |= HSK_OCSP_REQUESTED;
119
120
755
  return 0;
121
790
}
122
123
static int client_recv(gnutls_session_t session, status_request_ext_st *priv,
124
           const uint8_t *data, size_t size)
125
550
{
126
550
  if (size != 0)
127
8
    return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
128
542
  else {
129
542
    priv->expect_cstatus = 1;
130
542
    return 0;
131
542
  }
132
550
}
133
134
/*
135
 * Servers return a certificate response along with their certificate
136
 * by sending a "CertificateStatus" message immediately after the
137
 * "Certificate" message (and before any "ServerKeyExchange" or
138
 * "CertificateRequest" messages).  If a server returns a
139
 * "CertificateStatus" message, then the server MUST have included an
140
 * extension of type "status_request" with empty "extension_data" in
141
 * the extended server hello.
142
 *
143
 * According to the description above, as a server we could simply 
144
 * return GNUTLS_E_INT_RET_0 on this function. In that case we would
145
 * only need to use the callbacks at the time we need to send the data,
146
 * and skip the status response packet if no such data are there.
147
 * However, that behavior would break gnutls 3.3.x which expects the status 
148
 * response to be always send if the extension is present.
149
 *
150
 * Instead we ensure that this extension is parsed after the CS/certificate
151
 * are selected (with the _GNUTLS_EXT_TLS_POST_CS type), and we discover
152
 * (or not) the response to send early.
153
 */
154
static int server_send(gnutls_session_t session, gnutls_buffer_st *extdata,
155
           status_request_ext_st *priv)
156
526
{
157
526
  int ret;
158
526
  gnutls_certificate_credentials_t cred;
159
526
  gnutls_status_request_ocsp_func func;
160
526
  void *func_ptr;
161
526
  const version_entry_st *ver = get_version(session);
162
163
526
  cred = (gnutls_certificate_credentials_t)_gnutls_get_cred(
164
526
    session, GNUTLS_CRD_CERTIFICATE);
165
526
  if (cred == NULL) /* no certificate authentication */
166
0
    return 0;
167
168
  /* no need to set sresp; responses are send during certificate sending and
169
   * no response is required from server side. */
170
526
  if (ver && ver->multi_ocsp)
171
0
    return 0;
172
173
  /* Under TLS1.2 we obtain the response at this point in order to respond
174
   * appropriately (empty extension vs no response) */
175
526
  if (session->internals.selected_ocsp_length > 0) {
176
0
    if (session->internals.selected_ocsp[0].response.data) {
177
0
      if (session->internals.selected_ocsp[0].exptime != 0 &&
178
0
          (gnutls_time(0) >=
179
0
           session->internals.selected_ocsp[0].exptime)) {
180
0
        gnutls_assert();
181
0
        return 0;
182
0
      }
183
184
0
      ret = _gnutls_set_datum(
185
0
        &priv->sresp,
186
0
        session->internals.selected_ocsp[0]
187
0
          .response.data,
188
0
        session->internals.selected_ocsp[0]
189
0
          .response.size);
190
0
      if (ret < 0)
191
0
        return gnutls_assert_val(ret);
192
0
      return GNUTLS_E_INT_RET_0;
193
0
    } else {
194
0
      return 0;
195
0
    }
196
526
  } else if (session->internals.selected_ocsp_func) {
197
0
    func = session->internals.selected_ocsp_func;
198
0
    func_ptr = session->internals.selected_ocsp_func_ptr;
199
200
0
    if (func == NULL)
201
0
      return 0;
202
203
0
    ret = func(session, func_ptr, &priv->sresp);
204
0
    if (ret == GNUTLS_E_NO_CERTIFICATE_STATUS)
205
0
      return 0;
206
0
    else if (ret < 0)
207
0
      return gnutls_assert_val(ret);
208
526
  } else {
209
526
    return 0;
210
526
  }
211
212
0
  return GNUTLS_E_INT_RET_0;
213
526
}
214
215
static int _gnutls_status_request_send_params(gnutls_session_t session,
216
                gnutls_buffer_st *extdata)
217
27.7k
{
218
27.7k
  gnutls_ext_priv_data_t epriv;
219
27.7k
  status_request_ext_st *priv;
220
27.7k
  int ret;
221
222
  /* Do not bother sending the OCSP status request extension
223
   * if we are not using certificate authentication */
224
27.7k
  if (_gnutls_get_cred(session, GNUTLS_CRD_CERTIFICATE) == NULL)
225
6.24k
    return 0;
226
227
21.4k
  if (session->security_parameters.entity == GNUTLS_CLIENT) {
228
20.9k
    if (session->internals.flags & GNUTLS_NO_STATUS_REQUEST)
229
0
      return 0;
230
231
20.9k
    ret = _gnutls_hello_ext_get_priv(
232
20.9k
      session, GNUTLS_EXTENSION_STATUS_REQUEST, &epriv);
233
20.9k
    if (ret < 0 || epriv == NULL) /* it is ok not to have it */
234
0
      return 0;
235
20.9k
    priv = epriv;
236
237
20.9k
    return client_send(session, extdata, priv);
238
20.9k
  } else {
239
526
    epriv = priv = gnutls_calloc(1, sizeof(*priv));
240
526
    if (priv == NULL)
241
0
      return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
242
243
526
    _gnutls_hello_ext_set_priv(
244
526
      session, GNUTLS_EXTENSION_STATUS_REQUEST, epriv);
245
246
526
    return server_send(session, extdata, priv);
247
526
  }
248
21.4k
}
249
250
static int _gnutls_status_request_recv_params(gnutls_session_t session,
251
                const uint8_t *data, size_t size)
252
1.97k
{
253
1.97k
  gnutls_ext_priv_data_t epriv;
254
1.97k
  status_request_ext_st *priv;
255
1.97k
  int ret;
256
257
1.97k
  if (session->security_parameters.entity == GNUTLS_CLIENT) {
258
550
    ret = _gnutls_hello_ext_get_priv(
259
550
      session, GNUTLS_EXTENSION_STATUS_REQUEST, &epriv);
260
550
    if (ret < 0 || epriv == NULL) /* it is ok not to have it */
261
0
      return 0;
262
550
    priv = epriv;
263
264
550
    return client_recv(session, priv, data, size);
265
1.42k
  } else {
266
1.42k
    return server_recv(session, data, size);
267
1.42k
  }
268
1.97k
}
269
270
/**
271
 * gnutls_ocsp_status_request_enable_client:
272
 * @session: is a #gnutls_session_t type.
273
 * @responder_id: ignored, must be %NULL
274
 * @responder_id_size: ignored, must be zero
275
 * @extensions: ignored, must be %NULL
276
 *
277
 * This function is to be used by clients to request OCSP response
278
 * from the server, using the "status_request" TLS extension.  Only
279
 * OCSP status type is supported.
280
 *
281
 * Previous versions of GnuTLS supported setting @responder_id and
282
 * @extensions fields, but due to the difficult semantics of the
283
 * parameter usage, and other issues, this support was removed
284
 * since 3.6.0 and these parameters must be set to %NULL.
285
 *
286
 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned,
287
 *   otherwise a negative error code is returned.
288
 *
289
 * Since: 3.1.3
290
 **/
291
int gnutls_ocsp_status_request_enable_client(gnutls_session_t session,
292
               gnutls_datum_t *responder_id,
293
               size_t responder_id_size,
294
               gnutls_datum_t *extensions)
295
21.0k
{
296
21.0k
  status_request_ext_st *priv;
297
21.0k
  gnutls_ext_priv_data_t epriv;
298
299
21.0k
  if (session->security_parameters.entity == GNUTLS_SERVER)
300
0
    return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
301
302
21.0k
  epriv = priv = gnutls_calloc(1, sizeof(*priv));
303
21.0k
  if (priv == NULL)
304
0
    return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
305
306
21.0k
  _gnutls_hello_ext_set_priv(session, GNUTLS_EXTENSION_STATUS_REQUEST,
307
21.0k
           epriv);
308
309
21.0k
  session->internals.flags &= ~GNUTLS_NO_STATUS_REQUEST;
310
21.0k
  if (session->internals.priorities)
311
0
    session->internals.priorities->no_status_request = 0;
312
313
21.0k
  return 0;
314
21.0k
}
315
316
static void _gnutls_status_request_deinit_data(gnutls_ext_priv_data_t epriv)
317
21.5k
{
318
21.5k
  status_request_ext_st *priv = epriv;
319
320
21.5k
  if (priv == NULL)
321
0
    return;
322
323
21.5k
  gnutls_free(priv->sresp.data);
324
21.5k
  gnutls_free(priv);
325
21.5k
}
326
327
const hello_ext_entry_st ext_mod_status_request = {
328
  .name = "OCSP Status Request",
329
  .tls_id = STATUS_REQUEST_TLS_ID,
330
  .gid = GNUTLS_EXTENSION_STATUS_REQUEST,
331
  .validity = GNUTLS_EXT_FLAG_TLS | GNUTLS_EXT_FLAG_DTLS |
332
        GNUTLS_EXT_FLAG_CLIENT_HELLO |
333
        GNUTLS_EXT_FLAG_TLS12_SERVER_HELLO,
334
  .client_parse_point = _GNUTLS_EXT_TLS_POST_CS,
335
  .server_parse_point = _GNUTLS_EXT_TLS_POST_CS,
336
  .recv_func = _gnutls_status_request_recv_params,
337
  .send_func = _gnutls_status_request_send_params,
338
  .deinit_func = _gnutls_status_request_deinit_data,
339
  .cannot_be_overriden = 1
340
};
341
342
/* Functions to be called from handshake */
343
344
int _gnutls_send_server_certificate_status(gnutls_session_t session, int again)
345
4.64k
{
346
4.64k
  mbuffer_st *bufel = NULL;
347
4.64k
  uint8_t *data;
348
4.64k
  int data_size = 0;
349
4.64k
  int ret;
350
4.64k
  gnutls_ext_priv_data_t epriv;
351
4.64k
  status_request_ext_st *priv;
352
353
4.64k
  if (!(session->internals.hsk_flags & HSK_OCSP_REQUESTED))
354
4.28k
    return 0;
355
356
354
  if (again == 0) {
357
354
    ret = _gnutls_hello_ext_get_priv(
358
354
      session, GNUTLS_EXTENSION_STATUS_REQUEST, &epriv);
359
354
    if (ret < 0)
360
0
      return 0;
361
362
354
    priv = epriv;
363
364
354
    if (!priv->sresp.size)
365
354
      return 0;
366
367
0
    data_size = priv->sresp.size + 4;
368
0
    bufel = _gnutls_handshake_alloc(session, data_size);
369
0
    if (!bufel) {
370
0
      _gnutls_free_datum(&priv->sresp);
371
0
      return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
372
0
    }
373
374
0
    data = _mbuffer_get_udata_ptr(bufel);
375
376
0
    data[0] = 0x01;
377
0
    _gnutls_write_uint24(priv->sresp.size, &data[1]);
378
0
    memcpy(&data[4], priv->sresp.data, priv->sresp.size);
379
380
0
    _gnutls_free_datum(&priv->sresp);
381
0
  }
382
0
  return _gnutls_send_handshake(session, data_size ? bufel : NULL,
383
0
              GNUTLS_HANDSHAKE_CERTIFICATE_STATUS);
384
354
}
385
386
int _gnutls_parse_ocsp_response(gnutls_session_t session, const uint8_t *data,
387
        ssize_t data_size, gnutls_datum_t *resp)
388
124
{
389
124
  int ret;
390
124
  ssize_t r_size;
391
392
124
  resp->data = 0;
393
124
  resp->size = 0;
394
395
  /* minimum message is type (1) + response (3) + data */
396
124
  if (data_size < 4)
397
4
    return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
398
399
120
  if (data[0] != 0x01) {
400
39
    gnutls_assert();
401
39
    _gnutls_handshake_log("EXT[%p]: unknown status_type %d\n",
402
39
              session, data[0]);
403
39
    return 0;
404
39
  }
405
406
81
  DECR_LENGTH_RET(data_size, 1, GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
407
81
  data++;
408
409
81
  DECR_LENGTH_RET(data_size, 3, GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
410
411
81
  r_size = _gnutls_read_uint24(data);
412
81
  data += 3;
413
414
81
  DECR_LENGTH_RET(data_size, r_size, GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
415
416
47
  if (r_size < 1)
417
4
    return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
418
419
43
  ret = _gnutls_set_datum(resp, data, r_size);
420
43
  if (ret < 0)
421
0
    return gnutls_assert_val(ret);
422
423
43
  return 0;
424
43
}
425
426
int _gnutls_recv_server_certificate_status(gnutls_session_t session)
427
9.45k
{
428
9.45k
  uint8_t *data;
429
9.45k
  ssize_t data_size;
430
9.45k
  gnutls_buffer_st buf;
431
9.45k
  int ret;
432
9.45k
  gnutls_datum_t resp;
433
9.45k
  status_request_ext_st *priv = NULL;
434
9.45k
  gnutls_ext_priv_data_t epriv;
435
9.45k
  cert_auth_info_t info =
436
9.45k
    _gnutls_get_auth_info(session, GNUTLS_CRD_CERTIFICATE);
437
438
9.45k
  if (info == NULL)
439
3.48k
    return 0;
440
441
5.97k
  ret = _gnutls_hello_ext_get_priv(
442
5.97k
    session, GNUTLS_EXTENSION_STATUS_REQUEST, &epriv);
443
5.97k
  if (ret < 0)
444
0
    return 0;
445
446
5.97k
  priv = epriv;
447
448
5.97k
  if (!priv->expect_cstatus)
449
5.03k
    return 0;
450
451
941
  ret = _gnutls_recv_handshake(
452
941
    session, GNUTLS_HANDSHAKE_CERTIFICATE_STATUS, 1, &buf);
453
941
  if (ret < 0)
454
848
    return gnutls_assert_val_fatal(ret);
455
456
93
  priv->expect_cstatus = 0;
457
458
93
  data = buf.data;
459
93
  data_size = buf.length;
460
461
93
  if (data_size == 0) {
462
86
    ret = 0;
463
86
    goto error;
464
86
  }
465
466
7
  ret = _gnutls_parse_ocsp_response(session, data, data_size, &resp);
467
7
  if (ret < 0) {
468
2
    gnutls_assert();
469
2
    goto error;
470
2
  }
471
472
5
  if (resp.data && resp.size > 0) {
473
3
    for (unsigned int i = 0; i < info->nocsp; i++)
474
0
      gnutls_free(info->raw_ocsp_list[i].data);
475
3
    gnutls_free(info->raw_ocsp_list);
476
477
3
    info->raw_ocsp_list = gnutls_malloc(sizeof(gnutls_datum_t));
478
3
    if (info->raw_ocsp_list == NULL) {
479
0
      ret = GNUTLS_E_MEMORY_ERROR;
480
0
      goto error;
481
0
    }
482
3
    info->raw_ocsp_list[0].data = resp.data;
483
3
    info->raw_ocsp_list[0].size = resp.size;
484
3
    info->nocsp = 1;
485
3
  }
486
487
5
  ret = 0;
488
489
93
error:
490
93
  _gnutls_buffer_clear(&buf);
491
492
93
  return ret;
493
5
}
494
495
#endif