Annotation of embedaddon/strongswan/src/libcharon/plugins/certexpire/certexpire_export.c, revision 1.1.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>