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