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