added a BUILD_FROM_FD option, supporting credential parsing from stdin
[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 }
396 cred = lib->creds->create(lib->creds, this->type, this->subtype,
397 pgp ? BUILD_BLOB_PGP : BUILD_BLOB_ASN1_DER, blob,
398 this->flags ? BUILD_X509_FLAG : BUILD_END,
399 this->flags, BUILD_END);
400 chunk_clear(&blob);
401 return cred;
402 }
403
404 /**
405 * build the credential from a file
406 */
407 static void *build_from_file(private_builder_t *this, char *file)
408 {
409 void *cred = NULL;
410 struct stat sb;
411 void *addr;
412 int fd;
413
414 fd = open(file, O_RDONLY);
415 if (fd == -1)
416 {
417 DBG1(" opening '%s' failed: %s", file, strerror(errno));
418 return NULL;
419 }
420
421 if (fstat(fd, &sb) == -1)
422 {
423 DBG1(" getting file size of '%s' failed: %s", file, strerror(errno));
424 close(fd);
425 return NULL;
426 }
427
428 addr = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
429 if (addr == MAP_FAILED)
430 {
431 DBG1(" mapping '%s' failed: %s", file, strerror(errno));
432 close(fd);
433 return NULL;
434 }
435
436 cred = build_from_blob(this, chunk_create(addr, sb.st_size));
437
438 munmap(addr, sb.st_size);
439 close(fd);
440 return cred;
441 }
442
443 /**
444 * build the credential from a file
445 */
446 static void *build_from_fd(private_builder_t *this, int fd)
447 {
448 char buf[8096];
449 char *pos = buf;
450 ssize_t len, total = 0;
451
452 while (TRUE)
453 {
454 len = read(fd, pos, buf + sizeof(buf) - pos);
455 if (len < 0)
456 {
457 DBG1("reading from file descriptor failed: %s", strerror(errno));
458 return NULL;
459 }
460 if (len == 0)
461 {
462 break;
463 }
464 total += len;
465 if (total == sizeof(buf))
466 {
467 DBG1("buffer too small to read from file descriptor");
468 return NULL;
469 }
470 }
471 return build_from_blob(this, chunk_create(buf, total));
472 }
473
474 /**
475 * Implementation of builder_t.build
476 */
477 static void *build(private_builder_t *this)
478 {
479 void *cred = NULL;
480
481 if (this->pem.ptr)
482 {
483 cred = build_from_blob(this, this->pem);
484 }
485 else if (this->file)
486 {
487 cred = build_from_file(this, this->file);
488 }
489 else if (this->fd != -1)
490 {
491 cred = build_from_fd(this, this->fd);
492 }
493 free(this);
494 return cred;
495 }
496
497 /**
498 * passphrase callback to use if passphrase given
499 */
500 static chunk_t given_passphrase_cb(chunk_t *passphrase, int try)
501 {
502 if (try > 1)
503 { /* try only once for given passphrases */
504 return chunk_empty;
505 }
506 return *passphrase;
507 }
508
509 /**
510 * Implementation of builder_t.add
511 */
512 static void add(private_builder_t *this, builder_part_t part, ...)
513 {
514 va_list args;
515
516 switch (part)
517 {
518 case BUILD_FROM_FILE:
519 va_start(args, part);
520 this->file = va_arg(args, char*);
521 va_end(args);
522 break;
523 case BUILD_FROM_FD:
524 va_start(args, part);
525 this->fd = va_arg(args, int);
526 va_end(args);
527 break;
528 case BUILD_BLOB_PEM:
529 va_start(args, part);
530 this->pem = va_arg(args, chunk_t);
531 va_end(args);
532 break;
533 case BUILD_PASSPHRASE:
534 va_start(args, part);
535 this->passphrase = va_arg(args, chunk_t);
536 va_end(args);
537 if (this->passphrase.len && this->passphrase.ptr)
538 {
539 this->cb = (void*)given_passphrase_cb;
540 this->data = &this->passphrase;
541 }
542 break;
543 case BUILD_PASSPHRASE_CALLBACK:
544 va_start(args, part);
545 this->cb = va_arg(args, chunk_t(*)(void*,int));
546 this->data = va_arg(args, void*);
547 va_end(args);
548 break;
549 case BUILD_X509_FLAG:
550 va_start(args, part);
551 this->flags = va_arg(args, int);
552 va_end(args);
553 break;
554 default:
555 builder_cancel(&this->public);
556 break;
557 }
558 }
559
560 /**
561 * Generic PEM builder.
562 */
563 static builder_t *pem_builder(credential_type_t type, int subtype)
564 {
565 private_builder_t *this = malloc_thing(private_builder_t);
566
567 this->public.add = (void(*)(builder_t *this, builder_part_t part, ...))add;
568 this->public.build = (void*(*)(builder_t *this))build;
569
570 this->type = type;
571 this->subtype = subtype;
572 this->file = NULL;
573 this->fd = -1;
574 this->pem = chunk_empty;
575 this->passphrase = chunk_empty;
576 this->cb = NULL;
577 this->data = NULL;
578 this->flags = 0;
579
580 return &this->public;
581 }
582
583 /**
584 * Private key PEM builder.
585 */
586 builder_t *private_key_pem_builder(key_type_t type)
587 {
588 return pem_builder(CRED_PRIVATE_KEY, type);
589 }
590
591 /**
592 * Public key PEM builder.
593 */
594 builder_t *public_key_pem_builder(key_type_t type)
595 {
596 return pem_builder(CRED_PUBLIC_KEY, type);
597 }
598
599 /**
600 * Certificate PEM builder.
601 */
602 builder_t *certificate_pem_builder(certificate_type_t type)
603 {
604 return pem_builder(CRED_CERTIFICATE, type);
605 }
606