Coverage Report

Created: 2023-03-26 08:33

/src/gnutls/lib/x509_b64.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (C) 2000-2012 Free Software Foundation, Inc.
3
 * Copyright (C) 2017 Red Hat, Inc.
4
 *
5
 * Author: 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
/* Functions that relate to base64 encoding and decoding.
25
 */
26
27
#include "gnutls_int.h"
28
#include "errors.h"
29
#include <datum.h>
30
#include <x509_b64.h>
31
#include <nettle/base64.h>
32
33
#define INCR(what, size, max_len) \
34
0
  do { \
35
0
  what+=size; \
36
0
  if (what > max_len) { \
37
0
    gnutls_assert(); \
38
0
    gnutls_free( result->data); result->data = NULL; \
39
0
    return GNUTLS_E_INTERNAL_ERROR; \
40
0
  } \
41
0
  } while(0)
42
43
/* encodes data and puts the result into result (locally allocated)
44
 * The result_size (including the null terminator) is the return value.
45
 */
46
int
47
_gnutls_fbase64_encode(const char *msg, const uint8_t * data,
48
           size_t data_size, gnutls_datum_t * result)
49
0
{
50
0
  int tmp;
51
0
  unsigned int i;
52
0
  uint8_t tmpres[66];
53
0
  uint8_t *ptr;
54
0
  char top[80];
55
0
  char bottom[80];
56
0
  size_t size, max, bytes;
57
0
  int pos, top_len = 0, bottom_len = 0;
58
0
  unsigned raw_encoding = 0;
59
60
0
  if (msg == NULL || msg[0] == 0)
61
0
    raw_encoding = 1;
62
63
0
  if (!raw_encoding) {
64
0
    if (strlen(msg) > 50) {
65
0
      gnutls_assert();
66
0
      return GNUTLS_E_BASE64_ENCODING_ERROR;
67
0
    }
68
69
0
    _gnutls_str_cpy(top, sizeof(top), "-----BEGIN ");
70
0
    _gnutls_str_cat(top, sizeof(top), msg);
71
0
    _gnutls_str_cat(top, sizeof(top), "-----\n");
72
73
0
    _gnutls_str_cpy(bottom, sizeof(bottom), "-----END ");
74
0
    _gnutls_str_cat(bottom, sizeof(bottom), msg);
75
0
    _gnutls_str_cat(bottom, sizeof(bottom), "-----\n");
76
77
0
    top_len = strlen(top);
78
0
    bottom_len = strlen(bottom);
79
0
  }
80
81
0
  max = B64FSIZE(top_len + bottom_len, data_size);
82
83
0
  result->data = gnutls_malloc(max + 1);
84
0
  if (result->data == NULL) {
85
0
    gnutls_assert();
86
0
    return GNUTLS_E_MEMORY_ERROR;
87
0
  }
88
89
0
  bytes = 0;
90
0
  INCR(bytes, top_len, max);
91
0
  pos = top_len;
92
93
0
  memcpy(result->data, top, top_len);
94
95
0
  for (i = 0; i < data_size; i += 48) {
96
0
    if (data_size - i < 48)
97
0
      tmp = data_size - i;
98
0
    else
99
0
      tmp = 48;
100
101
0
    size = BASE64_ENCODE_RAW_LENGTH(tmp);
102
0
    if (sizeof(tmpres) < size)
103
0
      return
104
0
          gnutls_assert_val(GNUTLS_E_BASE64_ENCODING_ERROR);
105
106
0
    base64_encode_raw((void *)tmpres, tmp, &data[i]);
107
108
0
    INCR(bytes, size + 1, max);
109
0
    ptr = &result->data[pos];
110
111
0
    memcpy(ptr, tmpres, size);
112
0
    ptr += size;
113
0
    pos += size;
114
0
    if (!raw_encoding) {
115
0
      *ptr++ = '\n';
116
0
      pos++;
117
0
    } else {
118
0
      bytes--;
119
0
    }
120
0
  }
121
122
0
  INCR(bytes, bottom_len, max);
123
124
0
  memcpy(&result->data[bytes - bottom_len], bottom, bottom_len);
125
0
  result->data[bytes] = 0;
126
0
  result->size = bytes;
127
128
0
  return max + 1;
129
0
}
130
131
/**
132
 * gnutls_pem_base64_encode:
133
 * @msg: is a message to be put in the header (may be %NULL)
134
 * @data: contain the raw data
135
 * @result: the place where base64 data will be copied
136
 * @result_size: holds the size of the result
137
 *
138
 * This function will convert the given data to printable data, using
139
 * the base64 encoding. This is the encoding used in PEM messages.
140
 *
141
 * The output string will be null terminated, although the output size will
142
 * not include the terminating null.
143
 *
144
 * Returns: On success %GNUTLS_E_SUCCESS (0) is returned,
145
 *   %GNUTLS_E_SHORT_MEMORY_BUFFER is returned if the buffer given is
146
 *   not long enough, or 0 on success.
147
 **/
