Coverage Report

Created: 2026-09-03 07:13

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/strongswan/src/libstrongswan/plugins/pem/pem_builder.c
Line
Count
Source
1
/*
2
 * Copyright (C) 2013 Tobias Brunner
3
 * Copyright (C) 2009 Martin Willi
4
 * Copyright (C) 2001-2008 Andreas Steffen
5
 *
6
 * Copyright (C) secunet Security Networks AG
7
 *
8
 * This program is free software; you can redistribute it and/or modify it
9
 * under the terms of the GNU General Public License as published by the
10
 * Free Software Foundation; either version 2 of the License, or (at your
11
 * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
12
 *
13
 * This program is distributed in the hope that it will be useful, but
14
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
16
 * for more details.
17
 */
18
19
#include "pem_builder.h"
20
21
#include <stdio.h>
22
#include <stdlib.h>
23
#include <unistd.h>
24
#include <errno.h>
25
#include <string.h>
26
#include <stddef.h>
27
#include <fcntl.h>
28
#include <sys/types.h>
29
#include <sys/stat.h>
30
31
#include <utils/debug.h>
32
#include <library.h>
33
#include <utils/lexparser.h>
34
#include <asn1/asn1.h>
35
#include <crypto/hashers/hasher.h>
36
#include <crypto/crypters/crypter.h>
37
#include <credentials/certificates/x509.h>
38
39
0
#define PKCS5_SALT_LEN  8  /* bytes */
40
41
/**
42
 * check the presence of a pattern in a character string, skip if found
43
 */
44
static bool present(char* pattern, chunk_t* ch)
45
12.2M
{
46
12.2M
  u_int len = strlen(pattern);
47
48
12.2M
  if (ch->len >= len && strneq(ch->ptr, pattern, len))
49
125k
  {
50
125k
    *ch = chunk_skip(*ch, len);
51
125k
    return TRUE;
52
125k
  }
53
12.1M
  return FALSE;
54
12.2M
}
55
56
/**
57
 * find a boundary of the form -----tag name-----
58
 */
59
static bool find_boundary(char* tag, chunk_t *line)
60
1.59M
{
61
1.59M
  chunk_t name = chunk_empty;
62
63
1.59M
  if (!present("-----", line) ||
64
94.7k
    !present(tag, line) ||
65
22.6k
    !line->len || *line->ptr != ' ')
66
1.57M
  {
67
1.57M
    return FALSE;
68
1.57M
  }
69
17.6k
  *line = chunk_skip(*line, 1);
70
71
  /* extract name */
72
17.6k
  name.ptr = line->ptr;
73
10.6M
  while (line->len > 0)
74
10.6M
  {
75
10.6M
    if (present("-----", line))
76
7.99k
    {
77
7.99k
      DBG2(DBG_ASN, "  -----%s %.*s-----", tag, (int)name.len, name.ptr);
78
7.99k
      return TRUE;
79
7.99k
    }
80
10.5M
    line->ptr++;  line->len--;  name.len++;
81
10.5M
  }
82
9.64k
  return FALSE;
83
17.6k
}
84
85
/*
86
 * decrypts a passphrase protected encrypted data block
87
 */
88
static status_t pem_decrypt(chunk_t *blob, encryption_algorithm_t alg,
89
              size_t key_size, chunk_t iv, chunk_t passphrase)
