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
87 * String to use in empty fields, if using fixed_fields
93 * Maximum number of expiration dates we store (for subject + IM CAs + CA)
95 #define MAX_TRUSTCHAIN_LENGTH 7
101 /** certificate subject as subjectAltName or CN of a DN */
103 /** list of expiration dates, 0 if no certificate */
104 time_t expire
[MAX_TRUSTCHAIN_LENGTH
];
108 * Hashtable hash function
110 static u_int
hash(entry_t
*key
)
112 return chunk_hash(chunk_create(key
->id
, strlen(key
->id
)));
116 * Hashtable equals function
118 static bool equals(entry_t
*a
, entry_t
*b
)
120 return streq(a
->id
, b
->id
);
124 * Export a single trustchain to a path
126 static void export_csv(private_certexpire_export_t
*this, char *path
,
129 enumerator_t
*enumerator
;
138 localtime_r(&t
, &tm
);
140 strftime(buf
, sizeof(buf
), path
, &tm
);
141 file
= fopen(buf
, "a");
144 DBG1(DBG_CFG
, "exporting expiration dates of %d trustchain%s to '%s'",
145 chains
->get_count(chains
),
146 chains
->get_count(chains
) == 1 ?
"" : "s", buf
);
147 this->mutex
->lock(this->mutex
);
148 enumerator
= chains
->create_enumerator(chains
);
149 while (enumerator
->enumerate(enumerator
, NULL
, &entry
))
151 fprintf(file
, "%s%s", entry
->id
, this->separator
);
152 for (i
= 0; i
< MAX_TRUSTCHAIN_LENGTH
; i
++)
154 if (entry
->expire
[i
])
156 localtime_r(&entry
->expire
[i
], &tm
);
157 strftime(buf
, sizeof(buf
), this->format
, &tm
);
158 fprintf(file
, "%s", buf
);
160 if (i
== MAX_TRUSTCHAIN_LENGTH
- 1)
164 else if (entry
->expire
[i
])
166 fprintf(file
, "%s", this->separator
);
168 else if (this->fixed_fields
)
170 fprintf(file
, "%s%s", this->empty_string
, this->separator
);
173 chains
->remove_at(chains
, enumerator
);
176 enumerator
->destroy(enumerator
);
177 this->mutex
->unlock(this->mutex
);
182 DBG1(DBG_CFG
, "opening CSV file '%s' failed: %s", buf
, strerror(errno
));
187 * Export cached trustchain expiration dates to CSV files
189 static void cron_export(private_certexpire_export_t
*this)
191 if (this->local_path
)
193 export_csv(this, this->local_path
, this->local
);
195 if (this->remote_path
)
197 export_csv(this, this->remote_path
, this->remote
);
201 METHOD(certexpire_export_t
, add
, void,
202 private_certexpire_export_t
*this, linked_list_t
*trustchain
, bool local
)
204 enumerator_t
*enumerator
;
208 /* don't store expiration dates if no path configured */
211 if (!this->local_path
)
218 if (!this->remote_path
)
224 count
= min(trustchain
->get_count(trustchain
), MAX_TRUSTCHAIN_LENGTH
) - 1;
226 enumerator
= trustchain
->create_enumerator(trustchain
);
227 /* get subject cert */
228 if (enumerator
->enumerate(enumerator
, &cert
))
230 identification_t
*id
;
236 /* prefer FQDN subjectAltName... */
237 if (cert
->get_type(cert
) == CERT_X509
)
239 x509_t
*x509
= (x509_t
*)cert
;
242 sans
= x509
->create_subjectAltName_enumerator(x509
);
243 while (sans
->enumerate(sans
, &id
))
245 if (id
->get_type(id
) == ID_FQDN
)
247 snprintf(entry
->id
, sizeof(entry
->id
), "%Y", id
);
253 /* fallback to CN of DN */
260 id
= cert
->get_subject(cert
);
261 parts
= id
->create_part_enumerator(id
);
262 while (parts
->enumerate(parts
, &part
, &data
))
264 if (part
== ID_PART_RDN_CN
)
266 snprintf(entry
->id
, sizeof(entry
->id
), "%.*s",
267 (int)data
.len
, data
.ptr
);
271 parts
->destroy(parts
);
273 /* no usable identity? skip */
276 enumerator
->destroy(enumerator
);
281 /* get intermediate CA expiration dates */
282 cert
->get_validity(cert
, NULL
, NULL
, &entry
->expire
[0]);
283 for (i
= 1; i
< count
&& enumerator
->enumerate(enumerator
, &cert
); i
++)
285 cert
->get_validity(cert
, NULL
, NULL
, &entry
->expire
[i
]);
287 /* get CA expiration date, as last array entry */
288 if (enumerator
->enumerate(enumerator
, &cert
))
290 cert
->get_validity(cert
, NULL
, NULL
,
291 &entry
->expire
[MAX_TRUSTCHAIN_LENGTH
- 1]);
293 this->mutex
->lock(this->mutex
);
296 entry
= this->local
->put(this->local
, entry
, entry
);
300 entry
= this->remote
->put(this->remote
, entry
, entry
);
302 this->mutex
->unlock(this->mutex
);
308 { /* export directly if no cron job defined */
311 export_csv(this, this->local_path
, this->local
);
315 export_csv(this, this->remote_path
, this->remote
);
319 enumerator
->destroy(enumerator
);
322 METHOD(certexpire_export_t
, destroy
, void,
323 private_certexpire_export_t
*this)
325 entry_t
*key
, *value
;
326 enumerator_t
*enumerator
;
328 enumerator
= this->local
->create_enumerator(this->local
);
329 while (enumerator
->enumerate(enumerator
, &key
, &value
))
333 enumerator
->destroy(enumerator
);
334 enumerator
= this->remote
->create_enumerator(this->remote
);
335 while (enumerator
->enumerate(enumerator
, &key
, &value
))
339 enumerator
->destroy(enumerator
);
341 this->local
->destroy(this->local
);
342 this->remote
->destroy(this->remote
);
343 DESTROY_IF(this->cron
);
344 this->mutex
->destroy(this->mutex
);
351 certexpire_export_t
*certexpire_export_create()
353 private_certexpire_export_t
*this;
361 .local
= hashtable_create((hashtable_hash_t
)hash
,
362 (hashtable_equals_t
)equals
, 4),
363 .remote
= hashtable_create((hashtable_hash_t
)hash
,
364 (hashtable_equals_t
)equals
, 32),
365 .mutex
= mutex_create(MUTEX_TYPE_DEFAULT
),
366 .local_path
= lib
->settings
->get_str(lib
->settings
,
367 "charon.plugins.certexpire.csv.local", NULL
),
368 .remote_path
= lib
->settings
->get_str(lib
->settings
,
369 "charon.plugins.certexpire.csv.remote", NULL
),
370 .separator
= lib
->settings
->get_str(lib
->settings
,
371 "charon.plugins.certexpire.csv.separator", ","),
372 .format
= lib
->settings
->get_str(lib
->settings
,
373 "charon.plugins.certexpire.csv.format", "%d:%m:%Y"),
374 .fixed_fields
= lib
->settings
->get_bool(lib
->settings
,
375 "charon.plugins.certexpire.csv.fixed_fields", TRUE
),
376 .empty_string
= lib
->settings
->get_str(lib
->settings
,
377 "charon.plugins.certexpire.csv.empty_string", ""),
380 cron
= lib
->settings
->get_str(lib
->settings
,
381 "charon.plugins.certexpire.csv.cron", NULL
);
384 this->cron
= certexpire_cron_create(cron
,
385 (certexpire_cron_job_t
)cron_export
, this);
387 return &this->public;