148
int
149
gnutls_pem_base64_encode(const char *msg, const gnutls_datum_t * data,
150
       char *result, size_t *result_size)
151
0
{
152
0
  gnutls_datum_t res;
153
0
  int ret;
154
155
0
  ret = _gnutls_fbase64_encode(msg, data->data, data->size, &res);
156
0
  if (ret < 0)
157
0
    return ret;
158
159
0
  if (result == NULL || *result_size < (unsigned)res.size) {
160
0
    gnutls_free(res.data);
161
0
    *result_size = res.size + 1;
162
0
    return GNUTLS_E_SHORT_MEMORY_BUFFER;
163
0
  } else {
164
0
    memcpy(result, res.data, res.size);
165
0
    gnutls_free(res.data);
166
0
    *result_size = res.size;
167
0
  }
168
169
0
  return 0;
170
0
}
171
172
/**
173
 * gnutls_pem_base64_encode2:
174
 * @header: is a message to be put in the encoded header (may be %NULL)
175
 * @data: contains the raw data
176
 * @result: will hold the newly allocated encoded data
177
 *
178
 * This function will convert the given data to printable data, using
179
 * the base64 encoding.  This is the encoding used in PEM messages.
180
 * This function will allocate the required memory to hold the encoded
181
 * data.
182
 *
183
 * You should use gnutls_free() to free the returned data.
184
 *
185
 * Note, that prior to GnuTLS 3.4.0 this function was available
186
 * under the name gnutls_pem_base64_encode_alloc(). There is
187
 * compatibility macro pointing to this function.
188
 *
189
 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise
190
 *   an error code is returned.
191
 *
192
 * Since: 3.4.0
193
 **/
194
int
195
gnutls_pem_base64_encode2(const char *header,
196
        const gnutls_datum_t * data, gnutls_datum_t * result)
197
0
{
198
0
  int ret;
199
200
0
  if (result == NULL)
201
0
    return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
202
203
0
  ret = _gnutls_fbase64_encode(header, data->data, data->size, result);
204
0
  if (ret < 0)
205
0
    return gnutls_assert_val(ret);
206
207
0
  return 0;
208
0
}
209
210
/* copies data to result but removes newlines and <CR>
211
 * returns the size of the data copied.
212
 *
213
 * It will fail with GNUTLS_E_BASE64_DECODING_ERROR if the
214
 * end-result is the empty string.
215
 */
216
inline static int
217
cpydata(const uint8_t * data, int data_size, gnutls_datum_t * result)
218
0
{
219
0
  int i, j;
220
221
0
  result->data = gnutls_malloc(data_size + 1);
222
0
  if (result->data == NULL)
223
0
    return GNUTLS_E_MEMORY_ERROR;
224
225
0
  for (j = i = 0; i < data_size; i++) {
226
0
    if (data[i] == '\n' || data[i] == '\r' || data[i] == ' '
227
0
        || data[i] == '\t')
228
0
      continue;
229
0
    else if (data[i] == '-')
230
0
      break;
231
0
    result->data[j] = data[i];
232
0
    j++;
233
0
  }
234
235
0
  result->size = j;
236
0
  result->data[j] = 0;
237
238
0
  if (j == 0) {
239
0
    gnutls_free(result->data);
240
0
    return gnutls_assert_val(GNUTLS_E_BASE64_DECODING_ERROR);
241
0
  }
242
243
0
  return j;
244
0
}
245
246
/* decodes data and puts the result into result (locally allocated).
247
 * Note that encodings of zero-length strings are being rejected
248
 * with GNUTLS_E_BASE64_DECODING_ERROR.
249
 *
250
 * The result_size is the return value.
251
 */
252
int
253
_gnutls_base64_decode(const uint8_t * data, size_t data_size,
254
          gnutls_datum_t * result)