90
0
{
91
0
  hasher_t *hasher;
92
0
  crypter_t *crypter;
93
0
  chunk_t salt = { iv.ptr, PKCS5_SALT_LEN };
94
0
  chunk_t hash;
95
0
  chunk_t decrypted;
96
0
  chunk_t key = {alloca(key_size), key_size};
97
0
  uint8_t padding, *last_padding_pos, *first_padding_pos;
98
0
  size_t block_size;
99
100
0
  if (!key_size)
101
0
  {
102
0
    return FAILED;
103
0
  }
104
105
  /* build key from passphrase and IV */
106
0
  hasher = lib->crypto->create_hasher(lib->crypto, HASH_MD5);
107
0
  if (hasher == NULL)
108
0
  {
109
0
    DBG1(DBG_ASN, "  MD5 hash algorithm not available");
110
0
    return NOT_SUPPORTED;
111
0
  }
112
0
  hash.len = hasher->get_hash_size(hasher);
113
0
  hash.ptr = alloca(hash.len);
114
0
  if (!hasher->get_hash(hasher, passphrase, NULL) ||
115
0
    !hasher->get_hash(hasher, salt, hash.ptr))
116
0
  {
117
0
    hasher->destroy(hasher);
118
0
    return FAILED;
119
0
  }
120
0
  memcpy(key.ptr, hash.ptr, hash.len);
121
122
0
  if (key.len > hash.len)
123
0
  {
124
0
    if (!hasher->get_hash(hasher, hash, NULL) ||
125
0
      !hasher->get_hash(hasher, passphrase, NULL) ||
126
0
      !hasher->get_hash(hasher, salt, hash.ptr))
127
0
    {
128
0
      hasher->destroy(hasher);
129
0
      return FAILED;
130
0
    }
131
0
    memcpy(key.ptr + hash.len, hash.ptr, key.len - hash.len);
132
0
  }
133
0
  hasher->destroy(hasher);
134
135
  /* decrypt blob */
136
0
  crypter = lib->crypto->create_crypter(lib->crypto, alg, key_size);
137
0
  if (crypter == NULL)
138
0
  {
139
0
    DBG1(DBG_ASN, "  %N encryption algorithm not available",
140
0
       encryption_algorithm_names, alg);
141
0
    memwipe(key.ptr, key.len);
142
0
    return NOT_SUPPORTED;
143
0
  }
144
145
0
  block_size = crypter->get_block_size(crypter);
146
0
  if (iv.len != crypter->get_iv_size(crypter) || blob->len % block_size)
147
0
  {
148
0
    DBG1(DBG_ASN, "  data size is not multiple of block size");
149
0
    crypter->destroy(crypter);
150
0
    memwipe(key.ptr, key.len);
151
0
    return PARSE_ERROR;
152
0
  }
153
0
  if (!crypter->set_key(crypter, key) ||
154
0
    !crypter->decrypt(crypter, *blob, iv, &decrypted))
155
0
  {
156
0
    crypter->destroy(crypter);
157
0
    memwipe(key.ptr, key.len);
158
0
    return FAILED;
159
0
  }
160
0
  crypter->destroy(crypter);
161
0
  memcpy(blob->ptr, decrypted.ptr, blob->len);
162
0
  chunk_clear(&decrypted);
163
0
  memwipe(key.ptr, key.len);
164
165
  /* determine amount of padding */
166
0
  last_padding_pos = blob->ptr + blob->len - 1;
167
0
  padding = *last_padding_pos;
168
0
  if (!padding || padding > block_size)
169
0
  {
170
0
    DBG1(DBG_ASN, "  invalid passphrase");
171
0
    return INVALID_ARG;
172
0
  }
173
0
  first_padding_pos = last_padding_pos - padding;
174
  /* check the padding pattern */
175
0
  while (--last_padding_pos > first_padding_pos)
176
0
  {
177
0
    if (*last_padding_pos != padding)
178
0
    {
179
0
      DBG1(DBG_ASN, "  invalid passphrase");
180
0
      return INVALID_ARG;
181
0
    }
182
0
  }
183
  /* remove padding */
184
0
  blob->len -= padding;
185
0
  return SUCCESS;
186
0
}
187
188
/**
189
 * Converts a PEM encoded file into its binary form (RFC 1421, RFC 934)
190
 */
