Coverage Report

Created: 2026-08-14 07:10

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gnutls/lib/tls13/certificate.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 "compress.h"
25
#include "errors.h"
26
#include "extv.h"
27
#include "handshake.h"
28
#include "tls13/certificate.h"
29
#include "auth/cert.h"
30
#include "mbuffers.h"
31
#include "ext/compress_certificate.h"
32
#include "ext/status_request.h"
33
34
static int parse_cert_extension(void *ctx, unsigned tls_id, const uint8_t *data,
35
        unsigned data_size);
36
static int parse_cert_list(gnutls_session_t session, uint8_t *data,
37
         size_t data_size);
38
static int compress_certificate(gnutls_buffer_st *buf, unsigned cert_pos_mark,
39
        gnutls_compression_method_t comp_method);
40
static int decompress_certificate(gnutls_session_t session,
41
          gnutls_buffer_st *buf);
42
43
int _gnutls13_recv_certificate(gnutls_session_t session)
44
4.79k
{
45
4.79k
  int ret, err, decompress_cert = 0;
46
4.79k
  gnutls_buffer_st buf;
47
4.79k
  unsigned optional = 0;
48
49
4.79k
  if (!session->internals.initial_negotiation_completed &&
50
4.79k
      session->internals.hsk_flags & HSK_PSK_SELECTED)
51
19
    return 0;
52
53
4.77k
  if (session->security_parameters.entity == GNUTLS_SERVER) {
54
    /* if we didn't request a certificate, there will not be any */
55
390
    if (session->internals.send_cert_req == 0)
56
390
      return 0;
57
58
0
    if (session->internals.send_cert_req != GNUTLS_CERT_REQUIRE)
59
0
      optional = 1;
60
0
  }
61
62
4.38k
  ret = _gnutls_recv_handshake(session, GNUTLS_HANDSHAKE_CERTIFICATE_PKT,
63
4.38k
             0, &buf);
64
4.38k
  if (ret == GNUTLS_E_UNEXPECTED_HANDSHAKE_PACKET) {
65
    /* check if we received compressed certificate */
66
35
    err = _gnutls_recv_handshake(
67
35
      session, GNUTLS_HANDSHAKE_COMPRESSED_CERTIFICATE_PKT, 0,
68
35
      &buf);
69
35
    if (err >= 0) {
70
      /* fail if we receive unsolicited compressed certificate */
71
15
      if (!(session->internals.hsk_flags &
72
15
            HSK_COMP_CRT_REQ_SENT))
73
15
        return gnutls_assert_val(
74
15
          GNUTLS_E_UNEXPECTED_PACKET);
75
76
0
      decompress_cert = 1;
77
0
      ret = err;
78
0
    }
79
35
  }
80
4.36k
  if (ret < 0) {
81
187
    if (ret == GNUTLS_E_UNEXPECTED_HANDSHAKE_PACKET &&
82
20
        session->internals.send_cert_req)
83
0
      return gnutls_assert_val(GNUTLS_E_NO_CERTIFICATE_FOUND);
84
85
187
    return gnutls_assert_val(ret);
86
187
  }
87
88
4.18k
  if (buf.length == 0) {
89
6
    gnutls_assert();
90
6
    ret = GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER;
91
6
    goto cleanup;
92
6
  }
93
94
4.17k
  if (decompress_cert) {
95
0
    ret = decompress_certificate(session, &buf);
96
0
    if (ret < 0) {
97
0
      gnutls_assert();
98
0
      goto cleanup;
99
0
    }
100
0
  }
101
102
4.17k
  if (session->internals.initial_negotiation_completed &&
103
0
      session->internals.post_handshake_cr_context.size > 0) {
104
0
    gnutls_datum_t context;
105
106
    /* verify whether the context matches */
107
0
    ret = _gnutls_buffer_pop_datum_prefix8(&buf, &context);
108
0
    if (ret < 0) {
109
0
      gnutls_assert();
110
0
      goto cleanup;
111
0
    }
112
113
0
    if (context.size !=
114
0
          session->internals.post_handshake_cr_context.size ||
115
0
        !memeq(context.data,
116
0
         session->internals.post_handshake_cr_context.data,
117
0
         context.size)) {
118
0
      ret = GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER;
119
0
      gnutls_assert();
120
0
      goto cleanup;
121
0
    }
122
4.17k
  } else {
123
4.17k
    if (buf.data[0] != 0) {
124
      /* The context field must be empty during handshake */
125
23
      gnutls_assert();
126
23
      ret = GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER;
127
23
      goto cleanup;
128
23
    }
129
130
    /* buf.length is positive */
131
4.15k
    buf.data++;
132
4.15k
    buf.length--;
133
4.15k
  }
134
135
4.15k
  _gnutls_handshake_log("HSK[%p]: parsing certificate message\n",
136
4.15k
            session);
137
138
4.15k
  ret = parse_cert_list(session, buf.data, buf.length);
139
4.15k
  if (ret < 0) {
140
285
    if (ret == GNUTLS_E_NO_CERTIFICATE_FOUND) {
141
3
      if (optional)
142
0
        ret = 0;
143
3
      else if (session->security_parameters.entity ==
144
3
         GNUTLS_SERVER)
145
0
        ret = GNUTLS_E_CERTIFICATE_REQUIRED;
146
3
    }
147
285
    gnutls_assert();
148
285
    goto cleanup;
149
285
  }
150
151
3.86k
  session->internals.hsk_flags |= HSK_CRT_VRFY_EXPECTED;
152
153
3.86k
  ret = 0;
154
4.18k
cleanup:
155
156
4.18k
  _gnutls_buffer_clear(&buf);
157
4.18k
  return ret;
158
3.86k
}
159
160
struct ocsp_req_ctx_st {
161
  gnutls_pcert_st *pcert;
162
  unsigned cert_index;
163
  gnutls_session_t session;
164
  gnutls_certificate_credentials_t cred;
165
};
166
167
static int append_status_request(void *_ctx, gnutls_buffer_st *buf)
168
0
{
169
0
  struct ocsp_req_ctx_st *ctx = _ctx;
170
0
  gnutls_session_t session = ctx->session;
171
0
  int ret;
172
0
  gnutls_datum_t resp;
173
0
  unsigned free_resp = 0;
174
175
0
  assert(session->internals.selected_ocsp_func != NULL ||
176
0
         session->internals.selected_ocsp_length != 0);
177
178
  /* The global ocsp callback function can only be used to return
179
   * a single certificate request */
180
0
  if (session->internals.selected_ocsp_length == 1 &&
181
0
      ctx->cert_index != 0)
182
0
    return 0;
183
184
0
  if (session->internals.selected_ocsp_length > 0) {
185
0
    if (ctx->cert_index < session->internals.selected_ocsp_length) {
186
0
      if ((session->internals.selected_ocsp[ctx->cert_index]
187
0
               .exptime != 0 &&
188
0
           gnutls_time(NULL) >=
189
0
             session->internals
190
0
               .selected_ocsp[ctx->cert_index]
191
0
               .exptime) ||
192
0
          session->internals.selected_ocsp[ctx->cert_index]
193
0
              .response.data == NULL) {
194
0
        return 0;
195
0
      }
196
197
0
      resp.data = session->internals
198
0
              .selected_ocsp[ctx->cert_index]
199
0
              .response.data;
200
0
      resp.size = session->internals
201
0
              .selected_ocsp[ctx->cert_index]
202
0
              .response.size;
203
0
      ret = 0;
204
0
    } else {
205
0
      return 0;
206
0
    }
207
0
  } else if (session->internals.selected_ocsp_func) {
208
0
    if (ctx->cert_index == 0) {
209
0
      ret = session->internals.selected_ocsp_func(
210
0
        session,
211
0
        session->internals.selected_ocsp_func_ptr,
212
0
        &resp);
213
0
      free_resp = 1;
214
0
    } else {
215
0
      return 0;
216
0
    }
217
0
  } else
218
0
    return 0;
219
220
0
  if (ret == GNUTLS_E_NO_CERTIFICATE_STATUS || resp.data == NULL) {
221
0
    return 0;
222
0
  } else if (ret < 0) {
223
0
    return gnutls_assert_val(ret);
224
0
  }
225
226
0
  ret = _gnutls_buffer_append_data(buf, "\x01", 1);
227
0
  if (ret < 0) {
228
0
    gnutls_assert();
229
0
    goto cleanup;
230
0
  }
231
232
0
  ret = _gnutls_buffer_append_data_prefix24(buf, resp.data, resp.size);
233
0
  if (ret < 0) {
234
0
    gnutls_assert();
235
0
    goto cleanup;
236
0
  }
237
238
0
  ret = 0;
239
0
cleanup:
240
0
  if (free_resp)
241
0
    gnutls_free(resp.data);
242
0
  return ret;
243
0
}
244
245
int _gnutls13_send_certificate(gnutls_session_t session, unsigned again)
246
390
{
247
390
  int ret, compress_cert;
248
390
  gnutls_pcert_st *apr_cert_list = NULL;
249
390
  gnutls_privkey_t apr_pkey = NULL;
250
390
  int apr_cert_list_length = 0;
251
390
  mbuffer_st *bufel = NULL;
252
390
  gnutls_buffer_st buf;
253
390
  unsigned pos_mark, ext_pos_mark, cert_pos_mark;
254
390
  unsigned i;
255
390
  struct ocsp_req_ctx_st ctx;
256
390
  gnutls_certificate_credentials_t cred;
257
390
  gnutls_compression_method_t comp_method;
258
390
  gnutls_handshake_description_t h_type;
259
260
390
  comp_method = gnutls_compress_certificate_get_selected_method(session);
261
390
  compress_cert = comp_method != GNUTLS_COMP_UNKNOWN;
262
390
  h_type = compress_cert ? GNUTLS_HANDSHAKE_COMPRESSED_CERTIFICATE_PKT :
263
390
         GNUTLS_HANDSHAKE_CERTIFICATE_PKT;
264
265
390
  if (again == 0) {
266
390
    if (!session->internals.initial_negotiation_completed &&
267
390
        session->internals.hsk_flags & HSK_PSK_SELECTED)
268
0
      return 0;
269
270
390
    if (session->security_parameters.entity == GNUTLS_SERVER &&
271
390
        session->internals.resumed)
272
0
      return 0;
273
274
390
    cred = (gnutls_certificate_credentials_t)_gnutls_get_cred(
275
390
      session, GNUTLS_CRD_CERTIFICATE);
276
390
    if (cred == NULL) {
277
0
      gnutls_assert();
278
0
      return GNUTLS_E_INSUFFICIENT_CREDENTIALS;
279
0
    }
280
281
390
    if (session->security_parameters.entity == GNUTLS_CLIENT &&
282
0
        !(session->internals.hsk_flags & HSK_CRT_ASKED)) {
283
0
      return 0;
284
0
    }
285
286
390
    ret = _gnutls_get_selected_cert(session, &apr_cert_list,
287
390
            &apr_cert_list_length,
288
390
            &apr_pkey);
289
390
    if (ret < 0)
290
0
      return gnutls_assert_val(ret);
291
292
390
    ret = _gnutls_buffer_init_handshake_mbuffer(&buf);
293
390
    if (ret < 0)
294
0
      return gnutls_assert_val(ret);
295
296
390
    cert_pos_mark = buf.length;
297
298
390
    if (session->security_parameters.entity == GNUTLS_CLIENT) {
299
0
      ret = _gnutls_buffer_append_data_prefix8(
300
0
        &buf,
301
0
        session->internals.post_handshake_cr_context
302
0
          .data,
303
0
        session->internals.post_handshake_cr_context
304
0
          .size);
305
0
      if (ret < 0) {
306
0
        gnutls_assert();
307
0
        goto cleanup;
308
0
      }
309
310
390
    } else {
311
390
      ret = _gnutls_buffer_append_uint8(&buf, 0);
312
390
      if (ret < 0) {
313
0
        gnutls_assert();
314
0
        goto cleanup;
315
0
      }
316
390
    }
317
318
    /* mark total size */
319
390
    pos_mark = buf.length;
320
390
    ret = _gnutls_buffer_append_uint24(&buf, 0);
321
390
    if (ret < 0) {
322
0
      gnutls_assert();
323
0
      goto cleanup;
324
0
    }
325
326
780
    for (i = 0; i < (unsigned)apr_cert_list_length; i++) {
327
390
      ret = _gnutls_buffer_append_data_prefix24(
328
390
        &buf, apr_cert_list[i].cert.data,
329
390
        apr_cert_list[i].cert.size);
330
390
      if (ret < 0) {
331
0
        gnutls_assert();
332
0
        goto cleanup;
333
0
      }
334
390
#ifdef ENABLE_OCSP
335
390
      if ((session->internals.selected_ocsp_length > 0 ||
336
390
           session->internals.selected_ocsp_func) &&
337
0
          (((session->internals.hsk_flags &
338
0
             HSK_OCSP_REQUESTED) &&
339
0
            IS_SERVER(session)) ||
340
0
           ((session->internals.hsk_flags &
341
0
             HSK_CLIENT_OCSP_REQUESTED) &&
342
0
            !IS_SERVER(session)))) {
343
        /* append status response if available */
344
0
        ret = _gnutls_extv_append_init(&buf);
345
0
        if (ret < 0) {
346
0
          gnutls_assert();
347
0
          goto cleanup;
348
0
        }
349
0
        ext_pos_mark = ret;
350
351
0
        ctx.pcert = &apr_cert_list[i];
352
0
        ctx.cert_index = i;
353
0
        ctx.session = session;
354
0
        ctx.cred = cred;
355
0
        ret = _gnutls_extv_append(
356
0
          &buf, STATUS_REQUEST_TLS_ID, &ctx,
357
0
          append_status_request);
358
0
        if (ret < 0) {
359
0
          gnutls_assert();
360
0
          goto cleanup;
361
0
        }
362
363
0
        ret = _gnutls_extv_append_final(
364
0
          &buf, ext_pos_mark, 0);
365
0
        if (ret < 0) {
366
0
          gnutls_assert();
367
0
          goto cleanup;
368
0
        }
369
0
      } else
370
390
#endif
371
390
      {
372
390
        ret = _gnutls_buffer_append_uint16(&buf, 0);
373
390
        if (ret < 0) {
374
0
          gnutls_assert();
375
0
          goto cleanup;
376
0
        }
377
390
      }
378
390
    }
379
380
390
    _gnutls_write_uint24(buf.length - pos_mark - 3,
381
390
             &buf.data[pos_mark]);
382
383
390
    if (compress_cert) {
384
0
      ret = compress_certificate(&buf, cert_pos_mark,
385
0
               comp_method);
386
0
      if (ret < 0) {
387
0
        gnutls_assert();
388
0
        goto cleanup;
389
0
      }
390
0
    }
391
392
390
    bufel = _gnutls_buffer_to_mbuffer(&buf);
393
390
  }
394
395
390
  return _gnutls_send_handshake(session, bufel, h_type);
396
397
0
cleanup:
398
0
  _gnutls_buffer_clear(&buf);
399
0
  return ret;
400
390
}
401
402
typedef struct crt_cert_ctx_st {
403
  gnutls_session_t session;
404
  gnutls_datum_t *ocsp;
405
  unsigned idx;
406
} crt_cert_ctx_st;
407
408
static int parse_cert_extension(void *_ctx, unsigned tls_id,
409
        const uint8_t *data, unsigned data_size)
