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