191
static status_t pem_to_bin(chunk_t *blob, bool *pgp)
192
11.5k
{
193
11.5k
  typedef enum {
194
11.5k
    PEM_PRE    = 0,
195
11.5k
    PEM_MSG    = 1,
196
11.5k
    PEM_HEADER = 2,
197
11.5k
    PEM_BODY   = 3,
198
11.5k
    PEM_POST   = 4,
199
11.5k
    PEM_ABORT  = 5
200
11.5k
  } state_t;
201
202
11.5k
  encryption_algorithm_t alg = ENCR_UNDEFINED;
203
11.5k
  size_t key_size = 0;
204
11.5k
  bool encrypted = FALSE;
205
11.5k
  state_t state  = PEM_PRE;
206
11.5k
  chunk_t src    = *blob;
207
11.5k
  chunk_t dst    = *blob;
208
11.5k
  chunk_t line   = chunk_empty;
209
11.5k
  chunk_t iv     = chunk_empty;
210
11.5k
  u_char iv_buf[HASH_SIZE_MD5];
211
11.5k
  status_t status = NOT_FOUND;
212
11.5k
  enumerator_t *enumerator;
213
11.5k
  shared_key_t *shared;
214
215
11.5k
  dst.len = 0;
216
11.5k
  iv.ptr = iv_buf;
217
11.5k
  iv.len = 0;
218
219
1.60M
  while (fetchline(&src, &line))
220
1.59M
  {
221
1.59M
    if (state == PEM_PRE)
222
727k
    {
223
727k
      if (find_boundary("BEGIN", &line))
224
7.28k
      {
225
7.28k
        state = PEM_MSG;
226
7.28k
      }
227
727k
      continue;
228
727k
    }
229
865k
    else
230
865k
    {
231
865k
      if (find_boundary("END", &line))
232
714
      {
233
714
        state = PEM_POST;
234
714
        break;
235
714
      }
236
864k
      if (state == PEM_MSG)
237
6.66k
      {
238
6.66k
        state = PEM_HEADER;
239
6.66k
        if (memchr(line.ptr, ':', line.len) == NULL)
240
2.06k
        {
241
2.06k
          state = PEM_BODY;
242
2.06k
        }
243
6.66k
      }
244
864k
      if (state == PEM_HEADER)
245
225k
      {
246
225k
        err_t ugh = NULL;
247
225k
        chunk_t name  = chunk_empty;
248
225k
        chunk_t value = chunk_empty;
249
250
        /* an empty line separates HEADER and BODY */
251
225k
        if (line.len == 0)
252
291
        {
253
291
          state = PEM_BODY;
254
291
          continue;
255
291
        }
256
257
        /* we are looking for a parameter: value pair */
258
225k
        DBG2(DBG_ASN, "  %.*s", (int)line.len, line.ptr);
259
225k
        ugh = extract_parameter_value(&name, &value, &line);
260
225k
        if (ugh != NULL)
261
72.5k
        {
262
72.5k
          continue;
263
72.5k
        }
264
152k
        if (match("Proc-Type", &name) && value.len && *value.ptr == '4')
265
10.2k
        {
266
10.2k
          encrypted = TRUE;
267
10.2k
        }
268
142k
        else if (match("DEK-Info", &name))
269
25.3k
        {
270
25.3k
          chunk_t dek;
271
272
25.3k
          if (!extract_token(&dek, ',', &value))
273
7.44k
          {
274
7.44k
            dek = value;
275
7.44k
          }
276
25.3k
          if (match("DES-EDE3-CBC", &dek))
277
6.67k
          {
278
6.67k
            alg = ENCR_3DES;
279
6.67k
            key_size = 24;
280
6.67k
          }
281
18.7k
          else if (match("AES-128-CBC", &dek))
282
3.92k
          {
283
3.92k
            alg = ENCR_AES_CBC;
284
3.92k
            key_size = 16;
285
3.92k
          }
286
14.8k
          else if (match("AES-192-CBC", &dek))
287
3.38k
          {
288
3.38k
            alg = ENCR_AES_CBC;
289
3.38k
            key_size = 24;
290
3.38k
          }
291
11.4k
          else if (match("AES-256-CBC", &dek))
292
10.9k
          {
293
10.9k
            alg = ENCR_AES_CBC;
294
10.9k
            key_size = 32;
295
10.9k
          }
296
495
          else
297
495
          {
298
495
            DBG1(DBG_ASN, "  encryption algorithm '%.*s'"
299
495
               " not supported", (int)dek.len, dek.ptr);
300
495
            return NOT_SUPPORTED;
301
495
          }
302
24.9k
          if (!eat_whitespace(&value) || value.len > 2*sizeof(iv_buf))
303
361
          {
304
361
            return PARSE_ERROR;
305
361
          }
306
24.5k
          iv = chunk_from_hex(value, iv_buf);
307
24.5k
        }
308
152k
      }
309
639k
      else /* state is PEM_BODY */
310
639k
      {
311
639k
        chunk_t data;
312
313
        /* remove any trailing whitespace */
314
639k
        if (!extract_token(&data ,' ', &line))
315
498k
        {
316
498k
          data = line;
317
498k
        }
318
319
        /* check for PGP armor checksum */
320
639k
        if (data.len && *data.ptr == '=')
321
4.18k
        {
322
4.18k
          *pgp = TRUE;
323
4.18k
          data.ptr++;
324
4.18k
          data.len--;
325
4.18k
          DBG2(DBG_ASN, "  armor checksum: %.*s", (int)data.len,
326
4.18k
             data.ptr);
327
4.18k
          continue;
328
4.18k
        }
329
330
635k
        if (blob->len - dst.len < data.len / 4 * 3)
331
0
        {
332
0
          state = PEM_ABORT;
333
0
        }
334
635k
        data = chunk_from_base64(data, dst.ptr);
335
336
635k
        dst.ptr += data.len;
337
635k
        dst.len += data.len;
338
635k
      }
339
864k
    }
340
1.59M
  }
341
  /* set length to size of binary blob */
342
10.6k
  blob->len = dst.len;
343
344
10.6k
  if (state != PEM_POST || !blob->len)
345
9.98k
  {
346
9.98k
    DBG1(DBG_LIB, "  file coded in unknown format, discarded");
347
9.98k
    return PARSE_ERROR;
348
9.98k
  }
349
691
  if (!encrypted)
350
660
  {
351
660
    return SUCCESS;
352
660
  }
353
31
  if (alg == ENCR_UNDEFINED || !key_size)
354
13
  {
355
13
    DBG1(DBG_ASN, "  encrypted PEM file has no valid DEK-Info");
356
13
    return PARSE_ERROR;
357
13
  }
358
359
18
  enumerator = lib->credmgr->create_shared_enumerator(lib->credmgr,
360
18
                      SHARED_PRIVATE_KEY_PASS, NULL, NULL);
361
18
  while (enumerator->enumerate(enumerator, &shared, NULL, NULL))
362
0
  {
363
0
    chunk_t passphrase, chunk;
364
365
0
    passphrase = shared->get_key(shared);
366
0
    chunk = chunk_clone(*blob);
367
0
    status = pem_decrypt(&chunk, alg, key_size, iv, passphrase);
368
0
    if (status == SUCCESS)
369
0
    {
370
0
      memcpy(blob->ptr, chunk.ptr, chunk.len);
371
0
      blob->len = chunk.len;
372
0
    }
373
0
    chunk_clear(&chunk);
374
0
    if (status != INVALID_ARG)
375
0
    { /* try again only if passphrase invalid */
376
0
      break;
377
0
    }
378
0
  }
379
18
  enumerator->destroy(enumerator);
380
18
  return status;
381
31
}
382
383
/**
384
 * Check if a blob looks like an ASN1 SEQUENCE or SET with BER indefinite length
385
 */
