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