Annotation of embedaddon/strongswan/src/libcharon/plugins/certexpire/certexpire_export.c, revision 1.1

1.1     ! misho       1: /*
        !             2:  * Copyright (C) 2011 Martin Willi
        !             3:  * Copyright (C) 2011 revosec AG
        !             4:  *
        !             5:  * This program is free software; you can redistribute it and/or modify it
        !             6:  * under the terms of the GNU General Public License as published by the
        !             7:  * Free Software Foundation; either version 2 of the License, or (at your
        !             8:  * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
        !             9:  *
        !            10:  * This program is distributed in the hope that it will be useful, but
        !            11:  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
        !            12:  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
        !            13:  * for more details.
        !            14:  */
        !            15: 
        !            16: #include "certexpire_export.h"
        !            17: 
        !            18: #include "certexpire_cron.h"
        !            19: 
        !            20: #include <time.h>
        !            21: #include <limits.h>
        !            22: #include <errno.h>
        !            23: 
        !            24: #include <utils/debug.h>
        !            25: #include <daemon.h>
        !            26: #include <collections/hashtable.h>
        !            27: #include <threading/mutex.h>
        !            28: #include <credentials/certificates/x509.h>
        !            29: 
        !            30: typedef struct private_certexpire_export_t private_certexpire_export_t;
        !            31: 
        !            32: /**
        !            33:  * Private data of an certexpire_export_t object.
        !            34:  */
        !            35: struct private_certexpire_export_t {
        !            36: 
        !            37:        /**
        !            38:         * Public certexpire_export_t interface.
        !            39:         */
        !            40:        certexpire_export_t public;
        !            41: 
        !            42:        /**
        !            43:         * hashtable caching local trustchains, mapping entry_t => entry_t
        !            44:         */
        !            45:        hashtable_t *local;
        !            46: 
        !            47:        /**
        !            48:         * hashtable caching remote trustchains, mapping entry_t => entry_t
        !            49:         */
        !            50:        hashtable_t *remote;
        !            51: 
        !            52:        /**
        !            53:         * Mutex to lock hashtables
        !            54:         */
        !            55:        mutex_t *mutex;
        !            56: 
        !            57:        /**
        !            58:         * Cronjob for export
        !            59:         */
        !            60:        certexpire_cron_t *cron;
        !            61: 
        !            62:        /**
        !            63:         * strftime() format to generate local CSV file
        !            64:         */
        !            65:        char *local_path;
        !            66: 
        !            67:        /**
        !            68:         * strftime() format to generate remote CSV file
        !            69:         */
        !            70:        char *remote_path;
        !            71: 
        !            72:        /**
        !            73:         * stftime() format of the exported expiration date
        !            74:         */
        !            75:        char *format;
        !            76: 
        !            77:        /**
        !            78:         * CSV field separator
        !            79:         */
        !            80:        char *separator;
        !            81: 
        !            82:        /**
        !            83:         * TRUE to use fixed field count, CA at end
        !            84:         */
        !            85:        bool fixed_fields;
        !            86: 
        !            87:        /**
        !            88:         * String to use in empty fields, if using fixed_fields
        !            89:         */
        !            90:        char *empty_string;
        !            91: 
        !            92:        /**
        !            93:         * Force export of all trustchains we have a private key for
        !            94:         */
        !            95:        bool force;
        !            96: };
        !            97: 
        !            98: /**
        !            99:  * Maximum number of expiration dates we store (for subject + IM CAs + CA)
        !           100:  */
        !           101: #define MAX_TRUSTCHAIN_LENGTH 7
        !           102: 
        !           103: /**
        !           104:  * Hashtable entry
        !           105:  */
        !           106: typedef struct {
        !           107:        /** certificate subject as subjectAltName or CN of a DN */
        !           108:        char id[128];
        !           109:        /** list of expiration dates, 0 if no certificate */
        !           110:        time_t expire[MAX_TRUSTCHAIN_LENGTH];
        !           111: } entry_t;
        !           112: 
        !           113: /**
        !           114:  * Hashtable hash function
        !           115:  */
        !           116: static u_int hash(entry_t *key)
        !           117: {
        !           118:        return chunk_hash(chunk_create(key->id, strlen(key->id)));
        !           119: }
        !           120: 
        !           121: /**
        !           122:  * Hashtable equals function
        !           123:  */
        !           124: static bool equals(entry_t *a, entry_t *b)
        !           125: {
        !           126:        return streq(a->id, b->id);
        !           127: }
        !           128: 
        !           129: /**
        !           130:  * Export a single trustchain to a path
        !           131:  */
        !           132: static void export_csv(private_certexpire_export_t *this, char *path,
        !           133:                                           hashtable_t *chains)
        !           134: {
        !           135:        enumerator_t *enumerator;
        !           136:        char buf[PATH_MAX];
        !           137:        entry_t *entry;
        !           138:        FILE *file;
        !           139:        struct tm tm;
        !           140:        time_t t;
        !           141:        int i;
        !           142: 
        !           143:        t = time(NULL);
        !           144:        localtime_r(&t, &tm);
        !           145: 
        !           146:        strftime(buf, sizeof(buf), path, &tm);
        !           147:        file = fopen(buf, "a");
        !           148:        if (file)
        !           149:        {
        !           150:                DBG1(DBG_CFG, "exporting expiration dates of %d trustchain%s to '%s'",
        !           151:                         chains->get_count(chains),
        !           152:                         chains->get_count(chains) == 1 ? "" : "s", buf);
        !           153:                this->mutex->lock(this->mutex);
        !           154:                enumerator = chains->create_enumerator(chains);
        !           155:                while (enumerator->enumerate(enumerator, NULL, &entry))
        !           156:                {
        !           157:                        fprintf(file, "%s%s", entry->id, this->separator);
        !           158:                        for (i = 0; i < MAX_TRUSTCHAIN_LENGTH; i++)
        !           159:                        {
        !           160:                                if (entry->expire[i])
        !           161:                                {
        !           162:                                        localtime_r(&entry->expire[i], &tm);
        !           163:                                        strftime(buf, sizeof(buf), this->format, &tm);
        !           164:                                        fprintf(file, "%s", buf);
        !           165:                                }
        !           166:                                if (i == MAX_TRUSTCHAIN_LENGTH - 1)
        !           167:                                {
        !           168:                                        fprintf(file, "\n");
        !           169:                                }
        !           170:                                else if (entry->expire[i])
        !           171:                                {
        !           172:                                        fprintf(file, "%s", this->separator);
        !           173:                                }
        !           174:                                else if (this->fixed_fields)
        !           175:                                {
        !           176:                                        fprintf(file, "%s%s", this->empty_string, this->separator);
        !           177:                                }
        !           178:                        }
        !           179:                        chains->remove_at(chains, enumerator);
        !           180:                        free(entry);
        !           181:                }
        !           182:                enumerator->destroy(enumerator);
        !           183:                this->mutex->unlock(this->mutex);
        !           184:                fclose(file);
        !           185:        }
        !           186:        else
        !           187:        {
        !           188:                DBG1(DBG_CFG, "opening CSV file '%s' failed: %s", buf, strerror(errno));
        !           189:        }
        !           190: }
        !           191: 
        !           192: METHOD(certexpire_export_t, add, void,
        !           193:        private_certexpire_export_t *this, linked_list_t *trustchain, bool local)
        !           194: {
        !           195:        enumerator_t *enumerator;
        !           196:        certificate_t *cert;
        !           197:        int count;
        !           198: 
        !           199:        /* don't store expiration dates if no path configured */
        !           200:        if (local)
        !           201:        {
        !           202:                if (!this->local_path)
        !           203:                {
        !           204:                        return;
        !           205:                }
        !           206:        }
        !           207:        else
        !           208:        {
        !           209:                if (!this->remote_path)
        !           210:                {
        !           211:                        return;
        !           212:                }
        !           213:        }
        !           214: 
        !           215:        count = min(trustchain->get_count(trustchain), MAX_TRUSTCHAIN_LENGTH) - 1;
        !           216: 
        !           217:        enumerator = trustchain->create_enumerator(trustchain);
        !           218:        /* get subject cert */
        !           219:        if (enumerator->enumerate(enumerator, &cert))
        !           220:        {
        !           221:                identification_t *id;
        !           222:                entry_t *entry;
        !           223:                int i;
        !           224: 
        !           225:                INIT(entry);
        !           226: 
        !           227:                /* prefer FQDN subjectAltName... */
        !           228:                if (cert->get_type(cert) == CERT_X509)
        !           229:                {
        !           230:                        x509_t *x509 = (x509_t*)cert;
        !           231:                        enumerator_t *sans;
        !           232: 
        !           233:                        sans = x509->create_subjectAltName_enumerator(x509);
        !           234:                        while (sans->enumerate(sans, &id))
        !           235:                        {
        !           236:                                if (id->get_type(id) == ID_FQDN)
        !           237:                                {
        !           238:                                        snprintf(entry->id, sizeof(entry->id), "%Y", id);
        !           239:                                        break;
        !           240:                                }
        !           241:                        }
        !           242:                        sans->destroy(sans);
        !           243:                }
        !           244:                /* fallback to CN of DN */
        !           245:                if (!entry->id[0])
        !           246:                {
        !           247:                        enumerator_t *parts;
        !           248:                        id_part_t part;
        !           249:                        chunk_t data;
        !           250: 
        !           251:                        id = cert->get_subject(cert);
        !           252:                        parts = id->create_part_enumerator(id);
        !           253:                        while (parts->enumerate(parts, &part, &data))
        !           254:                        {
        !           255:                                if (part == ID_PART_RDN_CN)
        !           256:                                {
        !           257:                                        snprintf(entry->id, sizeof(entry->id), "%.*s",
        !           258:                                                         (int)data.len, data.ptr);
        !           259:                                        break;
        !           260:                                }
        !           261:                        }
        !           262:                        parts->destroy(parts);
        !           263:                }
        !           264:                /* no usable identity? skip */
        !           265:                if (!entry->id[0])
        !           266:                {
        !           267:                        enumerator->destroy(enumerator);
        !           268:                        free(entry);
        !           269:                        return;
        !           270:                }
        !           271: 
        !           272:                /* get intermediate CA expiration dates */
        !           273:                cert->get_validity(cert, NULL, NULL, &entry->expire[0]);
        !           274:                for (i = 1; i < count && enumerator->enumerate(enumerator, &cert); i++)
        !           275:                {
        !           276:                        cert->get_validity(cert, NULL, NULL, &entry->expire[i]);
        !           277:                }
        !           278:                /* get CA expiration date, as last array entry */
        !           279:                if (enumerator->enumerate(enumerator, &cert))
        !           280:                {
        !           281:                        cert->get_validity(cert, NULL, NULL,
        !           282:                                                           &entry->expire[MAX_TRUSTCHAIN_LENGTH - 1]);
        !           283:                }
        !           284:                this->mutex->lock(this->mutex);
        !           285:                if (local)
        !           286:                {
        !           287:                        entry = this->local->put(this->local, entry, entry);
        !           288:                }
        !           289:                else
        !           290:                {
        !           291:                        entry = this->remote->put(this->remote, entry, entry);
        !           292:                }
        !           293:                this->mutex->unlock(this->mutex);
        !           294:                if (entry)
        !           295:                {
        !           296:                        free(entry);
        !           297:                }
        !           298:                if (!this->cron)
        !           299:                {       /* export directly if no cron job defined */
        !           300:                        if (local)
        !           301:                        {
        !           302:                                export_csv(this, this->local_path, this->local);
        !           303:                        }
        !           304:                        else
        !           305:                        {
        !           306:                                export_csv(this, this->remote_path, this->remote);
        !           307:                        }
        !           308:                }
        !           309:        }
        !           310:        enumerator->destroy(enumerator);
        !           311: }
        !           312: 
        !           313: /**
        !           314:  * Add trustchains we have a private key for to the list
        !           315:  */
        !           316: static void add_local_certs(private_certexpire_export_t *this)
        !           317: {
        !           318:        enumerator_t *enumerator;
        !           319:        certificate_t *cert;
        !           320: 
        !           321:        enumerator = lib->credmgr->create_cert_enumerator(lib->credmgr,
        !           322:                                                                                        CERT_X509, KEY_ANY, NULL, FALSE);
        !           323:        while (enumerator->enumerate(enumerator, &cert))
        !           324:        {
        !           325:                linked_list_t *trustchain;
        !           326:                private_key_t *private;
        !           327:                public_key_t *public;
        !           328:                identification_t *keyid;
        !           329:                chunk_t chunk;
        !           330:                x509_t *x509 = (x509_t*)cert;
        !           331: 
        !           332:                trustchain = linked_list_create();
        !           333: 
        !           334:                public = cert->get_public_key(cert);
        !           335:                if (public)
        !           336:                {
        !           337:                        if (public->get_fingerprint(public, KEYID_PUBKEY_INFO_SHA1, &chunk))
        !           338:                        {
        !           339:                                keyid = identification_create_from_encoding(ID_KEY_ID, chunk);
        !           340:                                private = lib->credmgr->get_private(lib->credmgr,
        !           341:                                                                                public->get_type(public), keyid, NULL);
        !           342:                                keyid->destroy(keyid);
        !           343:                                if (private)
        !           344:                                {
        !           345:                                        trustchain->insert_last(trustchain, cert->get_ref(cert));
        !           346: 
        !           347:                                        while (!(x509->get_flags(x509) & X509_SELF_SIGNED))
        !           348:                                        {
        !           349:                                                cert = lib->credmgr->get_cert(lib->credmgr, CERT_X509,
        !           350:                                                                                KEY_ANY, cert->get_issuer(cert), FALSE);
        !           351:                                                if (!cert)
        !           352:                                                {
        !           353:                                                        break;
        !           354:                                                }
        !           355:                                                x509 = (x509_t*)cert;
        !           356:                                                trustchain->insert_last(trustchain, cert);
        !           357:                                        }
        !           358:                                        private->destroy(private);
        !           359:                                }
        !           360:                        }
        !           361:                        public->destroy(public);
        !           362:                }
        !           363:                add(this, trustchain, TRUE);
        !           364:                trustchain->destroy_offset(trustchain, offsetof(certificate_t, destroy));
        !           365:        }
        !           366:        enumerator->destroy(enumerator);
        !           367: }
        !           368: 
        !           369: /**
        !           370:  * Export cached trustchain expiration dates to CSV files
        !           371:  */
        !           372: static void cron_export(private_certexpire_export_t *this)
        !           373: {
        !           374:        if (this->local_path)
        !           375:        {
        !           376:                if (this->force)
        !           377:                {
        !           378:                        add_local_certs(this);
        !           379:                }
        !           380:                export_csv(this, this->local_path, this->local);
        !           381:        }
        !           382:        if (this->remote_path)
        !           383:        {
        !           384:                export_csv(this, this->remote_path, this->remote);
        !           385:        }
        !           386: }
        !           387: 
        !           388: METHOD(certexpire_export_t, destroy, void,
        !           389:        private_certexpire_export_t *this)
        !           390: {
        !           391:        entry_t *key, *value;
        !           392:        enumerator_t *enumerator;
        !           393: 
        !           394:        enumerator = this->local->create_enumerator(this->local);
        !           395:        while (enumerator->enumerate(enumerator, &key, &value))
        !           396:        {
        !           397:                free(value);
        !           398:        }
        !           399:        enumerator->destroy(enumerator);
        !           400:        enumerator = this->remote->create_enumerator(this->remote);
        !           401:        while (enumerator->enumerate(enumerator, &key, &value))
        !           402:        {
        !           403:                free(value);
        !           404:        }
        !           405:        enumerator->destroy(enumerator);
        !           406: 
        !           407:        this->local->destroy(this->local);
        !           408:        this->remote->destroy(this->remote);
        !           409:        DESTROY_IF(this->cron);
        !           410:        this->mutex->destroy(this->mutex);
        !           411:        free(this);
        !           412: }
        !           413: 
        !           414: /**
        !           415:  * See header
        !           416:  */
        !           417: certexpire_export_t *certexpire_export_create()
        !           418: {
        !           419:        private_certexpire_export_t *this;
        !           420:        char *cron;
        !           421: 
        !           422:        INIT(this,
        !           423:                .public = {
        !           424:                        .add = _add,
        !           425:                        .destroy = _destroy,
        !           426:                },
        !           427:                .local = hashtable_create((hashtable_hash_t)hash,
        !           428:                                                                  (hashtable_equals_t)equals, 4),
        !           429:                .remote = hashtable_create((hashtable_hash_t)hash,
        !           430:                                                                   (hashtable_equals_t)equals, 32),
        !           431:                .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
        !           432:                .local_path = lib->settings->get_str(lib->settings,
        !           433:                                                                        "%s.plugins.certexpire.csv.local",
        !           434:                                                                        NULL, lib->ns),
        !           435:                .remote_path = lib->settings->get_str(lib->settings,
        !           436:                                                                        "%s.plugins.certexpire.csv.remote",
        !           437:                                                                        NULL, lib->ns),
        !           438:                .separator = lib->settings->get_str(lib->settings,
        !           439:                                                                        "%s.plugins.certexpire.csv.separator",
        !           440:                                                                        ",", lib->ns),
        !           441:                .format = lib->settings->get_str(lib->settings,
        !           442:                                                                        "%s.plugins.certexpire.csv.format",
        !           443:                                                                        "%d:%m:%Y", lib->ns),
        !           444:                .fixed_fields = lib->settings->get_bool(lib->settings,
        !           445:                                                                        "%s.plugins.certexpire.csv.fixed_fields",
        !           446:                                                                        TRUE, lib->ns),
        !           447:                .empty_string = lib->settings->get_str(lib->settings,
        !           448:                                                                        "%s.plugins.certexpire.csv.empty_string",
        !           449:                                                                        "", lib->ns),
        !           450:                .force = lib->settings->get_bool(lib->settings,
        !           451:                                                                        "%s.plugins.certexpire.csv.force",
        !           452:                                                                        TRUE, lib->ns),
        !           453:        );
        !           454: 
        !           455:        cron = lib->settings->get_str(lib->settings,
        !           456:                                                                  "%s.plugins.certexpire.csv.cron",
        !           457:                                                                  NULL, lib->ns);
        !           458:        if (cron)
        !           459:        {
        !           460:                this->cron = certexpire_cron_create(cron,
        !           461:                                                                        (certexpire_cron_job_t)cron_export, this);
        !           462:        }
        !           463:        return &this->public;
        !           464: }

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