386
static bool is_ber_indefinite_length(chunk_t blob)
387
24.6k
{
388
24.6k
  if (blob.len >= 4)
389
24.0k
  {
390
24.0k
    switch (blob.ptr[0])
391
24.0k
    {
392
14.5k
      case ASN1_SEQUENCE:
393
14.9k
      case ASN1_SET:
394
        /* BER indefinite length uses 0x80, and is terminated with
395
         * end-of-content using 0x00,0x00 */
396
14.9k
        return blob.ptr[1] == 0x80 &&
397
665
             blob.ptr[blob.len - 2] == 0 &&
398
586
             blob.ptr[blob.len - 1] == 0;
399
9.06k
      default:
400
9.06k
        break;
401
24.0k
    }
402
24.0k
  }
403
9.64k
  return FALSE;
404
24.6k
}
405
406
/**
407
 * load the credential from a blob
408
 */
409
static void *load_from_blob(chunk_t blob, credential_type_t type, int subtype,
410
              identification_t *subject, x509_flag_t flags)
411
24.6k
{
412
24.6k
  void *cred = NULL;
413
24.6k
  bool pgp = FALSE;
414
415
24.6k
  blob = chunk_clone(blob);
416
24.6k
  if (!is_ber_indefinite_length(blob) && !is_asn1(blob))
417
11.5k
  {
418
11.5k
    if (pem_to_bin(&blob, &pgp) != SUCCESS)
419
10.8k
    {
420
10.8k
      chunk_clear(&blob);
421
10.8k
      return NULL;
422
10.8k
    }
423
660
    if (pgp && type == CRED_PRIVATE_KEY)
424
0
    {
425
      /* PGP encoded keys are parsed with a KEY_ANY key type, as it
426
       * can contain any type of key. However, ipsec.secrets uses
427
       * RSA for PGP keys, which is actually wrong. */
428
0
      subtype = KEY_ANY;
429
0
    }
430
660
  }
431
  /* if CERT_ANY is given, ASN1 encoded blob is handled as X509 */
432
13.7k
  if (type == CRED_CERTIFICATE && subtype == CERT_ANY)
433
0
  {
434
0
    subtype = pgp ? CERT_GPG : CERT_X509;
435
0
  }
436
13.7k
  if (type == CRED_CERTIFICATE && subtype == CERT_TRUSTED_PUBKEY && subject)
437
0
  {
438
0
    cred = lib->creds->create(lib->creds, type, subtype,
439
0
                BUILD_BLOB_ASN1_DER, blob, BUILD_SUBJECT, subject,
440
0
                BUILD_END);
441
0
  }
442
13.7k
  else
443
13.7k
  {
444
13.7k
    cred = lib->creds->create(lib->creds, type, subtype,
445
13.7k
                pgp ? BUILD_BLOB_PGP : BUILD_BLOB_ASN1_DER, blob,
446
13.7k
                flags ? BUILD_X509_FLAG : BUILD_END,
447
13.7k
                flags, BUILD_END);
448
13.7k
  }
449
13.7k
  chunk_clear(&blob);
450
13.7k
  return cred;
451
24.6k
}
452
453
/**
454
 * load the credential from a file
455
 */
