pluto and scepclient use the curl and ldap fetcher plugins
[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 #include <freeswan.h>
29
30 #include <library.h>
31 #include <debug.h>
32 #include <asn1/asn1.h>
33
34 #include "constants.h"
35 #include "defs.h"
36 #include "log.h"
37 #include "id.h"
38 #include "pem.h"
39 #include "x509.h"
40 #include "ca.h"
41 #include "whack.h"
42 #include "ocsp.h"
43 #include "crl.h"
44 #include "fetch.h"
45
46 fetch_req_t empty_fetch_req = {
47 NULL , /* next */
48 0 , /* installed */
49 0 , /* trials */
50 { NULL, 0}, /* issuer */
51 { NULL, 0}, /* authKeyID */
52 { NULL, 0}, /* authKeySerialNumber */
53 NULL /* distributionPoints */
54 };
55
56 /* chained list of crl fetch requests */
57 static fetch_req_t *crl_fetch_reqs = NULL;
58
59 /* chained list of ocsp fetch requests */
60 static ocsp_location_t *ocsp_fetch_reqs = NULL;
61
62 #ifdef THREADS
63 static pthread_t thread;
64 static pthread_mutex_t certs_and_keys_mutex = PTHREAD_MUTEX_INITIALIZER;
65 static pthread_mutex_t authcert_list_mutex = PTHREAD_MUTEX_INITIALIZER;
66 static pthread_mutex_t crl_list_mutex = PTHREAD_MUTEX_INITIALIZER;
67 static pthread_mutex_t ocsp_cache_mutex = PTHREAD_MUTEX_INITIALIZER;
68 static pthread_mutex_t ca_info_list_mutex = PTHREAD_MUTEX_INITIALIZER;
69 static pthread_mutex_t crl_fetch_list_mutex = PTHREAD_MUTEX_INITIALIZER;
70 static pthread_mutex_t ocsp_fetch_list_mutex = PTHREAD_MUTEX_INITIALIZER;
71 static pthread_mutex_t fetch_wake_mutex = PTHREAD_MUTEX_INITIALIZER;
72 static pthread_cond_t fetch_wake_cond = PTHREAD_COND_INITIALIZER;
73
74 /**
75 * lock access to my certs and keys
76 */
77 void lock_certs_and_keys(const char *who)
78 {
79 pthread_mutex_lock(&certs_and_keys_mutex);
80 DBG(DBG_CONTROLMORE,
81 DBG_log("certs and keys locked by '%s'", who)
82 )
83 }
84
85 /**
86 * Unlock access to my certs and keys
87 */
88 void unlock_certs_and_keys(const char *who)
89 {
90 DBG(DBG_CONTROLMORE,
91 DBG_log("certs and keys unlocked by '%s'", who)
92 )
93 pthread_mutex_unlock(&certs_and_keys_mutex);
94 }
95
96 /**
97 * Lock access to the chained authcert list
98 */
99 void lock_authcert_list(const char *who)
100 {
101 pthread_mutex_lock(&authcert_list_mutex);
102 DBG(DBG_CONTROLMORE,
103 DBG_log("authcert list locked by '%s'", who)
104 )
105 }
106
107 /**
108 * Unlock access to the chained authcert list
109 */
110 void unlock_authcert_list(const char *who)
111 {
112 DBG(DBG_CONTROLMORE,
113 DBG_log("authcert list unlocked by '%s'", who)
114 )
115 pthread_mutex_unlock(&authcert_list_mutex);
116 }
117
118 /**
119 * Lock access to the chained crl list
120 */
121 void lock_crl_list(const char *who)
122 {
123 pthread_mutex_lock(&crl_list_mutex);
124 DBG(DBG_CONTROLMORE,
125 DBG_log("crl list locked by '%s'", who)
126 )
127 }
128
129 /**
130 * Unlock access to the chained crl list
131 */
132 void unlock_crl_list(const char *who)
133 {
134 DBG(DBG_CONTROLMORE,
135 DBG_log("crl list unlocked by '%s'", who)
136 )
137 pthread_mutex_unlock(&crl_list_mutex);
138 }
139
140 /**
141 * Lock access to the ocsp cache
142 */
143 extern void lock_ocsp_cache(const char *who)
144 {
145 pthread_mutex_lock(&ocsp_cache_mutex);
146 DBG(DBG_CONTROLMORE,
147 DBG_log("ocsp cache locked by '%s'", who)
148 )
149 }
150
151 /**
152 * Unlock access to the ocsp cache
153 */
154 extern void unlock_ocsp_cache(const char *who)
155 {
156 DBG(DBG_CONTROLMORE,
157 DBG_log("ocsp cache unlocked by '%s'", who)
158 )
159 pthread_mutex_unlock(&ocsp_cache_mutex);
160 }
161
162 /**
163 * Lock access to the ca info list
164 */
165 extern void lock_ca_info_list(const char *who)
166 {
167 pthread_mutex_lock(&ca_info_list_mutex);
168 DBG(DBG_CONTROLMORE,
169 DBG_log("ca info list locked by '%s'", who)
170 )
171 }
172
173 /**
174 * Unlock access to the ca info list
175 */
176 extern void unlock_ca_info_list(const char *who)
177 {
178 DBG(DBG_CONTROLMORE,
179 DBG_log("ca info list unlocked by '%s'", who)
180 )
181 pthread_mutex_unlock(&ca_info_list_mutex);
182 }
183
184 /**
185 * Lock access to the chained crl fetch request list
186 */
187 static void lock_crl_fetch_list(const char *who)
188 {
189 pthread_mutex_lock(&crl_fetch_list_mutex);
190 DBG(DBG_CONTROLMORE,
191 DBG_log("crl fetch request list locked by '%s'", who)
192 )
193 }
194
195 /**
196 * Unlock access to the chained crl fetch request list
197 */
198 static void unlock_crl_fetch_list(const char *who)
199 {
200 DBG(DBG_CONTROLMORE,
201 DBG_log("crl fetch request list unlocked by '%s'", who)
202 )
203 pthread_mutex_unlock(&crl_fetch_list_mutex);
204 }
205
206 /**
207 * Lock access to the chained ocsp fetch request list
208 */
209 static void lock_ocsp_fetch_list(const char *who)
210 {
211 pthread_mutex_lock(&ocsp_fetch_list_mutex);
212 DBG(DBG_CONTROLMORE,
213 DBG_log("ocsp fetch request list locked by '%s'", who)
214 )
215 }
216
217 /**
218 * Unlock access to the chained ocsp fetch request list
219 */
220 static void unlock_ocsp_fetch_list(const char *who)
221 {
222 DBG(DBG_CONTROLMORE,
223 DBG_log("ocsp fetch request list unlocked by '%s'", who)
224 )
225 pthread_mutex_unlock(&ocsp_fetch_list_mutex);
226 }
227
228 /**
229 * Wakes up the sleeping fetch thread
230 */
231 void wake_fetch_thread(const char *who)
232 {
233 if (crl_check_interval > 0)
234 {
235 DBG(DBG_CONTROLMORE,
236 DBG_log("fetch thread wake call by '%s'", who)
237 )
238 pthread_mutex_lock(&fetch_wake_mutex);
239 pthread_cond_signal(&fetch_wake_cond);
240 pthread_mutex_unlock(&fetch_wake_mutex);
241 }
242 }
243 #else /* !THREADS */
244 #define lock_crl_fetch_list(who) /* do nothing */
245 #define unlock_crl_fetch_list(who) /* do nothing */
246 #define lock_ocsp_fetch_list(who) /* do nothing */
247 #define unlock_ocsp_fetch_list(who) /* do nothing */
248 #endif /* !THREADS */
249
250 /**
251 * Free the dynamic memory used to store fetch requests
252 */
253 static void free_fetch_request(fetch_req_t *req)
254 {
255 free(req->issuer.ptr);
256 free(req->authKeySerialNumber.ptr);
257 free(req->authKeyID.ptr);
258 free_generalNames(req->distributionPoints, TRUE);
259 free(req);
260 }
261
262 #ifdef THREADS
263 /**
264 * Fetch an ASN.1 blob coded in PEM or DER format from a URL
265 */
266 bool fetch_asn1_blob(char *url, chunk_t *blob)
267 {
268 err_t ugh = NULL;
269
270 DBG1(" fetching crl from '%s' ...", url);
271 if (lib->fetcher->fetch(lib->fetcher, url, blob, FETCH_END) != SUCCESS)
272 {
273 DBG1("crl fetching failed");
274 return FALSE;
275 }
276
277 if (is_asn1(*blob))
278 {
279 DBG2(" fetched blob coded in DER format");
280 }
281 else
282 {
283 bool pgp = FALSE;
284
285 ugh = pemtobin(blob, NULL, "", &pgp);
286 if (ugh != NULL)
287 {
288 free(blob->ptr);
289 return FALSE;
290 };
291 if (is_asn1(*blob))
292 {
293 DBG2(" fetched blob coded in PEM format");
294 }
295 else
296 {
297 DBG1("crl fetched successfully but data coded in unknown format");
298 free(blob->ptr);
299 return FALSE;
300 }
301 }
302 return TRUE;
303 }
304
305 /**
306 * Complete a distributionPoint URI with ca information
307 */
308 static char* complete_uri(chunk_t distPoint, const char *ldaphost)
309 {
310 char *uri;
311 char *ptr = distPoint.ptr;
312 size_t len = distPoint.len;
313
314 char *symbol = memchr(ptr, ':', len);
315
316 if (symbol != NULL)
317 {
318 size_t type_len = symbol - ptr;
319
320 if (type_len >= 4 && strncasecmp(ptr, "ldap", 4) == 0)
321 {
322 ptr = symbol + 1;
323 len -= (type_len + 1);
324
325 if (len > 2 && *ptr++ == '/' && *ptr++ == '/')
326 {
327 len -= 2;
328 symbol = memchr(ptr, '/', len);
329
330 if (symbol != NULL && symbol - ptr == 0 && ldaphost != NULL)
331 {
332 uri = malloc(distPoint.len + strlen(ldaphost) + 1);
333
334 /* insert the ldaphost into the uri */
335 sprintf(uri, "%.*s%s%.*s"
336 , (int)(distPoint.len - len), distPoint.ptr
337 , ldaphost
338 , (int)len, symbol);
339 return uri;
340 }
341 }
342 }
343 }
344
345 /* default action: copy distributionPoint without change */
346 uri = malloc(distPoint.len + 1);
347 sprintf(uri, "%.*s", (int)distPoint.len, distPoint.ptr);
348 return uri;
349 }
350
351 /**
352 * Try to fetch the crls defined by the fetch requests
353 */
354 static void fetch_crls(bool cache_crls)
355 {
356 fetch_req_t *req;
357 fetch_req_t **reqp;
358
359 lock_crl_fetch_list("fetch_crls");
360 req = crl_fetch_reqs;
361 reqp = &crl_fetch_reqs;
362
363 while (req != NULL)
364 {
365 bool valid_crl = FALSE;
366 chunk_t blob = chunk_empty;
367 generalName_t *gn = req->distributionPoints;
368 const char *ldaphost;
369 ca_info_t *ca;
370
371 lock_ca_info_list("fetch_crls");
372
373 ca = get_ca_info(req->issuer, req->authKeySerialNumber, req->authKeyID);
374 ldaphost = (ca == NULL)? NULL : ca->ldaphost;
375
376 while (gn != NULL)
377 {
378 char *uri = complete_uri(gn->name, ldaphost);
379
380 if (fetch_asn1_blob(uri, &blob))
381 {
382 chunk_t crl_uri = chunk_clone(gn->name);
383
384 if (insert_crl(blob, crl_uri, cache_crls))
385 {
386 DBG(DBG_CONTROL,
387 DBG_log("we have a valid crl")
388 )
389 valid_crl = TRUE;
390 free(uri);
391 break;
392 }
393 }
394 free(uri);
395 gn = gn->next;
396 }
397
398 unlock_ca_info_list("fetch_crls");
399
400 if (valid_crl)
401 {
402 /* delete fetch request */
403 fetch_req_t *req_free = req;
404
405 req = req->next;
406 *reqp = req;
407 free_fetch_request(req_free);
408 }
409 else
410 {
411 /* try again next time */
412 req->trials++;
413 reqp = &req->next;
414 req = req->next;
415 }
416 }
417 unlock_crl_fetch_list("fetch_crls");
418 }
419
420 static void fetch_ocsp_status(ocsp_location_t* location)
421 {
422 chunk_t request, response;
423 char *uri;
424
425 request = build_ocsp_request(location);
426 response = chunk_empty;
427
428 /* we need a null terminated string for curl */
429 uri = malloc(location->uri.len + 1);
430 memcpy(uri, location->uri.ptr, location->uri.len);
431 *(uri + location->uri.len) = '\0';
432
433 DBG1(" requesting ocsp status from '%s' ...", uri);
434 if (lib->fetcher->fetch(lib->fetcher, uri, &response,
435 FETCH_REQUEST_DATA, request,
436 FETCH_REQUEST_TYPE, "application/ocsp-request",
437 FETCH_END) == SUCCESS)
438 {
439 parse_ocsp(location, response);
440 }
441 else
442 {
443 DBG1("ocsp request to %s failed", uri);
444 }
445
446 free(uri);
447 free(request.ptr);
448 chunk_free(&location->nonce);
449
450 /* increment the trial counter of the unresolved fetch requests */
451 {
452 ocsp_certinfo_t *certinfo = location->certinfo;
453
454 while (certinfo != NULL)
455 {
456 certinfo->trials++;
457 certinfo = certinfo->next;
458 }
459 }
460 }
461
462 /**
463 * Try to fetch the necessary ocsp information
464 */
465 static void fetch_ocsp(void)
466 {
467 ocsp_location_t *location;
468
469 lock_ocsp_fetch_list("fetch_ocsp");
470 location = ocsp_fetch_reqs;
471
472 /* fetch the ocps status for all locations */
473 while (location != NULL)
474 {
475 if (location->certinfo != NULL)
476 {
477 fetch_ocsp_status(location);
478 }
479 location = location->next;
480 }
481
482 unlock_ocsp_fetch_list("fetch_ocsp");
483 }
484
485 static void* fetch_thread(void *arg)
486 {
487 struct timespec wait_interval;
488
489 DBG(DBG_CONTROL,
490 DBG_log("fetch thread started")
491 )
492
493 pthread_mutex_lock(&fetch_wake_mutex);
494
495 while(1)
496 {
497 int status;
498
499 wait_interval.tv_nsec = 0;
500 wait_interval.tv_sec = time(NULL) + crl_check_interval;
501
502 DBG(DBG_CONTROL,
503 DBG_log("next regular crl check in %ld seconds", crl_check_interval)
504 )
505 status = pthread_cond_timedwait(&fetch_wake_cond, &fetch_wake_mutex
506 , &wait_interval);
507
508 if (status == ETIMEDOUT)
509 {
510 DBG(DBG_CONTROL,
511 DBG_log(" ");
512 DBG_log("*time to check crls and the ocsp cache")
513 )
514 check_ocsp();
515 check_crls();
516 }
517 else
518 {
519 DBG(DBG_CONTROL,
520 DBG_log("fetch thread was woken up")
521 )
522 }
523 fetch_ocsp();
524 fetch_crls(cache_crls);
525 }
526 }
527 #endif /* THREADS*/
528
529 /**
530 * Initializes curl and starts the fetching thread
531 */
532 void init_fetch(void)
533 {
534 if (crl_check_interval > 0)
535 {
536 #ifdef THREADS
537 int status = pthread_create( &thread, NULL, fetch_thread, NULL);
538
539 if (status != 0)
540 {
541 plog("fetching thread could not be started, status = %d", status);
542 }
543 #else /* !THREADS */
544 plog("warning: not compiled with pthread support");
545 #endif /* !THREADS */
546 }
547 }
548
549 void free_crl_fetch(void)
550 {
551 lock_crl_fetch_list("free_crl_fetch");
552
553 while (crl_fetch_reqs != NULL)
554 {
555 fetch_req_t *req = crl_fetch_reqs;
556 crl_fetch_reqs = req->next;
557 free_fetch_request(req);
558 }
559
560 unlock_crl_fetch_list("free_crl_fetch");
561 }
562
563 /**
564 * Free the chained list of ocsp requests
565 */
566 void free_ocsp_fetch(void)
567 {
568 lock_ocsp_fetch_list("free_ocsp_fetch");
569 free_ocsp_locations(&ocsp_fetch_reqs);
570 unlock_ocsp_fetch_list("free_ocsp_fetch");
571 }
572
573
574 /**
575 * Add additional distribution points
576 */
577 void add_distribution_points(const generalName_t *newPoints ,generalName_t **distributionPoints)
578 {
579 while (newPoints != NULL)
580 {
581 /* skip empty distribution point */
582 if (newPoints->name.len > 0)
583 {
584 bool add = TRUE;
585 generalName_t *gn = *distributionPoints;
586
587 while (gn != NULL)
588 {
589 if (gn->kind == newPoints->kind
590 && gn->name.len == newPoints->name.len
591 && memeq(gn->name.ptr, newPoints->name.ptr, gn->name.len))
592 {
593 /* skip if the distribution point is already present */
594 add = FALSE;
595 break;
596 }
597 gn = gn->next;
598 }
599
600 if (add)
601 {
602 /* clone additional distribution point */
603 gn = clone_thing(*newPoints);
604 gn->name = chunk_clone(newPoints->name);
605
606 /* insert additional CRL distribution point */
607 gn->next = *distributionPoints;
608 *distributionPoints = gn;
609 }
610 }
611 newPoints = newPoints->next;
612 }
613 }
614
615 fetch_req_t* build_crl_fetch_request(chunk_t issuer, chunk_t authKeySerialNumber,
616 chunk_t authKeyID, const generalName_t *gn)
617 {
618 fetch_req_t *req = malloc_thing(fetch_req_t);
619 *req = empty_fetch_req;
620
621 /* note current time */
622 req->installed = time(NULL);
623
624 /* clone fields */
625 req->issuer = chunk_clone(issuer);
626 req->authKeySerialNumber = chunk_clone(authKeySerialNumber);
627 req->authKeyID = chunk_clone(authKeyID);
628
629 /* copy distribution points */
630 add_distribution_points(gn, &req->distributionPoints);
631
632 return req;
633 }
634
635 /**
636 * Add a crl fetch request to the chained list
637 */
638 void add_crl_fetch_request(fetch_req_t *req)
639 {
640 fetch_req_t *r;
641
642 lock_crl_fetch_list("add_crl_fetch_request");
643 r = crl_fetch_reqs;
644
645 while (r != NULL)
646 {
647 if ((req->authKeyID.ptr != NULL)? same_keyid(req->authKeyID, r->authKeyID)
648 : (same_dn(req->issuer, r->issuer)
649 && same_serial(req->authKeySerialNumber, r->authKeySerialNumber)))
650 {
651 /* there is already a fetch request */
652 DBG(DBG_CONTROL,
653 DBG_log("crl fetch request already exists")
654 )
655
656 /* there might be new distribution points */
657 add_distribution_points(req->distributionPoints, &r->distributionPoints);
658
659 unlock_crl_fetch_list("add_crl_fetch_request");
660 free_fetch_request(req);
661 return;
662 }
663 r = r->next;
664 }
665
666 /* insert new fetch request at the head of the queue */
667 req->next = crl_fetch_reqs;
668 crl_fetch_reqs = req;
669
670 DBG(DBG_CONTROL,
671 DBG_log("crl fetch request added")
672 )
673 unlock_crl_fetch_list("add_crl_fetch_request");
674 }
675
676 /**
677 * Add an ocsp fetch request to the chained list
678 */
679 void add_ocsp_fetch_request(ocsp_location_t *location, chunk_t serialNumber)
680 {
681 ocsp_certinfo_t certinfo;
682
683 certinfo.serialNumber = serialNumber;
684
685 lock_ocsp_fetch_list("add_ocsp_fetch_request");
686 add_certinfo(location, &certinfo, &ocsp_fetch_reqs, TRUE);
687 unlock_ocsp_fetch_list("add_ocsp_fetch_request");
688 }
689
690 /**
691 * List all distribution points
692 */
693 void list_distribution_points(const generalName_t *gn)
694 {
695 bool first_gn = TRUE;
696
697 while (gn != NULL)
698 {
699 whack_log(RC_COMMENT, " %s '%.*s'", (first_gn)? "distPts: "
700 :" ", (int)gn->name.len, gn->name.ptr);
701 first_gn = FALSE;
702 gn = gn->next;
703 }
704 }
705
706 /**
707 * List all fetch requests in the chained list
708 */
709 void list_crl_fetch_requests(bool utc)
710 {
711 fetch_req_t *req;
712
713 lock_crl_fetch_list("list_crl_fetch_requests");
714 req = crl_fetch_reqs;
715
716 if (req != NULL)
717 {
718 whack_log(RC_COMMENT, " ");
719 whack_log(RC_COMMENT, "List of CRL fetch requests:");
720 whack_log(RC_COMMENT, " ");
721 }
722
723 while (req != NULL)
724 {
725 u_char buf[BUF_LEN];
726
727 whack_log(RC_COMMENT, "%T, trials: %d"
728 , &req->installed, utc, req->trials);
729 dntoa(buf, BUF_LEN, req->issuer);
730 whack_log(RC_COMMENT, " issuer: '%s'", buf);
731 if (req->authKeyID.ptr != NULL)
732 {
733 datatot(req->authKeyID.ptr, req->authKeyID.len, ':'
734 , buf, BUF_LEN);
735 whack_log(RC_COMMENT, " authkey: %s", buf);
736 }
737 if (req->authKeySerialNumber.ptr != NULL)
738 {
739 datatot(req->authKeySerialNumber.ptr, req->authKeySerialNumber.len, ':'
740 , buf, BUF_LEN);
741 whack_log(RC_COMMENT, " aserial: %s", buf);
742 }
743 list_distribution_points(req->distributionPoints);
744 req = req->next;
745 }
746 unlock_crl_fetch_list("list_crl_fetch_requests");
747 }
748
749 void list_ocsp_fetch_requests(bool utc)
750 {
751 lock_ocsp_fetch_list("list_ocsp_fetch_requests");
752 list_ocsp_locations(ocsp_fetch_reqs, TRUE, utc, FALSE);
753 unlock_ocsp_fetch_list("list_ocsp_fetch_requests");
754
755 }