summaryrefslogtreecommitdiff
path: root/src/pluto/fetch.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/pluto/fetch.c')
-rw-r--r--src/pluto/fetch.c1081
1 files changed, 1081 insertions, 0 deletions
diff --git a/src/pluto/fetch.c b/src/pluto/fetch.c
new file mode 100644
index 000000000..e3e56d3a8
--- /dev/null
+++ b/src/pluto/fetch.c
@@ -0,0 +1,1081 @@
+/* Dynamic fetching of X.509 CRLs
+ * Copyright (C) 2002 Stephane Laroche <stephane.laroche@colubris.com>
+ * Copyright (C) 2002-2004 Andreas Steffen, Zuercher Hochschule Winterthur
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * RCSID $Id: fetch.c,v 1.12 2006/05/16 14:19:27 as Exp $
+ */
+
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <time.h>
+#include <string.h>
+
+#ifdef THREADS
+#include <pthread.h>
+#endif
+
+#ifdef LIBCURL
+#include <curl/curl.h>
+#endif
+
+#include <freeswan.h>
+
+#ifdef LIBLDAP
+#include <ldap.h>
+#endif
+
+#include "constants.h"
+#include "defs.h"
+#include "log.h"
+#include "id.h"
+#include "asn1.h"
+#include "pem.h"
+#include "x509.h"
+#include "ca.h"
+#include "whack.h"
+#include "ocsp.h"
+#include "crl.h"
+#include "fetch.h"
+
+fetch_req_t empty_fetch_req = {
+ NULL , /* next */
+ 0 , /* installed */
+ 0 , /* trials */
+ { NULL, 0}, /* issuer */
+ { NULL, 0}, /* authKeyID */
+ { NULL, 0}, /* authKeySerialNumber */
+ NULL /* distributionPoints */
+};
+
+/* chained list of crl fetch requests */
+static fetch_req_t *crl_fetch_reqs = NULL;
+
+/* chained list of ocsp fetch requests */
+static ocsp_location_t *ocsp_fetch_reqs = NULL;
+
+#ifdef THREADS
+static pthread_t thread;
+static pthread_mutex_t certs_and_keys_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t authcert_list_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t crl_list_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t ocsp_cache_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t ca_info_list_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t crl_fetch_list_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t ocsp_fetch_list_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t fetch_wake_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t fetch_wake_cond = PTHREAD_COND_INITIALIZER;
+
+/*
+ * lock access to my certs and keys
+ */
+void
+lock_certs_and_keys(const char *who)
+{
+ pthread_mutex_lock(&certs_and_keys_mutex);
+ DBG(DBG_CONTROLMORE,
+ DBG_log("certs and keys locked by '%s'", who)
+ )
+}
+
+/*
+ * unlock access to my certs and keys
+ */
+void
+unlock_certs_and_keys(const char *who)
+{
+ DBG(DBG_CONTROLMORE,
+ DBG_log("certs and keys unlocked by '%s'", who)
+ )
+ pthread_mutex_unlock(&certs_and_keys_mutex);
+}
+
+/*
+ * lock access to the chained authcert list
+ */
+void
+lock_authcert_list(const char *who)
+{
+ pthread_mutex_lock(&authcert_list_mutex);
+ DBG(DBG_CONTROLMORE,
+ DBG_log("authcert list locked by '%s'", who)
+ )
+}
+
+/*
+ * unlock access to the chained authcert list
+ */
+void
+unlock_authcert_list(const char *who)
+{
+ DBG(DBG_CONTROLMORE,
+ DBG_log("authcert list unlocked by '%s'", who)
+ )
+ pthread_mutex_unlock(&authcert_list_mutex);
+}
+
+/*
+ * lock access to the chained crl list
+ */
+void
+lock_crl_list(const char *who)
+{
+ pthread_mutex_lock(&crl_list_mutex);
+ DBG(DBG_CONTROLMORE,
+ DBG_log("crl list locked by '%s'", who)
+ )
+}
+
+/*
+ * unlock access to the chained crl list
+ */
+void
+unlock_crl_list(const char *who)
+{
+ DBG(DBG_CONTROLMORE,
+ DBG_log("crl list unlocked by '%s'", who)
+ )
+ pthread_mutex_unlock(&crl_list_mutex);
+}
+
+/*
+ * lock access to the ocsp cache
+ */
+extern void
+lock_ocsp_cache(const char *who)
+{
+ pthread_mutex_lock(&ocsp_cache_mutex);
+ DBG(DBG_CONTROLMORE,
+ DBG_log("ocsp cache locked by '%s'", who)
+ )
+}
+
+/*
+ * unlock access to the ocsp cache
+ */
+extern void
+unlock_ocsp_cache(const char *who)
+{
+ DBG(DBG_CONTROLMORE,
+ DBG_log("ocsp cache unlocked by '%s'", who)
+ )
+ pthread_mutex_unlock(&ocsp_cache_mutex);
+}
+
+/*
+ * lock access to the ca info list
+ */
+extern void
+lock_ca_info_list(const char *who)
+{
+ pthread_mutex_lock(&ca_info_list_mutex);
+ DBG(DBG_CONTROLMORE,
+ DBG_log("ca info list locked by '%s'", who)
+ )
+}
+
+/*
+ * unlock access to the ca info list
+ */
+extern void
+unlock_ca_info_list(const char *who)
+{
+ DBG(DBG_CONTROLMORE,
+ DBG_log("ca info list unlocked by '%s'", who)
+ )
+ pthread_mutex_unlock(&ca_info_list_mutex);
+}
+
+/*
+ * lock access to the chained crl fetch request list
+ */
+static void
+lock_crl_fetch_list(const char *who)
+{
+ pthread_mutex_lock(&crl_fetch_list_mutex);
+ DBG(DBG_CONTROLMORE,
+ DBG_log("crl fetch request list locked by '%s'", who)
+ )
+}
+
+/*
+ * unlock access to the chained crl fetch request list
+ */
+static void
+unlock_crl_fetch_list(const char *who)
+{
+ DBG(DBG_CONTROLMORE,
+ DBG_log("crl fetch request list unlocked by '%s'", who)
+ )
+ pthread_mutex_unlock(&crl_fetch_list_mutex);
+}
+
+/*
+ * lock access to the chained ocsp fetch request list
+ */
+static void
+lock_ocsp_fetch_list(const char *who)
+{
+ pthread_mutex_lock(&ocsp_fetch_list_mutex);
+ DBG(DBG_CONTROLMORE,
+ DBG_log("ocsp fetch request list locked by '%s'", who)
+ )
+}
+
+/*
+ * unlock access to the chained ocsp fetch request list
+ */
+static void
+unlock_ocsp_fetch_list(const char *who)
+{
+ DBG(DBG_CONTROLMORE,
+ DBG_log("ocsp fetch request list unlocked by '%s'", who)
+ )
+ pthread_mutex_unlock(&ocsp_fetch_list_mutex);
+}
+
+/*
+ * wakes up the sleeping fetch thread
+ */
+void
+wake_fetch_thread(const char *who)
+{
+ if (crl_check_interval > 0)
+ {
+ DBG(DBG_CONTROLMORE,
+ DBG_log("fetch thread wake call by '%s'", who)
+ )
+ pthread_mutex_lock(&fetch_wake_mutex);
+ pthread_cond_signal(&fetch_wake_cond);
+ pthread_mutex_unlock(&fetch_wake_mutex);
+ }
+}
+#else /* !THREADS */
+#define lock_crl_fetch_list(who) /* do nothing */
+#define unlock_crl_fetch_list(who) /* do nothing */
+#define lock_ocsp_fetch_list(who) /* do nothing */
+#define unlock_ocsp_fetch_list(who) /* do nothing */
+#endif /* !THREADS */
+
+/*
+ * free the dynamic memory used to store fetch requests
+ */
+static void
+free_fetch_request(fetch_req_t *req)
+{
+ pfree(req->issuer.ptr);
+ pfreeany(req->authKeySerialNumber.ptr);
+ pfreeany(req->authKeyID.ptr);
+ free_generalNames(req->distributionPoints, TRUE);
+ pfree(req);
+}
+
+/* writes data into a dynamically resizeable chunk_t
+ * needed for libcurl responses
+ */
+size_t
+write_buffer(void *ptr, size_t size, size_t nmemb, void *data)
+{
+ size_t realsize = size * nmemb;
+ chunk_t *mem = (chunk_t*)data;
+
+ mem->ptr = (u_char *)realloc(mem->ptr, mem->len + realsize);
+ if (mem->ptr) {
+ memcpy(&(mem->ptr[mem->len]), ptr, realsize);
+ mem->len += realsize;
+ }
+ return realsize;
+}
+
+#ifdef THREADS
+/*
+ * fetches a binary blob from a url with libcurl
+ */
+static err_t
+fetch_curl(char *url, chunk_t *blob)
+{
+#ifdef LIBCURL
+ char errorbuffer[CURL_ERROR_SIZE] = "";
+ chunk_t response = empty_chunk;
+ CURLcode res;
+
+ /* get it with libcurl */
+ CURL *curl = curl_easy_init();
+
+ if (curl != NULL)
+ {
+ DBG(DBG_CONTROL,
+ DBG_log("Trying cURL '%s'", url)
+ )
+
+ curl_easy_setopt(curl, CURLOPT_URL, url);
+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_buffer);
+ curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&response);
+ curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, &errorbuffer);
+ curl_easy_setopt(curl, CURLOPT_FAILONERROR, TRUE);
+ curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, FETCH_CMD_TIMEOUT);
+
+ res = curl_easy_perform(curl);
+
+ if (res == CURLE_OK)
+ {
+ blob->len = response.len;
+ blob->ptr = alloc_bytes(response.len, "curl blob");
+ memcpy(blob->ptr, response.ptr, response.len);
+ }
+ else
+ {
+ plog("fetching uri (%s) with libcurl failed: %s", url, errorbuffer);
+ }
+ curl_easy_cleanup(curl);
+ /* not using freeanychunk because of realloc (no leak detective) */
+ curl_free(response.ptr);
+ }
+ return strlen(errorbuffer) > 0 ? "libcurl error" : NULL;
+#else /* !LIBCURL */
+ return "warning: not compiled with libcurl support";
+#endif /* !LIBCURL */
+}
+
+#ifdef LIBLDAP
+/*
+ * parses the result returned by an ldap query
+ */
+static err_t
+parse_ldap_result(LDAP * ldap, LDAPMessage *result, chunk_t *blob)
+{
+ err_t ugh = NULL;
+
+ LDAPMessage * entry = ldap_first_entry(ldap, result);
+
+ if (entry != NULL)
+ {
+ BerElement *ber = NULL;
+ char *attr;
+
+ attr = ldap_first_attribute(ldap, entry, &ber);
+
+ if (attr != NULL)
+ {
+ struct berval **values = ldap_get_values_len(ldap, entry, attr);
+
+ if (values != NULL)
+ {
+ if (values[0] != NULL)
+ {
+ blob->len = values[0]->bv_len;
+ blob->ptr = alloc_bytes(blob->len, "ldap blob");
+ memcpy(blob->ptr, values[0]->bv_val, blob->len);
+ if (values[1] != NULL)
+ {
+ plog("warning: more than one value was fetched from LDAP URL");
+ }
+ }
+ else
+ {
+ ugh = "no values in attribute";
+ }
+ ldap_value_free_len(values);
+ }
+ else
+ {
+ ugh = ldap_err2string(ldap_result2error(ldap, entry, 0));
+ }
+ ldap_memfree(attr);
+ }
+ else
+ {
+ ugh = ldap_err2string(ldap_result2error(ldap, entry, 0));
+ }
+ ber_free(ber, 0);
+ }
+ else
+ {
+ ugh = ldap_err2string(ldap_result2error(ldap, result, 0));
+ }
+ return ugh;
+}
+
+/*
+ * fetches a binary blob from an ldap url
+ */
+static err_t
+fetch_ldap_url(char *url, chunk_t *blob)
+{
+ LDAPURLDesc *lurl;
+ err_t ugh = NULL;
+ int rc;
+
+ DBG(DBG_CONTROL,
+ DBG_log("Trying LDAP URL '%s'", url)
+ )
+
+ rc = ldap_url_parse(url, &lurl);
+
+ if (rc == LDAP_SUCCESS)
+ {
+ LDAP *ldap = ldap_init(lurl->lud_host, lurl->lud_port);
+
+ if (ldap != NULL)
+ {
+ int ldap_version = LDAP_VERSION3;
+ struct timeval timeout;
+
+ timeout.tv_sec = FETCH_CMD_TIMEOUT;
+ timeout.tv_usec = 0;
+ ldap_set_option(ldap, LDAP_OPT_PROTOCOL_VERSION, &ldap_version);
+ ldap_set_option(ldap, LDAP_OPT_NETWORK_TIMEOUT, &timeout);
+
+ rc = ldap_simple_bind_s(ldap, NULL, NULL);
+
+ if (rc == LDAP_SUCCESS)
+ {
+ LDAPMessage *result;
+
+ timeout.tv_sec = FETCH_CMD_TIMEOUT;
+ timeout.tv_usec = 0;
+
+ rc = ldap_search_st(ldap, lurl->lud_dn
+ , lurl->lud_scope
+ , lurl->lud_filter
+ , lurl->lud_attrs
+ , 0, &timeout, &result);
+
+ if (rc == LDAP_SUCCESS)
+ {
+ ugh = parse_ldap_result(ldap, result, blob);
+ ldap_msgfree(result);
+ }
+ else
+ {
+ ugh = ldap_err2string(rc);
+ }
+ }
+ else
+ {
+ ugh = ldap_err2string(rc);
+ }
+ ldap_unbind_s(ldap);
+ }
+ else
+ {
+ ugh = "ldap init";
+ }
+ ldap_free_urldesc(lurl);
+ }
+ else
+ {
+ ugh = ldap_err2string(rc);
+ }
+ return ugh;
+}
+#else /* !LIBLDAP */
+static err_t
+fetch_ldap_url(char *url, chunk_t *blob)
+{
+ return "LDAP URL fetching not activated in pluto source code";
+}
+#endif /* !LIBLDAP */
+
+/*
+ * fetch an ASN.1 blob coded in PEM or DER format from a URL
+ */
+static err_t
+fetch_asn1_blob(char *url, chunk_t *blob)
+{
+ err_t ugh = NULL;
+
+ if (strlen(url) >= 4 && strncasecmp(url, "ldap", 4) == 0)
+ {
+ ugh = fetch_ldap_url(url, blob);
+ }
+ else
+ {
+ ugh = fetch_curl(url, blob);
+ }
+ if (ugh != NULL)
+ return ugh;
+
+ if (is_asn1(*blob))
+ {
+ DBG(DBG_PARSING,
+ DBG_log(" fetched blob coded in DER format")
+ )
+ }
+ else
+ {
+ bool pgp = FALSE;
+
+ ugh = pemtobin(blob, NULL, "", &pgp);
+ if (ugh == NULL)
+ {
+ if (is_asn1(*blob))
+ {
+ DBG(DBG_PARSING,
+ DBG_log(" fetched blob coded in PEM format")
+ )
+ }
+ else
+ {
+ ugh = "blob coded in unknown format";
+ pfree(blob->ptr);
+ }
+ }
+ else
+ {
+ pfree(blob->ptr);
+ }
+ }
+ return ugh;
+}
+
+/*
+ * complete a distributionPoint URI with ca information
+ */
+static char*
+complete_uri(chunk_t distPoint, const char *ldaphost)
+{
+ char *uri;
+ char *ptr = distPoint.ptr;
+ size_t len = distPoint.len;
+
+ char *symbol = memchr(ptr, ':', len);
+
+ if (symbol != NULL)
+ {
+ size_t type_len = symbol - ptr;
+
+ if (type_len >= 4 && strncasecmp(ptr, "ldap", 4) == 0)
+ {
+ ptr = symbol + 1;
+ len -= (type_len + 1);
+
+ if (len > 2 && *ptr++ == '/' && *ptr++ == '/')
+ {
+ len -= 2;
+ symbol = memchr(ptr, '/', len);
+
+ if (symbol != NULL && symbol - ptr == 0 && ldaphost != NULL)
+ {
+ uri = alloc_bytes(distPoint.len+strlen(ldaphost)+1, "uri");
+
+ /* insert the ldaphost into the uri */
+ sprintf(uri, "%.*s%s%.*s"
+ , (int)(distPoint.len - len), distPoint.ptr
+ , ldaphost
+ , (int)len, symbol);
+ return uri;
+ }
+ }
+ }
+ }
+
+ /* default action: copy distributionPoint without change */
+ uri = alloc_bytes(distPoint.len+1, "uri");
+ sprintf(uri, "%.*s", (int)distPoint.len, distPoint.ptr);
+ return uri;
+}
+
+/*
+ * try to fetch the crls defined by the fetch requests
+ */
+static void
+fetch_crls(bool cache_crls)
+{
+ fetch_req_t *req;
+ fetch_req_t **reqp;
+
+ lock_crl_fetch_list("fetch_crls");
+ req = crl_fetch_reqs;
+ reqp = &crl_fetch_reqs;
+
+ while (req != NULL)
+ {
+ bool valid_crl = FALSE;
+ chunk_t blob = empty_chunk;
+ generalName_t *gn = req->distributionPoints;
+ const char *ldaphost;
+ ca_info_t *ca;
+
+ lock_ca_info_list("fetch_crls");
+
+ ca = get_ca_info(req->issuer, req->authKeySerialNumber, req->authKeyID);
+ ldaphost = (ca == NULL)? NULL : ca->ldaphost;
+
+ while (gn != NULL)
+ {
+ char *uri = complete_uri(gn->name, ldaphost);
+
+ err_t ugh = fetch_asn1_blob(uri, &blob);
+ pfree(uri);
+
+ if (ugh != NULL)
+ {
+ plog("fetch failed: %s", ugh);
+ }
+ else
+ {
+ chunk_t crl_uri;
+
+ clonetochunk(crl_uri, gn->name.ptr, gn->name.len, "crl uri");
+ if (insert_crl(blob, crl_uri, cache_crls))
+ {
+ DBG(DBG_CONTROL,
+ DBG_log("we have a valid crl")
+ )
+ valid_crl = TRUE;
+ break;
+ }
+ }
+ gn = gn->next;
+ }
+
+ unlock_ca_info_list("fetch_crls");
+
+ if (valid_crl)
+ {
+ /* delete fetch request */
+ fetch_req_t *req_free = req;
+
+ req = req->next;
+ *reqp = req;
+ free_fetch_request(req_free);
+ }
+ else
+ {
+ /* try again next time */
+ req->trials++;
+ reqp = &req->next;
+ req = req->next;
+ }
+ }
+ unlock_crl_fetch_list("fetch_crls");
+}
+
+static void
+fetch_ocsp_status(ocsp_location_t* location)
+{
+#ifdef LIBCURL
+ chunk_t request;
+ chunk_t response = empty_chunk;
+
+ CURL* curl;
+ CURLcode res;
+
+ request = build_ocsp_request(location);
+
+ DBG(DBG_CONTROL,
+ DBG_log("sending ocsp request to location '%.*s'"
+ , (int)location->uri.len, location->uri.ptr)
+ )
+ DBG(DBG_RAW,
+ DBG_dump_chunk("OCSP request", request)
+ )
+
+ /* send via http post using libcurl */
+ curl = curl_easy_init();
+
+ if (curl != NULL)
+ {
+ char errorbuffer[CURL_ERROR_SIZE];
+ struct curl_slist *headers = NULL;
+ char* uri = alloc_bytes(location->uri.len+1, "ocsp uri");
+
+ /* we need a null terminated string for curl */
+ memcpy(uri, location->uri.ptr, location->uri.len);
+ *(uri + location->uri.len) = '\0';
+
+ /* set content type header */
+ headers = curl_slist_append(headers, "Content-Type: application/ocsp-request");
+ curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
+
+ curl_easy_setopt(curl, CURLOPT_URL, uri);
+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_buffer);
+ curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&response);
+ curl_easy_setopt(curl, CURLOPT_POSTFIELDS, request.ptr);
+ curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, request.len);
+ curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, &errorbuffer);
+ curl_easy_setopt(curl, CURLOPT_FAILONERROR, TRUE);
+ curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, FETCH_CMD_TIMEOUT);
+
+ res = curl_easy_perform(curl);
+
+ if (res == CURLE_OK)
+ {
+ DBG(DBG_CONTROL,
+ DBG_log("received ocsp response")
+ )
+ DBG(DBG_RAW,
+ DBG_dump_chunk("OCSP response:\n", response)
+ )
+ parse_ocsp(location, response);
+ }
+ else
+ {
+ plog("failed to fetch ocsp status from '%s': %s", uri, errorbuffer);
+ }
+ curl_slist_free_all(headers);
+ curl_easy_cleanup(curl);
+ pfree(uri);
+ /* not using freeanychunk because of realloc (no leak detective) */
+ curl_free(response.ptr);
+ }
+ freeanychunk(location->nonce);
+ freeanychunk(request);
+
+ /* increment the trial counter of the unresolved fetch requests */
+ {
+ ocsp_certinfo_t *certinfo = location->certinfo;
+
+ while (certinfo != NULL)
+ {
+ certinfo->trials++;
+ certinfo = certinfo->next;
+ }
+ }
+ return;
+#else /* !LIBCURL */
+ plog("ocsp error: pluto wasn't compiled with libcurl support");
+#endif /* !LIBCURL */
+}
+
+/*
+ * try to fetch the necessary ocsp information
+ */
+static void
+fetch_ocsp(void)
+{
+ ocsp_location_t *location;
+
+ lock_ocsp_fetch_list("fetch_ocsp");
+ location = ocsp_fetch_reqs;
+
+ /* fetch the ocps status for all locations */
+ while (location != NULL)
+ {
+ if (location->certinfo != NULL)
+ fetch_ocsp_status(location);
+ location = location->next;
+ }
+
+ unlock_ocsp_fetch_list("fetch_ocsp");
+}
+
+static void*
+fetch_thread(void *arg)
+{
+ struct timespec wait_interval;
+
+ DBG(DBG_CONTROL,
+ DBG_log("fetch thread started")
+ )
+
+ pthread_mutex_lock(&fetch_wake_mutex);
+
+ while(1)
+ {
+ int status;
+
+ wait_interval.tv_nsec = 0;
+ wait_interval.tv_sec = time(NULL) + crl_check_interval;
+
+ DBG(DBG_CONTROL,
+ DBG_log("next regular crl check in %ld seconds", crl_check_interval)
+ )
+ status = pthread_cond_timedwait(&fetch_wake_cond, &fetch_wake_mutex
+ , &wait_interval);
+
+ if (status == ETIMEDOUT)
+ {
+ DBG(DBG_CONTROL,
+ DBG_log(" ");
+ DBG_log("*time to check crls and the ocsp cache")
+ )
+ check_ocsp();
+ check_crls();
+ }
+ else
+ {
+ DBG(DBG_CONTROL,
+ DBG_log("fetch thread was woken up")
+ )
+ }
+ fetch_ocsp();
+ fetch_crls(cache_crls);
+ }
+}
+#endif /* THREADS*/
+
+/*
+ * initializes curl and starts the fetching thread
+ */
+void
+init_fetch(void)
+{
+ int status;
+
+#ifdef LIBCURL
+ /* init curl */
+ status = curl_global_init(CURL_GLOBAL_NOTHING);
+ if (status != CURLE_OK)
+ {
+ plog("libcurl could not be initialized, status = %d", status);
+ }
+#endif /* LIBCURL */
+
+ if (crl_check_interval > 0)
+ {
+#ifdef THREADS
+ status = pthread_create( &thread, NULL, fetch_thread, NULL);
+ if (status != 0)
+ {
+ plog("fetching thread could not be started, status = %d", status);
+ }
+#else /* !THREADS */
+ plog("warning: not compiled with pthread support");
+#endif /* !THREADS */
+ }
+}
+
+void
+free_crl_fetch(void)
+{
+ lock_crl_fetch_list("free_crl_fetch");
+
+ while (crl_fetch_reqs != NULL)
+ {
+ fetch_req_t *req = crl_fetch_reqs;
+ crl_fetch_reqs = req->next;
+ free_fetch_request(req);
+ }
+
+ unlock_crl_fetch_list("free_crl_fetch");
+
+#ifdef LIBCURL
+ if (crl_check_interval > 0)
+ {
+ /* cleanup curl */
+ curl_global_cleanup();
+ }
+#endif /* LIBCURL */
+}
+
+/*
+ * free the chained list of ocsp requests
+ */
+void
+free_ocsp_fetch(void)
+{
+ lock_ocsp_fetch_list("free_ocsp_fetch");
+ free_ocsp_locations(&ocsp_fetch_reqs);
+ unlock_ocsp_fetch_list("free_ocsp_fetch");
+}
+
+
+/*
+ * add additional distribution points
+ */
+void
+add_distribution_points(const generalName_t *newPoints ,generalName_t **distributionPoints)
+{
+ while (newPoints != NULL)
+ {
+ /* skip empty distribution point */
+ if (newPoints->name.len > 0)
+ {
+ bool add = TRUE;
+ generalName_t *gn = *distributionPoints;
+
+ while (gn != NULL)
+ {
+ if (gn->kind == newPoints->kind
+ && gn->name.len == newPoints->name.len
+ && memcmp(gn->name.ptr, newPoints->name.ptr, gn->name.len) == 0)
+ {
+ /* skip if the distribution point is already present */
+ add = FALSE;
+ break;
+ }
+ gn = gn->next;
+ }
+
+ if (add)
+ {
+ /* clone additional distribution point */
+ gn = clone_thing(*newPoints, "generalName");
+ clonetochunk(gn->name, newPoints->name.ptr, newPoints->name.len
+ , "crl uri");
+
+ /* insert additional CRL distribution point */
+ gn->next = *distributionPoints;
+ *distributionPoints = gn;
+ }
+ }
+ newPoints = newPoints->next;
+ }
+}
+
+fetch_req_t*
+build_crl_fetch_request(chunk_t issuer, chunk_t authKeySerialNumber
+, chunk_t authKeyID, const generalName_t *gn)
+{
+ fetch_req_t *req = alloc_thing(fetch_req_t, "fetch request");
+ *req = empty_fetch_req;
+
+ /* note current time */
+ req->installed = time(NULL);
+
+ /* clone fields */
+ clonetochunk(req->issuer, issuer.ptr, issuer.len, "issuer");
+ if (authKeySerialNumber.ptr != NULL)
+ {
+ clonetochunk(req->authKeySerialNumber, authKeySerialNumber.ptr
+ , authKeySerialNumber.len, "authKeySerialNumber");
+ }
+ if (authKeyID.ptr != NULL)
+ {
+ clonetochunk(req->authKeyID, authKeyID.ptr, authKeyID.len, "authKeyID");
+ }
+
+ /* copy distribution points */
+ add_distribution_points(gn, &req->distributionPoints);
+
+ return req;
+}
+
+/*
+ * add a crl fetch request to the chained list
+ */
+void
+add_crl_fetch_request(fetch_req_t *req)
+{
+ fetch_req_t *r;
+
+ lock_crl_fetch_list("add_crl_fetch_request");
+ r = crl_fetch_reqs;
+
+ while (r != NULL)
+ {
+ if ((req->authKeyID.ptr != NULL)? same_keyid(req->authKeyID, r->authKeyID)
+ : (same_dn(req->issuer, r->issuer)
+ && same_serial(req->authKeySerialNumber, r->authKeySerialNumber)))
+ {
+ /* there is already a fetch request */
+ DBG(DBG_CONTROL,
+ DBG_log("crl fetch request already exists")
+ )
+
+ /* there might be new distribution points */
+ add_distribution_points(req->distributionPoints, &r->distributionPoints);
+
+ unlock_crl_fetch_list("add_crl_fetch_request");
+ free_fetch_request(req);
+ return;
+ }
+ r = r->next;
+ }
+
+ /* insert new fetch request at the head of the queue */
+ req->next = crl_fetch_reqs;
+ crl_fetch_reqs = req;
+
+ DBG(DBG_CONTROL,
+ DBG_log("crl fetch request added")
+ )
+ unlock_crl_fetch_list("add_crl_fetch_request");
+}
+
+/*
+ * add an ocsp fetch request to the chained list
+ */
+void
+add_ocsp_fetch_request(ocsp_location_t *location, chunk_t serialNumber)
+{
+ ocsp_certinfo_t certinfo;
+
+ certinfo.serialNumber = serialNumber;
+
+ lock_ocsp_fetch_list("add_ocsp_fetch_request");
+ add_certinfo(location, &certinfo, &ocsp_fetch_reqs, TRUE);
+ unlock_ocsp_fetch_list("add_ocsp_fetch_request");
+}
+
+/*
+ * list all distribution points
+ */
+void
+list_distribution_points(const generalName_t *gn)
+{
+ bool first_gn = TRUE;
+
+ while (gn != NULL)
+ {
+ whack_log(RC_COMMENT, " %s '%.*s'", (first_gn)? "distPts: "
+ :" ", (int)gn->name.len, gn->name.ptr);
+ first_gn = FALSE;
+ gn = gn->next;
+ }
+}
+
+/*
+ * list all fetch requests in the chained list
+ */
+void
+list_crl_fetch_requests(bool utc)
+{
+ fetch_req_t *req;
+
+ lock_crl_fetch_list("list_crl_fetch_requests");
+ req = crl_fetch_reqs;
+
+ if (req != NULL)
+ {
+ whack_log(RC_COMMENT, " ");
+ whack_log(RC_COMMENT, "List of CRL fetch requests:");
+ whack_log(RC_COMMENT, " ");
+ }
+
+ while (req != NULL)
+ {
+ u_char buf[BUF_LEN];
+
+ whack_log(RC_COMMENT, "%s, trials: %d"
+ , timetoa(&req->installed, utc), req->trials);
+ dntoa(buf, BUF_LEN, req->issuer);
+ whack_log(RC_COMMENT, " issuer: '%s'", buf);
+ if (req->authKeyID.ptr != NULL)
+ {
+ datatot(req->authKeyID.ptr, req->authKeyID.len, ':'
+ , buf, BUF_LEN);
+ whack_log(RC_COMMENT, " authkey: %s", buf);
+ }
+ if (req->authKeySerialNumber.ptr != NULL)
+ {
+ datatot(req->authKeySerialNumber.ptr, req->authKeySerialNumber.len, ':'
+ , buf, BUF_LEN);
+ whack_log(RC_COMMENT, " aserial: %s", buf);
+ }
+ list_distribution_points(req->distributionPoints);
+ req = req->next;
+ }
+ unlock_crl_fetch_list("list_crl_fetch_requests");
+}
+
+void
+list_ocsp_fetch_requests(bool utc)
+{
+ lock_ocsp_fetch_list("list_ocsp_fetch_requests");
+ list_ocsp_locations(ocsp_fetch_reqs, TRUE, utc, FALSE);
+ unlock_ocsp_fetch_list("list_ocsp_fetch_requests");
+
+}