Annotation of embedaddon/strongswan/src/libstrongswan/plugins/pem/pem_builder.c, revision 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>