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