a8919e630aba0706ec1c5b1d133b76608463aeca
[strongswan.git] / src / pluto / fetch.c
1 /* Dynamic fetching of X.509 CRLs
2 * Copyright (C) 2002 Stephane Laroche <stephane.laroche@colubris.com>
3 * Copyright (C) 2002-2004 Andreas Steffen, Zuercher Hochschule Winterthur
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 *
15 * RCSID $Id$
16 */
17
18 #include <stdlib.h>
19 #include <errno.h>
20 #include <sys/time.h>
21 #include <time.h>
22 #include <string.h>
23
24 #ifdef THREADS
25 #include <pthread.h>
26 #endif
27
28 #ifdef LIBCURL
29 #include <curl/curl.h>
30 #endif
31
32 #include <freeswan.h>
33
34 #ifdef LIBLDAP
35 #ifndef LDAP_DEPRECATED
36 #define LDAP_DEPRECATED 1
37 #endif
38 #include <ldap.h>
39 #endif
40
41 #include "asn1/asn1.h"
42
43 #include "constants.h"
44 #include "defs.h"
45 #include "log.h"
46 #include "id.h"
47 #include "pem.h"
48 #include "x509.h"
49 #include "ca.h"
50 #include "whack.h"
51 #include "ocsp.h"
52 #include "crl.h"
53 #include "fetch.h"
54
55 fetch_req_t empty_fetch_req = {
56 NULL , /* next */
57 0 , /* installed */
58 0 , /* trials */
59 { NULL, 0}, /* issuer */
60 { NULL, 0}, /* authKeyID */
61 { NULL, 0}, /* authKeySerialNumber */
62 NULL /* distributionPoints */
63 };
64
65 /* chained list of crl fetch requests */
66 static fetch_req_t *crl_fetch_reqs = NULL;
67
68 /* chained list of ocsp fetch requests */
69 static ocsp_location_t *ocsp_fetch_reqs = NULL;
70
71 #ifdef THREADS
72 static pthread_t thread;
73 static pthread_mutex_t certs_and_keys_mutex = PTHREAD_MUTEX_INITIALIZER;
74 static pthread_mutex_t authcert_list_mutex = PTHREAD_MUTEX_INITIALIZER;
75 static pthread_mutex_t crl_list_mutex = PTHREAD_MUTEX_INITIALIZER;
76 static pthread_mutex_t ocsp_cache_mutex = PTHREAD_MUTEX_INITIALIZER;
77 static pthread_mutex_t ca_info_list_mutex = PTHREAD_MUTEX_INITIALIZER;
78 static pthread_mutex_t crl_fetch_list_mutex = PTHREAD_MUTEX_INITIALIZER;
79 static pthread_mutex_t ocsp_fetch_list_mutex = PTHREAD_MUTEX_INITIALIZER;
80 static pthread_mutex_t fetch_wake_mutex = PTHREAD_MUTEX_INITIALIZER;
81 static pthread_cond_t fetch_wake_cond = PTHREAD_COND_INITIALIZER;
82
83 /*
84 * lock access to my certs and keys
85 */
86 void
87 lock_certs_and_keys(const char *who)
88 {
89 pthread_mutex_lock(&certs_and_keys_mutex);
90 DBG(DBG_CONTROLMORE,
91 DBG_log("certs and keys locked by '%s'", who)
92 )
93 }
94
95 /*
96 * unlock access to my certs and keys
97 */
98 void
99 unlock_certs_and_keys(const char *who)
100 {
101 DBG(DBG_CONTROLMORE,
102 DBG_log("certs and keys unlocked by '%s'", who)
103 )
104 pthread_mutex_unlock(&certs_and_keys_mutex);
105 }
106
107 /*
108 * lock access to the chained authcert list
109 */
110 void
111 lock_authcert_list(const char *who)
112 {
113 pthread_mutex_lock(&authcert_list_mutex);
114 DBG(DBG_CONTROLMORE,
115 DBG_log("authcert list locked by '%s'", who)
116 )
117 }
118
119 /*
120 * unlock access to the chained authcert list
121 */
122 void
123 unlock_authcert_list(const char *who)
124 {
125 DBG(DBG_CONTROLMORE,
126 DBG_log("authcert list unlocked by '%s'", who)
127 )
128 pthread_mutex_unlock(&authcert_list_mutex);
129 }
130
131 /*
132 * lock access to the chained crl list
133 */
134 void
135 lock_crl_list(const char *who)
136 {
137 pthread_mutex_lock(&crl_list_mutex);
138 DBG(DBG_CONTROLMORE,
139 DBG_log("crl list locked by '%s'", who)
140 )
141 }
142
143 /*
144 * unlock access to the chained crl list
145 */
146 void
147 unlock_crl_list(const char *who)
148 {
149 DBG(DBG_CONTROLMORE,
150 DBG_log("crl list unlocked by '%s'", who)
151 )
152 pthread_mutex_unlock(&crl_list_mutex);
153 }
154
155 /*
156 * lock access to the ocsp cache
157 */
158 extern void
159 lock_ocsp_cache(const char *who)
160 {
161 pthread_mutex_lock(&ocsp_cache_mutex);
162 DBG(DBG_CONTROLMORE,
163 DBG_log("ocsp cache locked by '%s'", who)
164 )
165 }
166
167 /*
168 * unlock access to the ocsp cache
169 */
170 extern void
171 unlock_ocsp_cache(const char *who)
172 {
173 DBG(DBG_CONTROLMORE,
174 DBG_log("ocsp cache unlocked by '%s'", who)
175 )
176 pthread_mutex_unlock(&ocsp_cache_mutex);
177 }
178
179 /*
180 * lock access to the ca info list
181 */
182 extern void
183 lock_ca_info_list(const char *who)
184 {
185 pthread_mutex_lock(&ca_info_list_mutex);
186 DBG(DBG_CONTROLMORE,
187 DBG_log("ca info list locked by '%s'", who)
188 )
189 }
190
191 /*
192 * unlock access to the ca info list
193 */
194 extern void
195 unlock_ca_info_list(const char *who)
196 {
197 DBG(DBG_CONTROLMORE,
198 DBG_log("ca info list unlocked by '%s'", who)
199 )
200 pthread_mutex_unlock(&ca_info_list_mutex);
201 }
202
203 /*
204 * lock access to the chained crl fetch request list
205 */
206 static void
207 lock_crl_fetch_list(const char *who)
208 {
209 pthread_mutex_lock(&crl_fetch_list_mutex);
210 DBG(DBG_CONTROLMORE,
211 DBG_log("crl fetch request list locked by '%s'", who)
212 )
213 }
214
215 /*
216 * unlock access to the chained crl fetch request list
217 */
218 static void
219 unlock_crl_fetch_list(const char *who)
220 {
221 DBG(DBG_CONTROLMORE,
222 DBG_log("crl fetch request list unlocked by '%s'", who)
223 )
224 pthread_mutex_unlock(&crl_fetch_list_mutex);
225 }
226
227 /*
228 * lock access to the chained ocsp fetch request list
229 */
230 static void
231 lock_ocsp_fetch_list(const char *who)
232 {
233 pthread_mutex_lock(&ocsp_fetch_list_mutex);
234 DBG(DBG_CONTROLMORE,
235 DBG_log("ocsp fetch request list locked by '%s'", who)
236 )
237 }
238
239 /*
240 * unlock access to the chained ocsp fetch request list
241 */
242 static void
243 unlock_ocsp_fetch_list(const char *who)
244 {
245 DBG(DBG_CONTROLMORE,
246 DBG_log("ocsp fetch request list unlocked by '%s'", who)
247 )
248 pthread_mutex_unlock(&ocsp_fetch_list_mutex);
249 }
250
251 /*
252 * wakes up the sleeping fetch thread
253 */
254 void
255 wake_fetch_thread(const char *who)
256 {
257 if (crl_check_interval > 0)
258 {
259 DBG(DBG_CONTROLMORE,
260 DBG_log("fetch thread wake call by '%s'", who)
261 )
262 pthread_mutex_lock(&fetch_wake_mutex);
263 pthread_cond_signal(&fetch_wake_cond);
264 pthread_mutex_unlock(&fetch_wake_mutex);
265 }
266 }
267 #else /* !THREADS */
268 #define lock_crl_fetch_list(who) /* do nothing */
269 #define unlock_crl_fetch_list(who) /* do nothing */
270 #define lock_ocsp_fetch_list(who) /* do nothing */
271 #define unlock_ocsp_fetch_list(who) /* do nothing */
272 #endif /* !THREADS */
273
274 /*
275 * free the dynamic memory used to store fetch requests
276 */
277 static void
278 free_fetch_request(fetch_req_t *req)
279 {
280 free(req->issuer.ptr);
281 free(req->authKeySerialNumber.ptr);
282 free(req->authKeyID.ptr);
283 free_generalNames(req->distributionPoints, TRUE);
284 free(req);
285 }
286
287 /* writes data into a dynamically resizeable chunk_t
288 * needed for libcurl responses
289 */
290 size_t
291 write_buffer(void *ptr, size_t size, size_t nmemb, void *data)
292 {
293 size_t realsize = size * nmemb;
294 chunk_t *mem = (chunk_t*)data;
295
296 mem->ptr = (u_char *)realloc(mem->ptr, mem->len + realsize);
297 if (mem->ptr) {
298 memcpy(&(mem->ptr[mem->len]), ptr, realsize);
299 mem->len += realsize;
300 }
301 return realsize;
302 }
303
304 #ifdef THREADS
305 /*
306 * fetches a binary blob from a url with libcurl
307 */
308 static err_t
309 fetch_curl(char *url, chunk_t *blob)
310 {
311 #ifdef LIBCURL
312 char errorbuffer[CURL_ERROR_SIZE] = "";
313 chunk_t response = chunk_empty;
314 CURLcode res;
315
316 /* get it with libcurl */
317 CURL *curl = curl_easy_init();
318
319 if (curl != NULL)
320 {
321 DBG(DBG_CONTROL,
322 DBG_log("Trying cURL '%s'", url)
323 )
324
325 curl_easy_setopt(curl, CURLOPT_URL, url);
326 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_buffer);
327 curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&response);
328 curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errorbuffer);
329 curl_easy_setopt(curl, CURLOPT_FAILONERROR, TRUE);
330 curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, FETCH_CMD_TIMEOUT);
331
332 res = curl_easy_perform(curl);
333
334 if (res == CURLE_OK)
335 {
336 blob->len = response.len;
337 blob->ptr = malloc(response.len);
338 memcpy(blob->ptr, response.ptr, response.len);
339 }
340 else
341 {
342 plog("fetching uri (%s) with libcurl failed: %s", url, errorbuffer);
343 }
344 curl_easy_cleanup(curl);
345 curl_free(response.ptr);
346 }
347 return strlen(errorbuffer) > 0 ? "libcurl error" : NULL;
348 #else /* !LIBCURL */
349 return "warning: not compiled with libcurl support";
350 #endif /* !LIBCURL */
351 }
352
353 #ifdef LIBLDAP
354 /*
355 * parses the result returned by an ldap query
356 */
357 static err_t
358 parse_ldap_result(LDAP * ldap, LDAPMessage *result, chunk_t *blob)
359 {
360 err_t ugh = NULL;
361
362 LDAPMessage * entry = ldap_first_entry(ldap, result);
363
364 if (entry != NULL)
365 {
366 BerElement *ber = NULL;
367 char *attr;
368
369 attr = ldap_first_attribute(ldap, entry, &ber);
370
371 if (attr != NULL)
372 {
373 struct berval **values = ldap_get_values_len(ldap, entry, attr);
374
375 if (values != NULL)
376 {
377 if (values[0] != NULL)
378 {
379 blob->len = values[0]->bv_len;
380 blob->ptr = malloc(blob->len);
381 memcpy(blob->ptr, values[0]->bv_val, blob->len);
382 if (values[1] != NULL)
383 {
384 plog("warning: more than one value was fetched from LDAP URL");
385 }
386 }
387 else
388 {
389 ugh = "no values in attribute";
390 }
391 ldap_value_free_len(values);
392 }
393 else
394 {
395 ugh = ldap_err2string(ldap_result2error(ldap, entry, 0));
396 }
397 ldap_memfree(attr);
398 }
399 else
400 {
401 ugh = ldap_err2string(ldap_result2error(ldap, entry, 0));
402 }
403 ber_free(ber, 0);
404 }
405 else
406 {
407 ugh = ldap_err2string(ldap_result2error(ldap, result, 0));
408 }
409 return ugh;
410 }
411
412 /*
413 * fetches a binary blob from an ldap url
414 */
415 static err_t
416 fetch_ldap_url(char *url, chunk_t *blob)
417 {
418 LDAPURLDesc *lurl;
419 err_t ugh = NULL;
420 int rc;
421
422 DBG(DBG_CONTROL,
423 DBG_log("Trying LDAP URL '%s'", url)
424 )
425
426 rc = ldap_url_parse(url, &lurl);
427
428 if (rc == LDAP_SUCCESS)
429 {
430 LDAP *ldap = ldap_init(lurl->lud_host, lurl->lud_port);
431
432 if (ldap != NULL)
433 {
434 int ldap_version = LDAP_VERSION3;
435 struct timeval timeout;
436
437 timeout.tv_sec = FETCH_CMD_TIMEOUT;
438 timeout.tv_usec = 0;
439 ldap_set_option(ldap, LDAP_OPT_PROTOCOL_VERSION, &ldap_version);
440 ldap_set_option(ldap, LDAP_OPT_NETWORK_TIMEOUT, &timeout);
441
442 rc = ldap_simple_bind_s(ldap, NULL, NULL);
443
444 if (rc == LDAP_SUCCESS)
445 {
446 LDAPMessage *result;
447
448 timeout.tv_sec = FETCH_CMD_TIMEOUT;
449 timeout.tv_usec = 0;
450
451 rc = ldap_search_st(ldap, lurl->lud_dn
452 , lurl->lud_scope
453 , lurl->lud_filter
454 , lurl->lud_attrs
455 , 0, &timeout, &result);
456
457 if (rc == LDAP_SUCCESS)
458 {
459 ugh = parse_ldap_result(ldap, result, blob);
460 ldap_msgfree(result);
461 }
462 else
463 {
464 ugh = ldap_err2string(rc);
465 }
466 }
467 else
468 {
469 ugh = ldap_err2string(rc);
470 }
471 ldap_unbind_s(ldap);
472 }
473 else
474 {
475 ugh = "ldap init";
476 }
477 ldap_free_urldesc(lurl);
478 }
479 else
480 {
481 ugh = ldap_err2string(rc);
482 }
483 return ugh;
484 }
485 #else /* !LIBLDAP */
486 static err_t
487 fetch_ldap_url(char *url, chunk_t *blob)
488 {
489 return "LDAP URL fetching not activated in pluto source code";
490 }
491 #endif /* !LIBLDAP */
492
493 /*
494 * fetch an ASN.1 blob coded in PEM or DER format from a URL
495 */
496 static err_t
497 fetch_asn1_blob(char *url, chunk_t *blob)
498 {
499 err_t ugh = NULL;
500
501 if (strlen(url) >= 4 && strncasecmp(url, "ldap", 4) == 0)
502 {
503 ugh = fetch_ldap_url(url, blob);
504 }
505 else
506 {
507 ugh = fetch_curl(url, blob);
508 }
509 if (ugh != NULL)
510 return ugh;
511
512 if (is_asn1(*blob))
513 {
514 DBG(DBG_PARSING,
515 DBG_log(" fetched blob coded in DER format")
516 )
517 }
518 else
519 {
520 bool pgp = FALSE;
521
522 ugh = pemtobin(blob, NULL, "", &pgp);
523 if (ugh == NULL)
524 {
525 if (is_asn1(*blob))
526 {
527 DBG(DBG_PARSING,
528 DBG_log(" fetched blob coded in PEM format")
529 )
530 }
531 else
532 {
533 ugh = "blob coded in unknown format";
534 free(blob->ptr);
535 }
536 }
537 else
538 {
539 free(blob->ptr);
540 }
541 }
542 return ugh;
543 }
544
545 /*
546 * complete a distributionPoint URI with ca information
547 */
548 static char*
549 complete_uri(chunk_t distPoint, const char *ldaphost)
550 {
551 char *uri;
552 char *ptr = distPoint.ptr;
553 size_t len = distPoint.len;
554
555 char *symbol = memchr(ptr, ':', len);
556
557 if (symbol != NULL)
558 {
559 size_t type_len = symbol - ptr;
560
561 if (type_len >= 4 && strncasecmp(ptr, "ldap", 4) == 0)
562 {
563 ptr = symbol + 1;
564 len -= (type_len + 1);
565
566 if (len > 2 && *ptr++ == '/' && *ptr++ == '/')
567 {
568 len -= 2;
569 symbol = memchr(ptr, '/', len);
570
571 if (symbol != NULL && symbol - ptr == 0 && ldaphost != NULL)
572 {
573 uri = malloc(distPoint.len + strlen(ldaphost) + 1);
574
575 /* insert the ldaphost into the uri */
576 sprintf(uri, "%.*s%s%.*s"
577 , (int)(distPoint.len - len), distPoint.ptr
578 , ldaphost
579 , (int)len, symbol);
580 return uri;
581 }
582 }
583 }
584 }
585
586 /* default action: copy distributionPoint without change */
587 uri = malloc(distPoint.len + 1);
588 sprintf(uri, "%.*s", (int)distPoint.len, distPoint.ptr);
589 return uri;
590 }
591
592 /*
593 * try to fetch the crls defined by the fetch requests
594 */
595 static void
596 fetch_crls(bool cache_crls)
597 {
598 fetch_req_t *req;
599 fetch_req_t **reqp;
600
601 lock_crl_fetch_list("fetch_crls");
602 req = crl_fetch_reqs;
603 reqp = &crl_fetch_reqs;
604
605 while (req != NULL)
606 {
607 bool valid_crl = FALSE;
608 chunk_t blob = chunk_empty;
609 generalName_t *gn = req->distributionPoints;
610 const char *ldaphost;
611 ca_info_t *ca;
612
613 lock_ca_info_list("fetch_crls");
614
615 ca = get_ca_info(req->issuer, req->authKeySerialNumber, req->authKeyID);
616 ldaphost = (ca == NULL)? NULL : ca->ldaphost;
617
618 while (gn != NULL)
619 {
620 char *uri = complete_uri(gn->name, ldaphost);
621
622 err_t ugh = fetch_asn1_blob(uri, &blob);
623 free(uri);
624
625 if (ugh != NULL)
626 {
627 plog("fetch failed: %s", ugh);
628 }
629 else
630 {
631 chunk_t crl_uri = chunk_clone(gn->name);
632
633 if (insert_crl(blob, crl_uri, cache_crls))
634 {
635 DBG(DBG_CONTROL,
636 DBG_log("we have a valid crl")
637 )
638 valid_crl = TRUE;
639 break;
640 }
641 }
642 gn = gn->next;
643 }
644
645 unlock_ca_info_list("fetch_crls");
646
647 if (valid_crl)
648 {
649 /* delete fetch request */
650 fetch_req_t *req_free = req;
651
652 req = req->next;
653 *reqp = req;
654 free_fetch_request(req_free);
655 }
656 else
657 {
658 /* try again next time */
659 req->trials++;
660 reqp = &req->next;
661 req = req->next;
662 }
663 }
664 unlock_crl_fetch_list("fetch_crls");
665 }
666
667 static void
668 fetch_ocsp_status(ocsp_location_t* location)
669 {
670 #ifdef LIBCURL
671 chunk_t request;
672 chunk_t response = chunk_empty;
673
674 CURL* curl;
675 CURLcode res;
676
677 request = build_ocsp_request(location);
678
679 DBG(DBG_CONTROL,
680 DBG_log("sending ocsp request to location '%.*s'"
681 , (int)location->uri.len, location->uri.ptr)
682 )
683 DBG(DBG_RAW,
684 DBG_dump_chunk("OCSP request", request)
685 )
686
687 /* send via http post using libcurl */
688 curl = curl_easy_init();
689
690 if (curl != NULL)
691 {
692 char errorbuffer[CURL_ERROR_SIZE];
693 struct curl_slist *headers = NULL;
694 char* uri = malloc(location->uri.len + 1);
695
696 /* we need a null terminated string for curl */
697 memcpy(uri, location->uri.ptr, location->uri.len);
698 *(uri + location->uri.len) = '\0';
699
700 /* set content type header */
701 headers = curl_slist_append(headers, "Content-Type: application/ocsp-request");
702 curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
703
704 curl_easy_setopt(curl, CURLOPT_URL, uri);
705 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_buffer);
706 curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&response);
707 curl_easy_setopt(curl, CURLOPT_POSTFIELDS, (void*)request.ptr);
708 curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, request.len);
709 curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errorbuffer);
710 curl_easy_setopt(curl, CURLOPT_FAILONERROR, TRUE);
711 curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, FETCH_CMD_TIMEOUT);
712
713 res = curl_easy_perform(curl);
714
715 if (res == CURLE_OK)
716 {
717 DBG(DBG_CONTROL,
718 DBG_log("received ocsp response")
719 )
720 DBG(DBG_RAW,
721 DBG_dump_chunk("OCSP response:\n", response)
722 )
723 parse_ocsp(location, response);
724 }
725 else
726 {
727 plog("failed to fetch ocsp status from '%s': %s", uri, errorbuffer);
728 }
729 curl_slist_free_all(headers);
730 curl_easy_cleanup(curl);
731 free(uri);
732 curl_free(response.ptr);
733 }
734 free(request.ptr);
735 chunk_free(&location->nonce);
736
737 /* increment the trial counter of the unresolved fetch requests */
738 {
739 ocsp_certinfo_t *certinfo = location->certinfo;
740
741 while (certinfo != NULL)
742 {
743 certinfo->trials++;
744 certinfo = certinfo->next;
745 }
746 }
747 return;
748 #else /* !LIBCURL */
749 plog("ocsp error: pluto wasn't compiled with libcurl support");
750 #endif /* !LIBCURL */
751 }
752
753 /*
754 * try to fetch the necessary ocsp information
755 */
756 static void
757 fetch_ocsp(void)
758 {
759 ocsp_location_t *location;
760
761 lock_ocsp_fetch_list("fetch_ocsp");
762 location = ocsp_fetch_reqs;
763
764 /* fetch the ocps status for all locations */
765 while (location != NULL)
766 {
767 if (location->certinfo != NULL)
768 fetch_ocsp_status(location);
769 location = location->next;
770 }
771
772 unlock_ocsp_fetch_list("fetch_ocsp");
773 }
774
775 static void*
776 fetch_thread(void *arg)
777 {
778 struct timespec wait_interval;
779
780 DBG(DBG_CONTROL,
781 DBG_log("fetch thread started")
782 )
783
784 pthread_mutex_lock(&fetch_wake_mutex);
785
786 while(1)
787 {
788 int status;
789
790 wait_interval.tv_nsec = 0;
791 wait_interval.tv_sec = time(NULL) + crl_check_interval;
792
793 DBG(DBG_CONTROL,
794 DBG_log("next regular crl check in %ld seconds", crl_check_interval)
795 )
796 status = pthread_cond_timedwait(&fetch_wake_cond, &fetch_wake_mutex
797 , &wait_interval);
798
799 if (status == ETIMEDOUT)
800 {
801 DBG(DBG_CONTROL,
802 DBG_log(" ");
803 DBG_log("*time to check crls and the ocsp cache")
804 )
805 check_ocsp();
806 check_crls();
807 }
808 else
809 {
810 DBG(DBG_CONTROL,
811 DBG_log("fetch thread was woken up")
812 )
813 }
814 fetch_ocsp();
815 fetch_crls(cache_crls);
816 }
817 }
818 #endif /* THREADS*/
819
820 /*
821 * initializes curl and starts the fetching thread
822 */
823 void
824 init_fetch(void)
825 {
826 #if defined(LIBCURL) || defined (THREADS)
827 int status;
828 #endif
829
830 #ifdef LIBCURL
831 /* init curl */
832 status = curl_global_init(CURL_GLOBAL_NOTHING);
833 if (status != CURLE_OK)
834 {
835 plog("libcurl could not be initialized, status = %d", status);
836 }
837 #endif /* LIBCURL */
838
839 if (crl_check_interval > 0)
840 {
841 #ifdef THREADS
842 status = pthread_create( &thread, NULL, fetch_thread, NULL);
843 if (status != 0)
844 {
845 plog("fetching thread could not be started, status = %d", status);
846 }
847 #else /* !THREADS */
848 plog("warning: not compiled with pthread support");
849 #endif /* !THREADS */
850 }
851 }
852
853 void
854 free_crl_fetch(void)
855 {
856 lock_crl_fetch_list("free_crl_fetch");
857
858 while (crl_fetch_reqs != NULL)
859 {
860 fetch_req_t *req = crl_fetch_reqs;
861 crl_fetch_reqs = req->next;
862 free_fetch_request(req);
863 }
864
865 unlock_crl_fetch_list("free_crl_fetch");
866
867 #ifdef LIBCURL
868 if (crl_check_interval > 0)
869 {
870 /* cleanup curl */
871 curl_global_cleanup();
872 }
873 #endif /* LIBCURL */
874 }
875
876 /*
877 * free the chained list of ocsp requests
878 */
879 void
880 free_ocsp_fetch(void)
881 {
882 lock_ocsp_fetch_list("free_ocsp_fetch");
883 free_ocsp_locations(&ocsp_fetch_reqs);
884 unlock_ocsp_fetch_list("free_ocsp_fetch");
885 }
886
887
888 /*
889 * add additional distribution points
890 */
891 void
892 add_distribution_points(const generalName_t *newPoints ,generalName_t **distributionPoints)
893 {
894 while (newPoints != NULL)
895 {
896 /* skip empty distribution point */
897 if (newPoints->name.len > 0)
898 {
899 bool add = TRUE;
900 generalName_t *gn = *distributionPoints;
901
902 while (gn != NULL)
903 {
904 if (gn->kind == newPoints->kind
905 && gn->name.len == newPoints->name.len
906 && memeq(gn->name.ptr, newPoints->name.ptr, gn->name.len))
907 {
908 /* skip if the distribution point is already present */
909 add = FALSE;
910 break;
911 }
912 gn = gn->next;
913 }
914
915 if (add)
916 {
917 /* clone additional distribution point */
918 gn = clone_thing(*newPoints);
919 gn->name = chunk_clone(newPoints->name);
920
921 /* insert additional CRL distribution point */
922 gn->next = *distributionPoints;
923 *distributionPoints = gn;
924 }
925 }
926 newPoints = newPoints->next;
927 }
928 }
929
930 fetch_req_t*
931 build_crl_fetch_request(chunk_t issuer, chunk_t authKeySerialNumber
932 , chunk_t authKeyID, const generalName_t *gn)
933 {
934 fetch_req_t *req = malloc_thing(fetch_req_t);
935 *req = empty_fetch_req;
936
937 /* note current time */
938 req->installed = time(NULL);
939
940 /* clone fields */
941 req->issuer = chunk_clone(issuer);
942 req->authKeySerialNumber = chunk_clone(authKeySerialNumber);
943 req->authKeyID = chunk_clone(authKeyID);
944
945 /* copy distribution points */
946 add_distribution_points(gn, &req->distributionPoints);
947
948 return req;
949 }
950
951 /*
952 * add a crl fetch request to the chained list
953 */
954 void
955 add_crl_fetch_request(fetch_req_t *req)
956 {
957 fetch_req_t *r;
958
959 lock_crl_fetch_list("add_crl_fetch_request");
960 r = crl_fetch_reqs;
961
962 while (r != NULL)
963 {
964 if ((req->authKeyID.ptr != NULL)? same_keyid(req->authKeyID, r->authKeyID)
965 : (same_dn(req->issuer, r->issuer)
966 && same_serial(req->authKeySerialNumber, r->authKeySerialNumber)))
967 {
968 /* there is already a fetch request */
969 DBG(DBG_CONTROL,
970 DBG_log("crl fetch request already exists")
971 )
972
973 /* there might be new distribution points */
974 add_distribution_points(req->distributionPoints, &r->distributionPoints);
975
976 unlock_crl_fetch_list("add_crl_fetch_request");
977 free_fetch_request(req);
978 return;
979 }
980 r = r->next;
981 }
982
983 /* insert new fetch request at the head of the queue */
984 req->next = crl_fetch_reqs;
985 crl_fetch_reqs = req;
986
987 DBG(DBG_CONTROL,
988 DBG_log("crl fetch request added")
989 )
990 unlock_crl_fetch_list("add_crl_fetch_request");
991 }
992
993 /*
994 * add an ocsp fetch request to the chained list
995 */
996 void
997 add_ocsp_fetch_request(ocsp_location_t *location, chunk_t serialNumber)
998 {
999 ocsp_certinfo_t certinfo;
1000
1001 certinfo.serialNumber = serialNumber;
1002
1003 lock_ocsp_fetch_list("add_ocsp_fetch_request");
1004 add_certinfo(location, &certinfo, &ocsp_fetch_reqs, TRUE);
1005 unlock_ocsp_fetch_list("add_ocsp_fetch_request");
1006 }
1007
1008 /*
1009 * list all distribution points
1010 */
1011 void
1012 list_distribution_points(const generalName_t *gn)
1013 {
1014 bool first_gn = TRUE;
1015
1016 while (gn != NULL)
1017 {
1018 whack_log(RC_COMMENT, " %s '%.*s'", (first_gn)? "distPts: "
1019 :" ", (int)gn->name.len, gn->name.ptr);
1020 first_gn = FALSE;
1021 gn = gn->next;
1022 }
1023 }
1024
1025 /*
1026 * list all fetch requests in the chained list
1027 */
1028 void
1029 list_crl_fetch_requests(bool utc)
1030 {
1031 fetch_req_t *req;
1032
1033 lock_crl_fetch_list("list_crl_fetch_requests");
1034 req = crl_fetch_reqs;
1035
1036 if (req != NULL)
1037 {
1038 whack_log(RC_COMMENT, " ");
1039 whack_log(RC_COMMENT, "List of CRL fetch requests:");
1040 whack_log(RC_COMMENT, " ");
1041 }
1042
1043 while (req != NULL)
1044 {
1045 u_char buf[BUF_LEN];
1046
1047 whack_log(RC_COMMENT, "%T, trials: %d"
1048 , &req->installed, utc, req->trials);
1049 dntoa(buf, BUF_LEN, req->issuer);
1050 whack_log(RC_COMMENT, " issuer: '%s'", buf);
1051 if (req->authKeyID.ptr != NULL)
1052 {
1053 datatot(req->authKeyID.ptr, req->authKeyID.len, ':'
1054 , buf, BUF_LEN);
1055 whack_log(RC_COMMENT, " authkey: %s", buf);
1056 }
1057 if (req->authKeySerialNumber.ptr != NULL)
1058 {
1059 datatot(req->authKeySerialNumber.ptr, req->authKeySerialNumber.len, ':'
1060 , buf, BUF_LEN);
1061 whack_log(RC_COMMENT, " aserial: %s", buf);
1062 }
1063 list_distribution_points(req->distributionPoints);
1064 req = req->next;
1065 }
1066 unlock_crl_fetch_list("list_crl_fetch_requests");
1067 }
1068
1069 void
1070 list_ocsp_fetch_requests(bool utc)
1071 {
1072 lock_ocsp_fetch_list("list_ocsp_fetch_requests");
1073 list_ocsp_locations(ocsp_fetch_reqs, TRUE, utc, FALSE);
1074 unlock_ocsp_fetch_list("list_ocsp_fetch_requests");
1075
1076 }