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