255
0
{
256
0
  int ret;
257
0
  size_t size;
258
0
  gnutls_datum_t pdata;
259
0
  struct base64_decode_ctx ctx;
260
261
0
  if (data_size == 0) {
262
0
    result->data = (unsigned char *)gnutls_strdup("");
263
0
    if (result->data == NULL)
264
0
      return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
265
0
    result->size = 0;
266
0
    return 0;
267
0
  }
268
269
0
  ret = cpydata(data, data_size, &pdata);
270
0
  if (ret < 0) {
271
0
    gnutls_assert();
272
0
    return ret;
273
0
  }
274
275
0
  base64_decode_init(&ctx);
276
277
0
  size = BASE64_DECODE_LENGTH(pdata.size);
278
0
  if (size == 0) {
279
0
    ret = gnutls_assert_val(GNUTLS_E_BASE64_DECODING_ERROR);
280
0
    goto cleanup;
281
0
  }
282
283
0
  result->data = gnutls_malloc(size);
284
0
  if (result->data == NULL) {
285
0
    ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
286
0
    goto cleanup;
287
0
  }
288
289
0
  ret = base64_decode_update(&ctx, &size, result->data,
290
0
           pdata.size, (void *)pdata.data);
291
0
  if (ret == 0 || size == 0) {
292
0
    gnutls_assert();
293
0
    ret = GNUTLS_E_BASE64_DECODING_ERROR;
294
0
    goto fail;
295
0
  }
296
297
0
  ret = base64_decode_final(&ctx);
298
0
  if (ret != 1) {
299
0
    ret = gnutls_assert_val(GNUTLS_E_BASE64_DECODING_ERROR);
300
0
    goto fail;
301
0
  }
302
303
0
  result->size = size;
304
305
0
  ret = size;
306
0
  goto cleanup;
307
308
0
 fail:
309
0
  gnutls_free(result->data);
310
311
0
 cleanup:
312
0
  gnutls_free(pdata.data);
313
0
  return ret;
314
0
}
315
316
/* Searches the given string for ONE PEM encoded certificate, and
317
 * stores it in the result.
318
 *
319
 * The result_size (always non-zero) is the return value,
320
 * or a negative error code.
321
 */
322
0
#define ENDSTR "-----"
323
int
324
_gnutls_fbase64_decode(const char *header, const uint8_t * data,
325
           size_t data_size, gnutls_datum_t * result)
326
0
{
327
0
  int ret;
328
0
  static const char top[] = "-----BEGIN ";
329
0
  static const char bottom[] = "-----END ";
330
0
  uint8_t *rdata, *kdata;
331
0
  int rdata_size;
332
0
  char pem_header[128];
333
334
0
  _gnutls_str_cpy(pem_header, sizeof(pem_header), top);
335
0
  if (header != NULL)
336
0
    _gnutls_str_cat(pem_header, sizeof(pem_header), header);
337
338
0
  rdata = memmem(data, data_size, pem_header, strlen(pem_header));
339
0
  if (rdata == NULL) {
340
0
    gnutls_assert();
341
0
    _gnutls_hard_log("Could not find '%s'\n", pem_header);
342
0
    return GNUTLS_E_BASE64_UNEXPECTED_HEADER_ERROR;
343
0
  }
344
345
0
  data_size -= MEMSUB(rdata, data);
346
347
0
  if (data_size < 4 + strlen(bottom)) {
348
0
    gnutls_assert();
349
0
    return GNUTLS_E_BASE64_DECODING_ERROR;
350
0
  }
351
352
0
  kdata = memmem(rdata + 1, data_size - 1, ENDSTR, sizeof(ENDSTR) - 1);
353
  /* allow CR as well.
354
   */
355
0
  if (kdata == NULL) {
356
0
    gnutls_assert();
357
0
    _gnutls_hard_log("Could not find '%s'\n", ENDSTR);
358
0
    return GNUTLS_E_BASE64_DECODING_ERROR;
359
0
  }
360
0
  data_size -= strlen(ENDSTR);
361
0
  data_size -= MEMSUB(kdata, rdata);
362
363
0
  rdata = kdata + strlen(ENDSTR);
364
365
  /* position is now after the ---BEGIN--- headers */
366
367
0
  kdata = memmem(rdata, data_size, bottom, strlen(bottom));
368
0
  if (kdata == NULL) {
369
0
    gnutls_assert();
370
0
    return GNUTLS_E_BASE64_DECODING_ERROR;
371
0
  }
372
373
  /* position of kdata is before the ----END--- footer 
374
   */
375
0
  rdata_size = MEMSUB(kdata, rdata);
376
377
0
  if (rdata_size < 4) {
378
0
    gnutls_assert();
379
0
    return GNUTLS_E_BASE64_DECODING_ERROR;
380
0
  }
381
382
0
  if ((ret = _gnutls_base64_decode(rdata, rdata_size, result)) < 0) {
383
0
    gnutls_assert();
384
0
    return GNUTLS_E_BASE64_DECODING_ERROR;
385
0
  }
386
387
0
  return ret;
388
0
}
389
390
/**
391
 * gnutls_pem_base64_decode:
392
 * @header: A null terminated string with the PEM header (eg. CERTIFICATE)
393
 * @b64_data: contain the encoded data
394
 * @result: the place where decoded data will be copied
395
 * @result_size: holds the size of the result
396
 *
397
 * This function will decode the given encoded data.  If the header
398
 * given is non %NULL this function will search for "-----BEGIN header"
399
 * and decode only this part.  Otherwise it will decode the first PEM
400
 * packet found.
401
 *
402
 * Returns: On success %GNUTLS_E_SUCCESS (0) is returned,
403
 *   %GNUTLS_E_SHORT_MEMORY_BUFFER is returned if the buffer given is
404
 *   not long enough, or 0 on success.
405
 **/
