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>