Annotation of embedaddon/strongswan/src/swanctl/commands/load_creds.c, revision 1.1

1.1     ! misho       1: /*
        !             2:  * Copyright (C) 2016-2017 Tobias Brunner
        !             3:  * Copyright (C) 2015 Andreas Steffen
        !             4:  * HSR Hochschule fuer Technik Rapperswil
        !             5:  *
        !             6:  * Copyright (C) 2014 Martin Willi
        !             7:  * Copyright (C) 2014 revosec AG
        !             8:  *
        !             9:  * This program is free software; you can redistribute it and/or modify it
        !            10:  * under the terms of the GNU General Public License as published by the
        !            11:  * Free Software Foundation; either version 2 of the License, or (at your
        !            12:  * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
        !            13:  *
        !            14:  * This program is distributed in the hope that it will be useful, but
        !            15:  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
        !            16:  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
        !            17:  * for more details.
        !            18:  */
        !            19: 
        !            20: #define _GNU_SOURCE
        !            21: #include <stdio.h>
        !            22: #include <errno.h>
        !            23: #include <unistd.h>
        !            24: #include <sys/stat.h>
        !            25: 
        !            26: #include "command.h"
        !            27: #include "swanctl.h"
        !            28: #include "load_creds.h"
        !            29: 
        !            30: #include <credentials/sets/mem_cred.h>
        !            31: #include <credentials/sets/callback_cred.h>
        !            32: #include <credentials/containers/pkcs12.h>
        !            33: #include <collections/hashtable.h>
        !            34: 
        !            35: #include <vici_cert_info.h>
        !            36: 
        !            37: /**
        !            38:  * Context used to track loaded secrets
        !            39:  */
        !            40: typedef struct {
        !            41:        /** vici connection */
        !            42:        vici_conn_t *conn;
        !            43:        /** format options */
        !            44:        command_format_options_t format;
        !            45:        /** read setting */
        !            46:        settings_t *cfg;
        !            47:        /** don't prompt user for password */
        !            48:        bool noprompt;
        !            49:        /** list of key ids of loaded private keys */
        !            50:        hashtable_t *keys;
        !            51:        /** list of unique ids of loaded shared keys */
        !            52:        hashtable_t *shared;
        !            53: } load_ctx_t;
        !            54: 
        !            55: /**
        !            56:  * Load a single certificate over vici
        !            57:  */
        !            58: static bool load_cert(load_ctx_t *ctx, char *dir, certificate_type_t type,
        !            59:                                          x509_flag_t flag, chunk_t data)
        !            60: {
        !            61:        vici_req_t *req;
        !            62:        vici_res_t *res;
        !            63:        bool ret = TRUE;
        !            64: 
        !            65:        req = vici_begin("load-cert");
        !            66: 
        !            67:        vici_add_key_valuef(req, "type", "%N", certificate_type_names, type);
        !            68:        if (type == CERT_X509)
        !            69:        {
        !            70:                vici_add_key_valuef(req, "flag", "%N", x509_flag_names, flag);
        !            71:        }
        !            72:        vici_add_key_value(req, "data", data.ptr, data.len);
        !            73: 
        !            74:        res = vici_submit(req, ctx->conn);
        !            75:        if (!res)
        !            76:        {
        !            77:                fprintf(stderr, "load-cert request failed: %s\n", strerror(errno));
        !            78:                return FALSE;
        !            79:        }
        !            80:        if (ctx->format & COMMAND_FORMAT_RAW)
        !            81:        {
        !            82:                vici_dump(res, "load-cert reply", ctx->format & COMMAND_FORMAT_PRETTY,
        !            83:                                  stdout);
        !            84:        }
        !            85:        else if (!streq(vici_find_str(res, "no", "success"), "yes"))
        !            86:        {
        !            87:                fprintf(stderr, "loading '%s' failed: %s\n",
        !            88:                                dir, vici_find_str(res, "", "errmsg"));
        !            89:                ret = FALSE;
        !            90:        }
        !            91:        else
        !            92:        {
        !            93:                printf("loaded certificate from '%s'\n", dir);
        !            94:        }
        !            95:        vici_free_res(res);
        !            96:        return ret;
        !            97: }
        !            98: 
        !            99: /**
        !           100:  * Load certificates from a directory
        !           101:  */
        !           102: static void load_certs(load_ctx_t *ctx, char *type_str, char *dir)
        !           103: {
        !           104:        enumerator_t *enumerator;
        !           105:        certificate_type_t type;
        !           106:        x509_flag_t flag;
        !           107:        struct stat st;
        !           108:        chunk_t *map;
        !           109:        char *path, buf[PATH_MAX];
        !           110: 
        !           111:        vici_cert_info_from_str(type_str, &type, &flag);
        !           112: 
        !           113:        snprintf(buf, sizeof(buf), "%s%s%s", swanctl_dir, DIRECTORY_SEPARATOR, dir);
        !           114:        dir = buf;
        !           115: 
        !           116:        enumerator = enumerator_create_directory(dir);
        !           117:        if (enumerator)
        !           118:        {
        !           119:                while (enumerator->enumerate(enumerator, NULL, &path, &st))
        !           120:                {
        !           121:                        if (S_ISREG(st.st_mode))
        !           122:                        {
        !           123:                                map = chunk_map(path, FALSE);
        !           124:                                if (map)
        !           125:                                {
        !           126:                                        load_cert(ctx, path, type, flag, *map);
        !           127:                                        chunk_unmap(map);
        !           128:                                }
        !           129:                                else
        !           130:                                {
        !           131:                                        fprintf(stderr, "mapping '%s' failed: %s, skipped\n",
        !           132:                                                        path, strerror(errno));
        !           133:                                }
        !           134:                        }
        !           135:                }
        !           136:                enumerator->destroy(enumerator);
        !           137:        }
        !           138: }
        !           139: 
        !           140: /**
        !           141:  * Load a single private key over vici
        !           142:  */
        !           143: static bool load_key(load_ctx_t *ctx, char *dir, char *type, chunk_t data)
        !           144: {
        !           145:        vici_req_t *req;
        !           146:        vici_res_t *res;
        !           147:        bool ret = TRUE;
        !           148:        char *id;
        !           149: 
        !           150:        req = vici_begin("load-key");
        !           151: 
        !           152:        if (streq(type, "private") ||
        !           153:                streq(type, "pkcs8"))
        !           154:        {       /* as used by vici */
        !           155:                vici_add_key_valuef(req, "type", "any");
        !           156:        }
        !           157:        else
        !           158:        {
        !           159:                vici_add_key_valuef(req, "type", "%s", type);
        !           160:        }
        !           161:        vici_add_key_value(req, "data", data.ptr, data.len);
        !           162: 
        !           163:        res = vici_submit(req, ctx->conn);
        !           164:        if (!res)
        !           165:        {
        !           166:                fprintf(stderr, "load-key request failed: %s\n", strerror(errno));
        !           167:                return FALSE;
        !           168:        }
        !           169:        if (ctx->format & COMMAND_FORMAT_RAW)
        !           170:        {
        !           171:                vici_dump(res, "load-key reply", ctx->format & COMMAND_FORMAT_PRETTY,
        !           172:                                  stdout);
        !           173:        }
        !           174:        else if (!streq(vici_find_str(res, "no", "success"), "yes"))
        !           175:        {
        !           176:                fprintf(stderr, "loading '%s' failed: %s\n",
        !           177:                                dir, vici_find_str(res, "", "errmsg"));
        !           178:                ret = FALSE;
        !           179:        }
        !           180:        else
        !           181:        {
        !           182:                printf("loaded %s key from '%s'\n", type, dir);
        !           183:                id = vici_find_str(res, "", "id");
        !           184:                free(ctx->keys->remove(ctx->keys, id));
        !           185:        }
        !           186:        vici_free_res(res);
        !           187:        return ret;
        !           188: }
        !           189: 
        !           190: /**
        !           191:  * Load a private key of any type to vici
        !           192:  */
        !           193: static bool load_key_anytype(load_ctx_t *ctx, char *path,
        !           194:                                                         private_key_t *private)
        !           195: {
        !           196:        bool loaded = FALSE;
        !           197:        chunk_t encoding;
        !           198: 
        !           199:        if (!private->get_encoding(private, PRIVKEY_ASN1_DER, &encoding))
        !           200:        {
        !           201:                fprintf(stderr, "encoding private key from '%s' failed\n", path);
        !           202:                return FALSE;
        !           203:        }
        !           204:        switch (private->get_type(private))
        !           205:        {
        !           206:                case KEY_RSA:
        !           207:                        loaded = load_key(ctx, path, "rsa", encoding);
        !           208:                        break;
        !           209:                case KEY_ECDSA:
        !           210:                        loaded = load_key(ctx, path, "ecdsa", encoding);
        !           211:                        break;
        !           212:                case KEY_BLISS:
        !           213:                        loaded = load_key(ctx, path, "bliss", encoding);
        !           214:                        break;
        !           215:                default:
        !           216:                        fprintf(stderr, "unsupported key type in '%s'\n", path);
        !           217:                        break;
        !           218:        }
        !           219:        chunk_clear(&encoding);
        !           220:        return loaded;
        !           221: }
        !           222: 
        !           223: /**
        !           224:  * Data passed to password callback
        !           225:  */
        !           226: typedef struct {
        !           227:        char prompt[128];
        !           228:        mem_cred_t *cache;
        !           229: } cb_data_t;
        !           230: 
        !           231: /**
        !           232:  * Callback function to prompt for private key passwords
        !           233:  */
        !           234: CALLBACK(password_cb, shared_key_t*,
        !           235:        cb_data_t *data, shared_key_type_t type,
        !           236:        identification_t *me, identification_t *other,
        !           237:        id_match_t *match_me, id_match_t *match_other)
        !           238: {
        !           239:        shared_key_t *shared;
        !           240:        char *pwd = NULL;
        !           241: 
        !           242:        if (type != SHARED_PRIVATE_KEY_PASS)
        !           243:        {
        !           244:                return NULL;
        !           245:        }
        !           246: #ifdef HAVE_GETPASS
        !           247:        pwd = getpass(data->prompt);
        !           248: #endif
        !           249:        if (!pwd || strlen(pwd) == 0)
        !           250:        {
        !           251:                return NULL;
        !           252:        }
        !           253:        if (match_me)
        !           254:        {
        !           255:                *match_me = ID_MATCH_PERFECT;
        !           256:        }
        !           257:        if (match_other)
        !           258:        {
        !           259:                *match_other = ID_MATCH_PERFECT;
        !           260:        }
        !           261:        shared = shared_key_create(type, chunk_clone(chunk_from_str(pwd)));
        !           262:        /* cache secret if it is required more than once (PKCS#12) */
        !           263:        data->cache->add_shared(data->cache, shared, NULL);
        !           264:        return shared->get_ref(shared);
        !           265: }
        !           266: 
        !           267: /**
        !           268:  * Determine credential type and subtype from a type string
        !           269:  */
        !           270: static bool determine_credtype(char *type, credential_type_t *credtype,
        !           271:                                                           int *subtype)
        !           272: {
        !           273:        struct {
        !           274:                char *type;
        !           275:                credential_type_t credtype;
        !           276:                int subtype;
        !           277:        } map[] = {
        !           278:                { "private",            CRED_PRIVATE_KEY,               KEY_ANY,                        },
        !           279:                { "pkcs8",                      CRED_PRIVATE_KEY,               KEY_ANY,                        },
        !           280:                { "rsa",                        CRED_PRIVATE_KEY,               KEY_RSA,                        },
        !           281:                { "ecdsa",                      CRED_PRIVATE_KEY,               KEY_ECDSA,                      },
        !           282:                { "bliss",                      CRED_PRIVATE_KEY,               KEY_BLISS,                      },
        !           283:                { "pkcs12",                     CRED_CONTAINER,                 CONTAINER_PKCS12,       },
        !           284:        };
        !           285:        int i;
        !           286: 
        !           287:        for (i = 0; i < countof(map); i++)
        !           288:        {
        !           289:                if (streq(map[i].type, type))
        !           290:                {
        !           291:                        *credtype = map[i].credtype;
        !           292:                        *subtype = map[i].subtype;
        !           293:                        return TRUE;
        !           294:                }
        !           295:        }
        !           296:        return FALSE;
        !           297: }
        !           298: 
        !           299: /**
        !           300:  * Try to parse a potentially encrypted credential using password prompt
        !           301:  */
        !           302: static void* decrypt(char *name, char *type, chunk_t encoding)
        !           303: {
        !           304:        credential_type_t credtype;
        !           305:        int subtype;
        !           306:        void *cred;
        !           307:        callback_cred_t *cb;
        !           308:        cb_data_t data;
        !           309: 
        !           310:        if (!determine_credtype(type, &credtype, &subtype))
        !           311:        {
        !           312:                return NULL;
        !           313:        }
        !           314: 
        !           315:        snprintf(data.prompt, sizeof(data.prompt), "Password for %s file '%s': ",
        !           316:                         type, name);
        !           317: 
        !           318:        data.cache = mem_cred_create();
        !           319:        lib->credmgr->add_set(lib->credmgr, &data.cache->set);
        !           320:        cb = callback_cred_create_shared(password_cb, &data);
        !           321:        lib->credmgr->add_set(lib->credmgr, &cb->set);
        !           322: 
        !           323:        cred = lib->creds->create(lib->creds, credtype, subtype,
        !           324:                                                          BUILD_BLOB_PEM, encoding, BUILD_END);
        !           325: 
        !           326:        lib->credmgr->remove_set(lib->credmgr, &data.cache->set);
        !           327:        data.cache->destroy(data.cache);
        !           328:        lib->credmgr->remove_set(lib->credmgr, &cb->set);
        !           329:        cb->destroy(cb);
        !           330: 
        !           331:        return cred;
        !           332: }
        !           333: 
        !           334: /**
        !           335:  * Try to parse a potentially encrypted credential using configured secret
        !           336:  */
        !           337: static void* decrypt_with_config(load_ctx_t *ctx, char *name, char *type,
        !           338:                                                                 chunk_t encoding)
        !           339: {
        !           340:        credential_type_t credtype;
        !           341:        int subtype;
        !           342:        enumerator_t *enumerator, *secrets;
        !           343:        char *section, *key, *value, *file;
        !           344:        shared_key_t *shared;
        !           345:        void *cred = NULL;
        !           346:        mem_cred_t *mem = NULL;
        !           347: 
        !           348:        if (!determine_credtype(type, &credtype, &subtype))
        !           349:        {
        !           350:                return NULL;
        !           351:        }
        !           352: 
        !           353:        /* load all secrets for this key type */
        !           354:        enumerator = ctx->cfg->create_section_enumerator(ctx->cfg, "secrets");
        !           355:        while (enumerator->enumerate(enumerator, &section))
        !           356:        {
        !           357:                if (strpfx(section, type))
        !           358:                {
        !           359:                        file = ctx->cfg->get_str(ctx->cfg, "secrets.%s.file", NULL, section);
        !           360:                        if (file && strcaseeq(file, name))
        !           361:                        {
        !           362:                                secrets = ctx->cfg->create_key_value_enumerator(ctx->cfg,
        !           363:                                                                                                                "secrets.%s", section);
        !           364:                                while (secrets->enumerate(secrets, &key, &value))
        !           365:                                {
        !           366:                                        if (strpfx(key, "secret"))
        !           367:                                        {
        !           368:                                                if (!mem)
        !           369:                                                {
        !           370:                                                        mem = mem_cred_create();
        !           371:                                                }
        !           372:                                                shared = shared_key_create(SHARED_PRIVATE_KEY_PASS,
        !           373:                                                                                        chunk_clone(chunk_from_str(value)));
        !           374:                                                mem->add_shared(mem, shared, NULL);
        !           375:                                        }
        !           376:                                }
        !           377:                                secrets->destroy(secrets);
        !           378:                        }
        !           379:                }
        !           380:        }
        !           381:        enumerator->destroy(enumerator);
        !           382: 
        !           383:        if (mem)
        !           384:        {
        !           385:                lib->credmgr->add_local_set(lib->credmgr, &mem->set, FALSE);
        !           386: 
        !           387:                cred = lib->creds->create(lib->creds, credtype, subtype,
        !           388:                                                                  BUILD_BLOB_PEM, encoding, BUILD_END);
        !           389: 
        !           390:                lib->credmgr->remove_local_set(lib->credmgr, &mem->set);
        !           391: 
        !           392:                if (!cred)
        !           393:                {
        !           394:                        fprintf(stderr, "configured decryption secret for '%s' invalid\n",
        !           395:                                        name);
        !           396:                }
        !           397: 
        !           398:                mem->destroy(mem);
        !           399:        }
        !           400: 
        !           401:        return cred;
        !           402: }
        !           403: 
        !           404: /**
        !           405:  * Try to decrypt and load a private key
        !           406:  */
        !           407: static bool load_encrypted_key(load_ctx_t *ctx, char *rel, char *path,
        !           408:                                                           char *type, chunk_t data)
        !           409: {
        !           410:        private_key_t *private;
        !           411:        bool loaded = FALSE;
        !           412: 
        !           413:        private = decrypt_with_config(ctx, rel, type, data);
        !           414:        if (!private && !ctx->noprompt)
        !           415:        {
        !           416:                private = decrypt(rel, type, data);
        !           417:        }
        !           418:        if (private)
        !           419:        {
        !           420:                loaded = load_key_anytype(ctx, path, private);
        !           421:                private->destroy(private);
        !           422:        }
        !           423:        return loaded;
        !           424: }
        !           425: 
        !           426: /**
        !           427:  * Load private keys from a directory
        !           428:  */
        !           429: static void load_keys(load_ctx_t *ctx, char *type, char *dir)
        !           430: {
        !           431:        enumerator_t *enumerator;
        !           432:        struct stat st;
        !           433:        chunk_t *map;
        !           434:        char *path, *rel, buf[PATH_MAX];
        !           435: 
        !           436:        snprintf(buf, sizeof(buf), "%s%s%s", swanctl_dir, DIRECTORY_SEPARATOR, dir);
        !           437:        dir = buf;
        !           438: 
        !           439:        enumerator = enumerator_create_directory(dir);
        !           440:        if (enumerator)
        !           441:        {
        !           442:                while (enumerator->enumerate(enumerator, &rel, &path, &st))
        !           443:                {
        !           444:                        if (S_ISREG(st.st_mode))
        !           445:                        {
        !           446:                                map = chunk_map(path, FALSE);
        !           447:                                if (map)
        !           448:                                {
        !           449:                                        if (!load_encrypted_key(ctx, rel, path, type, *map))
        !           450:                                        {
        !           451:                                                load_key(ctx, path, type, *map);
        !           452:                                        }
        !           453:                                        chunk_unmap(map);
        !           454:                                }
        !           455:                                else
        !           456:                                {
        !           457:                                        fprintf(stderr, "mapping '%s' failed: %s, skipped\n",
        !           458:                                                        path, strerror(errno));
        !           459:                                }
        !           460:                        }
        !           461:                }
        !           462:                enumerator->destroy(enumerator);
        !           463:        }
        !           464: }
        !           465: 
        !           466: /**
        !           467:  * Load credentials from a PKCS#12 container over vici
        !           468:  */
        !           469: static bool load_pkcs12(load_ctx_t *ctx, char *path, pkcs12_t *p12)
        !           470: {
        !           471:        enumerator_t *enumerator;
        !           472:        certificate_t *cert;
        !           473:        private_key_t *private;
        !           474:        chunk_t encoding;
        !           475:        bool loaded = TRUE;
        !           476: 
        !           477:        enumerator = p12->create_cert_enumerator(p12);
        !           478:        while (loaded && enumerator->enumerate(enumerator, &cert))
        !           479:        {
        !           480:                loaded = FALSE;
        !           481:                if (cert->get_encoding(cert, CERT_ASN1_DER, &encoding))
        !           482:                {
        !           483:                        loaded = load_cert(ctx, path, CERT_X509, X509_NONE, encoding);
        !           484:                        if (loaded)
        !           485:                        {
        !           486:                                fprintf(stderr, "  %Y\n", cert->get_subject(cert));
        !           487:                        }
        !           488:                        free(encoding.ptr);
        !           489:                }
        !           490:                else
        !           491:                {
        !           492:                        fprintf(stderr, "encoding certificate from '%s' failed\n", path);
        !           493:                }
        !           494:        }
        !           495:        enumerator->destroy(enumerator);
        !           496: 
        !           497:        enumerator = p12->create_key_enumerator(p12);
        !           498:        while (loaded && enumerator->enumerate(enumerator, &private))
        !           499:        {
        !           500:                loaded = load_key_anytype(ctx, path, private);
        !           501:        }
        !           502:        enumerator->destroy(enumerator);
        !           503: 
        !           504:        return loaded;
        !           505: }
        !           506: 
        !           507: /**
        !           508:  * Try to decrypt and load credentials from a container
        !           509:  */
        !           510: static bool load_encrypted_container(load_ctx_t *ctx, char *rel, char *path,
        !           511:                                                                         char *type, chunk_t data)
        !           512: {
        !           513:        container_t *container;
        !           514:        bool loaded = FALSE;
        !           515: 
        !           516:        container = decrypt_with_config(ctx, rel, type, data);
        !           517:        if (!container && !ctx->noprompt)
        !           518:        {
        !           519:                container = decrypt(rel, type, data);
        !           520:        }
        !           521:        if (container)
        !           522:        {
        !           523:                switch (container->get_type(container))
        !           524:                {
        !           525:                        case CONTAINER_PKCS12:
        !           526:                                loaded = load_pkcs12(ctx, path, (pkcs12_t*)container);
        !           527:                                break;
        !           528:                        default:
        !           529:                                break;
        !           530:                }
        !           531:                container->destroy(container);
        !           532:        }
        !           533:        return loaded;
        !           534: }
        !           535: 
        !           536: /**
        !           537:  * Load credential containers from a directory
        !           538:  */
        !           539: static void load_containers(load_ctx_t *ctx, char *type, char *dir)
        !           540: {
        !           541:        enumerator_t *enumerator;
        !           542:        struct stat st;
        !           543:        chunk_t *map;
        !           544:        char *path, *rel, buf[PATH_MAX];
        !           545: 
        !           546:        snprintf(buf, sizeof(buf), "%s%s%s", swanctl_dir, DIRECTORY_SEPARATOR, dir);
        !           547:        dir = buf;
        !           548: 
        !           549:        enumerator = enumerator_create_directory(dir);
        !           550:        if (enumerator)
        !           551:        {
        !           552:                while (enumerator->enumerate(enumerator, &rel, &path, &st))
        !           553:                {
        !           554:                        if (S_ISREG(st.st_mode))
        !           555:                        {
        !           556:                                map = chunk_map(path, FALSE);
        !           557:                                if (map)
        !           558:                                {
        !           559:                                        load_encrypted_container(ctx, rel, path, type, *map);
        !           560:                                        chunk_unmap(map);
        !           561:                                }
        !           562:                                else
        !           563:                                {
        !           564:                                        fprintf(stderr, "mapping '%s' failed: %s, skipped\n",
        !           565:                                                        path, strerror(errno));
        !           566:                                }
        !           567:                        }
        !           568:                }
        !           569:                enumerator->destroy(enumerator);
        !           570:        }
        !           571: }
        !           572: 
        !           573: /**
        !           574:  * Load a single private key on a token over vici
        !           575:  */
        !           576: static bool load_token(load_ctx_t *ctx, char *name, char *pin)
        !           577: {
        !           578:        vici_req_t *req;
        !           579:        vici_res_t *res;
        !           580:        enumerator_t *enumerator;
        !           581:        char *key, *value, *id;
        !           582:        bool ret = TRUE;
        !           583: 
        !           584:        req = vici_begin("load-token");
        !           585: 
        !           586:        enumerator = ctx->cfg->create_key_value_enumerator(ctx->cfg, "secrets.%s",
        !           587:                                                                                                           name);
        !           588:        while (enumerator->enumerate(enumerator, &key, &value))
        !           589:        {
        !           590:                vici_add_key_valuef(req, key, "%s", value);
        !           591:        }
        !           592:        enumerator->destroy(enumerator);
        !           593: 
        !           594:        if (pin)
        !           595:        {
        !           596:                vici_add_key_valuef(req, "pin", "%s", pin);
        !           597:        }
        !           598:        res = vici_submit(req, ctx->conn);
        !           599:        if (!res)
        !           600:        {
        !           601:                fprintf(stderr, "load-token request failed: %s\n", strerror(errno));
        !           602:                return FALSE;
        !           603:        }
        !           604:        if (ctx->format & COMMAND_FORMAT_RAW)
        !           605:        {
        !           606:                vici_dump(res, "load-token reply", ctx->format & COMMAND_FORMAT_PRETTY,
        !           607:                                  stdout);
        !           608:        }
        !           609:        else if (!streq(vici_find_str(res, "no", "success"), "yes"))
        !           610:        {
        !           611:                fprintf(stderr, "loading '%s' failed: %s\n",
        !           612:                                name, vici_find_str(res, "", "errmsg"));
        !           613:                ret = FALSE;
        !           614:        }
        !           615:        else
        !           616:        {
        !           617:                id = vici_find_str(res, "", "id");
        !           618:                printf("loaded key %s from token [keyid: %s]\n", name, id);
        !           619:                free(ctx->keys->remove(ctx->keys, id));
        !           620:        }
        !           621:        vici_free_res(res);
        !           622:        return ret;
        !           623: }
        !           624: 
        !           625: /**
        !           626:  * Load keys from tokens
        !           627:  */
        !           628: static void load_tokens(load_ctx_t *ctx)
        !           629: {
        !           630:        enumerator_t *enumerator;
        !           631:        char *section, *pin = NULL, prompt[128];
        !           632: 
        !           633:        enumerator = ctx->cfg->create_section_enumerator(ctx->cfg, "secrets");
        !           634:        while (enumerator->enumerate(enumerator, &section))
        !           635:        {
        !           636:                if (strpfx(section, "token"))
        !           637:                {
        !           638:                        if (!ctx->noprompt &&
        !           639:                                !ctx->cfg->get_str(ctx->cfg, "secrets.%s.pin", NULL, section))
        !           640:                        {
        !           641: #ifdef HAVE_GETPASS
        !           642:                                snprintf(prompt, sizeof(prompt), "PIN for %s: ", section);
        !           643:                                pin = strdupnull(getpass(prompt));
        !           644: #endif
        !           645:                        }
        !           646:                        load_token(ctx, section, pin);
        !           647:                        if (pin)
        !           648:                        {
        !           649:                                memwipe(pin, strlen(pin));
        !           650:                                free(pin);
        !           651:                                pin = NULL;
        !           652:                        }
        !           653:                }
        !           654:        }
        !           655:        enumerator->destroy(enumerator);
        !           656: }
        !           657: 
        !           658: 
        !           659: 
        !           660: /**
        !           661:  * Load a single secret over VICI
        !           662:  */
        !           663: static bool load_secret(load_ctx_t *ctx, char *section)
        !           664: {
        !           665:        enumerator_t *enumerator;
        !           666:        vici_req_t *req;
        !           667:        vici_res_t *res;
        !           668:        chunk_t data;
        !           669:        char *key, *value, *type = NULL;
        !           670:        bool ret = TRUE;
        !           671:        int i;
        !           672:        char *types[] = {
        !           673:                "eap",
        !           674:                "xauth",
        !           675:                "ntlm",
        !           676:                "ike",
        !           677:                "ppk",
        !           678:                "private",
        !           679:                "rsa",
        !           680:                "ecdsa",
        !           681:                "bliss",
        !           682:                "pkcs8",
        !           683:                "pkcs12",
        !           684:                "token",
        !           685:        };
        !           686: 
        !           687:        for (i = 0; i < countof(types); i++)
        !           688:        {
        !           689:                if (strpfx(section, types[i]))
        !           690:                {
        !           691:                        type = types[i];
        !           692:                        break;
        !           693:                }
        !           694:        }
        !           695:        if (!type)
        !           696:        {
        !           697:                fprintf(stderr, "ignoring unsupported secret '%s'\n", section);
        !           698:                return FALSE;
        !           699:        }
        !           700:        if (!streq(type, "eap") && !streq(type, "xauth") && !streq(type, "ntlm") &&
        !           701:                !streq(type, "ike") && !streq(type, "ppk"))
        !           702:        {       /* skip non-shared secrets */
        !           703:                return TRUE;
        !           704:        }
        !           705: 
        !           706:        value = ctx->cfg->get_str(ctx->cfg, "secrets.%s.secret", NULL, section);
        !           707:        if (!value)
        !           708:        {
        !           709:                fprintf(stderr, "missing secret in '%s', ignored\n", section);
        !           710:                return FALSE;
        !           711:        }
        !           712:        if (strcasepfx(value, "0x"))
        !           713:        {
        !           714:                data = chunk_from_hex(chunk_from_str(value + 2), NULL);
        !           715:        }
        !           716:        else if (strcasepfx(value, "0s"))
        !           717:        {
        !           718:                data = chunk_from_base64(chunk_from_str(value + 2), NULL);
        !           719:        }
        !           720:        else
        !           721:        {
        !           722:                data = chunk_clone(chunk_from_str(value));
        !           723:        }
        !           724: 
        !           725:        req = vici_begin("load-shared");
        !           726: 
        !           727:        vici_add_key_valuef(req, "id", "%s", section);
        !           728:        vici_add_key_valuef(req, "type", "%s", type);
        !           729:        vici_add_key_value(req, "data", data.ptr, data.len);
        !           730:        chunk_clear(&data);
        !           731: 
        !           732:        vici_begin_list(req, "owners");
        !           733:        enumerator = ctx->cfg->create_key_value_enumerator(ctx->cfg, "secrets.%s",
        !           734:                                                                                                           section);
        !           735:        while (enumerator->enumerate(enumerator, &key, &value))
        !           736:        {
        !           737:                if (strpfx(key, "id"))
        !           738:                {
        !           739:                        vici_add_list_itemf(req, "%s", value);
        !           740:                }
        !           741:        }
        !           742:        enumerator->destroy(enumerator);
        !           743:        vici_end_list(req);
        !           744: 
        !           745:        res = vici_submit(req, ctx->conn);
        !           746:        if (!res)
        !           747:        {
        !           748:                fprintf(stderr, "load-shared request failed: %s\n", strerror(errno));
        !           749:                return FALSE;
        !           750:        }
        !           751:        if (ctx->format & COMMAND_FORMAT_RAW)
        !           752:        {
        !           753:                vici_dump(res, "load-shared reply", ctx->format & COMMAND_FORMAT_PRETTY,
        !           754:                                  stdout);
        !           755:        }
        !           756:        else if (!streq(vici_find_str(res, "no", "success"), "yes"))
        !           757:        {
        !           758:                fprintf(stderr, "loading shared secret failed: %s\n",
        !           759:                                vici_find_str(res, "", "errmsg"));
        !           760:                ret = FALSE;
        !           761:        }
        !           762:        else
        !           763:        {
        !           764:                printf("loaded %s secret '%s'\n", type, section);
        !           765:        }
        !           766:        if (ret)
        !           767:        {
        !           768:                free(ctx->shared->remove(ctx->shared, section));
        !           769:        }
        !           770:        vici_free_res(res);
        !           771:        return ret;
        !           772: }
        !           773: 
        !           774: CALLBACK(get_id, int,
        !           775:        hashtable_t *ht, vici_res_t *res, char *name, void *value, int len)
        !           776: {
        !           777:        if (streq(name, "keys"))
        !           778:        {
        !           779:                char *str;
        !           780: 
        !           781:                if (asprintf(&str, "%.*s", len, value) != -1)
        !           782:                {
        !           783:                        free(ht->put(ht, str, str));
        !           784:                }
        !           785:        }
        !           786:        return 0;
        !           787: }
        !           788: 
        !           789: /**
        !           790:  * Get a list of currently loaded private and shared keys
        !           791:  */
        !           792: static void get_creds(load_ctx_t *ctx)
        !           793: {
        !           794:        vici_res_t *res;
        !           795: 
        !           796:        res = vici_submit(vici_begin("get-keys"), ctx->conn);
        !           797:        if (res)
        !           798:        {
        !           799:                if (ctx->format & COMMAND_FORMAT_RAW)
        !           800:                {
        !           801:                        vici_dump(res, "get-keys reply", ctx->format & COMMAND_FORMAT_PRETTY,
        !           802:                                          stdout);
        !           803:                }
        !           804:                vici_parse_cb(res, NULL, NULL, get_id, ctx->keys);
        !           805:                vici_free_res(res);
        !           806:        }
        !           807:        res = vici_submit(vici_begin("get-shared"), ctx->conn);
        !           808:        if (res)
        !           809:        {
        !           810:                if (ctx->format & COMMAND_FORMAT_RAW)
        !           811:                {
        !           812:                        vici_dump(res, "get-shared reply", ctx->format & COMMAND_FORMAT_PRETTY,
        !           813:                                          stdout);
        !           814:                }
        !           815:                vici_parse_cb(res, NULL, NULL, get_id, ctx->shared);
        !           816:                vici_free_res(res);
        !           817:        }
        !           818: }
        !           819: 
        !           820: /**
        !           821:  * Remove a given key
        !           822:  */
        !           823: static bool unload_key(load_ctx_t *ctx, char *command, char *id)
        !           824: {
        !           825:        vici_req_t *req;
        !           826:        vici_res_t *res;
        !           827:        char buf[BUF_LEN];
        !           828:        bool ret = TRUE;
        !           829: 
        !           830:        req = vici_begin(command);
        !           831: 
        !           832:        vici_add_key_valuef(req, "id", "%s", id);
        !           833: 
        !           834:        res = vici_submit(req, ctx->conn);
        !           835:        if (!res)
        !           836:        {
        !           837:                fprintf(stderr, "%s request failed: %s\n", command, strerror(errno));
        !           838:                return FALSE;
        !           839:        }
        !           840:        if (ctx->format & COMMAND_FORMAT_RAW)
        !           841:        {
        !           842:                snprintf(buf, sizeof(buf), "%s reply", command);
        !           843:                vici_dump(res, buf, ctx->format & COMMAND_FORMAT_PRETTY, stdout);
        !           844:        }
        !           845:        else if (!streq(vici_find_str(res, "no", "success"), "yes"))
        !           846:        {
        !           847:                fprintf(stderr, "unloading key '%s' failed: %s\n",
        !           848:                                id, vici_find_str(res, "", "errmsg"));
        !           849:                ret = FALSE;
        !           850:        }
        !           851:        vici_free_res(res);
        !           852:        return ret;
        !           853: }
        !           854: 
        !           855: /**
        !           856:  * Remove all keys in the given hashtable using the given command
        !           857:  */
        !           858: static void unload_keys(load_ctx_t *ctx, hashtable_t *ht, char *command)
        !           859: {
        !           860:        enumerator_t *enumerator;
        !           861:        char *id;
        !           862: 
        !           863:        enumerator = ht->create_enumerator(ht);
        !           864:        while (enumerator->enumerate(enumerator, &id, NULL))
        !           865:        {
        !           866:                unload_key(ctx, command, id);
        !           867:        }
        !           868:        enumerator->destroy(enumerator);
        !           869: }
        !           870: 
        !           871: /**
        !           872:  * Clear all currently loaded credentials
        !           873:  */
        !           874: static bool clear_creds(vici_conn_t *conn, command_format_options_t format)
        !           875: {
        !           876:        vici_res_t *res;
        !           877: 
        !           878:        res = vici_submit(vici_begin("clear-creds"), conn);
        !           879:        if (!res)
        !           880:        {
        !           881:                fprintf(stderr, "clear-creds request failed: %s\n", strerror(errno));
        !           882:                return FALSE;
        !           883:        }
        !           884:        if (format & COMMAND_FORMAT_RAW)
        !           885:        {
        !           886:                vici_dump(res, "clear-creds reply", format & COMMAND_FORMAT_PRETTY,
        !           887:                                  stdout);
        !           888:        }
        !           889:        vici_free_res(res);
        !           890:        return TRUE;
        !           891: }
        !           892: 
        !           893: /**
        !           894:  * See header.
        !           895:  */
        !           896: int load_creds_cfg(vici_conn_t *conn, command_format_options_t format,
        !           897:                                   settings_t *cfg, bool clear, bool noprompt)
        !           898: {
        !           899:        enumerator_t *enumerator;
        !           900:        char *section;
        !           901:        load_ctx_t ctx = {
        !           902:                .conn = conn,
        !           903:                .format = format,
        !           904:                .noprompt = noprompt,
        !           905:                .cfg = cfg,
        !           906:                .keys = hashtable_create(hashtable_hash_str, hashtable_equals_str, 8),
        !           907:                .shared = hashtable_create(hashtable_hash_str, hashtable_equals_str, 8),
        !           908:        };
        !           909: 
        !           910:        if (clear)
        !           911:        {
        !           912:                if (!clear_creds(conn, format))
        !           913:                {
        !           914:                        return ECONNREFUSED;
        !           915:                }
        !           916:        }
        !           917: 
        !           918:        get_creds(&ctx);
        !           919: 
        !           920:        load_certs(&ctx, "x509",     SWANCTL_X509DIR);
        !           921:        load_certs(&ctx, "x509ca",   SWANCTL_X509CADIR);
        !           922:        load_certs(&ctx, "x509ocsp", SWANCTL_X509OCSPDIR);
        !           923:        load_certs(&ctx, "x509aa",   SWANCTL_X509AADIR);
        !           924:        load_certs(&ctx, "x509ac",   SWANCTL_X509ACDIR);
        !           925:        load_certs(&ctx, "x509crl",  SWANCTL_X509CRLDIR);
        !           926:        load_certs(&ctx, "pubkey",   SWANCTL_PUBKEYDIR);
        !           927: 
        !           928:        load_keys(&ctx, "private", SWANCTL_PRIVATEDIR);
        !           929:        load_keys(&ctx, "rsa",     SWANCTL_RSADIR);
        !           930:        load_keys(&ctx, "ecdsa",   SWANCTL_ECDSADIR);
        !           931:        load_keys(&ctx, "bliss",   SWANCTL_BLISSDIR);
        !           932:        load_keys(&ctx, "pkcs8",   SWANCTL_PKCS8DIR);
        !           933: 
        !           934:        load_containers(&ctx, "pkcs12", SWANCTL_PKCS12DIR);
        !           935: 
        !           936:        load_tokens(&ctx);
        !           937: 
        !           938:        enumerator = cfg->create_section_enumerator(cfg, "secrets");
        !           939:        while (enumerator->enumerate(enumerator, &section))
        !           940:        {
        !           941:                load_secret(&ctx, section);
        !           942:        }
        !           943:        enumerator->destroy(enumerator);
        !           944: 
        !           945:        unload_keys(&ctx, ctx.keys, "unload-key");
        !           946:        unload_keys(&ctx, ctx.shared, "unload-shared");
        !           947: 
        !           948:        ctx.keys->destroy_function(ctx.keys, (void*)free);
        !           949:        ctx.shared->destroy_function(ctx.shared, (void*)free);
        !           950:        return 0;
        !           951: }
        !           952: 
        !           953: static int load_creds(vici_conn_t *conn)
        !           954: {
        !           955:        bool clear = FALSE, noprompt = FALSE;
        !           956:        command_format_options_t format = COMMAND_FORMAT_NONE;
        !           957:        settings_t *cfg;
        !           958:        char *arg, *file = NULL;
        !           959:        int ret;
        !           960: 
        !           961:        while (TRUE)
        !           962:        {
        !           963:                switch (command_getopt(&arg))
        !           964:                {
        !           965:                        case 'h':
        !           966:                                return command_usage(NULL);
        !           967:                        case 'c':
        !           968:                                clear = TRUE;
        !           969:                                continue;
        !           970:                        case 'n':
        !           971:                                noprompt = TRUE;
        !           972:                                continue;
        !           973:                        case 'P':
        !           974:                                format |= COMMAND_FORMAT_PRETTY;
        !           975:                                /* fall through to raw */
        !           976:                        case 'r':
        !           977:                                format |= COMMAND_FORMAT_RAW;
        !           978:                                continue;
        !           979:                        case 'f':
        !           980:                                file = arg;
        !           981:                                continue;
        !           982:                        case EOF:
        !           983:                                break;
        !           984:                        default:
        !           985:                                return command_usage("invalid --load-creds option");
        !           986:                }
        !           987:                break;
        !           988:        }
        !           989: 
        !           990:        cfg = load_swanctl_conf(file);
        !           991:        if (!cfg)
        !           992:        {
        !           993:                return EINVAL;
        !           994:        }
        !           995: 
        !           996:        ret = load_creds_cfg(conn, format, cfg, clear, noprompt);
        !           997: 
        !           998:        cfg->destroy(cfg);
        !           999: 
        !          1000:        return ret;
        !          1001: }
        !          1002: 
        !          1003: /**
        !          1004:  * Register the command.
        !          1005:  */
        !          1006: static void __attribute__ ((constructor))reg()
        !          1007: {
        !          1008:        command_register((command_t) {
        !          1009:                load_creds, 's', "load-creds", "(re-)load credentials",
        !          1010:                {"[--raw|--pretty] [--clear] [--noprompt]"},
        !          1011:                {
        !          1012:                        {"help",                'h', 0, "show usage information"},
        !          1013:                        {"clear",               'c', 0, "clear previously loaded credentials"},
        !          1014:                        {"noprompt",    'n', 0, "do not prompt for passwords"},
        !          1015:                        {"raw",                 'r', 0, "dump raw response message"},
        !          1016:                        {"pretty",              'P', 0, "dump raw response message in pretty print"},
        !          1017:                        {"file",                'f', 1, "custom path to swanctl.conf"},
        !          1018:                }
        !          1019:        });
        !          1020: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>