stroke: Serve ca section CA certificates directly, not over central CA set
[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 for the certificate enumerator
124 */
125 typedef struct {
126 private_stroke_ca_t *this;
127 certificate_type_t cert;
128 key_type_t key;
129 identification_t *id;
130 } cert_data_t;
131
132 /**
133 * destroy cert_data
134 */
135 static void cert_data_destroy(cert_data_t *data)
136 {
137 data->this->lock->unlock(data->this->lock);
138 free(data);
139 }
140
141 /**
142 * filter function for certs enumerator
143 */
144 static bool certs_filter(cert_data_t *data, ca_section_t **in,
145 certificate_t **out)
146 {
147 public_key_t *public;
148 certificate_t *cert = (*in)->cert;
149
150 if (data->cert == CERT_ANY || data->cert == cert->get_type(cert))
151 {
152 public = cert->get_public_key(cert);
153 if (public)
154 {
155 if (data->key == KEY_ANY || data->key == public->get_type(public))
156 {
157 if (data->id && public->has_fingerprint(public,
158 data->id->get_encoding(data->id)))
159 {
160 public->destroy(public);
161 *out = cert;
162 return TRUE;
163 }
164 }
165 public->destroy(public);
166 }
167 else if (data->key != KEY_ANY)
168 {
169 return FALSE;
170 }
171 if (data->id == NULL || cert->has_subject(cert, data->id))
172 {
173 *out = cert;
174 return TRUE;
175 }
176 }
177 return FALSE;
178 }
179
180 METHOD(credential_set_t, create_cert_enumerator, enumerator_t*,
181 private_stroke_ca_t *this, certificate_type_t cert, key_type_t key,
182 identification_t *id, bool trusted)
183 {
184 enumerator_t *enumerator;
185 cert_data_t *data;
186
187 INIT(data,
188 .this = this,
189 .cert = cert,
190 .key = key,
191 .id = id,
192 );
193
194 this->lock->read_lock(this->lock);
195 enumerator = this->sections->create_enumerator(this->sections);
196 return enumerator_create_filter(enumerator, (void*)certs_filter, data,
197 (void*)cert_data_destroy);
198 }
199
200 /**
201 * data to pass to create_inner_cdp
202 */
203 typedef struct {
204 private_stroke_ca_t *this;
205 certificate_type_t type;
206 identification_t *id;
207 } cdp_data_t;
208
209 /**
210 * destroy cdp enumerator data and unlock list
211 */
212 static void cdp_data_destroy(cdp_data_t *data)
213 {
214 data->this->lock->unlock(data->this->lock);
215 free(data);
216 }
217
218 /**
219 * inner enumerator constructor for CDP URIs
220 */
221 static enumerator_t *create_inner_cdp(ca_section_t *section, cdp_data_t *data)
222 {
223 public_key_t *public;
224 enumerator_t *enumerator = NULL;
225 linked_list_t *list;
226
227 if (data->type == CERT_X509_OCSP_RESPONSE)
228 {
229 list = section->ocsp;
230 }
231 else
232 {
233 list = section->crl;
234 }
235
236 public = section->cert->get_public_key(section->cert);
237 if (public)
238 {
239 if (!data->id)
240 {
241 enumerator = list->create_enumerator(list);
242 }
243 else
244 {
245 if (public->has_fingerprint(public, data->id->get_encoding(data->id)))
246 {
247 enumerator = list->create_enumerator(list);
248 }
249 }
250 public->destroy(public);
251 }
252 return enumerator;
253 }
254
255 /**
256 * inner enumerator constructor for "Hash and URL"
257 */
258 static enumerator_t *create_inner_cdp_hashandurl(ca_section_t *section, cdp_data_t *data)
259 {
260 enumerator_t *enumerator = NULL, *hash_enum;
261 identification_t *current;
262
263 if (!data->id || !section->certuribase)
264 {
265 return NULL;
266 }
267
268 hash_enum = section->hashes->create_enumerator(section->hashes);
269 while (hash_enum->enumerate(hash_enum, &current))
270 {
271 if (current->matches(current, data->id))
272 {
273 char *url, *hash;
274
275 url = malloc(strlen(section->certuribase) + 40 + 1);
276 strcpy(url, section->certuribase);
277 hash = chunk_to_hex(current->get_encoding(current), NULL, FALSE).ptr;
278 strncat(url, hash, 40);
279 free(hash);
280
281 enumerator = enumerator_create_single(url, free);
282 break;
283 }
284 }
285 hash_enum->destroy(hash_enum);
286 return enumerator;
287 }
288
289 METHOD(credential_set_t, create_cdp_enumerator, enumerator_t*,
290 private_stroke_ca_t *this, certificate_type_t type, identification_t *id)
291 {
292 cdp_data_t *data;
293
294 switch (type)
295 { /* we serve CRLs, OCSP responders and URLs for "Hash and URL" */
296 case CERT_X509:
297 case CERT_X509_CRL:
298 case CERT_X509_OCSP_RESPONSE:
299 case CERT_ANY:
300 break;
301 default:
302 return NULL;
303 }
304 data = malloc_thing(cdp_data_t);
305 data->this = this;
306 data->type = type;
307 data->id = id;
308
309 this->lock->read_lock(this->lock);
310 return enumerator_create_nested(this->sections->create_enumerator(this->sections),
311 (type == CERT_X509) ? (void*)create_inner_cdp_hashandurl : (void*)create_inner_cdp,
312 data, (void*)cdp_data_destroy);
313 }
314
315 METHOD(stroke_ca_t, add, void,
316 private_stroke_ca_t *this, stroke_msg_t *msg)
317 {
318 certificate_t *cert;
319 ca_section_t *ca;
320
321 if (msg->add_ca.cacert == NULL)
322 {
323 DBG1(DBG_CFG, "missing cacert parameter");
324 return;
325 }
326 cert = this->cred->load_ca(this->cred, msg->add_ca.cacert);
327 if (cert)
328 {
329 ca = ca_section_create(msg->add_ca.name, cert);
330 if (msg->add_ca.crluri)
331 {
332 ca->crl->insert_last(ca->crl, strdup(msg->add_ca.crluri));
333 }
334 if (msg->add_ca.crluri2)
335 {
336 ca->crl->insert_last(ca->crl, strdup(msg->add_ca.crluri2));
337 }
338 if (msg->add_ca.ocspuri)
339 {
340 ca->ocsp->insert_last(ca->ocsp, strdup(msg->add_ca.ocspuri));
341 }
342 if (msg->add_ca.ocspuri2)
343 {
344 ca->ocsp->insert_last(ca->ocsp, strdup(msg->add_ca.ocspuri2));
345 }
346 if (msg->add_ca.certuribase)
347 {
348 ca->certuribase = strdup(msg->add_ca.certuribase);
349 }
350 this->lock->write_lock(this->lock);
351 this->sections->insert_last(this->sections, ca);
352 this->lock->unlock(this->lock);
353 DBG1(DBG_CFG, "added ca '%s'", msg->add_ca.name);
354 }
355 }
356
357 METHOD(stroke_ca_t, del, void,
358 private_stroke_ca_t *this, stroke_msg_t *msg)
359 {
360 enumerator_t *enumerator;
361 ca_section_t *ca = NULL;
362
363 this->lock->write_lock(this->lock);
364 enumerator = this->sections->create_enumerator(this->sections);
365 while (enumerator->enumerate(enumerator, &ca))
366 {
367 if (streq(ca->name, msg->del_ca.name))
368 {
369 this->sections->remove_at(this->sections, enumerator);
370 break;
371 }
372 ca = NULL;
373 }
374 enumerator->destroy(enumerator);
375 this->lock->unlock(this->lock);
376 if (ca == NULL)
377 {
378 DBG1(DBG_CFG, "no ca named '%s' found\n", msg->del_ca.name);
379 return;
380 }
381 ca_section_destroy(ca);
382
383 lib->credmgr->flush_cache(lib->credmgr, CERT_ANY);
384 }
385
386 /**
387 * list crl or ocsp URIs
388 */
389 static void list_uris(linked_list_t *list, char *label, FILE *out)
390 {
391 bool first = TRUE;
392 char *uri;
393 enumerator_t *enumerator;
394
395 enumerator = list->create_enumerator(list);
396 while (enumerator->enumerate(enumerator, (void**)&uri))
397 {
398 if (first)
399 {
400 fprintf(out, "%s", label);
401 first = FALSE;
402 }
403 else
404 {
405 fprintf(out, " ");
406 }
407 fprintf(out, "'%s'\n", uri);
408 }
409 enumerator->destroy(enumerator);
410 }
411
412 METHOD(stroke_ca_t, check_for_hash_and_url, void,
413 private_stroke_ca_t *this, certificate_t* cert)
414 {
415 ca_section_t *section;
416 enumerator_t *enumerator;
417
418 hasher_t *hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1);
419 if (hasher == NULL)
420 {
421 DBG1(DBG_IKE, "unable to use hash-and-url: sha1 not supported");
422 return;
423 }
424
425 this->lock->write_lock(this->lock);
426 enumerator = this->sections->create_enumerator(this->sections);
427 while (enumerator->enumerate(enumerator, (void**)&section))
428 {
429 if (section->certuribase && cert->issued_by(cert, section->cert, NULL))
430 {
431 chunk_t hash, encoded;
432
433 if (cert->get_encoding(cert, CERT_ASN1_DER, &encoded))
434 {
435 if (hasher->allocate_hash(hasher, encoded, &hash))
436 {
437 section->hashes->insert_last(section->hashes,
438 identification_create_from_encoding(ID_KEY_ID, hash));
439 chunk_free(&hash);
440 }
441 chunk_free(&encoded);
442 }
443 break;
444 }
445 }
446 enumerator->destroy(enumerator);
447 this->lock->unlock(this->lock);
448
449 hasher->destroy(hasher);
450 }
451
452 METHOD(stroke_ca_t, list, void,
453 private_stroke_ca_t *this, stroke_msg_t *msg, FILE *out)
454 {
455 bool first = TRUE;
456 ca_section_t *section;
457 enumerator_t *enumerator;
458
459 this->lock->read_lock(this->lock);
460 enumerator = this->sections->create_enumerator(this->sections);
461 while (enumerator->enumerate(enumerator, (void**)&section))
462 {
463 certificate_t *cert = section->cert;
464 public_key_t *public = cert->get_public_key(cert);
465 chunk_t chunk;
466
467 if (first)
468 {
469 fprintf(out, "\n");
470 fprintf(out, "List of CA Information Sections:\n");
471 first = FALSE;
472 }
473 fprintf(out, "\n");
474 fprintf(out, " authname: \"%Y\"\n", cert->get_subject(cert));
475
476 /* list authkey and keyid */
477 if (public)
478 {
479 if (public->get_fingerprint(public, KEYID_PUBKEY_SHA1, &chunk))
480 {
481 fprintf(out, " authkey: %#B\n", &chunk);
482 }
483 if (public->get_fingerprint(public, KEYID_PUBKEY_INFO_SHA1, &chunk))
484 {
485 fprintf(out, " keyid: %#B\n", &chunk);
486 }
487 public->destroy(public);
488 }
489 list_uris(section->crl, " crluris: ", out);
490 list_uris(section->ocsp, " ocspuris: ", out);
491 if (section->certuribase)
492 {
493 fprintf(out, " certuribase: '%s'\n", section->certuribase);
494 }
495 }
496 enumerator->destroy(enumerator);
497 this->lock->unlock(this->lock);
498 }
499
500 METHOD(stroke_ca_t, destroy, void,
501 private_stroke_ca_t *this)
502 {
503 this->sections->destroy_function(this->sections, (void*)ca_section_destroy);
504 this->lock->destroy(this->lock);
505 free(this);
506 }
507
508 /*
509 * see header file
510 */
511 stroke_ca_t *stroke_ca_create(stroke_cred_t *cred)
512 {
513 private_stroke_ca_t *this;
514
515 INIT(this,
516 .public = {
517 .set = {
518 .create_private_enumerator = (void*)return_null,
519 .create_cert_enumerator = _create_cert_enumerator,
520 .create_shared_enumerator = (void*)return_null,
521 .create_cdp_enumerator = _create_cdp_enumerator,
522 .cache_cert = (void*)nop,
523 },
524 .add = _add,
525 .del = _del,
526 .list = _list,
527 .check_for_hash_and_url = _check_for_hash_and_url,
528 .destroy = _destroy,
529 },
530 .sections = linked_list_create(),
531 .lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
532 .cred = cred,
533 );
534
535 return &this->public;
536 }