406
int
407
gnutls_pem_base64_decode(const char *header,
408
       const gnutls_datum_t * b64_data,
409
       unsigned char *result, size_t *result_size)
410
0
{
411
0
  gnutls_datum_t res;
412
0
  int ret;
413
414
0
  ret =
415
0
      _gnutls_fbase64_decode(header, b64_data->data, b64_data->size,
416
0
           &res);
417
0
  if (ret < 0)
418
0
    return gnutls_assert_val(ret);
419
420
0
  if (result == NULL || *result_size < (unsigned)res.size) {
421
0
    gnutls_free(res.data);
422
0
    *result_size = res.size;
423
0
    return GNUTLS_E_SHORT_MEMORY_BUFFER;
424
0
  } else {
425
0
    memcpy(result, res.data, res.size);
426
0
    gnutls_free(res.data);
427
0
    *result_size = res.size;
428
0
  }
429
430
0
  return 0;
431
0
}
432
433
/**
434
 * gnutls_pem_base64_decode2:
435
 * @header: The PEM header (eg. CERTIFICATE)
436
 * @b64_data: contains the encoded data
437
 * @result: the location of decoded data
438
 *
439
 * This function will decode the given encoded data. The decoded data
440
 * will be allocated, and stored into result.  If the header given is
441
 * non null this function will search for "-----BEGIN header" and
442
 * decode only this part. Otherwise it will decode the first PEM
443
 * packet found.
444
 *
445
 * You should use gnutls_free() to free the returned data.
446
 *
447
 * Note, that prior to GnuTLS 3.4.0 this function was available
448
 * under the name gnutls_pem_base64_decode_alloc(). There is
449
 * compatibility macro pointing to this function.
450
 *
451
 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise
452
 *   an error code is returned.
453
 *
454
 * Since: 3.4.0
455
 **/
456
int
457
gnutls_pem_base64_decode2(const char *header,
458
        const gnutls_datum_t * b64_data,
459
        gnutls_datum_t * result)
460
0
{
461
0
  int ret;
462
463
0
  if (result == NULL)
464
0
    return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
465
466
0
  ret =
467
0
      _gnutls_fbase64_decode(header, b64_data->data, b64_data->size,
468
0
           result);
469
0
  if (ret < 0)
470
0
    return gnutls_assert_val(ret);
471
472
0
  return 0;
473
0
}
474
475
/**
476
 * gnutls_base64_decode2:
477
 * @base64: contains the encoded data
478
 * @result: the location of decoded data
479
 *
480
 * This function will decode the given base64 encoded data. The decoded data
481
 * will be allocated, and stored into result.
482
 *
483
 * You should use gnutls_free() to free the returned data.
484
 *
485
 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise
486
 *   an error code is returned.
487
 *
488
 * Since: 3.6.0
489
 **/
490
int
491
gnutls_base64_decode2(const gnutls_datum_t * base64, gnutls_datum_t * result)
492
0
{
493
0
  int ret;
494
495
0
  ret = _gnutls_base64_decode(base64->data, base64->size, result);
496
0
  if (ret < 0) {
497
0
    return gnutls_assert_val(ret);
498
0
  }
499
500
0
  return 0;
501
0
}
502
503
/**
504
 * gnutls_base64_encode2:
505
 * @data: contains the raw data
506
 * @result: will hold the newly allocated encoded data
507
 *
508
 * This function will convert the given data to printable data, using
509
 * the base64 encoding. This function will allocate the required
510
 * memory to hold the encoded data.
511
 *
512
 * You should use gnutls_free() to free the returned data.
513
 *
514
 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise
515
 *   an error code is returned.
516
 *
517
 * Since: 3.6.0
518
 **/
519
int gnutls_base64_encode2(const gnutls_datum_t * data, gnutls_datum_t * result)
520
0
{
521
0
  int ret;
522
523
0
  if (result == NULL)
524
0
    return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
525
526
0
  ret = _gnutls_fbase64_encode(NULL, data->data, data->size, result);
527
0
  if (ret < 0)
528
0
    return gnutls_assert_val(ret);
529
530
0
  return 0;
531
0
}