Annotation of embedaddon/strongswan/src/libstrongswan/plugins/pem/pem_builder.c, revision 1.1.1.1
1.1 misho 1: /*
2: * Copyright (C) 2013 Tobias Brunner
3: * Copyright (C) 2009 Martin Willi
4: * Copyright (C) 2001-2008 Andreas Steffen
5: * HSR Hochschule fuer Technik Rapperswil
6: *
7: * This program is free software; you can redistribute it and/or modify it
8: * under the terms of the GNU General Public License as published by the
9: * Free Software Foundation; either version 2 of the License, or (at your
10: * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
11: *
12: * This program is distributed in the hope that it will be useful, but
13: * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14: * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15: * for more details.
16: */
17:
18: #include "pem_builder.h"
19:
20: #include <stdio.h>
21: #include <stdlib.h>
22: #include <unistd.h>
23: #include <errno.h>
24: #include <string.h>
25: #include <stddef.h>
26: #include <fcntl.h>
27: #include <sys/types.h>
28: #include <sys/stat.h>
29:
30: #include <utils/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->len || *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: uint8_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.len && *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: if (!eat_whitespace(&value) || value.len > 2*sizeof(iv_buf))
292: {
293: return PARSE_ERROR;
294: }
295: iv = chunk_from_hex(value, iv_buf);
296: }
297: }
298: else /* state is PEM_BODY */
299: {
300: chunk_t data;
301:
302: /* remove any trailing whitespace */
303: if (!extract_token(&data ,' ', &line))
304: {
305: data = line;
306: }
307:
308: /* check for PGP armor checksum */
309: if (data.len && *data.ptr == '=')
310: {
311: *pgp = TRUE;
312: data.ptr++;
313: data.len--;
314: DBG2(DBG_ASN, " armor checksum: %.*s", (int)data.len,
315: data.ptr);
316: continue;
317: }
318:
319: if (blob->len - dst.len < data.len / 4 * 3)
320: {
321: state = PEM_ABORT;
322: }
323: data = chunk_from_base64(data, dst.ptr);
324:
325: dst.ptr += data.len;
326: dst.len += data.len;
327: }
328: }
329: }
330: /* set length to size of binary blob */
331: blob->len = dst.len;
332:
333: if (state != PEM_POST)
334: {
335: DBG1(DBG_LIB, " file coded in unknown format, discarded");
336: return PARSE_ERROR;
337: }
338: if (!encrypted)
339: {
340: return SUCCESS;
341: }
342:
343: enumerator = lib->credmgr->create_shared_enumerator(lib->credmgr,
344: SHARED_PRIVATE_KEY_PASS, NULL, NULL);
345: while (enumerator->enumerate(enumerator, &shared, NULL, NULL))
346: {
347: chunk_t passphrase, chunk;
348:
349: passphrase = shared->get_key(shared);
350: chunk = chunk_clone(*blob);
351: status = pem_decrypt(&chunk, alg, key_size, iv, passphrase);
352: if (status == SUCCESS)
353: {
354: memcpy(blob->ptr, chunk.ptr, chunk.len);
355: blob->len = chunk.len;
356: }
357: free(chunk.ptr);
358: if (status != INVALID_ARG)
359: { /* try again only if passphrase invalid */
360: break;
361: }
362: }
363: enumerator->destroy(enumerator);
364: return status;
365: }
366:
367: /**
368: * Check if a blob looks like an ASN1 SEQUENCE or SET with BER indefinite length
369: */
370: static bool is_ber_indefinite_length(chunk_t blob)
371: {
372: if (blob.len >= 4)
373: {
374: switch (blob.ptr[0])
375: {
376: case ASN1_SEQUENCE:
377: case ASN1_SET:
378: /* BER indefinite length uses 0x80, and is terminated with
379: * end-of-content using 0x00,0x00 */
380: return blob.ptr[1] == 0x80 &&
381: blob.ptr[blob.len - 2] == 0 &&
382: blob.ptr[blob.len - 1] == 0;
383: default:
384: break;
385: }
386: }
387: return FALSE;
388: }
389:
390: /**
391: * load the credential from a blob
392: */
393: static void *load_from_blob(chunk_t blob, credential_type_t type, int subtype,
394: identification_t *subject, x509_flag_t flags)
395: {
396: void *cred = NULL;
397: bool pgp = FALSE;
398:
399: blob = chunk_clone(blob);
400: if (!is_ber_indefinite_length(blob) && !is_asn1(blob))
401: {
402: if (pem_to_bin(&blob, &pgp) != SUCCESS)
403: {
404: chunk_clear(&blob);
405: return NULL;
406: }
407: if (pgp && type == CRED_PRIVATE_KEY)
408: {
409: /* PGP encoded keys are parsed with a KEY_ANY key type, as it
410: * can contain any type of key. However, ipsec.secrets uses
411: * RSA for PGP keys, which is actually wrong. */
412: subtype = KEY_ANY;
413: }
414: }
415: /* if CERT_ANY is given, ASN1 encoded blob is handled as X509 */
416: if (type == CRED_CERTIFICATE && subtype == CERT_ANY)
417: {
418: subtype = pgp ? CERT_GPG : CERT_X509;
419: }
420: if (type == CRED_CERTIFICATE && subtype == CERT_TRUSTED_PUBKEY && subject)
421: {
422: cred = lib->creds->create(lib->creds, type, subtype,
423: BUILD_BLOB_ASN1_DER, blob, BUILD_SUBJECT, subject,
424: BUILD_END);
425: }
426: else
427: {
428: cred = lib->creds->create(lib->creds, type, subtype,
429: pgp ? BUILD_BLOB_PGP : BUILD_BLOB_ASN1_DER, blob,
430: flags ? BUILD_X509_FLAG : BUILD_END,
431: flags, BUILD_END);
432: }
433: chunk_clear(&blob);
434: return cred;
435: }
436:
437: /**
438: * load the credential from a file
439: */
440: static void *load_from_file(char *file, credential_type_t type, int subtype,
441: identification_t *subject, x509_flag_t flags)
442: {
443: void *cred;
444: chunk_t *chunk;
445:
446: chunk = chunk_map(file, FALSE);
447: if (!chunk)
448: {
449: DBG1(DBG_LIB, " opening '%s' failed: %s", file, strerror(errno));
450: return NULL;
451: }
452: cred = load_from_blob(*chunk, type, subtype, subject, flags);
453: chunk_unmap(chunk);
454: return cred;
455: }
456:
457: /**
458: * Load all kind of PEM encoded credentials.
459: */
460: static void *pem_load(credential_type_t type, int subtype, va_list args)
461: {
462: char *file = NULL;
463: chunk_t pem = chunk_empty;
464: identification_t *subject = NULL;
465: int flags = 0;
466:
467: while (TRUE)
468: {
469: switch (va_arg(args, builder_part_t))
470: {
471: case BUILD_FROM_FILE:
472: file = va_arg(args, char*);
473: continue;
474: case BUILD_BLOB:
475: case BUILD_BLOB_PEM:
476: pem = va_arg(args, chunk_t);
477: continue;
478: case BUILD_SUBJECT:
479: subject = va_arg(args, identification_t*);
480: continue;
481: case BUILD_X509_FLAG:
482: flags = va_arg(args, int);
483: continue;
484: case BUILD_END:
485: break;
486: default:
487: return NULL;
488: }
489: break;
490: }
491:
492: if (pem.len)
493: {
494: return load_from_blob(pem, type, subtype, subject, flags);
495: }
496: if (file)
497: {
498: return load_from_file(file, type, subtype, subject, flags);
499: }
500: return NULL;
501: }
502:
503: /**
504: * Private key PEM loader.
505: */
506: private_key_t *pem_private_key_load(key_type_t type, va_list args)
507: {
508: return pem_load(CRED_PRIVATE_KEY, type, args);
509: }
510:
511: /**
512: * Public key PEM loader.
513: */
514: public_key_t *pem_public_key_load(key_type_t type, va_list args)
515: {
516: return pem_load(CRED_PUBLIC_KEY, type, args);
517: }
518:
519: /**
520: * Certificate PEM loader.
521: */
522: certificate_t *pem_certificate_load(certificate_type_t type, va_list args)
523: {
524: return pem_load(CRED_CERTIFICATE, type, args);
525: }
526:
527: /**
528: * Container PEM loader.
529: */
530: container_t *pem_container_load(container_type_t type, va_list args)
531: {
532: return pem_load(CRED_CONTAINER, type, args);
533: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>