Path: blob/main/crypto/heimdal/lib/hx509/ks_file.c
34907 views
/*1* Copyright (c) 2005 - 2007 Kungliga Tekniska Högskolan2* (Royal Institute of Technology, Stockholm, Sweden).3* All rights reserved.4*5* Redistribution and use in source and binary forms, with or without6* modification, are permitted provided that the following conditions7* are met:8*9* 1. Redistributions of source code must retain the above copyright10* notice, this list of conditions and the following disclaimer.11*12* 2. Redistributions in binary form must reproduce the above copyright13* notice, this list of conditions and the following disclaimer in the14* documentation and/or other materials provided with the distribution.15*16* 3. Neither the name of the Institute nor the names of its contributors17* may be used to endorse or promote products derived from this software18* without specific prior written permission.19*20* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND21* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE22* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE23* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE24* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL25* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS26* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)27* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT28* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY29* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF30* SUCH DAMAGE.31*/3233#include "hx_locl.h"3435typedef enum { USE_PEM, USE_DER } outformat;3637struct ks_file {38hx509_certs certs;39char *fn;40outformat format;41};4243/*44*45*/4647static int48parse_certificate(hx509_context context, const char *fn,49struct hx509_collector *c,50const hx509_pem_header *headers,51const void *data, size_t len,52const AlgorithmIdentifier *ai)53{54hx509_cert cert;55int ret;5657ret = hx509_cert_init_data(context, data, len, &cert);58if (ret)59return ret;6061ret = _hx509_collector_certs_add(context, c, cert);62hx509_cert_free(cert);63return ret;64}6566static int67try_decrypt(hx509_context context,68struct hx509_collector *collector,69const AlgorithmIdentifier *alg,70const EVP_CIPHER *c,71const void *ivdata,72const void *password,73size_t passwordlen,74const void *cipher,75size_t len)76{77heim_octet_string clear;78size_t keylen;79void *key;80int ret;8182keylen = EVP_CIPHER_key_length(c);8384key = malloc(keylen);85if (key == NULL) {86hx509_clear_error_string(context);87return ENOMEM;88}8990ret = EVP_BytesToKey(c, EVP_md5(), ivdata,91password, passwordlen,921, key, NULL);93if (ret <= 0) {94hx509_set_error_string(context, 0, HX509_CRYPTO_INTERNAL_ERROR,95"Failed to do string2key for private key");96return HX509_CRYPTO_INTERNAL_ERROR;97}9899clear.data = malloc(len);100if (clear.data == NULL) {101hx509_set_error_string(context, 0, ENOMEM,102"Out of memory to decrypt for private key");103ret = ENOMEM;104goto out;105}106clear.length = len;107108{109EVP_CIPHER_CTX *ctx;110111ctx = EVP_CIPHER_CTX_new();112if (ctx == NULL) {113hx509_set_error_string(context, 0, ENOMEM,114"Out of memory to decrypt for private key");115ret = ENOMEM;116goto out;117}118EVP_CipherInit_ex(ctx, c, NULL, key, ivdata, 0);119EVP_Cipher(ctx, clear.data, cipher, len);120EVP_CIPHER_CTX_free(ctx);121}122123ret = _hx509_collector_private_key_add(context,124collector,125alg,126NULL,127&clear,128NULL);129130memset(clear.data, 0, clear.length);131out:132free(clear.data);133memset(key, 0, keylen);134free(key);135return ret;136}137138static int139parse_pkcs8_private_key(hx509_context context, const char *fn,140struct hx509_collector *c,141const hx509_pem_header *headers,142const void *data, size_t length,143const AlgorithmIdentifier *ai)144{145PKCS8PrivateKeyInfo ki;146heim_octet_string keydata;147148int ret;149150ret = decode_PKCS8PrivateKeyInfo(data, length, &ki, NULL);151if (ret)152return ret;153154keydata.data = rk_UNCONST(data);155keydata.length = length;156157ret = _hx509_collector_private_key_add(context,158c,159&ki.privateKeyAlgorithm,160NULL,161&ki.privateKey,162&keydata);163free_PKCS8PrivateKeyInfo(&ki);164return ret;165}166167static int168parse_pem_private_key(hx509_context context, const char *fn,169struct hx509_collector *c,170const hx509_pem_header *headers,171const void *data, size_t len,172const AlgorithmIdentifier *ai)173{174int ret = 0;175const char *enc;176177enc = hx509_pem_find_header(headers, "Proc-Type");178if (enc) {179const char *dek;180char *type, *iv;181ssize_t ssize, size;182void *ivdata;183const EVP_CIPHER *cipher;184const struct _hx509_password *pw;185hx509_lock lock;186int decrypted = 0;187size_t i;188189lock = _hx509_collector_get_lock(c);190if (lock == NULL) {191hx509_set_error_string(context, 0, HX509_ALG_NOT_SUPP,192"Failed to get password for "193"password protected file %s", fn);194return HX509_ALG_NOT_SUPP;195}196197if (strcmp(enc, "4,ENCRYPTED") != 0) {198hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED,199"Private key encrypted in unknown method %s "200"in file",201enc, fn);202hx509_clear_error_string(context);203return HX509_PARSING_KEY_FAILED;204}205206dek = hx509_pem_find_header(headers, "DEK-Info");207if (dek == NULL) {208hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED,209"Encrypted private key missing DEK-Info");210return HX509_PARSING_KEY_FAILED;211}212213type = strdup(dek);214if (type == NULL) {215hx509_clear_error_string(context);216return ENOMEM;217}218219iv = strchr(type, ',');220if (iv == NULL) {221free(type);222hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED,223"IV missing");224return HX509_PARSING_KEY_FAILED;225}226227*iv++ = '\0';228229size = strlen(iv);230ivdata = malloc(size);231if (ivdata == NULL) {232hx509_clear_error_string(context);233free(type);234return ENOMEM;235}236237cipher = EVP_get_cipherbyname(type);238if (cipher == NULL) {239free(ivdata);240hx509_set_error_string(context, 0, HX509_ALG_NOT_SUPP,241"Private key encrypted with "242"unsupported cipher: %s",243type);244free(type);245return HX509_ALG_NOT_SUPP;246}247248#define PKCS5_SALT_LEN 8249250ssize = hex_decode(iv, ivdata, size);251free(type);252type = NULL;253iv = NULL;254255if (ssize < 0 || ssize < PKCS5_SALT_LEN || ssize < EVP_CIPHER_iv_length(cipher)) {256free(ivdata);257hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED,258"Salt have wrong length in "259"private key file");260return HX509_PARSING_KEY_FAILED;261}262263pw = _hx509_lock_get_passwords(lock);264if (pw != NULL) {265const void *password;266size_t passwordlen;267268for (i = 0; i < pw->len; i++) {269password = pw->val[i];270passwordlen = strlen(password);271272ret = try_decrypt(context, c, ai, cipher, ivdata,273password, passwordlen, data, len);274if (ret == 0) {275decrypted = 1;276break;277}278}279}280if (!decrypted) {281hx509_prompt prompt;282char password[128];283284memset(&prompt, 0, sizeof(prompt));285286prompt.prompt = "Password for keyfile: ";287prompt.type = HX509_PROMPT_TYPE_PASSWORD;288prompt.reply.data = password;289prompt.reply.length = sizeof(password);290291ret = hx509_lock_prompt(lock, &prompt);292if (ret == 0)293ret = try_decrypt(context, c, ai, cipher, ivdata, password,294strlen(password), data, len);295/* XXX add password to lock password collection ? */296memset(password, 0, sizeof(password));297}298free(ivdata);299300} else {301heim_octet_string keydata;302303keydata.data = rk_UNCONST(data);304keydata.length = len;305306ret = _hx509_collector_private_key_add(context, c, ai, NULL,307&keydata, NULL);308}309310return ret;311}312313314struct pem_formats {315const char *name;316int (*func)(hx509_context, const char *, struct hx509_collector *,317const hx509_pem_header *, const void *, size_t,318const AlgorithmIdentifier *);319const AlgorithmIdentifier *(*ai)(void);320} formats[] = {321{ "CERTIFICATE", parse_certificate, NULL },322{ "PRIVATE KEY", parse_pkcs8_private_key, NULL },323{ "RSA PRIVATE KEY", parse_pem_private_key, hx509_signature_rsa },324{ "EC PRIVATE KEY", parse_pem_private_key, hx509_signature_ecPublicKey }325};326327328struct pem_ctx {329int flags;330struct hx509_collector *c;331};332333static int334pem_func(hx509_context context, const char *type,335const hx509_pem_header *header,336const void *data, size_t len, void *ctx)337{338struct pem_ctx *pem_ctx = (struct pem_ctx*)ctx;339int ret = 0;340size_t j;341342for (j = 0; j < sizeof(formats)/sizeof(formats[0]); j++) {343const char *q = formats[j].name;344if (strcasecmp(type, q) == 0) {345const AlgorithmIdentifier *ai = NULL;346if (formats[j].ai != NULL)347ai = (*formats[j].ai)();348349ret = (*formats[j].func)(context, NULL, pem_ctx->c,350header, data, len, ai);351if (ret && (pem_ctx->flags & HX509_CERTS_UNPROTECT_ALL)) {352hx509_set_error_string(context, HX509_ERROR_APPEND, ret,353"Failed parseing PEM format %s", type);354return ret;355}356break;357}358}359if (j == sizeof(formats)/sizeof(formats[0])) {360ret = HX509_UNSUPPORTED_OPERATION;361hx509_set_error_string(context, 0, ret,362"Found no matching PEM format for %s", type);363return ret;364}365return 0;366}367368/*369*370*/371372static int373file_init_common(hx509_context context,374hx509_certs certs, void **data, int flags,375const char *residue, hx509_lock lock, outformat format)376{377char *p, *pnext;378struct ks_file *ksf = NULL;379hx509_private_key *keys = NULL;380int ret;381struct pem_ctx pem_ctx;382383pem_ctx.flags = flags;384pem_ctx.c = NULL;385386*data = NULL;387388if (lock == NULL)389lock = _hx509_empty_lock;390391ksf = calloc(1, sizeof(*ksf));392if (ksf == NULL) {393hx509_clear_error_string(context);394return ENOMEM;395}396ksf->format = format;397398ksf->fn = strdup(residue);399if (ksf->fn == NULL) {400hx509_clear_error_string(context);401ret = ENOMEM;402goto out;403}404405/*406* XXX this is broken, the function should parse the file before407* overwriting it408*/409410if (flags & HX509_CERTS_CREATE) {411ret = hx509_certs_init(context, "MEMORY:ks-file-create",4120, lock, &ksf->certs);413if (ret)414goto out;415*data = ksf;416return 0;417}418419ret = _hx509_collector_alloc(context, lock, &pem_ctx.c);420if (ret)421goto out;422423for (p = ksf->fn; p != NULL; p = pnext) {424FILE *f;425426pnext = strchr(p, ',');427if (pnext)428*pnext++ = '\0';429430431if ((f = fopen(p, "r")) == NULL) {432ret = ENOENT;433hx509_set_error_string(context, 0, ret,434"Failed to open PEM file \"%s\": %s",435p, strerror(errno));436goto out;437}438rk_cloexec_file(f);439440ret = hx509_pem_read(context, f, pem_func, &pem_ctx);441fclose(f);442if (ret != 0 && ret != HX509_PARSING_KEY_FAILED)443goto out;444else if (ret == HX509_PARSING_KEY_FAILED) {445size_t length;446void *ptr;447size_t i;448449ret = rk_undumpdata(p, &ptr, &length);450if (ret) {451hx509_clear_error_string(context);452goto out;453}454455for (i = 0; i < sizeof(formats)/sizeof(formats[0]); i++) {456const AlgorithmIdentifier *ai = NULL;457if (formats[i].ai != NULL)458ai = (*formats[i].ai)();459460ret = (*formats[i].func)(context, p, pem_ctx.c, NULL, ptr, length, ai);461if (ret == 0)462break;463}464rk_xfree(ptr);465if (ret) {466hx509_clear_error_string(context);467goto out;468}469}470}471472ret = _hx509_collector_collect_certs(context, pem_ctx.c, &ksf->certs);473if (ret)474goto out;475476ret = _hx509_collector_collect_private_keys(context, pem_ctx.c, &keys);477if (ret == 0) {478int i;479480for (i = 0; keys[i]; i++)481_hx509_certs_keys_add(context, ksf->certs, keys[i]);482_hx509_certs_keys_free(context, keys);483}484485out:486if (ret == 0)487*data = ksf;488else {489if (ksf->fn)490free(ksf->fn);491free(ksf);492}493if (pem_ctx.c)494_hx509_collector_free(pem_ctx.c);495496return ret;497}498499static int500file_init_pem(hx509_context context,501hx509_certs certs, void **data, int flags,502const char *residue, hx509_lock lock)503{504return file_init_common(context, certs, data, flags, residue, lock, USE_PEM);505}506507static int508file_init_der(hx509_context context,509hx509_certs certs, void **data, int flags,510const char *residue, hx509_lock lock)511{512return file_init_common(context, certs, data, flags, residue, lock, USE_DER);513}514515static int516file_free(hx509_certs certs, void *data)517{518struct ks_file *ksf = data;519hx509_certs_free(&ksf->certs);520free(ksf->fn);521free(ksf);522return 0;523}524525struct store_ctx {526FILE *f;527outformat format;528};529530static int531store_func(hx509_context context, void *ctx, hx509_cert c)532{533struct store_ctx *sc = ctx;534heim_octet_string data;535int ret = 0;536537ret = hx509_cert_binary(context, c, &data);538if (ret)539return ret;540541switch (sc->format) {542case USE_DER:543fwrite(data.data, data.length, 1, sc->f);544free(data.data);545break;546case USE_PEM:547hx509_pem_write(context, "CERTIFICATE", NULL, sc->f,548data.data, data.length);549free(data.data);550if (_hx509_cert_private_key_exportable(c)) {551hx509_private_key key = _hx509_cert_private_key(c);552ret = _hx509_private_key_export(context, key,553HX509_KEY_FORMAT_DER, &data);554if (ret)555break;556ret = hx509_pem_write(context, _hx509_private_pem_name(key), NULL,557sc->f, data.data, data.length);558free(data.data);559}560break;561}562563return ret;564}565566static int567file_store(hx509_context context,568hx509_certs certs, void *data, int flags, hx509_lock lock)569{570struct ks_file *ksf = data;571struct store_ctx sc;572int ret;573574sc.f = fopen(ksf->fn, "w");575if (sc.f == NULL) {576hx509_set_error_string(context, 0, ENOENT,577"Failed to open file %s for writing");578return ENOENT;579}580rk_cloexec_file(sc.f);581sc.format = ksf->format;582583ret = hx509_certs_iter_f(context, ksf->certs, store_func, &sc);584fclose(sc.f);585return ret;586}587588static int589file_add(hx509_context context, hx509_certs certs, void *data, hx509_cert c)590{591struct ks_file *ksf = data;592return hx509_certs_add(context, ksf->certs, c);593}594595static int596file_iter_start(hx509_context context,597hx509_certs certs, void *data, void **cursor)598{599struct ks_file *ksf = data;600return hx509_certs_start_seq(context, ksf->certs, cursor);601}602603static int604file_iter(hx509_context context,605hx509_certs certs, void *data, void *iter, hx509_cert *cert)606{607struct ks_file *ksf = data;608return hx509_certs_next_cert(context, ksf->certs, iter, cert);609}610611static int612file_iter_end(hx509_context context,613hx509_certs certs,614void *data,615void *cursor)616{617struct ks_file *ksf = data;618return hx509_certs_end_seq(context, ksf->certs, cursor);619}620621static int622file_getkeys(hx509_context context,623hx509_certs certs,624void *data,625hx509_private_key **keys)626{627struct ks_file *ksf = data;628return _hx509_certs_keys_get(context, ksf->certs, keys);629}630631static int632file_addkey(hx509_context context,633hx509_certs certs,634void *data,635hx509_private_key key)636{637struct ks_file *ksf = data;638return _hx509_certs_keys_add(context, ksf->certs, key);639}640641static struct hx509_keyset_ops keyset_file = {642"FILE",6430,644file_init_pem,645file_store,646file_free,647file_add,648NULL,649file_iter_start,650file_iter,651file_iter_end,652NULL,653file_getkeys,654file_addkey655};656657static struct hx509_keyset_ops keyset_pemfile = {658"PEM-FILE",6590,660file_init_pem,661file_store,662file_free,663file_add,664NULL,665file_iter_start,666file_iter,667file_iter_end,668NULL,669file_getkeys,670file_addkey671};672673static struct hx509_keyset_ops keyset_derfile = {674"DER-FILE",6750,676file_init_der,677file_store,678file_free,679file_add,680NULL,681file_iter_start,682file_iter,683file_iter_end,684NULL,685file_getkeys,686file_addkey687};688689690void691_hx509_ks_file_register(hx509_context context)692{693_hx509_ks_register(context, &keyset_file);694_hx509_ks_register(context, &keyset_pemfile);695_hx509_ks_register(context, &keyset_derfile);696}697698699