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