Coverage Report

Created: 2023-03-26 08:33

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