4 * @brief Implementation of ca_info_t.
9 * Copyright (C) 2007 Andreas Steffen
10 * Hochschule fuer Technik Rapperswil
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>.
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
38 #include <utils/linked_list.h>
39 #include <utils/identification.h>
40 #include <utils/fetcher.h>
42 typedef struct private_ca_info_t private_ca_info_t
;
45 * Private data of a ca_info_t object.
47 struct private_ca_info_t
{
49 * Public interface for this ca info record
54 * Name of the ca info record
59 * Time when ca info record was installed
64 * Distinguished Name of the CA
69 * List of attribute certificates
71 linked_list_t
*attrcerts
;
76 linked_list_t
*crluris
;
81 linked_list_t
*ocspuris
;
84 * CRL issued by this ca
89 * List of certificate info records
91 linked_list_t
*certinfos
;
94 * mutex controls access to the elements:
95 * name, crluris, ocspuris, crl, and certinfos
97 pthread_mutex_t mutex
;
101 * static options set by ca_info_set_options()
103 static strict_t strict_crl_policy
= STRICT_NO
;
104 static bool cache_crls
= FALSE
;
105 static u_int crl_check_interval
= 0;
108 * Implements ca_info_t.equals
110 static bool equals(const private_ca_info_t
*this, const private_ca_info_t
*that
)
112 return chunk_equals(this->cacert
->get_keyid(this->cacert
),
113 that
->cacert
->get_keyid(that
->cacert
));
117 * Implements ca_info_t.equals_name_release_info
119 static bool equals_name_release_info(private_ca_info_t
*this, const char *name
)
123 pthread_mutex_lock(&(this->mutex
));
124 found
= this->name
!= NULL
&& streq(this->name
, name
);
128 this->crluris
->destroy_offset(this->crluris
,
129 offsetof(identification_t
, destroy
));
130 this->crluris
= linked_list_create();
132 this->ocspuris
->destroy_offset(this->ocspuris
,
133 offsetof(identification_t
, destroy
));
134 this->ocspuris
= linked_list_create();
140 pthread_mutex_unlock(&(this->mutex
));
145 * Implements ca_info_t.is_crl_issuer
147 static bool is_cert_issuer(private_ca_info_t
*this, const x509_t
*cert
)
149 return cert
->is_issuer(cert
, this->cacert
);
153 * Implements ca_info_t.is_crl_issuer
155 static bool is_crl_issuer(private_ca_info_t
*this, const crl_t
*crl
)
157 return crl
->is_issuer(crl
, this->cacert
);
161 * Implements ca_info_t.is_strict
163 static bool is_strict(private_ca_info_t
*this)
165 bool strict
= strict_crl_policy
!= STRICT_NO
;
167 if (strict_crl_policy
== STRICT_IFURI
)
169 pthread_mutex_lock(&(this->mutex
));
170 strict
= this->crluris
->get_count(this->crluris
) > 0 ||
171 this->ocspuris
->get_count(this->ocspuris
) > 0;
172 pthread_mutex_unlock(&(this->mutex
));
178 * Implements ca_info_t.has_crl
180 static bool has_crl(private_ca_info_t
*this)
184 pthread_mutex_lock(&(this->mutex
));
185 found
= this->crl
!= NULL
;
186 pthread_mutex_unlock(&(this->mutex
));
192 * Implements ca_info_t.has_certinfos
194 static bool has_certinfos(private_ca_info_t
*this)
198 pthread_mutex_lock(&(this->mutex
));
199 found
= this->certinfos
->get_count(this->certinfos
) > 0;
200 pthread_mutex_unlock(&(this->mutex
));
206 * Implements ca_info_t.add_crl
208 static void add_crl(private_ca_info_t
*this, crl_t
*crl
)
210 pthread_mutex_lock(&(this->mutex
));
214 if (crl
->is_newer(crl
, this->crl
))
216 this->crl
->destroy(this->crl
);
218 DBG1(" this crl is newer - existing crl replaced");
223 DBG1(" this crl is not newer - existing crl retained");
232 pthread_mutex_unlock(&(this->mutex
));
236 * Implements ca_info_t.list_crl
238 static void list_crl(private_ca_info_t
*this, FILE *out
, bool utc
)
240 pthread_mutex_lock(&this->mutex
);
241 this->crl
->list(this->crl
, out
, utc
);
242 pthread_mutex_unlock(&this->mutex
);
246 * Implements ca_info_t.list_certinfos
248 static void list_certinfos(private_ca_info_t
*this, FILE *out
, bool utc
)
250 iterator_t
*iterator
;
251 certinfo_t
*certinfo
;
254 pthread_mutex_lock(&this->mutex
);
256 authkey
= this->cacert
->get_subjectKeyID(this->cacert
);
257 fprintf(out
," authname: '%D'\n", this->cacert
->get_subject(this->cacert
));
258 fprintf(out
," authkey: %#B\n", &authkey
);
260 iterator
= this->certinfos
->create_iterator(this->certinfos
, TRUE
);
261 while (iterator
->iterate(iterator
, (void**)&certinfo
))
263 time_t nextUpdate
, thisUpdate
, now
;
267 nextUpdate
= certinfo
->get_nextUpdate(certinfo
);
268 thisUpdate
= certinfo
->get_thisUpdate(certinfo
);
269 serial
= certinfo
->get_serialNumber(certinfo
);
271 fprintf(out
, "%#T, until %#T, ", &thisUpdate
, utc
, &nextUpdate
, utc
);
272 if (now
> nextUpdate
)
274 fprintf(out
, "expired (%V ago)\n", &now
, &nextUpdate
);
278 fprintf(out
, "ok (expires in %V)\n", &now
, &nextUpdate
);
280 fprintf(out
, " serial: %#B, %N\n", &serial
,
281 cert_status_names
, certinfo
->get_status(certinfo
));
283 iterator
->destroy(iterator
);
285 pthread_mutex_unlock(&this->mutex
);
289 * Find an exact copy of an identification in a linked list
291 static identification_t
* find_identification(linked_list_t
*list
, identification_t
*id
)
293 identification_t
*found_id
= NULL
, *current_id
;
295 iterator_t
*iterator
= list
->create_iterator(list
, TRUE
);
297 while (iterator
->iterate(iterator
, (void**)¤t_id
))
299 if (id
->equals(id
, current_id
))
301 found_id
= current_id
;
305 iterator
->destroy(iterator
);
311 * Add a unique identification to a linked list
313 static identification_t
*add_identification(linked_list_t
*list
, identification_t
*id
)
315 identification_t
*found_id
= find_identification(list
, id
);
324 list
->insert_last(list
, (void*)id
);
330 * Implements ca_info_t.add_crluri
332 static void add_crluri(private_ca_info_t
*this, chunk_t uri
)
335 (strncasecmp(uri
.ptr
, "http", 4) != 0 &&
336 strncasecmp(uri
.ptr
, "ldap", 4) != 0 &&
337 strncasecmp(uri
.ptr
, "file", 4) != 0 &&
338 strncasecmp(uri
.ptr
, "ftp", 3) != 0))
340 DBG1(" invalid crl uri '%#B'", uri
);
345 identification_t
*crluri
= identification_create_from_encoding(ID_DER_ASN1_GN_URI
, uri
);
347 pthread_mutex_lock(&(this->mutex
));
348 add_identification(this->crluris
, crluri
);
349 pthread_mutex_unlock(&(this->mutex
));
354 * Implements ca_info_t.add_ocspuri
356 static void add_ocspuri(private_ca_info_t
*this, chunk_t uri
)
358 if (uri
.len
< 7 || strncasecmp(uri
.ptr
, "http", 4) != 0)
360 DBG1(" invalid ocsp uri '%.*s'", uri
.len
, uri
.ptr
);
365 identification_t
*ocspuri
= identification_create_from_encoding(ID_DER_ASN1_GN_URI
, uri
);
367 pthread_mutex_lock(&(this->mutex
));
368 add_identification(this->ocspuris
, ocspuri
);
369 pthread_mutex_unlock(&(this->mutex
));
374 * Implements ca_info_t.add_info.
376 void add_info (private_ca_info_t
*this, const private_ca_info_t
*that
)
378 pthread_mutex_lock(&(this->mutex
));
380 if (this->name
== NULL
&& that
->name
!= NULL
)
382 this->name
= strdup(that
->name
);
385 pthread_mutex_unlock(&(this->mutex
));
388 identification_t
*uri
;
390 iterator_t
*iterator
= that
->crluris
->create_iterator(that
->crluris
, TRUE
);
392 while (iterator
->iterate(iterator
, (void**)&uri
))
394 add_crluri(this, uri
->get_encoding(uri
));
396 iterator
->destroy(iterator
);
400 identification_t
*uri
;
402 iterator_t
*iterator
= that
->ocspuris
->create_iterator(that
->ocspuris
, TRUE
);
404 while (iterator
->iterate(iterator
, (void**)&uri
))
406 add_ocspuri(this, uri
->get_encoding(uri
));
408 iterator
->destroy(iterator
);
413 * Implements ca_info_t.get_certificate.
415 static x509_t
* get_certificate(private_ca_info_t
* this)
421 * caches a crl by saving it to a given crl directory
423 void cache_crl(private_ca_info_t
* this, const char *crl_dir
, crl_t
*crl
)
425 char buffer
[BUF_LEN
];
431 chunk_t authKeyID
= this->cacert
->get_subjectKeyID(this->cacert
);
435 uri
.len
= 7 + strlen(crl_dir
) + 1 + 2*authKeyID
.len
+ 4;
437 if (uri
.len
>= BUF_LEN
)
439 DBG1("file uri exceeds buffer length of %d bytes - crl not saved", BUF_LEN
);
443 /* print the file uri prefix */
444 n
= snprintf(pos
, len
, "file://");
447 /* remember the start of the path string */
450 /* print the default crl directory path */
451 n
= snprintf(pos
, len
, "%s/", crl_dir
);
454 /* create and print a unique crl filename derived from the authKeyID */
455 while (authKeyID
.len
-- > 0)
457 n
= snprintf(pos
, len
, "%02x", *authKeyID
.ptr
++);
461 /* add the file suffix */
462 n
= snprintf(pos
, len
, ".crl");
464 if (crl
->write_to_file(crl
, path
, 0022, TRUE
))
466 identification_t
*crluri
= identification_create_from_encoding(ID_DER_ASN1_GN_URI
, uri
);
468 add_identification(this->crluris
, crluri
);
473 * Implements ca_info_t.verify_by_crl.
475 static cert_status_t
verify_by_crl(private_ca_info_t
* this, certinfo_t
*certinfo
,
478 rsa_public_key_t
*issuer_public_key
= this->cacert
->get_public_key(this->cacert
);
481 pthread_mutex_lock(&(this->mutex
));
482 if (this->crl
== NULL
)
485 DBG1("no crl is locally available");
489 stale
= !this->crl
->is_valid(this->crl
);
490 DBG1("crl is %s", stale?
"stale":"valid");
493 if (stale
&& crl_check_interval
> 0)
495 iterator_t
*iterator
= this->crluris
->create_iterator(this->crluris
, TRUE
);
496 identification_t
*uri
;
498 while (iterator
->iterate(iterator
, (void**)&uri
))
501 char uri_string
[BUF_LEN
];
502 chunk_t uri_chunk
= uri
->get_encoding(uri
);
503 chunk_t response_chunk
;
505 snprintf(uri_string
, BUF_LEN
, "%.*s", uri_chunk
.len
, uri_chunk
.ptr
);
506 fetcher
= fetcher_create(uri_string
);
508 response_chunk
= fetcher
->get(fetcher
);
509 fetcher
->destroy(fetcher
);
510 if (response_chunk
.ptr
!= NULL
)
512 crl_t
*crl
= crl_create_from_chunk(response_chunk
);
516 free(response_chunk
.ptr
);
519 if (!is_crl_issuer(this, crl
))
521 DBG1(" fetched crl has wrong issuer");
525 if (!crl
->verify(crl
, issuer_public_key
))
527 DBG1("fetched crl signature is invalid");
531 DBG2("fetched crl signature is valid");
533 if (this->crl
== NULL
)
537 else if (crl
->is_newer(crl
, this->crl
))
539 this->crl
->destroy(this->crl
);
541 DBG1("this crl is newer - existing crl replaced");
546 DBG1("this crl is not newer - existing crl retained");
549 if (crl
->is_valid(crl
))
551 if (cache_crls
&& strncasecmp(uri_string
, "file", 4) != 0)
553 cache_crl(this, crl_dir
, crl
);
555 /* we found a valid crl and therefore exit the fetch loop */
560 DBG1("fetched crl is stale");
564 iterator
->destroy(iterator
);
569 if (!this->crl
->verify(this->crl
, issuer_public_key
))
571 DBG1("crl signature is invalid");
574 DBG2("crl signature is valid");
576 this->crl
->get_status(this->crl
, certinfo
);
580 pthread_mutex_unlock(&(this->mutex
));
581 return certinfo
->get_status(certinfo
);
585 * Implements ca_info_t.verify_by_ocsp.
587 static cert_status_t
verify_by_ocsp(private_ca_info_t
* this,
588 certinfo_t
*certinfo
,
589 credential_store_t
*credentials
)
592 iterator_t
*iterator
;
593 certinfo_t
*cached_certinfo
= NULL
;
596 pthread_mutex_lock(&(this->mutex
));
598 /* do we support OCSP at all? */
599 if (this->ocspuris
->get_count(this->ocspuris
) == 0)
604 iterator
= this->certinfos
->create_iterator(this->certinfos
, TRUE
);
606 /* find the list insertion point in alphabetical order */
607 while(iterator
->iterate(iterator
, (void**)&cached_certinfo
))
609 comparison
= certinfo
->compare_serialNumber(certinfo
, cached_certinfo
);
617 /* do we have a valid certinfo_t for this serial number in our cache? */
620 stale
= cached_certinfo
->get_nextUpdate(cached_certinfo
) < time(NULL
);
621 DBG1("ocsp status in cache is %s", stale ?
"stale":"fresh");
626 DBG1("ocsp status is not in cache");
633 ocsp
= ocsp_create(this->cacert
, this->ocspuris
);
634 ocsp
->fetch(ocsp
, certinfo
, credentials
);
635 if (certinfo
->get_status(certinfo
) != CERT_UNDEFINED
)
639 cached_certinfo
= certinfo_create(certinfo
->get_serialNumber(certinfo
));
643 iterator
->insert_after(iterator
, (void *)cached_certinfo
);
647 iterator
->insert_before(iterator
, (void *)cached_certinfo
);
650 cached_certinfo
->update(cached_certinfo
, certinfo
);
656 certinfo
->update(certinfo
, cached_certinfo
);
659 iterator
->destroy(iterator
);
662 pthread_mutex_unlock(&(this->mutex
));
663 return certinfo
->get_status(certinfo
);
667 * Implements ca_info_t.purge_ocsp
669 static void purge_ocsp(private_ca_info_t
*this)
671 pthread_mutex_lock(&(this->mutex
));
673 this->certinfos
->destroy_offset(this->certinfos
,
674 offsetof(certinfo_t
, destroy
));
675 this->certinfos
= linked_list_create();
677 pthread_mutex_unlock(&(this->mutex
));
681 * Implements ca_info_t.destroy
683 static void destroy(private_ca_info_t
*this)
685 this->attrcerts
->destroy_offset(this->attrcerts
,
686 offsetof(x509ac_t
, destroy
));
687 this->crluris
->destroy_offset(this->crluris
,
688 offsetof(identification_t
, destroy
));
689 this->ocspuris
->destroy_offset(this->ocspuris
,
690 offsetof(identification_t
, destroy
));
691 this->certinfos
->destroy_offset(this->certinfos
,
692 offsetof(certinfo_t
, destroy
));
693 DESTROY_IF(this->crl
);
699 * list the info of this CA
701 static void list(private_ca_info_t
* this, FILE* out
, bool utc
)
704 identification_t
*uri
;
705 iterator_t
*iterator
;
708 pthread_mutex_lock(&(this->mutex
));
709 fprintf(out
, "%#T", &this->installed
, utc
);
713 fprintf(out
, ", \"%s\"\n", this->name
);
720 fprintf(out
, " authname: '%D'\n", this->cacert
->get_subject(this->cacert
));
721 chunk
= this->cacert
->get_subjectKeyID(this->cacert
);
722 fprintf(out
, " authkey: %#B\n", &chunk
);
723 chunk
= this->cacert
->get_keyid(this->cacert
);
724 fprintf(out
, " keyid: %#B\n", &chunk
);
727 iterator
= this->crluris
->create_iterator(this->crluris
, TRUE
);
728 while (iterator
->iterate(iterator
, (void**)&uri
))
730 fprintf(out
, " %s '%D'\n", first ?
"crluris:":" ", uri
);
733 iterator
->destroy(iterator
);
736 iterator
= this->ocspuris
->create_iterator(this->ocspuris
, TRUE
);
737 while (iterator
->iterate(iterator
, (void**)&uri
))
739 fprintf(out
, " %s '%D'\n", first ?
"ocspuris:":" ", uri
);
742 iterator
->destroy(iterator
);
743 pthread_mutex_unlock(&(this->mutex
));
747 * Described in header.
749 void ca_info_set_options(strict_t strict
, bool cache
, u_int interval
)
751 strict_crl_policy
= strict
;
753 crl_check_interval
= interval
;
757 * Described in header.
759 ca_info_t
*ca_info_create(const char *name
, x509_t
*cacert
)
761 private_ca_info_t
*this = malloc_thing(private_ca_info_t
);
764 this->installed
= time(NULL
);
765 this->name
= (name
== NULL
)? NULL
:strdup(name
);
766 this->cacert
= cacert
;
767 this->attrcerts
= linked_list_create();
768 this->crluris
= linked_list_create();
769 this->ocspuris
= linked_list_create();
770 this->certinfos
= linked_list_create();
773 /* initialize the mutex */
774 pthread_mutex_init(&(this->mutex
), NULL
);
776 /* public functions */
777 this->public.equals
= (bool (*) (const ca_info_t
*,const ca_info_t
*))equals
;
778 this->public.equals_name_release_info
= (bool (*) (ca_info_t
*,const char*))equals_name_release_info
;
779 this->public.is_cert_issuer
= (bool (*) (ca_info_t
*,const x509_t
*))is_cert_issuer
;
780 this->public.is_crl_issuer
= (bool (*) (ca_info_t
*,const crl_t
*))is_crl_issuer
;
781 this->public.is_strict
= (bool (*) (ca_info_t
*))is_strict
;
782 this->public.add_info
= (void (*) (ca_info_t
*,const ca_info_t
*))add_info
;
783 this->public.add_crl
= (void (*) (ca_info_t
*,crl_t
*))add_crl
;
784 this->public.has_crl
= (bool (*) (ca_info_t
*))has_crl
;
785 this->public.has_certinfos
= (bool (*) (ca_info_t
*))has_certinfos
;
786 this->public.list
= (void (*) (ca_info_t
*,FILE*,bool))list
;
787 this->public.list_crl
= (void (*) (ca_info_t
*,FILE*,bool))list_crl
;
788 this->public.list_certinfos
= (void (*) (ca_info_t
*,FILE*,bool))list_certinfos
;
789 this->public.add_crluri
= (void (*) (ca_info_t
*,chunk_t
))add_crluri
;
790 this->public.add_ocspuri
= (void (*) (ca_info_t
*,chunk_t
))add_ocspuri
;
791 this->public.get_certificate
= (x509_t
* (*) (ca_info_t
*))get_certificate
;
792 this->public.verify_by_crl
= (cert_status_t (*) (ca_info_t
*,certinfo_t
*, const char*))verify_by_crl
;
793 this->public.verify_by_ocsp
= (cert_status_t (*) (ca_info_t
*,certinfo_t
*,credential_store_t
*))verify_by_ocsp
;
794 this->public.purge_ocsp
= (void (*) (ca_info_t
*))purge_ocsp
;
795 this->public.destroy
= (void (*) (ca_info_t
*))destroy
;
797 return &this->public;