added workaround to parse PEM encoded PGP key with KEY_RSA
[strongswan.git] / src / libstrongswan / plugins / pem / pem_builder.c
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>
25 #include <fcntl.h>
26 #include <sys/types.h>
27 #include <sys/mman.h>
28 #include <sys/stat.h>
29
30 #include <debug.h>
31 #include <library.h>
32 #include <utils/lexparser.h>
33 #include <asn1/asn1.h>
34 #include <crypto/hashers/hasher.h>
35 #include <crypto/crypters/crypter.h>
36
37 #define PKCS5_SALT_LEN 8 /* bytes */
38
39 typedef struct private_builder_t private_builder_t;
40
41 /**
42 * Builder implementation for PEM decoding
43 */
44 struct private_builder_t {
45 /** implements the builder interface */
46 builder_t public;
47 /** credential type we are building */
48 credential_type_t type;
49 /** subtype (keytype, certtype) of the credential we build */
50 int subtype;
51 /** path to file, if we are reading from a file */
52 char *file;
53 /** file description, if we are reading from a fd */
54 int fd;
55 /** PEM encoding of the credential */
56 chunk_t pem;
57 /** PEM decryption passphrase, if given */
58 chunk_t passphrase;
59 /** supplied callback to read passphrase */
60 chunk_t (*cb)(void *data, int try);
61 /** user data to callback */
62 void *data;
63 /** X509 flags to pass along */
64 int flags;
65 };
66
67 /**
68 * check the presence of a pattern in a character string, skip if found
69 */
70 static bool present(char* pattern, chunk_t* ch)
71 {
72 u_int len = strlen(pattern);
73
74 if (ch->len >= len && strneq(ch->ptr, pattern, len))
75 {
76 *ch = chunk_skip(*ch, len);
77 return TRUE;
78 }
79 return FALSE;
80 }
81
82 /**
83 * find a boundary of the form -----tag name-----
84 */
85 static bool find_boundary(char* tag, chunk_t *line)
86 {
87 chunk_t name = chunk_empty;
88
89 if (!present("-----", line) ||
90 !present(tag, line) ||
91 *line->ptr != ' ')
92 {
93 return FALSE;
94 }
95 *line = chunk_skip(*line, 1);
96
97 /* extract name */
98 name.ptr = line->ptr;
99 while (line->len > 0)
100 {
101 if (present("-----", line))
102 {
103 DBG2(" -----%s %.*s-----", tag, (int)name.len, name.ptr);
104 return TRUE;
105 }
106 line->ptr++; line->len--; name.len++;
107 }
108 return FALSE;
109 }
110
111 /*
112 * decrypts a passphrase protected encrypted data block
113 */
114 static status_t pem_decrypt(chunk_t *blob, encryption_algorithm_t alg,
115 size_t key_size, chunk_t iv, chunk_t passphrase)
116 {
117 hasher_t *hasher;
118 crypter_t *crypter;
119 chunk_t salt = { iv.ptr, PKCS5_SALT_LEN };
120 chunk_t hash;
121 chunk_t decrypted;
122 chunk_t key = {alloca(key_size), key_size};
123 u_int8_t padding, *last_padding_pos, *first_padding_pos;
124
125 /* build key from passphrase and IV */
126 hasher = lib->crypto->create_hasher(lib->crypto, HASH_MD5);
127 if (hasher == NULL)
128 {
129 DBG1(" MD5 hash algorithm not available");
130 return NOT_SUPPORTED;
131 }
132 hash.len = hasher->get_hash_size(hasher);
133 hash.ptr = alloca(hash.len);
134 hasher->get_hash(hasher, passphrase, NULL);
135 hasher->get_hash(hasher, salt, hash.ptr);
136 memcpy(key.ptr, hash.ptr, hash.len);
137
138 if (key.len > hash.len)
139 {
140 hasher->get_hash(hasher, hash, NULL);
141 hasher->get_hash(hasher, passphrase, NULL);
142 hasher->get_hash(hasher, salt, hash.ptr);
143 memcpy(key.ptr + hash.len, hash.ptr, key.len - hash.len);
144 }
145 hasher->destroy(hasher);
146
147 /* decrypt blob */
148 crypter = lib->crypto->create_crypter(lib->crypto, alg, key_size);
149 if (crypter == NULL)
150 {
151 DBG1(" %N encryption algorithm not available",
152 encryption_algorithm_names, alg);
153 return NOT_SUPPORTED;
154 }
155 crypter->set_key(crypter, key);
156
157 if (iv.len != crypter->get_block_size(crypter) ||
158 blob->len % iv.len)
159 {
160 crypter->destroy(crypter);
161 DBG1(" data size is not multiple of block size");
162 return PARSE_ERROR;
163 }
164 crypter->decrypt(crypter, *blob, iv, &decrypted);
165 crypter->destroy(crypter);
166 memcpy(blob->ptr, decrypted.ptr, blob->len);
167 chunk_free(&decrypted);
168
169 /* determine amount of padding */
170 last_padding_pos = blob->ptr + blob->len - 1;
171 padding = *last_padding_pos;
172 if (padding > blob->len)
173 {
174 first_padding_pos = blob->ptr;
175 }
176 else
177 {
178 first_padding_pos = last_padding_pos - padding;
179 }
180 /* check the padding pattern */
181 while (--last_padding_pos > first_padding_pos)
182 {
183 if (*last_padding_pos != padding)
184 {
185 DBG1(" invalid passphrase");
186 return INVALID_ARG;
187 }
188 }
189 /* remove padding */
190 blob->len -= padding;
191 return SUCCESS;
192 }
193
194 /**
195 * Converts a PEM encoded file into its binary form (RFC 1421, RFC 934)
196 */
197 status_t pem_to_bin(chunk_t *blob, private_builder_t *this, bool *pgp)
198 {
199 typedef enum {
200 PEM_PRE = 0,
201 PEM_MSG = 1,
202 PEM_HEADER = 2,
203 PEM_BODY = 3,
204 PEM_POST = 4,
205 PEM_ABORT = 5
206 } state_t;
207
208 encryption_algorithm_t alg = ENCR_UNDEFINED;
209 size_t key_size = 0;
210 bool encrypted = FALSE;
211 state_t state = PEM_PRE;
212 chunk_t src = *blob;
213 chunk_t dst = *blob;
214 chunk_t line = chunk_empty;
215 chunk_t iv = chunk_empty;
216 chunk_t passphrase;
217 int try = 0;
218 u_char iv_buf[HASH_SIZE_MD5];
219
220 dst.len = 0;
221 iv.ptr = iv_buf;
222 iv.len = 0;
223
224 while (fetchline(&src, &line))
225 {
226 if (state == PEM_PRE)
227 {
228 if (find_boundary("BEGIN", &line))
229 {
230 state = PEM_MSG;
231 }
232 continue;
233 }
234 else
235 {
236 if (find_boundary("END", &line))
237 {
238 state = PEM_POST;
239 break;
240 }
241 if (state == PEM_MSG)
242 {
243 state = PEM_HEADER;
244 if (memchr(line.ptr, ':', line.len) == NULL)
245 {
246 state = PEM_BODY;
247 }
248 }
249 if (state == PEM_HEADER)
250 {
251 err_t ugh = NULL;
252 chunk_t name = chunk_empty;
253 chunk_t value = chunk_empty;
254
255 /* an empty line separates HEADER and BODY */
256 if (line.len == 0)
257 {
258 state = PEM_BODY;
259 continue;
260 }
261
262 /* we are looking for a parameter: value pair */
263 DBG2(" %.*s", (int)line.len, line.ptr);
264 ugh = extract_parameter_value(&name, &value, &line);
265 if (ugh != NULL)
266 {
267 continue;
268 }
269 if (match("Proc-Type", &name) && *value.ptr == '4')
270 {
271 encrypted = TRUE;
272 }
273 else if (match("DEK-Info", &name))
274 {
275 chunk_t dek;
276
277 if (!extract_token(&dek, ',', &value))
278 {
279 dek = value;
280 }
281 if (match("DES-EDE3-CBC", &dek))
282 {
283 alg = ENCR_3DES;
284 key_size = 24;
285 }
286 else if (match("AES-128-CBC", &dek))
287 {
288 alg = ENCR_AES_CBC;
289 key_size = 16;
290 }
291 else if (match("AES-192-CBC", &dek))
292 {
293 alg = ENCR_AES_CBC;
294 key_size = 24;
295 }
296 else if (match("AES-256-CBC", &dek))
297 {
298 alg = ENCR_AES_CBC;
299 key_size = 32;
300 }
301 else
302 {
303 DBG1(" encryption algorithm '%.*s' not supported",
304 dek.len, dek.ptr);
305 return NOT_SUPPORTED;
306 }
307 eat_whitespace(&value);
308 iv = chunk_from_hex(value, iv.ptr);
309 }
310 }
311 else /* state is PEM_BODY */
312 {
313 chunk_t data;
314
315 /* remove any trailing whitespace */
316 if (!extract_token(&data ,' ', &line))
317 {
318 data = line;
319 }
320
321 /* check for PGP armor checksum */
322 if (*data.ptr == '=')
323 {
324 *pgp = TRUE;
325 data.ptr++;
326 data.len--;
327 DBG2(" armor checksum: %.*s", (int)data.len, data.ptr);
328 continue;
329 }
330
331 if (blob->len - dst.len < data.len / 4 * 3)
332 {
333 state = PEM_ABORT;
334 }
335 data = chunk_from_base64(data, dst.ptr);
336
337 dst.ptr += data.len;
338 dst.len += data.len;
339 }
340 }
341 }
342 /* set length to size of binary blob */
343 blob->len = dst.len;
344
345 if (state != PEM_POST)
346 {
347 DBG1(" file coded in unknown format, discarded");
348 return PARSE_ERROR;
349 }
350 if (!encrypted)
351 {
352 return SUCCESS;
353 }
354 if (!this->cb)
355 {
356 DBG1(" missing passphrase");
357 return INVALID_ARG;
358 }
359 while (TRUE)
360 {
361 passphrase = this->cb(this->data, ++try);
362 if (!passphrase.len || !passphrase.ptr)
363 {
364 return INVALID_ARG;
365 }
366 switch (pem_decrypt(blob, alg, key_size, iv, passphrase))
367 {
368 case INVALID_ARG:
369 /* bad passphrase, retry */
370 continue;
371 case SUCCESS:
372 return SUCCESS;
373 default:
374 return FAILED;
375 }
376 }
377 }
378
379 /**
380 * build the credential from a blob
381 */
382 static void *build_from_blob(private_builder_t *this, chunk_t blob)
383 {
384 void *cred = NULL;
385 bool pgp = FALSE;
386
387 blob = chunk_clone(blob);
388 if (!is_asn1(blob))
389 {
390 if (pem_to_bin(&blob, this, &pgp) != SUCCESS)
391 {
392 chunk_clear(&blob);
393 return NULL;
394 }
395 if (pgp && this->type == CRED_PRIVATE_KEY)
396 {
397 /* PGP encoded keys are parsed with a KEY_ANY key type, as it
398 * can contain any type of key. However, ipsec.secrets uses
399 * RSA for PGP keys, which is actually wrong. */
400 this->subtype = KEY_ANY;
401 }
402 }
403 cred = lib->creds->create(lib->creds, this->type, this->subtype,
404 pgp ? BUILD_BLOB_PGP : BUILD_BLOB_ASN1_DER, blob,
405 this->flags ? BUILD_X509_FLAG : BUILD_END,
406 this->flags, BUILD_END);
407 chunk_clear(&blob);
408 return cred;
409 }
410
411 /**
412 * build the credential from a file
413 */
414 static void *build_from_file(private_builder_t *this, char *file)
415 {
416 void *cred = NULL;
417 struct stat sb;
418 void *addr;
419 int fd;
420
421 fd = open(file, O_RDONLY);
422 if (fd == -1)
423 {
424 DBG1(" opening '%s' failed: %s", file, strerror(errno));
425 return NULL;
426 }
427
428 if (fstat(fd, &sb) == -1)
429 {
430 DBG1(" getting file size of '%s' failed: %s", file, strerror(errno));
431 close(fd);
432 return NULL;
433 }
434
435 addr = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
436 if (addr == MAP_FAILED)
437 {
438 DBG1(" mapping '%s' failed: %s", file, strerror(errno));
439 close(fd);
440 return NULL;
441 }
442
443 cred = build_from_blob(this, chunk_create(addr, sb.st_size));
444
445 munmap(addr, sb.st_size);
446 close(fd);
447 return cred;
448 }
449
450 /**
451 * build the credential from a file
452 */
453 static void *build_from_fd(private_builder_t *this, int fd)
454 {
455 char buf[8096];
456 char *pos = buf;
457 ssize_t len, total = 0;
458
459 while (TRUE)
460 {
461 len = read(fd, pos, buf + sizeof(buf) - pos);
462 if (len < 0)
463 {
464 DBG1("reading from file descriptor failed: %s", strerror(errno));
465 return NULL;
466 }
467 if (len == 0)
468 {
469 break;
470 }
471 total += len;
472 if (total == sizeof(buf))
473 {
474 DBG1("buffer too small to read from file descriptor");
475 return NULL;
476 }
477 }
478 return build_from_blob(this, chunk_create(buf, total));
479 }
480
481 /**
482 * Implementation of builder_t.build
483 */
484 static void *build(private_builder_t *this)
485 {
486 void *cred = NULL;
487
488 if (this->pem.ptr)
489 {
490 cred = build_from_blob(this, this->pem);
491 }
492 else if (this->file)
493 {
494 cred = build_from_file(this, this->file);
495 }
496 else if (this->fd != -1)
497 {
498 cred = build_from_fd(this, this->fd);
499 }
500 free(this);
501 return cred;
502 }
503
504 /**
505 * passphrase callback to use if passphrase given
506 */
507 static chunk_t given_passphrase_cb(chunk_t *passphrase, int try)
508 {
509 if (try > 1)
510 { /* try only once for given passphrases */
511 return chunk_empty;
512 }
513 return *passphrase;
514 }
515
516 /**
517 * Implementation of builder_t.add
518 */
519 static void add(private_builder_t *this, builder_part_t part, ...)
520 {
521 va_list args;
522
523 switch (part)
524 {
525 case BUILD_FROM_FILE:
526 va_start(args, part);
527 this->file = va_arg(args, char*);
528 va_end(args);
529 break;
530 case BUILD_FROM_FD:
531 va_start(args, part);
532 this->fd = va_arg(args, int);
533 va_end(args);
534 break;
535 case BUILD_BLOB_PEM:
536 va_start(args, part);
537 this->pem = va_arg(args, chunk_t);
538 va_end(args);
539 break;
540 case BUILD_PASSPHRASE:
541 va_start(args, part);
542 this->passphrase = va_arg(args, chunk_t);
543 va_end(args);
544 if (this->passphrase.len && this->passphrase.ptr)
545 {
546 this->cb = (void*)given_passphrase_cb;
547 this->data = &this->passphrase;
548 }
549 break;
550 case BUILD_PASSPHRASE_CALLBACK:
551 va_start(args, part);
552 this->cb = va_arg(args, chunk_t(*)(void*,int));
553 this->data = va_arg(args, void*);
554 va_end(args);
555 break;
556 case BUILD_X509_FLAG:
557 va_start(args, part);
558 this->flags = va_arg(args, int);
559 va_end(args);
560 break;
561 default:
562 builder_cancel(&this->public);
563 break;
564 }
565 }
566
567 /**
568 * Generic PEM builder.
569 */
570 static builder_t *pem_builder(credential_type_t type, int subtype)
571 {
572 private_builder_t *this = malloc_thing(private_builder_t);
573
574 this->public.add = (void(*)(builder_t *this, builder_part_t part, ...))add;
575 this->public.build = (void*(*)(builder_t *this))build;
576
577 this->type = type;
578 this->subtype = subtype;
579 this->file = NULL;
580 this->fd = -1;
581 this->pem = chunk_empty;
582 this->passphrase = chunk_empty;
583 this->cb = NULL;
584 this->data = NULL;
585 this->flags = 0;
586
587 return &this->public;
588 }
589
590 /**
591 * Private key PEM builder.
592 */
593 builder_t *private_key_pem_builder(key_type_t type)
594 {
595 return pem_builder(CRED_PRIVATE_KEY, type);
596 }
597
598 /**
599 * Public key PEM builder.
600 */
601 builder_t *public_key_pem_builder(key_type_t type)
602 {
603 return pem_builder(CRED_PUBLIC_KEY, type);
604 }
605
606 /**
607 * Certificate PEM builder.
608 */
609 builder_t *certificate_pem_builder(certificate_type_t type)
610 {
611 return pem_builder(CRED_CERTIFICATE, type);
612 }
613