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>