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>