Moved data structures to new collections subfolder
[strongswan.git] / src / libcharon / plugins / stroke / stroke_ca.c
1 /*
2 * Copyright (C) 2008 Tobias Brunner
3 * Copyright (C) 2008 Martin Willi
4 * Hochschule fuer Technik Rapperswil
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2 of the License, or (at your
9 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * for more details.
15 */
16
17 #include "stroke_ca.h"
18 #include "stroke_cred.h"
19
20 #include <threading/rwlock.h>
21 #include <collections/linked_list.h>
22 #include <crypto/hashers/hasher.h>
23
24 #include <daemon.h>
25
26 typedef struct private_stroke_ca_t private_stroke_ca_t;
27
28 /**
29 * private data of stroke_ca
30 */
31 struct private_stroke_ca_t {
32
33 /**
34 * public functions
35 */
36 stroke_ca_t public;
37
38 /**
39 * read-write lock to lists
40 */
41 rwlock_t *lock;
42
43 /**
44 * list of starters CA sections and its certificates (ca_section_t)
45 */
46 linked_list_t *sections;
47
48 /**
49 * stroke credentials, stores our CA certificates
50 */
51 stroke_cred_t *cred;
52 };
53
54 typedef struct ca_section_t ca_section_t;
55
56 /**
57 * loaded ipsec.conf CA sections
58 */
59 struct ca_section_t {
60
61 /**
62 * name of the CA section
63 */
64 char *name;
65
66 /**
67 * reference to cert in trusted_credential_t
68 */
69 certificate_t *cert;
70
71 /**
72 * CRL URIs
73 */
74 linked_list_t *crl;
75
76 /**
77 * OCSP URIs
78 */
79 linked_list_t *ocsp;
80
81 /**
82 * Hashes of certificates issued by this CA
83 */
84 linked_list_t *hashes;
85
86 /**
87 * Base URI used for certificates from this CA
88 */
89 char *certuribase;
90 };
91
92 /**
93 * create a new CA section
94 */
95 static ca_section_t *ca_section_create(char *name, certificate_t *cert)
96 {
97 ca_section_t *ca = malloc_thing(ca_section_t);
98
99 ca->name = strdup(name);
100 ca->crl = linked_list_create();
101 ca->ocsp = linked_list_create();
102 ca->cert = cert;
103 ca->hashes = linked_list_create();
104 ca->certuribase = NULL;
105 return ca;
106 }
107
108 /**
109 * destroy a ca section entry
110 */
111 static void ca_section_destroy(ca_section_t *this)
112 {
113 this->crl->destroy_function(this->crl, free);
114 this->ocsp->destroy_function(this->ocsp, free);
115 this->hashes->destroy_offset(this->hashes, offsetof(identification_t, destroy));
116 this->cert->destroy(this->cert);
117 free(this->certuribase);
118 free(this->name);
119 free(this);
120 }
121
122 /**
123 * data to pass to create_inner_cdp
124 */
125 typedef struct {
126 private_stroke_ca_t *this;
127 certificate_type_t type;
128 identification_t *id;
129 } cdp_data_t;
130
131 /**
132 * destroy cdp enumerator data and unlock list
133 */
134 static void cdp_data_destroy(cdp_data_t *data)
135 {
136 data->this->lock->unlock(data->this->lock);
137 free(data);
138 }
139
140 /**
141 * inner enumerator constructor for CDP URIs
142 */
143 static enumerator_t *create_inner_cdp(ca_section_t *section, cdp_data_t *data)
144 {
145 public_key_t *public;
146 enumerator_t *enumerator = NULL;
147 linked_list_t *list;
148
149 if (data->type == CERT_X509_OCSP_RESPONSE)
150 {
151 list = section->ocsp;
152 }
153 else
154 {
155 list = section->crl;
156 }
157
158 public = section->cert->get_public_key(section->cert);
159 if (public)
160 {
161 if (!data->id)
162 {
163 enumerator = list->create_enumerator(list);
164 }
165 else
166 {
167 if (public->has_fingerprint(public, data->id->get_encoding(data->id)))
168 {
169 enumerator = list->create_enumerator(list);
170 }
171 }
172 public->destroy(public);
173 }
174 return enumerator;
175 }
176
177 /**
178 * inner enumerator constructor for "Hash and URL"
179 */
180 static enumerator_t *create_inner_cdp_hashandurl(ca_section_t *section, cdp_data_t *data)
181 {
182 enumerator_t *enumerator = NULL, *hash_enum;
183 identification_t *current;
184
185 if (!data->id || !section->certuribase)
186 {
187 return NULL;
188 }
189
190 hash_enum = section->hashes->create_enumerator(section->hashes);
191 while (hash_enum->enumerate(hash_enum, &current))
192 {
193 if (current->matches(current, data->id))
194 {
195 char *url, *hash;
196
197 url = malloc(strlen(section->certuribase) + 40 + 1);
198 strcpy(url, section->certuribase);
199 hash = chunk_to_hex(current->get_encoding(current), NULL, FALSE).ptr;
200 strncat(url, hash, 40);
201 free(hash);
202
203 enumerator = enumerator_create_single(url, free);
204 break;
205 }
206 }
207 hash_enum->destroy(hash_enum);
208 return enumerator;
209 }
210
211 METHOD(credential_set_t, create_cdp_enumerator, enumerator_t*,
212 private_stroke_ca_t *this, certificate_type_t type, identification_t *id)
213 {
214 cdp_data_t *data;
215
216 switch (type)
217 { /* we serve CRLs, OCSP responders and URLs for "Hash and URL" */
218 case CERT_X509:
219 case CERT_X509_CRL:
220 case CERT_X509_OCSP_RESPONSE:
221 case CERT_ANY:
222 break;
223 default:
224 return NULL;
225 }
226 data = malloc_thing(cdp_data_t);
227 data->this = this;
228 data->type = type;
229 data->id = id;
230
231 this->lock->read_lock(this->lock);
232 return enumerator_create_nested(this->sections->create_enumerator(this->sections),
233 (type == CERT_X509) ? (void*)create_inner_cdp_hashandurl : (void*)create_inner_cdp,
234 data, (void*)cdp_data_destroy);
235 }
236
237 METHOD(stroke_ca_t, add, void,
238 private_stroke_ca_t *this, stroke_msg_t *msg)
239 {
240 certificate_t *cert;
241 ca_section_t *ca;
242
243 if (msg->add_ca.cacert == NULL)
244 {
245 DBG1(DBG_CFG, "missing cacert parameter");
246 return;
247 }
248 cert = this->cred->load_ca(this->cred, msg->add_ca.cacert);
249 if (cert)
250 {
251 ca = ca_section_create(msg->add_ca.name, cert);
252 if (msg->add_ca.crluri)
253 {
254 ca->crl->insert_last(ca->crl, strdup(msg->add_ca.crluri));
255 }
256 if (msg->add_ca.crluri2)
257 {
258 ca->crl->insert_last(ca->crl, strdup(msg->add_ca.crluri2));
259 }
260 if (msg->add_ca.ocspuri)
261 {
262 ca->ocsp->insert_last(ca->ocsp, strdup(msg->add_ca.ocspuri));
263 }
264 if (msg->add_ca.ocspuri2)
265 {
266 ca->ocsp->insert_last(ca->ocsp, strdup(msg->add_ca.ocspuri2));
267 }
268 if (msg->add_ca.certuribase)
269 {
270 ca->certuribase = strdup(msg->add_ca.certuribase);
271 }
272 this->lock->write_lock(this->lock);
273 this->sections->insert_last(this->sections, ca);
274 this->lock->unlock(this->lock);
275 DBG1(DBG_CFG, "added ca '%s'", msg->add_ca.name);
276 }
277 }
278
279 METHOD(stroke_ca_t, del, void,
280 private_stroke_ca_t *this, stroke_msg_t *msg)
281 {
282 enumerator_t *enumerator;
283 ca_section_t *ca = NULL;
284
285 this->lock->write_lock(this->lock);
286 enumerator = this->sections->create_enumerator(this->sections);
287 while (enumerator->enumerate(enumerator, &ca))
288 {
289 if (streq(ca->name, msg->del_ca.name))
290 {
291 this->sections->remove_at(this->sections, enumerator);
292 break;
293 }
294 ca = NULL;
295 }
296 enumerator->destroy(enumerator);
297 this->lock->unlock(this->lock);
298 if (ca == NULL)
299 {
300 DBG1(DBG_CFG, "no ca named '%s' found\n", msg->del_ca.name);
301 return;
302 }
303 ca_section_destroy(ca);
304
305 lib->credmgr->flush_cache(lib->credmgr, CERT_ANY);
306 }
307
308 /**
309 * list crl or ocsp URIs
310 */
311 static void list_uris(linked_list_t *list, char *label, FILE *out)
312 {
313 bool first = TRUE;
314 char *uri;
315 enumerator_t *enumerator;
316
317 enumerator = list->create_enumerator(list);
318 while (enumerator->enumerate(enumerator, (void**)&uri))
319 {
320 if (first)
321 {
322 fprintf(out, "%s", label);
323 first = FALSE;
324 }
325 else
326 {
327 fprintf(out, " ");
328 }
329 fprintf(out, "'%s'\n", uri);
330 }
331 enumerator->destroy(enumerator);
332 }
333
334 METHOD(stroke_ca_t, check_for_hash_and_url, void,
335 private_stroke_ca_t *this, certificate_t* cert)
336 {
337 ca_section_t *section;
338 enumerator_t *enumerator;
339
340 hasher_t *hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1);
341 if (hasher == NULL)
342 {
343 DBG1(DBG_IKE, "unable to use hash-and-url: sha1 not supported");
344 return;
345 }
346
347 this->lock->write_lock(this->lock);
348 enumerator = this->sections->create_enumerator(this->sections);
349 while (enumerator->enumerate(enumerator, (void**)&section))
350 {
351 if (section->certuribase && cert->issued_by(cert, section->cert, NULL))
352 {
353 chunk_t hash, encoded;
354
355 if (cert->get_encoding(cert, CERT_ASN1_DER, &encoded))
356 {
357 if (hasher->allocate_hash(hasher, encoded, &hash))
358 {
359 section->hashes->insert_last(section->hashes,
360 identification_create_from_encoding(ID_KEY_ID, hash));
361 chunk_free(&hash);
362 }
363 chunk_free(&encoded);
364 }
365 break;
366 }
367 }
368 enumerator->destroy(enumerator);
369 this->lock->unlock(this->lock);
370
371 hasher->destroy(hasher);
372 }
373
374 METHOD(stroke_ca_t, list, void,
375 private_stroke_ca_t *this, stroke_msg_t *msg, FILE *out)
376 {
377 bool first = TRUE;
378 ca_section_t *section;
379 enumerator_t *enumerator;
380
381 this->lock->read_lock(this->lock);
382 enumerator = this->sections->create_enumerator(this->sections);
383 while (enumerator->enumerate(enumerator, (void**)&section))
384 {
385 certificate_t *cert = section->cert;
386 public_key_t *public = cert->get_public_key(cert);
387 chunk_t chunk;
388
389 if (first)
390 {
391 fprintf(out, "\n");
392 fprintf(out, "List of CA Information Sections:\n");
393 first = FALSE;
394 }
395 fprintf(out, "\n");
396 fprintf(out, " authname: \"%Y\"\n", cert->get_subject(cert));
397
398 /* list authkey and keyid */
399 if (public)
400 {
401 if (public->get_fingerprint(public, KEYID_PUBKEY_SHA1, &chunk))
402 {
403 fprintf(out, " authkey: %#B\n", &chunk);
404 }
405 if (public->get_fingerprint(public, KEYID_PUBKEY_INFO_SHA1, &chunk))
406 {
407 fprintf(out, " keyid: %#B\n", &chunk);
408 }
409 public->destroy(public);
410 }
411 list_uris(section->crl, " crluris: ", out);
412 list_uris(section->ocsp, " ocspuris: ", out);
413 if (section->certuribase)
414 {
415 fprintf(out, " certuribase: '%s'\n", section->certuribase);
416 }
417 }
418 enumerator->destroy(enumerator);
419 this->lock->unlock(this->lock);
420 }
421
422 METHOD(stroke_ca_t, destroy, void,
423 private_stroke_ca_t *this)
424 {
425 this->sections->destroy_function(this->sections, (void*)ca_section_destroy);
426 this->lock->destroy(this->lock);
427 free(this);
428 }
429
430 /*
431 * see header file
432 */
433 stroke_ca_t *stroke_ca_create(stroke_cred_t *cred)
434 {
435 private_stroke_ca_t *this;
436
437 INIT(this,
438 .public = {
439 .set = {
440 .create_private_enumerator = (void*)return_null,
441 .create_cert_enumerator = (void*)return_null,
442 .create_shared_enumerator = (void*)return_null,
443 .create_cdp_enumerator = _create_cdp_enumerator,
444 .cache_cert = (void*)nop,
445 },
446 .add = _add,
447 .del = _del,
448 .list = _list,
449 .check_for_hash_and_url = _check_for_hash_and_url,
450 .destroy = _destroy,
451 },
452 .sections = linked_list_create(),
453 .lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
454 .cred = cred,
455 );
456
457 return &this->public;
458 }
459