456
static void *load_from_file(char *file, credential_type_t type, int subtype,
457
              identification_t *subject, x509_flag_t flags)
458
0
{
459
0
  void *cred;
460
0
  chunk_t *chunk;
461
462
0
  chunk = chunk_map(file, FALSE);
463
0
  if (!chunk)
464
0
  {
465
0
    DBG1(DBG_LIB, "  opening '%s' failed: %s", file, strerror(errno));
466
0
    return NULL;
467
0
  }
468
0
  cred = load_from_blob(*chunk, type, subtype, subject, flags);
469
0
  chunk_unmap_clear(chunk);
470
0
  return cred;
471
0
}
472
473
/**
474
 * Load all kind of PEM encoded credentials.
475
 */
476
static void *pem_load(credential_type_t type, int subtype, va_list args)
477
87.1k
{
478
87.1k
  char *file = NULL;
479
87.1k
  chunk_t pem = chunk_empty;
480
87.1k
  identification_t *subject = NULL;
481
87.1k
  int flags = 0;
482
483
87.1k
  while (TRUE)
484
111k
  {
485
111k
    switch (va_arg(args, builder_part_t))
486
111k
    {
487
0
      case BUILD_FROM_FILE:
488
0
        file = va_arg(args, char*);
489
0
        continue;
490
24.6k
      case BUILD_BLOB:
491
24.6k
      case BUILD_BLOB_PEM:
492
24.6k
        pem = va_arg(args, chunk_t);
493
24.6k
        continue;
494
0
      case BUILD_SUBJECT:
495
0
        subject = va_arg(args, identification_t*);
496
0
        continue;
497
0
      case BUILD_X509_FLAG:
498
0
        flags = va_arg(args, int);
499
0
        continue;
500
24.6k
      case BUILD_END:
501
24.6k
        break;
502
62.4k
      default:
503
62.4k
        return NULL;
504
111k
    }
505
24.6k
    break;
506
111k
  }
507
508
24.6k
  if (pem.len)
509
24.6k
  {
510
24.6k
    return load_from_blob(pem, type, subtype, subject, flags);
511
24.6k
  }
512
0
  if (file)
513
0
  {
514
0
    return load_from_file(file, type, subtype, subject, flags);
515
0
  }
516
0
  return NULL;
517
0
}
518
519
/**
520
 * Private key PEM loader.
521
 */
522
private_key_t *pem_private_key_load(key_type_t type, va_list args)
523
2
{
524
2
  return pem_load(CRED_PRIVATE_KEY, type, args);
525
2
}
526
527
/**
528
 * Public key PEM loader.
529
 */
530
public_key_t *pem_public_key_load(key_type_t type, va_list args)
531
1.02k
{
532
1.02k
  return pem_load(CRED_PUBLIC_KEY, type, args);
533
1.02k
}
534
535
/**
536
 * Certificate PEM loader.
537
 */
538
certificate_t *pem_certificate_load(certificate_type_t type, va_list args)
539
86.0k
{
540
86.0k
  return pem_load(CRED_CERTIFICATE, type, args);
541
86.0k
}
542
543
/**
544
 * Container PEM loader.
545
 */
546
container_t *pem_container_load(container_type_t type, va_list args)
547
0
{
548
0
  return pem_load(CRED_CONTAINER, type, args);
549
0
}