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