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_ca
163 static bool is_ca(private_ca_info_t
*this)
165 return this->cacert
->is_ca(this->cacert
);
169 * Implements ca_info_t.is_strict
171 static bool is_strict(private_ca_info_t
*this)
173 bool strict
= strict_crl_policy
!= STRICT_NO
;
175 if (strict_crl_policy
== STRICT_IFURI
)
177 pthread_mutex_lock(&(this->mutex
));
178 strict
= this->crluris
->get_count(this->crluris
) > 0 ||
179 this->ocspuris
->get_count(this->ocspuris
) > 0;
180 pthread_mutex_unlock(&(this->mutex
));
186 * Implements ca_info_t.has_crl
188 static bool has_crl(private_ca_info_t
*this)
192 pthread_mutex_lock(&(this->mutex
));
193 found
= this->crl
!= NULL
;
194 pthread_mutex_unlock(&(this->mutex
));
200 * Implements ca_info_t.has_certinfos
202 static bool has_certinfos(private_ca_info_t
*this)
206 pthread_mutex_lock(&(this->mutex
));
207 found
= this->certinfos
->get_count(this->certinfos
) > 0;
208 pthread_mutex_unlock(&(this->mutex
));
214 * Implements ca_info_t.add_crl
216 static void add_crl(private_ca_info_t
*this, crl_t
*crl
)
218 pthread_mutex_lock(&(this->mutex
));
222 if (crl
->is_newer(crl
, this->crl
))
224 this->crl
->destroy(this->crl
);
226 DBG1(" this crl is newer - existing crl replaced");
231 DBG1(" this crl is not newer - existing crl retained");
240 pthread_mutex_unlock(&(this->mutex
));
244 * Implements ca_info_t.list_crl
246 static void list_crl(private_ca_info_t
*this, FILE *out
, bool utc
)
248 pthread_mutex_lock(&this->mutex
);
249 this->crl
->list(this->crl
, out
, utc
);
250 pthread_mutex_unlock(&this->mutex
);
254 * Implements ca_info_t.list_certinfos
256 static void list_certinfos(private_ca_info_t
*this, FILE *out
, bool utc
)
258 iterator_t
*iterator
;
259 certinfo_t
*certinfo
;
262 pthread_mutex_lock(&this->mutex
);
264 authkey
= this->cacert
->get_subjectKeyID(this->cacert
);
265 fprintf(out
," authname: '%D'\n", this->cacert
->get_subject(this->cacert
));
266 fprintf(out
," authkey: %#B\n", &authkey
);
268 iterator
= this->certinfos
->create_iterator(this->certinfos
, TRUE
);
269 while (iterator
->iterate(iterator
, (void**)&certinfo
))
271 time_t nextUpdate
, thisUpdate
, now
;
275 nextUpdate
= certinfo
->get_nextUpdate(certinfo
);
276 thisUpdate
= certinfo
->get_thisUpdate(certinfo
);
277 serial
= certinfo
->get_serialNumber(certinfo
);
279 fprintf(out
, "%#T, until %#T, ", &thisUpdate
, utc
, &nextUpdate
, utc
);
280 if (now
> nextUpdate
)
282 fprintf(out
, "expired (%V ago)\n", &now
, &nextUpdate
);
286 fprintf(out
, "ok (expires in %V)\n", &now
, &nextUpdate
);
288 fprintf(out
, " serial: %#B, %N\n", &serial
,
289 cert_status_names
, certinfo
->get_status(certinfo
));
291 iterator
->destroy(iterator
);
293 pthread_mutex_unlock(&this->mutex
);
297 * Find an exact copy of an identification in a linked list
299 static identification_t
* find_identification(linked_list_t
*list
, identification_t
*id
)
301 identification_t
*found_id
= NULL
, *current_id
;
303 iterator_t
*iterator
= list
->create_iterator(list
, TRUE
);
305 while (iterator
->iterate(iterator
, (void**)¤t_id
))
307 if (id
->equals(id
, current_id
))
309 found_id
= current_id
;
313 iterator
->destroy(iterator
);
319 * Add a unique identification to a linked list
321 static identification_t
*add_identification(linked_list_t
*list
, identification_t
*id
)
323 identification_t
*found_id
= find_identification(list
, id
);
332 list
->insert_last(list
, (void*)id
);
338 * Implements ca_info_t.add_crluri
340 static void add_crluri(private_ca_info_t
*this, chunk_t uri
)
343 (strncasecmp(uri
.ptr
, "http", 4) != 0 &&
344 strncasecmp(uri
.ptr
, "ldap", 4) != 0 &&
345 strncasecmp(uri
.ptr
, "file", 4) != 0 &&
346 strncasecmp(uri
.ptr
, "ftp", 3) != 0))
348 DBG1(" invalid crl uri '%#B'", uri
);
353 identification_t
*crluri
= identification_create_from_encoding(ID_DER_ASN1_GN_URI
, uri
);
355 pthread_mutex_lock(&(this->mutex
));
356 add_identification(this->crluris
, crluri
);
357 pthread_mutex_unlock(&(this->mutex
));
362 * Implements ca_info_t.add_ocspuri
364 static void add_ocspuri(private_ca_info_t
*this, chunk_t uri
)
366 if (uri
.len
< 7 || strncasecmp(uri
.ptr
, "http", 4) != 0)
368 DBG1(" invalid ocsp uri '%.*s'", uri
.len
, uri
.ptr
);
373 identification_t
*ocspuri
= identification_create_from_encoding(ID_DER_ASN1_GN_URI
, uri
);
375 pthread_mutex_lock(&(this->mutex
));
376 add_identification(this->ocspuris
, ocspuri
);
377 pthread_mutex_unlock(&(this->mutex
));
382 * Implements ca_info_t.add_info.
384 void add_info (private_ca_info_t
*this, const private_ca_info_t
*that
)
386 pthread_mutex_lock(&(this->mutex
));
388 if (this->name
== NULL
&& that
->name
!= NULL
)
390 this->name
= strdup(that
->name
);
393 pthread_mutex_unlock(&(this->mutex
));
396 identification_t
*uri
;
398 iterator_t
*iterator
= that
->crluris
->create_iterator(that
->crluris
, TRUE
);
400 while (iterator
->iterate(iterator
, (void**)&uri
))
402 add_crluri(this, uri
->get_encoding(uri
));
404 iterator
->destroy(iterator
);
408 identification_t
*uri
;
410 iterator_t
*iterator
= that
->ocspuris
->create_iterator(that
->ocspuris
, TRUE
);
412 while (iterator
->iterate(iterator
, (void**)&uri
))
414 add_ocspuri(this, uri
->get_encoding(uri
));
416 iterator
->destroy(iterator
);
421 * Implements ca_info_t.get_certificate.
423 static x509_t
* get_certificate(private_ca_info_t
* this)
429 * caches a crl by saving it to a given crl directory
431 void cache_crl(private_ca_info_t
* this, const char *crl_dir
, crl_t
*crl
)
433 char buffer
[BUF_LEN
];
439 chunk_t authKeyID
= this->cacert
->get_subjectKeyID(this->cacert
);
443 uri
.len
= 7 + strlen(crl_dir
) + 1 + 2*authKeyID
.len
+ 4;
445 if (uri
.len
>= BUF_LEN
)
447 DBG1("file uri exceeds buffer length of %d bytes - crl not saved", BUF_LEN
);
451 /* print the file uri prefix */
452 n
= snprintf(pos
, len
, "file://");
455 /* remember the start of the path string */
458 /* print the default crl directory path */
459 n
= snprintf(pos
, len
, "%s/", crl_dir
);
462 /* create and print a unique crl filename derived from the authKeyID */
463 while (authKeyID
.len
-- > 0)
465 n
= snprintf(pos
, len
, "%02x", *authKeyID
.ptr
++);
469 /* add the file suffix */
470 n
= snprintf(pos
, len
, ".crl");
472 if (crl
->write_to_file(crl
, path
, 0022, TRUE
))
474 identification_t
*crluri
= identification_create_from_encoding(ID_DER_ASN1_GN_URI
, uri
);
476 add_identification(this->crluris
, crluri
);
481 * Implements ca_info_t.verify_by_crl.
483 static cert_status_t
verify_by_crl(private_ca_info_t
* this, certinfo_t
*certinfo
,
486 rsa_public_key_t
*issuer_public_key
= this->cacert
->get_public_key(this->cacert
);
489 pthread_mutex_lock(&(this->mutex
));
490 if (this->crl
== NULL
)
493 DBG1("no crl is locally available");
497 stale
= !this->crl
->is_valid(this->crl
);
498 DBG1("crl is %s", stale?
"stale":"valid");
501 if (stale
&& crl_check_interval
> 0)
503 iterator_t
*iterator
= this->crluris
->create_iterator(this->crluris
, TRUE
);
504 identification_t
*uri
;
506 while (iterator
->iterate(iterator
, (void**)&uri
))
509 char uri_string
[BUF_LEN
];
510 chunk_t uri_chunk
= uri
->get_encoding(uri
);
511 chunk_t response_chunk
;
513 snprintf(uri_string
, BUF_LEN
, "%.*s", uri_chunk
.len
, uri_chunk
.ptr
);
514 fetcher
= fetcher_create(uri_string
);
516 response_chunk
= fetcher
->get(fetcher
);
517 fetcher
->destroy(fetcher
);
518 if (response_chunk
.ptr
!= NULL
)
520 crl_t
*crl
= crl_create_from_chunk(response_chunk
);
524 free(response_chunk
.ptr
);
527 if (!is_crl_issuer(this, crl
))
529 DBG1(" fetched crl has wrong issuer");
533 if (!crl
->verify(crl
, issuer_public_key
))
535 DBG1("fetched crl signature is invalid");
539 DBG2("fetched crl signature is valid");
541 if (this->crl
== NULL
)
545 else if (crl
->is_newer(crl
, this->crl
))
547 this->crl
->destroy(this->crl
);
549 DBG1("this crl is newer - existing crl replaced");
554 DBG1("this crl is not newer - existing crl retained");
557 if (crl
->is_valid(crl
))
559 if (cache_crls
&& strncasecmp(uri_string
, "file", 4) != 0)
561 cache_crl(this, crl_dir
, crl
);
563 /* we found a valid crl and therefore exit the fetch loop */
568 DBG1("fetched crl is stale");
572 iterator
->destroy(iterator
);
577 if (!this->crl
->verify(this->crl
, issuer_public_key
))
579 DBG1("crl signature is invalid");
582 DBG2("crl signature is valid");
584 this->crl
->get_status(this->crl
, certinfo
);
588 pthread_mutex_unlock(&(this->mutex
));
589 return certinfo
->get_status(certinfo
);
593 * Implements ca_info_t.verify_by_ocsp.
595 static cert_status_t
verify_by_ocsp(private_ca_info_t
* this,
596 certinfo_t
*certinfo
,
597 credential_store_t
*credentials
)
600 iterator_t
*iterator
;
601 certinfo_t
*cached_certinfo
= NULL
;
604 pthread_mutex_lock(&(this->mutex
));
606 /* do we support OCSP at all? */
607 if (this->ocspuris
->get_count(this->ocspuris
) == 0)
612 iterator
= this->certinfos
->create_iterator(this->certinfos
, TRUE
);
614 /* find the list insertion point in alphabetical order */
615 while(iterator
->iterate(iterator
, (void**)&cached_certinfo
))
617 comparison
= certinfo
->compare_serialNumber(certinfo
, cached_certinfo
);
625 /* do we have a valid certinfo_t for this serial number in our cache? */
628 stale
= cached_certinfo
->get_nextUpdate(cached_certinfo
) < time(NULL
);
629 DBG1("ocsp status in cache is %s", stale ?
"stale":"fresh");
634 DBG1("ocsp status is not in cache");
641 ocsp
= ocsp_create(this->cacert
, this->ocspuris
);
642 ocsp
->fetch(ocsp
, certinfo
, credentials
);
643 if (certinfo
->get_status(certinfo
) != CERT_UNDEFINED
)
647 cached_certinfo
= certinfo_create(certinfo
->get_serialNumber(certinfo
));
651 iterator
->insert_after(iterator
, (void *)cached_certinfo
);
655 iterator
->insert_before(iterator
, (void *)cached_certinfo
);
658 cached_certinfo
->update(cached_certinfo
, certinfo
);
664 certinfo
->update(certinfo
, cached_certinfo
);
667 iterator
->destroy(iterator
);
670 pthread_mutex_unlock(&(this->mutex
));
671 return certinfo
->get_status(certinfo
);
675 * Implements ca_info_t.purge_ocsp
677 static void purge_ocsp(private_ca_info_t
*this)
679 pthread_mutex_lock(&(this->mutex
));
681 this->certinfos
->destroy_offset(this->certinfos
,
682 offsetof(certinfo_t
, destroy
));
683 this->certinfos
= linked_list_create();
685 pthread_mutex_unlock(&(this->mutex
));
689 * Implements ca_info_t.destroy
691 static void destroy(private_ca_info_t
*this)
693 this->attrcerts
->destroy_offset(this->attrcerts
,
694 offsetof(x509ac_t
, destroy
));
695 this->crluris
->destroy_offset(this->crluris
,
696 offsetof(identification_t
, destroy
));
697 this->ocspuris
->destroy_offset(this->ocspuris
,
698 offsetof(identification_t
, destroy
));
699 this->certinfos
->destroy_offset(this->certinfos
,
700 offsetof(certinfo_t
, destroy
));
701 DESTROY_IF(this->crl
);
707 * list the info of this CA
709 static void list(private_ca_info_t
* this, FILE* out
, bool utc
)
712 identification_t
*uri
;
713 iterator_t
*iterator
;
716 pthread_mutex_lock(&(this->mutex
));
717 fprintf(out
, "%#T", &this->installed
, utc
);
721 fprintf(out
, ", \"%s\"\n", this->name
);
728 fprintf(out
, " authname: '%D'\n", this->cacert
->get_subject(this->cacert
));
729 chunk
= this->cacert
->get_subjectKeyID(this->cacert
);
730 fprintf(out
, " authkey: %#B\n", &chunk
);
731 chunk
= this->cacert
->get_keyid(this->cacert
);
732 fprintf(out
, " keyid: %#B\n", &chunk
);
735 iterator
= this->crluris
->create_iterator(this->crluris
, TRUE
);
736 while (iterator
->iterate(iterator
, (void**)&uri
))
738 fprintf(out
, " %s '%D'\n", first ?
"crluris:":" ", uri
);
741 iterator
->destroy(iterator
);
744 iterator
= this->ocspuris
->create_iterator(this->ocspuris
, TRUE
);
745 while (iterator
->iterate(iterator
, (void**)&uri
))
747 fprintf(out
, " %s '%D'\n", first ?
"ocspuris:":" ", uri
);
750 iterator
->destroy(iterator
);
751 pthread_mutex_unlock(&(this->mutex
));
755 * Described in header.
757 void ca_info_set_options(strict_t strict
, bool cache
, u_int interval
)
759 strict_crl_policy
= strict
;
761 crl_check_interval
= interval
;
765 * Described in header.
767 ca_info_t
*ca_info_create(const char *name
, x509_t
*cacert
)
769 private_ca_info_t
*this = malloc_thing(private_ca_info_t
);
772 this->installed
= time(NULL
);
773 this->name
= (name
== NULL
)? NULL
:strdup(name
);
774 this->cacert
= cacert
;
775 this->attrcerts
= linked_list_create();
776 this->crluris
= linked_list_create();
777 this->ocspuris
= linked_list_create();
778 this->certinfos
= linked_list_create();
781 /* initialize the mutex */
782 pthread_mutex_init(&(this->mutex
), NULL
);
784 /* public functions */
785 this->public.equals
= (bool (*) (const ca_info_t
*,const ca_info_t
*))equals
;
786 this->public.equals_name_release_info
= (bool (*) (ca_info_t
*,const char*))equals_name_release_info
;
787 this->public.is_cert_issuer
= (bool (*) (ca_info_t
*,const x509_t
*))is_cert_issuer
;
788 this->public.is_crl_issuer
= (bool (*) (ca_info_t
*,const crl_t
*))is_crl_issuer
;
789 this->public.is_ca
= (bool (*) (ca_info_t
*))is_ca
;
790 this->public.is_strict
= (bool (*) (ca_info_t
*))is_strict
;
791 this->public.add_info
= (void (*) (ca_info_t
*,const ca_info_t
*))add_info
;
792 this->public.add_crl
= (void (*) (ca_info_t
*,crl_t
*))add_crl
;
793 this->public.has_crl
= (bool (*) (ca_info_t
*))has_crl
;
794 this->public.has_certinfos
= (bool (*) (ca_info_t
*))has_certinfos
;
795 this->public.list
= (void (*) (ca_info_t
*,FILE*,bool))list
;
796 this->public.list_crl
= (void (*) (ca_info_t
*,FILE*,bool))list_crl
;
797 this->public.list_certinfos
= (void (*) (ca_info_t
*,FILE*,bool))list_certinfos
;
798 this->public.add_crluri
= (void (*) (ca_info_t
*,chunk_t
))add_crluri
;
799 this->public.add_ocspuri
= (void (*) (ca_info_t
*,chunk_t
))add_ocspuri
;
800 this->public.get_certificate
= (x509_t
* (*) (ca_info_t
*))get_certificate
;
801 this->public.verify_by_crl
= (cert_status_t (*) (ca_info_t
*,certinfo_t
*, const char*))verify_by_crl
;
802 this->public.verify_by_ocsp
= (cert_status_t (*) (ca_info_t
*,certinfo_t
*,credential_store_t
*))verify_by_ocsp
;
803 this->public.purge_ocsp
= (void (*) (ca_info_t
*))purge_ocsp
;
804 this->public.destroy
= (void (*) (ca_info_t
*))destroy
;
806 return &this->public;