2 * Copyright (C) 2011 Martin Willi
3 * Copyright (C) 2011 revosec AG
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>.
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
16 #include "certexpire_export.h"
18 #include "certexpire_cron.h"
25 #include <utils/hashtable.h>
26 #include <threading/mutex.h>
27 #include <credentials/certificates/x509.h>
29 typedef struct private_certexpire_export_t private_certexpire_export_t
;
32 * Private data of an certexpire_export_t object.
34 struct private_certexpire_export_t
{
37 * Public certexpire_export_t interface.
39 certexpire_export_t
public;
42 * hashtable caching local trustchains, mapping entry_t => entry_t
47 * hashtable caching remote trustchains, mapping entry_t => entry_t
52 * Mutex to lock hashtables
59 certexpire_cron_t
*cron
;
62 * strftime() format to generate local CSV file
67 * strftime() format to generate remote CSV file
72 * stftime() format of the exported expiration date
82 * TRUE to use fixed field count, CA at end
88 * Maximum number of expiration dates we store (for subject + IM CAs + CA)
90 #define MAX_TRUSTCHAIN_LENGTH 7
96 /** certificate subject as subjectAltName or CN of a DN */
98 /** list of expiration dates, 0 if no certificate */
99 time_t expire
[MAX_TRUSTCHAIN_LENGTH
];
103 * Hashtable hash function
105 static u_int
hash(entry_t
*key
)
107 return chunk_hash(chunk_create(key
->id
, strlen(key
->id
)));
111 * Hashtable equals function
113 static bool equals(entry_t
*a
, entry_t
*b
)
115 return streq(a
->id
, b
->id
);
119 * Export a single trustchain to a path
121 static void export_csv(private_certexpire_export_t
*this, char *path
,
124 enumerator_t
*enumerator
;
133 localtime_r(&t
, &tm
);
135 strftime(buf
, sizeof(buf
), path
, &tm
);
136 file
= fopen(buf
, "a");
139 DBG1(DBG_CFG
, "exporting expiration dates of %d trustchain%s to '%s'",
140 chains
->get_count(chains
),
141 chains
->get_count(chains
) == 1 ?
"" : "s", buf
);
142 this->mutex
->lock(this->mutex
);
143 enumerator
= chains
->create_enumerator(chains
);
144 while (enumerator
->enumerate(enumerator
, NULL
, &entry
))
146 fprintf(file
, "%s%s", entry
->id
, this->separator
);
147 for (i
= 0; i
< MAX_TRUSTCHAIN_LENGTH
; i
++)
149 if (entry
->expire
[i
])
151 localtime_r(&entry
->expire
[i
], &tm
);
152 strftime(buf
, sizeof(buf
), this->format
, &tm
);
153 fprintf(file
, "%s", buf
);
155 if (i
== MAX_TRUSTCHAIN_LENGTH
- 1)
159 else if (this->fixed_fields
|| entry
->expire
[i
])
161 fprintf(file
, "%s", this->separator
);
164 chains
->remove_at(chains
, enumerator
);
167 enumerator
->destroy(enumerator
);
168 this->mutex
->unlock(this->mutex
);
173 DBG1(DBG_CFG
, "opening CSV file '%s' failed: %s", buf
, strerror(errno
));
178 * Export cached trustchain expiration dates to CSV files
180 static void cron_export(private_certexpire_export_t
*this)
182 if (this->local_path
)
184 export_csv(this, this->local_path
, this->local
);
186 if (this->remote_path
)
188 export_csv(this, this->remote_path
, this->remote
);
192 METHOD(certexpire_export_t
, add
, void,
193 private_certexpire_export_t
*this, linked_list_t
*trustchain
, bool local
)
195 enumerator_t
*enumerator
;
199 /* don't store expiration dates if no path configured */
202 if (!this->local_path
)
209 if (!this->remote_path
)
215 count
= min(trustchain
->get_count(trustchain
), MAX_TRUSTCHAIN_LENGTH
) - 1;
217 enumerator
= trustchain
->create_enumerator(trustchain
);
218 /* get subject cert */
219 if (enumerator
->enumerate(enumerator
, &cert
))
221 identification_t
*id
;
227 /* prefer FQDN subjectAltName... */
228 if (cert
->get_type(cert
) == CERT_X509
)
230 x509_t
*x509
= (x509_t
*)cert
;
233 sans
= x509
->create_subjectAltName_enumerator(x509
);
234 while (sans
->enumerate(sans
, &id
))
236 if (id
->get_type(id
) == ID_FQDN
)
238 snprintf(entry
->id
, sizeof(entry
->id
), "%Y", id
);
244 /* fallback to CN of DN */
251 id
= cert
->get_subject(cert
);
252 parts
= id
->create_part_enumerator(id
);
253 while (parts
->enumerate(parts
, &part
, &data
))
255 if (part
== ID_PART_RDN_CN
)
257 snprintf(entry
->id
, sizeof(entry
->id
), "%.*s",
258 (int)data
.len
, data
.ptr
);
262 parts
->destroy(parts
);
264 /* no usable identity? skip */
267 enumerator
->destroy(enumerator
);
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
++)
276 cert
->get_validity(cert
, NULL
, NULL
, &entry
->expire
[i
]);
278 /* get CA expiration date, as last array entry */
279 if (enumerator
->enumerate(enumerator
, &cert
))
281 cert
->get_validity(cert
, NULL
, NULL
,
282 &entry
->expire
[MAX_TRUSTCHAIN_LENGTH
- 1]);
284 this->mutex
->lock(this->mutex
);
287 entry
= this->local
->put(this->local
, entry
, entry
);
291 entry
= this->remote
->put(this->remote
, entry
, entry
);
293 this->mutex
->unlock(this->mutex
);
299 { /* export directly if no cron job defined */
302 export_csv(this, this->local_path
, this->local
);
306 export_csv(this, this->remote_path
, this->remote
);
310 enumerator
->destroy(enumerator
);
313 METHOD(certexpire_export_t
, destroy
, void,
314 private_certexpire_export_t
*this)
316 entry_t
*key
, *value
;
317 enumerator_t
*enumerator
;
319 enumerator
= this->local
->create_enumerator(this->local
);
320 while (enumerator
->enumerate(enumerator
, &key
, &value
))
324 enumerator
->destroy(enumerator
);
325 enumerator
= this->remote
->create_enumerator(this->remote
);
326 while (enumerator
->enumerate(enumerator
, &key
, &value
))
330 enumerator
->destroy(enumerator
);
332 this->local
->destroy(this->local
);
333 this->remote
->destroy(this->remote
);
334 DESTROY_IF(this->cron
);
335 this->mutex
->destroy(this->mutex
);
342 certexpire_export_t
*certexpire_export_create()
344 private_certexpire_export_t
*this;
352 .local
= hashtable_create((hashtable_hash_t
)hash
,
353 (hashtable_equals_t
)equals
, 4),
354 .remote
= hashtable_create((hashtable_hash_t
)hash
,
355 (hashtable_equals_t
)equals
, 32),
356 .mutex
= mutex_create(MUTEX_TYPE_DEFAULT
),
357 .local_path
= lib
->settings
->get_str(lib
->settings
,
358 "charon.plugins.certexpire.csv.local", NULL
),
359 .remote_path
= lib
->settings
->get_str(lib
->settings
,
360 "charon.plugins.certexpire.csv.remote", NULL
),
361 .separator
= lib
->settings
->get_str(lib
->settings
,
362 "charon.plugins.certexpire.csv.separator", ","),
363 .format
= lib
->settings
->get_str(lib
->settings
,
364 "charon.plugins.certexpire.csv.format", "%d:%m:%Y"),
365 .fixed_fields
= lib
->settings
->get_bool(lib
->settings
,
366 "charon.plugins.certexpire.csv.fixed_fields", TRUE
),
369 cron
= lib
->settings
->get_str(lib
->settings
,
370 "charon.plugins.certexpire.csv.cron", NULL
);
373 this->cron
= certexpire_cron_create(cron
,
374 (certexpire_cron_job_t
)cron_export
, this);
376 return &this->public;