]> git.ipfire.org Git - people/ms/strongswan.git/blame - src/libcharon/plugins/certexpire/certexpire_export.c
libcharon: Use lib->ns instead of charon->name
[people/ms/strongswan.git] / src / libcharon / plugins / certexpire / certexpire_export.c
CommitLineData
f4bdc6a3
MW
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
e64fdfa1
MW
18#include "certexpire_cron.h"
19
20#include <time.h>
21#include <limits.h>
22#include <errno.h>
23
f05b4272 24#include <utils/debug.h>
aa8898bc 25#include <daemon.h>
12642a68 26#include <collections/hashtable.h>
f4bdc6a3
MW
27#include <threading/mutex.h>
28#include <credentials/certificates/x509.h>
29
30typedef struct private_certexpire_export_t private_certexpire_export_t;
31
32/**
33 * Private data of an certexpire_export_t object.
34 */
35struct 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;
e64fdfa1
MW
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;
3f455c73
MW
86
87 /**
88 * String to use in empty fields, if using fixed_fields
89 */
90 char *empty_string;
8fc89db7
MW
91
92 /**
93 * Force export of all trustchains we have a private key for
94 */
95 bool force;
f4bdc6a3
MW
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 */
106typedef 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 */
116static 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 */
124static bool equals(entry_t *a, entry_t *b)
125{
126 return streq(a->id, b->id);
127}
128
e64fdfa1
MW
129/**
130 * Export a single trustchain to a path
131 */
132static 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 }
3f455c73 170 else if (entry->expire[i])
e64fdfa1
MW
171 {
172 fprintf(file, "%s", this->separator);
173 }
3f455c73
MW
174 else if (this->fixed_fields)
175 {
176 fprintf(file, "%s%s", this->empty_string, this->separator);
177 }
e64fdfa1
MW
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
f4bdc6a3
MW
192METHOD(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
e64fdfa1
MW
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
f4bdc6a3
MW
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 }
e64fdfa1
MW
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 }
f4bdc6a3
MW
309 }
310 enumerator->destroy(enumerator);
311}
312
8fc89db7
MW
313/**
314 * Add trustchains we have a private key for to the list
315 */
316static 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 */
372static 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
f4bdc6a3
MW
388METHOD(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);
e64fdfa1 409 DESTROY_IF(this->cron);
f4bdc6a3
MW
410 this->mutex->destroy(this->mutex);
411 free(this);
412}
413
414/**
415 * See header
416 */
417certexpire_export_t *certexpire_export_create()
418{
419 private_certexpire_export_t *this;
e64fdfa1 420 char *cron;
f4bdc6a3
MW
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),
e64fdfa1 432 .local_path = lib->settings->get_str(lib->settings,
d223fe80
TB
433 "%s.plugins.certexpire.csv.local",
434 NULL, lib->ns),
e64fdfa1 435 .remote_path = lib->settings->get_str(lib->settings,
d223fe80
TB
436 "%s.plugins.certexpire.csv.remote",
437 NULL, lib->ns),
e64fdfa1 438 .separator = lib->settings->get_str(lib->settings,
d223fe80
TB
439 "%s.plugins.certexpire.csv.separator",
440 ",", lib->ns),
e64fdfa1 441 .format = lib->settings->get_str(lib->settings,
d223fe80
TB
442 "%s.plugins.certexpire.csv.format",
443 "%d:%m:%Y", lib->ns),
e64fdfa1 444 .fixed_fields = lib->settings->get_bool(lib->settings,
d223fe80
TB
445 "%s.plugins.certexpire.csv.fixed_fields",
446 TRUE, lib->ns),
3f455c73 447 .empty_string = lib->settings->get_str(lib->settings,
d223fe80
TB
448 "%s.plugins.certexpire.csv.empty_string",
449 "", lib->ns),
8fc89db7 450 .force = lib->settings->get_bool(lib->settings,
d223fe80
TB
451 "%s.plugins.certexpire.csv.force",
452 TRUE, lib->ns),
f4bdc6a3
MW
453 );
454
e64fdfa1 455 cron = lib->settings->get_str(lib->settings,
42500c27 456 "%s.plugins.certexpire.csv.cron",
d223fe80 457 NULL, lib->ns);
e64fdfa1
MW
458 if (cron)
459 {
460 this->cron = certexpire_cron_create(cron,
461 (certexpire_cron_job_t)cron_export, this);
462 }
f4bdc6a3
MW
463 return &this->public;
464}