refactored ca_info_t
[strongswan.git] / src / libstrongswan / crypto / ca.c
1 /**
2 * @file ca.c
3 *
4 * @brief Implementation of ca_info_t.
5 *
6 */
7
8 /*
9 * Copyright (C) 2007 Andreas Steffen
10 * Hochschule fuer Technik Rapperswil
11 *
12 * This program is free software; you can redistribute it and/or modify it
13 * under the terms of the GNU General Public License as published by the
14 * Free Software Foundation; either version 2 of the License, or (at your
15 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
19 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
20 * for more details.
21 */
22
23 #include <sys/stat.h>
24 #include <unistd.h>
25 #include <string.h>
26 #include <stdio.h>
27 #include <pthread.h>
28
29 #include "x509.h"
30 #include "crl.h"
31 #include "ca.h"
32 #include "certinfo.h"
33
34 #include <library.h>
35 #include <debug.h>
36 #include <utils/linked_list.h>
37 #include <utils/identification.h>
38
39 typedef struct private_ca_info_t private_ca_info_t;
40
41 /**
42 * Private data of a ca_info_t object.
43 */
44 struct private_ca_info_t {
45 /**
46 * Public interface for this ca info record
47 */
48 ca_info_t public;
49
50 /**
51 * Name of the ca info record
52 */
53 char *name;
54
55 /**
56 * Time when ca info record was installed
57 */
58 time_t installed;
59
60 /**
61 * Distinguished Name of the CA
62 */
63 x509_t *cacert;
64
65 /**
66 * List of crl URIs
67 */
68 linked_list_t *crluris;
69
70 /**
71 * List of ocsp URIs
72 */
73 linked_list_t *ocspuris;
74
75 /**
76 * CRL issued by this ca
77 */
78 crl_t *crl;
79
80 /**
81 * List of certificate info records
82 */
83 linked_list_t *certinfos;
84
85 /**
86 * mutex controls access to the elements:
87 * name, crluris, ocspuris, crl, and certinfos
88 */
89 pthread_mutex_t mutex;
90 };
91
92 /**
93 * Implements ca_info_t.equals
94 */
95 static bool equals(const private_ca_info_t *this, const private_ca_info_t *that)
96 {
97 return chunk_equals(this->cacert->get_keyid(this->cacert),
98 that->cacert->get_keyid(that->cacert));
99 }
100
101 /**
102 * Implements ca_info_t.equals_name_release_info
103 */
104 static bool equals_name_release_info(private_ca_info_t *this, const char *name)
105 {
106 bool found;
107
108 pthread_mutex_lock(&(this->mutex));
109 found = this->name != NULL && streq(this->name, name);
110
111 if (found)
112 {
113 this->crluris->destroy_offset(this->crluris,
114 offsetof(identification_t, destroy));
115 this->crluris = linked_list_create();
116
117 this->ocspuris->destroy_offset(this->ocspuris,
118 offsetof(identification_t, destroy));
119 this->ocspuris = linked_list_create();
120
121 free(this->name);
122 this->name = NULL;
123 }
124
125 pthread_mutex_unlock(&(this->mutex));
126 return found;
127 }
128
129 /**
130 * Implements ca_info_t.is_crl_issuer
131 */
132 static bool is_cert_issuer(private_ca_info_t *this, const x509_t *cert)
133 {
134 return cert->is_issuer(cert, this->cacert);
135 }
136
137 /**
138 * Implements ca_info_t.is_crl_issuer
139 */
140 static bool is_crl_issuer(private_ca_info_t *this, const crl_t *crl)
141 {
142 return crl->is_issuer(crl, this->cacert);
143 }
144
145 /**
146 * Implements ca_info_t.has_crl
147 */
148 static bool has_crl(private_ca_info_t *this)
149 {
150 bool found;
151
152 pthread_mutex_lock(&(this->mutex));
153 found = this->crl != NULL;
154 pthread_mutex_unlock(&(this->mutex));
155
156 return found;
157 }
158
159 /**
160 * Implements ca_info_t.add_crl
161 */
162 static void add_crl(private_ca_info_t *this, crl_t *crl)
163 {
164 pthread_mutex_lock(&(this->mutex));
165
166 if (this->crl)
167 {
168 if (crl->is_newer(crl, this->crl))
169 {
170 this->crl->destroy(this->crl);
171 this->crl = crl;
172 DBG1(" thisUpdate is newer - existing crl replaced");
173 }
174 else
175 {
176 crl->destroy(crl);
177 DBG1(" thisUpdate is not newer - existing crl retained");
178 }
179 }
180 else
181 {
182 this->crl = crl;
183 DBG2(" crl added");
184 }
185
186 pthread_mutex_unlock(&(this->mutex));
187 }
188
189 /**
190 * Implements ca_info_t.list_crl
191 */
192 static void list_crl(private_ca_info_t *this, FILE *out, bool utc)
193 {
194 pthread_mutex_lock(&(this->mutex));
195
196 fprintf(out, "%#U\n", this->crl, utc);
197
198 pthread_mutex_unlock(&(this->mutex));
199 }
200
201 /**
202 * Find an exact copy of an identification in a linked list
203 */
204 static identification_t* find_identification(linked_list_t *list, identification_t *id)
205 {
206 identification_t *found_id = NULL, *current_id;
207
208 iterator_t *iterator = list->create_iterator(list, TRUE);
209
210 while (iterator->iterate(iterator, (void**)&current_id))
211 {
212 if (id->equals(id, current_id))
213 {
214 found_id = current_id;
215 break;
216 }
217 }
218 iterator->destroy(iterator);
219
220 return found_id;
221 }
222
223 /**
224 * Add a unique identification to a linked list
225 */
226 static identification_t *add_identification(linked_list_t *list, identification_t *id)
227 {
228 identification_t *found_id = find_identification(list, id);
229
230 if (found_id)
231 {
232 id->destroy(id);
233 return found_id;
234 }
235 else
236 {
237 list->insert_last(list, (void*)id);
238 return id;
239 }
240 }
241
242 /**
243 * Implements ca_info_t.add_crluri
244 */
245 static void add_crluri(private_ca_info_t *this, chunk_t uri)
246 {
247 if (uri.len < 6 ||
248 (strncasecmp(uri.ptr, "http", 4) != 0 &&
249 strncasecmp(uri.ptr, "ldap", 4) != 0 &&
250 strncasecmp(uri.ptr, "file", 4) != 0 &&
251 strncasecmp(uri.ptr, "ftp", 3) != 0))
252 {
253 DBG1(" invalid crl uri '%#B'", uri);
254 return;
255 }
256 else
257 {
258 identification_t *crluri = identification_create_from_encoding(ID_DER_ASN1_GN_URI, uri);
259
260 pthread_mutex_lock(&(this->mutex));
261 add_identification(this->crluris, crluri);
262 pthread_mutex_unlock(&(this->mutex));
263 }
264 }
265
266 /**
267 * Implements ca_info_t.add_ocspuri
268 */
269 static void add_ocspuri(private_ca_info_t *this, chunk_t uri)
270 {
271 if (uri.len < 7 || strncasecmp(uri.ptr, "http", 4) != 0)
272 {
273 DBG1(" invalid ocsp uri '%.*s'", uri.len, uri.ptr);
274 return;
275 }
276 else
277 {
278 identification_t *ocspuri = identification_create_from_encoding(ID_DER_ASN1_GN_URI, uri);
279
280 pthread_mutex_lock(&(this->mutex));
281 add_identification(this->ocspuris, ocspuri);
282 pthread_mutex_unlock(&(this->mutex));
283 }
284 }
285
286 /**
287 * Implements ca_info_t.add_info.
288 */
289 void add_info (private_ca_info_t *this, const private_ca_info_t *that)
290 {
291 pthread_mutex_lock(&(this->mutex));
292
293 if (this->name == NULL && that->name != NULL)
294 {
295 this->name = strdup(that->name);
296 }
297
298 pthread_mutex_unlock(&(this->mutex));
299
300 {
301 identification_t *uri;
302
303 iterator_t *iterator = that->crluris->create_iterator(that->crluris, TRUE);
304
305 while (iterator->iterate(iterator, (void**)&uri))
306 {
307 add_crluri(this, uri->get_encoding(uri));
308 }
309 iterator->destroy(iterator);
310 }
311
312 {
313 identification_t *uri;
314
315 iterator_t *iterator = that->ocspuris->create_iterator(that->ocspuris, TRUE);
316
317 while (iterator->iterate(iterator, (void**)&uri))
318 {
319 add_ocspuri(this, uri->get_encoding(uri));
320 }
321 iterator->destroy(iterator);
322 }
323 }
324
325 /**
326 * Implements ca_info_t.get_certificate.
327 */
328 static x509_t* get_certificate(private_ca_info_t* this)
329 {
330 return this->cacert;
331 }
332
333 /**
334 * Implements ca_info_t.verify_by_crl.
335 */
336 static cert_status_t verify_by_crl(private_ca_info_t* this, const x509_t *cert,
337 certinfo_t *certinfo)
338 {
339 bool valid_signature;
340 rsa_public_key_t *issuer_public_key;
341
342
343 pthread_mutex_lock(&(this->mutex));
344
345 if (this->crl == NULL)
346 {
347 DBG1("crl not found");
348 goto err;
349 }
350 DBG2("crl found");
351
352 issuer_public_key = this->cacert->get_public_key(this->cacert);
353 valid_signature = this->crl->verify(this->crl, issuer_public_key);
354
355 if (!valid_signature)
356 {
357 DBG1("crl signature is invalid");
358 goto err;
359 }
360 DBG2("crl signature is valid");
361
362 this->crl->get_status(this->crl, certinfo);
363
364 err:
365 pthread_mutex_unlock(&(this->mutex));
366 return certinfo->get_status(certinfo);
367 }
368
369 /**
370 * Implements ca_info_t.verify_by_ocsp.
371 */
372 static cert_status_t verify_by_ocsp(private_ca_info_t* this, const x509_t *cert,
373 certinfo_t *certinfo)
374 {
375 /* TODO implement function */
376 return CERT_UNDEFINED;
377 }
378
379 /**
380 * Implements ca_info_t.destroy
381 */
382 static void destroy(private_ca_info_t *this)
383 {
384 this->crluris->destroy_offset(this->crluris,
385 offsetof(identification_t, destroy));
386 this->ocspuris->destroy_offset(this->ocspuris,
387 offsetof(identification_t, destroy));
388 this->certinfos->destroy_offset(this->certinfos,
389 offsetof(certinfo_t, destroy));
390 DESTROY_IF(this->crl);
391 free(this->name);
392 free(this);
393 }
394
395 /**
396 * output handler in printf()
397 */
398 static int print(FILE *stream, const struct printf_info *info,
399 const void *const *args)
400 {
401 private_ca_info_t *this = *((private_ca_info_t**)(args[0]));
402 bool utc = TRUE;
403 int written = 0;
404 const x509_t *cacert;
405 chunk_t keyid;
406
407 if (info->alt)
408 {
409 utc = *((bool*)args[1]);
410 }
411 if (this == NULL)
412 {
413 return fprintf(stream, "(null)");
414 }
415
416 pthread_mutex_lock(&(this->mutex));
417 written += fprintf(stream, "%#T", &this->installed, utc);
418
419 if (this->name)
420 {
421 written += fprintf(stream, ", \"%s\"\n", this->name);
422 }
423 else
424 {
425 written += fprintf(stream, "\n");
426 }
427
428 cacert = this->cacert;
429 written += fprintf(stream, " authname: '%D'\n", cacert->get_subject(cacert));
430
431 {
432 chunk_t keyid = cacert->get_keyid(cacert);
433
434 written += fprintf(stream, " keyid: %#B\n", &keyid);
435 }
436 {
437 identification_t *crluri;
438 iterator_t *iterator = this->crluris->create_iterator(this->crluris, TRUE);
439 bool first = TRUE;
440
441 while (iterator->iterate(iterator, (void**)&crluri))
442 {
443 written += fprintf(stream, " %s '%D'\n",
444 first? "crluris:":" ", crluri);
445 first = FALSE;
446 }
447 iterator->destroy(iterator);
448 }
449 {
450 identification_t *ocspuri;
451 iterator_t *iterator = this->ocspuris->create_iterator(this->ocspuris, TRUE);
452 bool first = TRUE;
453
454 while (iterator->iterate(iterator, (void**)&ocspuri))
455 {
456 written += fprintf(stream, " %s '%D'\n",
457 first? "ocspuris:":" ", ocspuri);
458 first = FALSE;
459 }
460 iterator->destroy(iterator);
461 }
462 pthread_mutex_unlock(&(this->mutex));
463 return written;
464 }
465
466 /**
467 * register printf() handlers
468 */
469 static void __attribute__ ((constructor))print_register()
470 {
471 register_printf_function(PRINTF_CAINFO, print, arginfo_ptr_alt_ptr_int);
472 }
473
474 /*
475 * Described in header.
476 */
477 ca_info_t *ca_info_create(const char *name, x509_t *cacert)
478 {
479 private_ca_info_t *this = malloc_thing(private_ca_info_t);
480
481 /* initialize */
482 this->installed = time(NULL);
483 this->name = (name == NULL)? NULL:strdup(name);
484 this->cacert = cacert;
485 this->crluris = linked_list_create();
486 this->ocspuris = linked_list_create();
487 this->certinfos = linked_list_create();
488 this->crl = NULL;
489
490 /* initialize the mutex */
491 pthread_mutex_init(&(this->mutex), NULL);
492
493 /* public functions */
494 this->public.equals = (bool (*) (const ca_info_t*,const ca_info_t*))equals;
495 this->public.equals_name_release_info = (bool (*) (ca_info_t*,const char*))equals_name_release_info;
496 this->public.is_cert_issuer = (bool (*) (ca_info_t*,const x509_t*))is_cert_issuer;
497 this->public.is_crl_issuer = (bool (*) (ca_info_t*,const crl_t*))is_crl_issuer;
498 this->public.add_info = (void (*) (ca_info_t*,const ca_info_t*))add_info;
499 this->public.add_crl = (void (*) (ca_info_t*,crl_t*))add_crl;
500 this->public.has_crl = (bool (*) (ca_info_t*))has_crl;
501 this->public.list_crl = (void (*) (ca_info_t*,FILE*,bool))list_crl;
502 this->public.add_crluri = (void (*) (ca_info_t*,chunk_t))add_crluri;
503 this->public.add_ocspuri = (void (*) (ca_info_t*,chunk_t))add_ocspuri;
504 this->public.get_certificate = (x509_t* (*) (ca_info_t*))get_certificate;
505 this->public.verify_by_crl = (cert_status_t (*) (ca_info_t*,const x509_t*,certinfo_t*))verify_by_crl;
506 this->public.verify_by_ocsp = (cert_status_t (*) (ca_info_t*,const x509_t*,certinfo_t*))verify_by_ocsp;
507 this->public.destroy = (void (*) (ca_info_t*))destroy;
508
509 return &this->public;
510 }