410
159
{
411
159
  crt_cert_ctx_st *ctx = _ctx;
412
159
  gnutls_session_t session = ctx->session;
413
159
  int ret;
414
415
159
  if (tls_id == STATUS_REQUEST_TLS_ID) {
416
118
#ifdef ENABLE_OCSP
417
118
    if (!_gnutls_hello_ext_is_present(session,
418
118
              ext_mod_status_request.gid)) {
419
0
      gnutls_assert();
420
0
      goto unexpected;
421
0
    }
422
423
118
    _gnutls_handshake_log("Found OCSP response on cert %d\n",
424
118
              ctx->idx);
425
426
118
    ret = _gnutls_parse_ocsp_response(session, data, data_size,
427
118
              ctx->ocsp);
428
118
    if (ret < 0)
429
25
      return gnutls_assert_val(ret);
430
118
#endif
431
118
  } else {
432
41
    goto unexpected;
433
41
  }
434
435
93
  return 0;
436
437
41
unexpected:
438
41
  _gnutls_debug_log("received unexpected certificate extension (%d)\n",
439
41
        (int)tls_id);
440
41
  return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_EXTENSION);
441
159
}
442
443
static int parse_cert_list(gnutls_session_t session, uint8_t *data,
444
         size_t data_size)
445
4.15k
{
446
4.15k
  int ret;
447
4.15k
  size_t len;
448
4.15k
  uint8_t *p = data;
449
4.15k
  cert_auth_info_t info;
450
4.15k
  gnutls_certificate_credentials_t cred;
451
4.15k
  size_t size;
452
4.15k
  int i;
453
4.15k
  unsigned npeer_certs, npeer_ocsp, j;
454
4.15k
  crt_cert_ctx_st ctx;
455
4.15k
  gnutls_datum_t *peer_certs = NULL;
456
4.15k
  gnutls_datum_t *peer_ocsp = NULL;
457
4.15k
  unsigned nentries = 0;
458
459
4.15k
  cred = (gnutls_certificate_credentials_t)_gnutls_get_cred(
460
4.15k
    session, GNUTLS_CRD_CERTIFICATE);
461
4.15k
  if (cred == NULL) {
462
7
    gnutls_assert();
463
7
    return GNUTLS_E_INSUFFICIENT_CREDENTIALS;
464
7
  }
465
466
4.14k
  if ((ret = _gnutls_auth_info_init(session, GNUTLS_CRD_CERTIFICATE,
467
4.14k
            sizeof(cert_auth_info_st), 1)) < 0) {
468
0
    gnutls_assert();
469
0
    return ret;
470
0
  }
471
472
4.14k
  if (data == NULL || data_size == 0) {
473
    /* no certificate was sent */
474
4
    return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
475
4
  }
476
477
4.14k
  info = _gnutls_get_auth_info(session, GNUTLS_CRD_CERTIFICATE);
478
4.14k
  if (info == NULL)
479
0
    return gnutls_assert_val(GNUTLS_E_INSUFFICIENT_CREDENTIALS);
480
481
4.14k
  DECR_LEN(data_size, 3);
482
4.13k
  size = _gnutls_read_uint24(p);
483
4.13k
  p += 3;
484
485
4.13k
  if (size != data_size)
486
72
    return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
487
488
4.06k
  if (size == 0)
489
3
    return gnutls_assert_val(GNUTLS_E_NO_CERTIFICATE_FOUND);
490
491
4.06k
  i = data_size;
492
493
8.54k
  while (i > 0) {
494
4.58k
    DECR_LEN(data_size, 3);
495
4.58k
    len = _gnutls_read_uint24(p);
496
4.58k
    if (len == 0)
497
7
      return gnutls_assert_val(
498
4.58k
        GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
499
500
4.57k
    DECR_LEN(data_size, len);
501
4.50k
    p += len + 3;
502
4.50k
    i -= len + 3;
503
504
4.50k
    DECR_LEN(data_size, 2);
505
4.50k
    len = _gnutls_read_uint16(p);
506
4.50k
    DECR_LEN(data_size, len);
507
508
4.48k
    i -= len + 2;
509
4.48k
    p += len + 2;
510
511
4.48k
    nentries++;
512
4.48k
  }
513
514
3.95k
  if (data_size != 0)
515
0
    return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
516
517
  /* this is unnecessary - keeping to avoid a regression due to a re-org
518
   * of the loop above */
519
3.95k
  if (nentries == 0)
520
0
    return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
521
522
3.95k
  npeer_ocsp = 0;
523
3.95k
  npeer_certs = 0;
524
525
  /* Ok we now allocate the memory to hold the
526
   * certificate list
527
   */
528
3.95k
  peer_certs = gnutls_calloc(nentries, sizeof(gnutls_datum_t));
529
3.95k
  if (peer_certs == NULL)
530
0
    return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
531
532
3.95k
  peer_ocsp = gnutls_calloc(nentries, sizeof(gnutls_datum_t));
533
3.95k
  if (peer_ocsp == NULL) {
534
0
    ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
535
0
    goto cleanup;
536
0
  }
537
538
3.95k
  p = data + 3;
539
540
  /* Now we start parsing the list (again).
541
   * We don't use DECR_LEN since the list has
542
   * been parsed before.
543
   */
544
545
3.95k
  ctx.session = session;
546
547
8.19k
  for (j = 0; j < nentries; j++) {
548
4.32k
    len = _gnutls_read_uint24(p);
549
4.32k
    p += 3;
550
551
4.32k
    ret = _gnutls_set_datum(&peer_certs[j], p, len);
552
4.32k
    if (ret < 0) {
553
0
      gnutls_assert();
554
0
      ret = GNUTLS_E_CERTIFICATE_ERROR;
555
0
      goto cleanup;
556
0
    }
557
4.32k
    npeer_certs++;
558
559
4.32k
    p += len;
560
561
4.32k
    len = _gnutls_read_uint16(p);
562
563
4.32k
    ctx.ocsp = &peer_ocsp[j];
564
4.32k
    ctx.idx = j;
565
566
4.32k
    ret = _gnutls_extv_parse(&ctx, parse_cert_extension, p,
567
4.32k
           len + 2);
568
4.32k
    if (ret < 0) {
569
92
      gnutls_assert();
570
92
      goto cleanup;
571
92
    }
572
573
4.23k
    p += len + 2;
574
4.23k
    npeer_ocsp++;
575
4.23k
  }
576
577
  /* The OCSP entries match the certificate entries, although
578
   * the contents of each OCSP entry may be NULL.
579
   */
580
3.86k
  for (j = 0; j < info->ncerts; j++)
581
0
    gnutls_free(info->raw_certificate_list[j].data);
582
3.86k
  gnutls_free(info->raw_certificate_list);
583
584
3.86k
  for (j = 0; j < info->nocsp; j++)
585
0
    gnutls_free(info->raw_ocsp_list[j].data);
586
3.86k
  gnutls_free(info->raw_ocsp_list);
587
588
3.86k
  info->raw_certificate_list = peer_certs;
589
3.86k
  info->ncerts = npeer_certs;
590
591
3.86k
  info->raw_ocsp_list = peer_ocsp;
592
3.86k
  info->nocsp = npeer_ocsp;
593
594
3.86k
  return 0;
595
596
92
cleanup:
597
360
  for (j = 0; j < npeer_certs; j++)
598
268
    gnutls_free(peer_certs[j].data);
599
600
268
  for (j = 0; j < npeer_ocsp; j++)
601
176
    gnutls_free(peer_ocsp[j].data);
602
92
  gnutls_free(peer_certs);
603
92
  gnutls_free(peer_ocsp);
604
92
  return ret;
605
3.95k
}
606
607
static int compress_certificate(gnutls_buffer_st *buf, unsigned cert_pos_mark,
608
        gnutls_compression_method_t comp_method)
609
0
{
610
0
  int ret, method_num;
611
0
  size_t comp_bound;
612
0
  gnutls_datum_t plain, comp = { NULL, 0 };
613
614
0
  method_num = _gnutls_compress_certificate_method2num(comp_method);
615
0
  if (method_num == GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER)
616
0
    return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER);
617
618
0
  plain.data = buf->data + cert_pos_mark;
619
0
  plain.size = buf->length - cert_pos_mark;
620
621
0
  comp_bound = _gnutls_compress_bound(comp_method, plain.size);
622
0
  if (comp_bound == 0)
623
0
    return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
624
0
  comp.data = gnutls_malloc(comp_bound);
625
0
  if (comp.data == NULL)
626
0
    return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
627
0
  ret = _gnutls_compress(comp_method, comp.data, comp_bound, plain.data,
628
0
             plain.size);
629
0
  if (ret < 0) {
630
0
    gnutls_assert();
631
0
    goto cleanup;
632
0
  }
633
0
  comp.size = ret;
634
635
0
  buf->length = cert_pos_mark;
636
0
  ret = _gnutls_buffer_append_uint16(buf, method_num);
637
0
  if (ret < 0) {
638
0
    gnutls_assert();
639
0
    goto cleanup;
640
0
  }
641
0
  ret = _gnutls_buffer_append_uint24(buf, plain.size);
642
0
  if (ret < 0) {
643
0
    gnutls_assert();
644
0
    goto cleanup;
645
0
  }
646
0
  ret = _gnutls_buffer_append_data_prefix24(buf, comp.data, comp.size);
647
0
  if (ret < 0) {
648
0
    gnutls_assert();
649
0
    goto cleanup;
650
0
  }
651
652
0
cleanup:
653
0
  gnutls_free(comp.data);
654
0
  return ret;
655
0
}
656
657
static int decompress_certificate(gnutls_session_t session,
658
          gnutls_buffer_st *buf)
