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