last patch removed, changed CURLOPT_FILE to CURLOPT_WRITEDATA
[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_easy_cleanup(curl);
341 /* not using freeanychunk because of realloc (no leak detective) */
342 curl_free(response.ptr);
343 }
344 return strlen(errorbuffer) > 0 ? "libcurl error" : NULL;
345 #else /* !LIBCURL */
346 return "warning: not compiled with libcurl support";
347 #endif /* !LIBCURL */
348 }
349
350 #ifdef LDAP_VER
351 /*
352 * parses the result returned by an ldap query
353 */
354 static err_t
355 parse_ldap_result(LDAP * ldap, LDAPMessage *result, chunk_t *blob)
356 {
357 err_t ugh = NULL;
358
359 LDAPMessage * entry = ldap_first_entry(ldap, result);
360
361 if (entry != NULL)
362 {
363 BerElement *ber = NULL;
364 char *attr;
365
366 attr = ldap_first_attribute(ldap, entry, &ber);
367
368 if (attr != NULL)
369 {
370 struct berval **values = ldap_get_values_len(ldap, entry, attr);
371
372 if (values != NULL)
373 {
374 if (values[0] != NULL)
375 {
376 blob->len = values[0]->bv_len;
377 blob->ptr = alloc_bytes(blob->len, "ldap blob");
378 memcpy(blob->ptr, values[0]->bv_val, blob->len);
379 if (values[1] != NULL)
380 {
381 plog("warning: more than one value was fetched from LDAP URL");
382 }
383 }
384 else
385 {
386 ugh = "no values in attribute";
387 }
388 ldap_value_free_len(values);
389 }
390 else
391 {
392 ugh = ldap_err2string(ldap_result2error(ldap, entry, 0));
393 }
394 ldap_memfree(attr);
395 }
396 else
397 {
398 ugh = ldap_err2string(ldap_result2error(ldap, entry, 0));
399 }
400 ber_free(ber, 0);
401 }
402 else
403 {
404 ugh = ldap_err2string(ldap_result2error(ldap, result, 0));
405 }
406 return ugh;
407 }
408
409 /*
410 * fetches a binary blob from an ldap url
411 */
412 static err_t
413 fetch_ldap_url(char *url, chunk_t *blob)
414 {
415 LDAPURLDesc *lurl;
416 err_t ugh = NULL;
417 int rc;
418
419 DBG(DBG_CONTROL,
420 DBG_log("Trying LDAP URL '%s'", url)
421 )
422
423 rc = ldap_url_parse(url, &lurl);
424
425 if (rc == LDAP_SUCCESS)
426 {
427 LDAP *ldap = ldap_init(lurl->lud_host, lurl->lud_port);
428
429 if (ldap != NULL)
430 {
431 int ldap_version = (LDAP_VER == 2)? LDAP_VERSION2 : LDAP_VERSION3;
432 struct timeval timeout;
433
434 timeout.tv_sec = FETCH_CMD_TIMEOUT;
435 timeout.tv_usec = 0;
436 ldap_set_option(ldap, LDAP_OPT_PROTOCOL_VERSION, &ldap_version);
437 ldap_set_option(ldap, LDAP_OPT_NETWORK_TIMEOUT, &timeout);
438
439 rc = ldap_simple_bind_s(ldap, NULL, NULL);
440
441 if (rc == LDAP_SUCCESS)
442 {
443 LDAPMessage *result;
444
445 timeout.tv_sec = FETCH_CMD_TIMEOUT;
446 timeout.tv_usec = 0;
447
448 rc = ldap_search_st(ldap, lurl->lud_dn
449 , lurl->lud_scope
450 , lurl->lud_filter
451 , lurl->lud_attrs
452 , 0, &timeout, &result);
453
454 if (rc == LDAP_SUCCESS)
455 {
456 ugh = parse_ldap_result(ldap, result, blob);
457 ldap_msgfree(result);
458 }
459 else
460 {
461 ugh = ldap_err2string(rc);
462 }
463 }
464 else
465 {
466 ugh = ldap_err2string(rc);
467 }
468 ldap_unbind_s(ldap);
469 }
470 else
471 {
472 ugh = "ldap init";
473 }
474 ldap_free_urldesc(lurl);
475 }
476 else
477 {
478 ugh = ldap_err2string(rc);
479 }
480 return ugh;
481 }
482 #else /* !LDAP_VER */
483 static err_t
484 fetch_ldap_url(char *url, chunk_t *blob)
485 {
486 return "LDAP URL fetching not activated in pluto source code";
487 }
488 #endif /* !LDAP_VER */
489
490 /*
491 * fetch an ASN.1 blob coded in PEM or DER format from a URL
492 */
493 static err_t
494 fetch_asn1_blob(char *url, chunk_t *blob)
495 {
496 err_t ugh = NULL;
497
498 if (strlen(url) >= 4 && strncasecmp(url, "ldap", 4) == 0)
499 {
500 ugh = fetch_ldap_url(url, blob);
501 }
502 else
503 {
504 ugh = fetch_curl(url, blob);
505 }
506 if (ugh != NULL)
507 return ugh;
508
509 if (is_asn1(*blob))
510 {
511 DBG(DBG_PARSING,
512 DBG_log(" fetched blob coded in DER format")
513 )
514 }
515 else
516 {
517 bool pgp = FALSE;
518
519 ugh = pemtobin(blob, NULL, "", &pgp);
520 if (ugh == NULL)
521 {
522 if (is_asn1(*blob))
523 {
524 DBG(DBG_PARSING,
525 DBG_log(" fetched blob coded in PEM format")
526 )
527 }
528 else
529 {
530 ugh = "blob coded in unknown format";
531 pfree(blob->ptr);
532 }
533 }
534 else
535 {
536 pfree(blob->ptr);
537 }
538 }
539 return ugh;
540 }
541
542 /*
543 * complete a distributionPoint URI with ca information
544 */
545 static char*
546 complete_uri(chunk_t distPoint, const char *ldaphost)
547 {
548 char *uri;
549 char *ptr = distPoint.ptr;
550 size_t len = distPoint.len;
551
552 char *symbol = memchr(ptr, ':', len);
553
554 if (symbol != NULL)
555 {
556 size_t type_len = symbol - ptr;
557
558 if (type_len >= 4 && strncasecmp(ptr, "ldap", 4) == 0)
559 {
560 ptr = symbol + 1;
561 len -= (type_len + 1);
562
563 if (len > 2 && *ptr++ == '/' && *ptr++ == '/')
564 {
565 len -= 2;
566 symbol = memchr(ptr, '/', len);
567
568 if (symbol != NULL && symbol - ptr == 0 && ldaphost != NULL)
569 {
570 uri = alloc_bytes(distPoint.len+strlen(ldaphost)+1, "uri");
571
572 /* insert the ldaphost into the uri */
573 sprintf(uri, "%.*s%s%.*s"
574 , (int)(distPoint.len - len), distPoint.ptr
575 , ldaphost
576 , (int)len, symbol);
577 return uri;
578 }
579 }
580 }
581 }
582
583 /* default action: copy distributionPoint without change */
584 uri = alloc_bytes(distPoint.len+1, "uri");
585 sprintf(uri, "%.*s", (int)distPoint.len, distPoint.ptr);
586 return uri;
587 }
588
589 /*
590 * try to fetch the crls defined by the fetch requests
591 */
592 static void
593 fetch_crls(bool cache_crls)
594 {
595 fetch_req_t *req;
596 fetch_req_t **reqp;
597
598 lock_crl_fetch_list("fetch_crls");
599 req = crl_fetch_reqs;
600 reqp = &crl_fetch_reqs;
601
602 while (req != NULL)
603 {
604 bool valid_crl = FALSE;
605 chunk_t blob = empty_chunk;
606 generalName_t *gn = req->distributionPoints;
607 const char *ldaphost;
608 ca_info_t *ca;
609
610 lock_ca_info_list("fetch_crls");
611
612 ca = get_ca_info(req->issuer, req->authKeySerialNumber, req->authKeyID);
613 ldaphost = (ca == NULL)? NULL : ca->ldaphost;
614
615 while (gn != NULL)
616 {
617 char *uri = complete_uri(gn->name, ldaphost);
618
619 err_t ugh = fetch_asn1_blob(uri, &blob);
620 pfree(uri);
621
622 if (ugh != NULL)
623 {
624 plog("fetch failed: %s", ugh);
625 }
626 else
627 {
628 chunk_t crl_uri;
629
630 clonetochunk(crl_uri, gn->name.ptr, gn->name.len, "crl uri");
631 if (insert_crl(blob, crl_uri, cache_crls))
632 {
633 DBG(DBG_CONTROL,
634 DBG_log("we have a valid crl")
635 )
636 valid_crl = TRUE;
637 break;
638 }
639 }
640 gn = gn->next;
641 }
642
643 unlock_ca_info_list("fetch_crls");
644
645 if (valid_crl)
646 {
647 /* delete fetch request */
648 fetch_req_t *req_free = req;
649
650 req = req->next;
651 *reqp = req;
652 free_fetch_request(req_free);
653 }
654 else
655 {
656 /* try again next time */
657 req->trials++;
658 reqp = &req->next;
659 req = req->next;
660 }
661 }
662 unlock_crl_fetch_list("fetch_crls");
663 }
664
665 static void
666 fetch_ocsp_status(ocsp_location_t* location)
667 {
668 #ifdef LIBCURL
669 chunk_t request;
670 chunk_t response = empty_chunk;
671
672 CURL* curl;
673 CURLcode res;
674
675 request = build_ocsp_request(location);
676
677 DBG(DBG_CONTROL,
678 DBG_log("sending ocsp request to location '%.*s'"
679 , (int)location->uri.len, location->uri.ptr)
680 )
681 DBG(DBG_RAW,
682 DBG_dump_chunk("OCSP request", request)
683 )
684
685 /* send via http post using libcurl */
686 curl = curl_easy_init();
687
688 if (curl != NULL)
689 {
690 char errorbuffer[CURL_ERROR_SIZE];
691 struct curl_slist *headers = NULL;
692 char* uri = alloc_bytes(location->uri.len+1, "ocsp uri");
693
694 /* we need a null terminated string for curl */
695 memcpy(uri, location->uri.ptr, location->uri.len);
696 *(uri + location->uri.len) = '\0';
697
698 /* set content type header */
699 headers = curl_slist_append(headers, "Content-Type: application/ocsp-request");
700 curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
701
702 curl_easy_setopt(curl, CURLOPT_URL, uri);
703 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_buffer);
704 curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&response);
705 curl_easy_setopt(curl, CURLOPT_POSTFIELDS, request.ptr);
706 curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, request.len);
707 curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, &errorbuffer);
708 curl_easy_setopt(curl, CURLOPT_FAILONERROR, TRUE);
709 curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, FETCH_CMD_TIMEOUT);
710
711 res = curl_easy_perform(curl);
712
713 if (res == CURLE_OK)
714 {
715 DBG(DBG_CONTROL,
716 DBG_log("received ocsp response")
717 )
718 DBG(DBG_RAW,
719 DBG_dump_chunk("OCSP response:\n", response)
720 )
721 parse_ocsp(location, response);
722 }
723 else
724 {
725 plog("failed to fetch ocsp status from '%s': %s", uri, errorbuffer);
726 }
727 curl_slist_free_all(headers);
728 curl_easy_cleanup(curl);
729 pfree(uri);
730 /* not using freeanychunk because of realloc (no leak detective) */
731 curl_free(response.ptr);
732 }
733 freeanychunk(location->nonce);
734 freeanychunk(request);
735
736 /* increment the trial counter of the unresolved fetch requests */
737 {
738 ocsp_certinfo_t *certinfo = location->certinfo;
739
740 while (certinfo != NULL)
741 {
742 certinfo->trials++;
743 certinfo = certinfo->next;
744 }
745 }
746 return;
747 #else /* !LIBCURL */
748 plog("ocsp error: pluto wasn't compiled with libcurl support");
749 #endif /* !LIBCURL */
750 }
751
752 /*
753 * try to fetch the necessary ocsp information
754 */
755 static void
756 fetch_ocsp(void)
757 {
758 ocsp_location_t *location;
759
760 lock_ocsp_fetch_list("fetch_ocsp");
761 location = ocsp_fetch_reqs;
762
763 /* fetch the ocps status for all locations */
764 while (location != NULL)
765 {
766 if (location->certinfo != NULL)
767 fetch_ocsp_status(location);
768 location = location->next;
769 }
770
771 unlock_ocsp_fetch_list("fetch_ocsp");
772 }
773
774 static void*
775 fetch_thread(void *arg)
776 {
777 struct timespec wait_interval;
778
779 DBG(DBG_CONTROL,
780 DBG_log("fetch thread started")
781 )
782
783 pthread_mutex_lock(&fetch_wake_mutex);
784
785 while(1)
786 {
787 int status;
788
789 wait_interval.tv_nsec = 0;
790 wait_interval.tv_sec = time(NULL) + crl_check_interval;
791
792 DBG(DBG_CONTROL,
793 DBG_log("next regular crl check in %ld seconds", crl_check_interval)
794 )
795 status = pthread_cond_timedwait(&fetch_wake_cond, &fetch_wake_mutex
796 , &wait_interval);
797
798 if (status == ETIMEDOUT)
799 {
800 DBG(DBG_CONTROL,
801 DBG_log(" ");
802 DBG_log("*time to check crls and the ocsp cache")
803 )
804 check_ocsp();
805 check_crls();
806 }
807 else
808 {
809 DBG(DBG_CONTROL,
810 DBG_log("fetch thread was woken up")
811 )
812 }
813 fetch_ocsp();
814 fetch_crls(cache_crls);
815 }
816 }
817 #endif /* THREADS*/
818
819 /*
820 * initializes curl and starts the fetching thread
821 */
822 void
823 init_fetch(void)
824 {
825 int status;
826
827 #ifdef LIBCURL
828 /* init curl */
829 status = curl_global_init(CURL_GLOBAL_NOTHING);
830 if (status != CURLE_OK)
831 {
832 plog("libcurl could not be initialized, status = %d", status);
833 }
834 #endif /* LIBCURL */
835
836 if (crl_check_interval > 0)
837 {
838 #ifdef THREADS
839 status = pthread_create( &thread, NULL, fetch_thread, NULL);
840 if (status != 0)
841 {
842 plog("fetching thread could not be started, status = %d", status);
843 }
844 #else /* !THREADS */
845 plog("warning: not compiled with pthread support");
846 #endif /* !THREADS */
847 }
848 }
849
850 void
851 free_crl_fetch(void)
852 {
853 lock_crl_fetch_list("free_crl_fetch");
854
855 while (crl_fetch_reqs != NULL)
856 {
857 fetch_req_t *req = crl_fetch_reqs;
858 crl_fetch_reqs = req->next;
859 free_fetch_request(req);
860 }
861
862 unlock_crl_fetch_list("free_crl_fetch");
863
864 #ifdef LIBCURL
865 if (crl_check_interval > 0)
866 {
867 /* cleanup curl */
868 curl_global_cleanup();
869 }
870 #endif /* LIBCURL */
871 }
872
873 /*
874 * free the chained list of ocsp requests
875 */
876 void
877 free_ocsp_fetch(void)
878 {
879 lock_ocsp_fetch_list("free_ocsp_fetch");
880 free_ocsp_locations(&ocsp_fetch_reqs);
881 unlock_ocsp_fetch_list("free_ocsp_fetch");
882 }
883
884
885 /*
886 * add additional distribution points
887 */
888 void
889 add_distribution_points(const generalName_t *newPoints ,generalName_t **distributionPoints)
890 {
891 while (newPoints != NULL)
892 {
893 /* skip empty distribution point */
894 if (newPoints->name.len > 0)
895 {
896 bool add = TRUE;
897 generalName_t *gn = *distributionPoints;
898
899 while (gn != NULL)
900 {
901 if (gn->kind == newPoints->kind
902 && gn->name.len == newPoints->name.len
903 && memcmp(gn->name.ptr, newPoints->name.ptr, gn->name.len) == 0)
904 {
905 /* skip if the distribution point is already present */
906 add = FALSE;
907 break;
908 }
909 gn = gn->next;
910 }
911
912 if (add)
913 {
914 /* clone additional distribution point */
915 gn = clone_thing(*newPoints, "generalName");
916 clonetochunk(gn->name, newPoints->name.ptr, newPoints->name.len
917 , "crl uri");
918
919 /* insert additional CRL distribution point */
920 gn->next = *distributionPoints;
921 *distributionPoints = gn;
922 }
923 }
924 newPoints = newPoints->next;
925 }
926 }
927
928 fetch_req_t*
929 build_crl_fetch_request(chunk_t issuer, chunk_t authKeySerialNumber
930 , chunk_t authKeyID, const generalName_t *gn)
931 {
932 fetch_req_t *req = alloc_thing(fetch_req_t, "fetch request");
933 *req = empty_fetch_req;
934
935 /* note current time */
936 req->installed = time(NULL);
937
938 /* clone fields */
939 clonetochunk(req->issuer, issuer.ptr, issuer.len, "issuer");
940 if (authKeySerialNumber.ptr != NULL)
941 {
942 clonetochunk(req->authKeySerialNumber, authKeySerialNumber.ptr
943 , authKeySerialNumber.len, "authKeySerialNumber");
944 }
945 if (authKeyID.ptr != NULL)
946 {
947 clonetochunk(req->authKeyID, authKeyID.ptr, authKeyID.len, "authKeyID");
948 }
949
950 /* copy distribution points */
951 add_distribution_points(gn, &req->distributionPoints);
952
953 return req;
954 }
955
956 /*
957 * add a crl fetch request to the chained list
958 */
959 void
960 add_crl_fetch_request(fetch_req_t *req)
961 {
962 fetch_req_t *r;
963
964 lock_crl_fetch_list("add_crl_fetch_request");
965 r = crl_fetch_reqs;
966
967 while (r != NULL)
968 {
969 if ((req->authKeyID.ptr != NULL)? same_keyid(req->authKeyID, r->authKeyID)
970 : (same_dn(req->issuer, r->issuer)
971 && same_serial(req->authKeySerialNumber, r->authKeySerialNumber)))
972 {
973 /* there is already a fetch request */
974 DBG(DBG_CONTROL,
975 DBG_log("crl fetch request already exists")
976 )
977
978 /* there might be new distribution points */
979 add_distribution_points(req->distributionPoints, &r->distributionPoints);
980
981 unlock_crl_fetch_list("add_crl_fetch_request");
982 free_fetch_request(req);
983 return;
984 }
985 r = r->next;
986 }
987
988 /* insert new fetch request at the head of the queue */
989 req->next = crl_fetch_reqs;
990 crl_fetch_reqs = req;
991
992 DBG(DBG_CONTROL,
993 DBG_log("crl fetch request added")
994 )
995 unlock_crl_fetch_list("add_crl_fetch_request");
996 }
997
998 /*
999 * add an ocsp fetch request to the chained list
1000 */
1001 void
1002 add_ocsp_fetch_request(ocsp_location_t *location, chunk_t serialNumber)
1003 {
1004 ocsp_certinfo_t certinfo;
1005
1006 certinfo.serialNumber = serialNumber;
1007
1008 lock_ocsp_fetch_list("add_ocsp_fetch_request");
1009 add_certinfo(location, &certinfo, &ocsp_fetch_reqs, TRUE);
1010 unlock_ocsp_fetch_list("add_ocsp_fetch_request");
1011 }
1012
1013 /*
1014 * list all distribution points
1015 */
1016 void
1017 list_distribution_points(const generalName_t *gn)
1018 {
1019 bool first_gn = TRUE;
1020
1021 while (gn != NULL)
1022 {
1023 whack_log(RC_COMMENT, " %s '%.*s'", (first_gn)? "distPts: "
1024 :" ", (int)gn->name.len, gn->name.ptr);
1025 first_gn = FALSE;
1026 gn = gn->next;
1027 }
1028 }
1029
1030 /*
1031 * list all fetch requests in the chained list
1032 */
1033 void
1034 list_crl_fetch_requests(bool utc)
1035 {
1036 fetch_req_t *req;
1037
1038 lock_crl_fetch_list("list_crl_fetch_requests");
1039 req = crl_fetch_reqs;
1040
1041 if (req != NULL)
1042 {
1043 whack_log(RC_COMMENT, " ");
1044 whack_log(RC_COMMENT, "List of CRL fetch requests:");
1045 whack_log(RC_COMMENT, " ");
1046 }
1047
1048 while (req != NULL)
1049 {
1050 u_char buf[BUF_LEN];
1051
1052 whack_log(RC_COMMENT, "%s, trials: %d"
1053 , timetoa(&req->installed, utc), req->trials);
1054 dntoa(buf, BUF_LEN, req->issuer);
1055 whack_log(RC_COMMENT, " issuer: '%s'", buf);
1056 if (req->authKeyID.ptr != NULL)
1057 {
1058 datatot(req->authKeyID.ptr, req->authKeyID.len, ':'
1059 , buf, BUF_LEN);
1060 whack_log(RC_COMMENT, " authkey: %s", buf);
1061 }
1062 if (req->authKeySerialNumber.ptr != NULL)
1063 {
1064 datatot(req->authKeySerialNumber.ptr, req->authKeySerialNumber.len, ':'
1065 , buf, BUF_LEN);
1066 whack_log(RC_COMMENT, " aserial: %s", buf);
1067 }
1068 list_distribution_points(req->distributionPoints);
1069 req = req->next;
1070 }
1071 unlock_crl_fetch_list("list_crl_fetch_requests");
1072 }
1073
1074 void
1075 list_ocsp_fetch_requests(bool utc)
1076 {
1077 lock_ocsp_fetch_list("list_ocsp_fetch_requests");
1078 list_ocsp_locations(ocsp_fetch_reqs, TRUE, utc, FALSE);
1079 unlock_ocsp_fetch_list("list_ocsp_fetch_requests");
1080
1081 }