659
0
{
660
0
  int ret;
661
0
  uint16_t method_num;
662
0
  uint32_t plain_exp_len;
663
0
  gnutls_datum_t comp, plain = { NULL, 0 };
664
0
  gnutls_compression_method_t comp_method;
665
666
0
  ret = _gnutls_buffer_pop_uint16(buf, &method_num);
667
0
  if (ret < 0)
668
0
    return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
669
0
  comp_method = _gnutls_compress_certificate_num2method(method_num);
670
671
0
  if (!_gnutls_compress_certificate_is_method_enabled(session,
672
0
                  comp_method))
673
0
    return gnutls_assert_val(GNUTLS_E_ILLEGAL_PARAMETER);
674
675
0
  ret = _gnutls_buffer_pop_uint24(buf, &plain_exp_len);
676
0
  if (ret < 0)
677
0
    return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
678
679
0
  ret = _gnutls_buffer_pop_datum_prefix24(buf, &comp);
680
0
  if (ret < 0 || buf->length > 0 || comp.size == 0)
681
0
    return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
682
683
0
  plain.data = gnutls_malloc(plain_exp_len);
684
0
  if (plain.data == NULL)
685
0
    return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
686
0
  ret = _gnutls_decompress(comp_method, plain.data, plain_exp_len,
687
0
         comp.data, comp.size);
688
0
  if (ret < 0) {
689
0
    ret = gnutls_assert_val(GNUTLS_E_CERTIFICATE_ERROR);
690
0
    goto cleanup;
691
0
  }
692
0
  plain.size = ret;
693
694
0
  if (plain.size != plain_exp_len) {
695
0
    ret = gnutls_assert_val(GNUTLS_E_CERTIFICATE_ERROR);
696
0
    goto cleanup;
697
0
  }
698
699
0
  _gnutls_buffer_clear(buf);
700
0
  ret = _gnutls_buffer_append_data(buf, plain.data, plain.size);
701
0
  if (ret < 0) {
702
0
    ret = gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
703
0
    goto cleanup;
704
0
  }
705
706
0
cleanup:
707
  gnutls_free(plain.data);
708
0
  return ret;
709
0
}