summaryrefslogtreecommitdiff
path: root/src/libcharon/credentials
diff options
context:
space:
mode:
authorRene Mayrhofer <rene@mayrhofer.eu.org>2010-05-25 19:01:36 +0000
committerRene Mayrhofer <rene@mayrhofer.eu.org>2010-05-25 19:01:36 +0000
commit1ac70afcc1f7d6d2738a34308810719b0976d29f (patch)
tree805f6ce2a15d1a717781d7cbceac8408a74b6b0c /src/libcharon/credentials
parented7d79f96177044949744da10f4431c1d6242241 (diff)
downloadvyos-strongswan-1ac70afcc1f7d6d2738a34308810719b0976d29f.tar.gz
vyos-strongswan-1ac70afcc1f7d6d2738a34308810719b0976d29f.zip
[svn-upgrade] Integrating new upstream version, strongswan (4.4.0)
Diffstat (limited to 'src/libcharon/credentials')
-rw-r--r--src/libcharon/credentials/credential_manager.c1681
-rw-r--r--src/libcharon/credentials/credential_manager.h203
-rw-r--r--src/libcharon/credentials/credential_set.h108
-rw-r--r--src/libcharon/credentials/sets/auth_cfg_wrapper.c223
-rw-r--r--src/libcharon/credentials/sets/auth_cfg_wrapper.h53
-rw-r--r--src/libcharon/credentials/sets/cert_cache.c390
-rw-r--r--src/libcharon/credentials/sets/cert_cache.h71
-rw-r--r--src/libcharon/credentials/sets/ocsp_response_wrapper.c147
-rw-r--r--src/libcharon/credentials/sets/ocsp_response_wrapper.h53
9 files changed, 2929 insertions, 0 deletions
diff --git a/src/libcharon/credentials/credential_manager.c b/src/libcharon/credentials/credential_manager.c
new file mode 100644
index 000000000..adea0b4be
--- /dev/null
+++ b/src/libcharon/credentials/credential_manager.c
@@ -0,0 +1,1681 @@
+/*
+ * Copyright (C) 2007 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * 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.
+ */
+
+#include "credential_manager.h"
+
+#include <daemon.h>
+#include <threading/thread_value.h>
+#include <threading/mutex.h>
+#include <threading/rwlock.h>
+#include <utils/linked_list.h>
+#include <credentials/sets/cert_cache.h>
+#include <credentials/sets/auth_cfg_wrapper.h>
+#include <credentials/sets/ocsp_response_wrapper.h>
+#include <credentials/certificates/x509.h>
+#include <credentials/certificates/crl.h>
+#include <credentials/certificates/ocsp_request.h>
+#include <credentials/certificates/ocsp_response.h>
+
+typedef struct private_credential_manager_t private_credential_manager_t;
+
+/**
+ * private data of credential_manager
+ */
+struct private_credential_manager_t {
+
+ /**
+ * public functions
+ */
+ credential_manager_t public;
+
+ /**
+ * list of credential sets
+ */
+ linked_list_t *sets;
+
+ /**
+ * thread local set of credentials, linked_list_t with credential_set_t's
+ */
+ thread_value_t *local_sets;
+
+ /**
+ * trust relationship and certificate cache
+ */
+ cert_cache_t *cache;
+
+ /**
+ * certificates queued for persistent caching
+ */
+ linked_list_t *cache_queue;
+
+ /**
+ * read-write lock to sets list
+ */
+ rwlock_t *lock;
+
+ /**
+ * mutex for cache queue
+ */
+ mutex_t *queue_mutex;
+};
+
+/** data to pass to create_private_enumerator */
+typedef struct {
+ private_credential_manager_t *this;
+ key_type_t type;
+ identification_t* keyid;
+} private_data_t;
+
+/** data to pass to create_cert_enumerator */
+typedef struct {
+ private_credential_manager_t *this;
+ certificate_type_t cert;
+ key_type_t key;
+ identification_t *id;
+ bool trusted;
+} cert_data_t;
+
+/** data to pass to create_cdp_enumerator */
+typedef struct {
+ private_credential_manager_t *this;
+ certificate_type_t type;
+ identification_t *id;
+} cdp_data_t;
+
+/** data to pass to create_shared_enumerator */
+typedef struct {
+ private_credential_manager_t *this;
+ shared_key_type_t type;
+ identification_t *me;
+ identification_t *other;
+} shared_data_t;
+
+/** enumerator over local and global sets */
+typedef struct {
+ /** implements enumerator_t */
+ enumerator_t public;
+ /** enumerator over global sets */
+ enumerator_t *global;
+ /** enumerator over local sets */
+ enumerator_t *local;
+} sets_enumerator_t;
+
+/**
+ * destroy a sets_enumerator_t
+ */
+static void sets_enumerator_destroy(sets_enumerator_t *this)
+{
+ DESTROY_IF(this->global);
+ DESTROY_IF(this->local);
+ free(this);
+}
+
+/**
+ * sets_enumerator_t.enumerate
+ */
+static bool sets_enumerator_enumerate(sets_enumerator_t *this,
+ credential_set_t **set)
+{
+ if (this->global)
+ {
+ if (this->global->enumerate(this->global, set))
+ {
+ return TRUE;
+ }
+ /* end of global sets, look for local */
+ this->global->destroy(this->global);
+ this->global = NULL;
+ }
+ if (this->local)
+ {
+ return this->local->enumerate(this->local, set);
+ }
+ return FALSE;
+}
+
+/**
+ * create an enumerator over both, global and local sets
+ */
+static enumerator_t *create_sets_enumerator(private_credential_manager_t *this)
+{
+ linked_list_t *local;
+ sets_enumerator_t *enumerator = malloc_thing(sets_enumerator_t);
+
+ enumerator->public.enumerate = (void*)sets_enumerator_enumerate;
+ enumerator->public.destroy = (void*)sets_enumerator_destroy;
+ enumerator->global = this->sets->create_enumerator(this->sets);
+ enumerator->local = NULL;
+ local = this->local_sets->get(this->local_sets);
+ if (local)
+ {
+ enumerator->local = local->create_enumerator(local);
+ }
+ return &enumerator->public;
+}
+
+/**
+ * cleanup function for cert data
+ */
+static void destroy_cert_data(cert_data_t *data)
+{
+ data->this->lock->unlock(data->this->lock);
+ free(data);
+}
+
+/**
+ * enumerator constructor for certificates
+ */
+static enumerator_t *create_cert(credential_set_t *set, cert_data_t *data)
+{
+ return set->create_cert_enumerator(set, data->cert, data->key,
+ data->id, data->trusted);
+}
+
+/**
+ * Implementation of credential_manager_t.create_cert_enumerator.
+ */
+static enumerator_t *create_cert_enumerator(private_credential_manager_t *this,
+ certificate_type_t certificate, key_type_t key,
+ identification_t *id, bool trusted)
+{
+ cert_data_t *data = malloc_thing(cert_data_t);
+ data->this = this;
+ data->cert = certificate;
+ data->key = key;
+ data->id = id;
+ data->trusted = trusted;
+
+ this->lock->read_lock(this->lock);
+ return enumerator_create_nested(create_sets_enumerator(this),
+ (void*)create_cert, data,
+ (void*)destroy_cert_data);
+}
+
+/**
+ * Implementation of credential_manager_t.get_cert.
+ */
+static certificate_t *get_cert(private_credential_manager_t *this,
+ certificate_type_t cert, key_type_t key,
+ identification_t *id, bool trusted)
+{
+ certificate_t *current, *found = NULL;
+ enumerator_t *enumerator;
+
+ enumerator = create_cert_enumerator(this, cert, key, id, trusted);
+ if (enumerator->enumerate(enumerator, &current))
+ {
+ /* TODO: best match? order by keyid, subject, sualtname */
+ found = current->get_ref(current);
+ }
+ enumerator->destroy(enumerator);
+ return found;
+}
+
+
+/**
+ * cleanup function for cdp data
+ */
+static void destroy_cdp_data(cdp_data_t *data)
+{
+ data->this->lock->unlock(data->this->lock);
+ free(data);
+}
+
+/**
+ * enumerator constructor for CDPs
+ */
+static enumerator_t *create_cdp(credential_set_t *set, cdp_data_t *data)
+{
+ return set->create_cdp_enumerator(set, data->type, data->id);
+}
+/**
+ * Implementation of credential_manager_t.create_cdp_enumerator.
+ */
+static enumerator_t * create_cdp_enumerator(private_credential_manager_t *this,
+ certificate_type_t type, identification_t *id)
+{
+ cdp_data_t *data = malloc_thing(cdp_data_t);
+ data->this = this;
+ data->type = type;
+ data->id = id;
+
+ this->lock->read_lock(this->lock);
+ return enumerator_create_nested(create_sets_enumerator(this),
+ (void*)create_cdp, data,
+ (void*)destroy_cdp_data);
+}
+
+/**
+ * cleanup function for private data
+ */
+static void destroy_private_data(private_data_t *data)
+{
+ data->this->lock->unlock(data->this->lock);
+ free(data);
+}
+
+/**
+ * enumerator constructor for private keys
+ */
+static enumerator_t *create_private(credential_set_t *set, private_data_t *data)
+{
+ return set->create_private_enumerator(set, data->type, data->keyid);
+}
+
+/**
+ * Implementation of credential_manager_t.create_private_enumerator.
+ */
+static enumerator_t* create_private_enumerator(
+ private_credential_manager_t *this,
+ key_type_t key, identification_t *keyid)
+{
+ private_data_t *data;
+
+ data = malloc_thing(private_data_t);
+ data->this = this;
+ data->type = key;
+ data->keyid = keyid;
+ this->lock->read_lock(this->lock);
+ return enumerator_create_nested(create_sets_enumerator(this),
+ (void*)create_private, data,
+ (void*)destroy_private_data);
+}
+
+/**
+ * Implementation of credential_manager_t.get_private_by_keyid.
+ */
+static private_key_t *get_private_by_keyid(private_credential_manager_t *this,
+ key_type_t key, identification_t *keyid)
+{
+ private_key_t *found = NULL;
+ enumerator_t *enumerator;
+
+ enumerator = create_private_enumerator(this, key, keyid);
+ if (enumerator->enumerate(enumerator, &found))
+ {
+ found->get_ref(found);
+ }
+ enumerator->destroy(enumerator);
+ return found;
+}
+
+/**
+ * cleanup function for shared data
+ */
+static void destroy_shared_data(shared_data_t *data)
+{
+ data->this->lock->unlock(data->this->lock);
+ free(data);
+}
+
+/**
+ * enumerator constructor for shared keys
+ */
+static enumerator_t *create_shared(credential_set_t *set, shared_data_t *data)
+{
+ return set->create_shared_enumerator(set, data->type, data->me, data->other);
+}
+
+/**
+ * Implementation of credential_manager_t.create_shared_enumerator.
+ */
+static enumerator_t *create_shared_enumerator(private_credential_manager_t *this,
+ shared_key_type_t type,
+ identification_t *me, identification_t *other)
+{
+ shared_data_t *data = malloc_thing(shared_data_t);
+ data->this = this;
+ data->type = type;
+ data->me = me;
+ data->other = other;
+
+ this->lock->read_lock(this->lock);
+ return enumerator_create_nested(create_sets_enumerator(this),
+ (void*)create_shared, data,
+ (void*)destroy_shared_data);
+}
+
+/**
+ * Implementation of credential_manager_t.get_shared.
+ */
+static shared_key_t *get_shared(private_credential_manager_t *this,
+ shared_key_type_t type, identification_t *me,
+ identification_t *other)
+{
+ shared_key_t *current, *found = NULL;
+ id_match_t *best_me = ID_MATCH_NONE, *best_other = ID_MATCH_NONE;
+ id_match_t *match_me, *match_other;
+ enumerator_t *enumerator;
+
+ enumerator = create_shared_enumerator(this, type, me, other);
+ while (enumerator->enumerate(enumerator, &current, &match_me, &match_other))
+ {
+ if (match_other > best_other ||
+ (match_other == best_other && match_me > best_me))
+ {
+ DESTROY_IF(found);
+ found = current->get_ref(current);
+ best_me = match_me;
+ best_other = match_other;
+ }
+ }
+ enumerator->destroy(enumerator);
+ return found;
+}
+
+/**
+ * add a credential set to the thread local list
+ */
+static void add_local_set(private_credential_manager_t *this,
+ credential_set_t *set)
+{
+ linked_list_t *sets;
+
+ sets = this->local_sets->get(this->local_sets);
+ if (!sets)
+ { /* first invocation */
+ sets = linked_list_create();
+ this->local_sets->set(this->local_sets, sets);
+ }
+ sets->insert_last(sets, set);
+}
+
+/**
+ * remove a credential set from the thread local list
+ */
+static void remove_local_set(private_credential_manager_t *this,
+ credential_set_t *set)
+{
+ linked_list_t *sets;
+
+ sets = this->local_sets->get(this->local_sets);
+ sets->remove(sets, set, NULL);
+}
+
+/**
+ * Implementation of credential_manager_t.cache_cert.
+ */
+static void cache_cert(private_credential_manager_t *this, certificate_t *cert)
+{
+ credential_set_t *set;
+ enumerator_t *enumerator;
+
+ if (this->lock->try_write_lock(this->lock))
+ {
+ enumerator = this->sets->create_enumerator(this->sets);
+ while (enumerator->enumerate(enumerator, &set))
+ {
+ set->cache_cert(set, cert);
+ }
+ enumerator->destroy(enumerator);
+ this->lock->unlock(this->lock);
+ }
+ else
+ { /* we can't cache now as other threads are active, queue for later */
+ this->queue_mutex->lock(this->queue_mutex);
+ this->cache_queue->insert_last(this->cache_queue, cert->get_ref(cert));
+ this->queue_mutex->unlock(this->queue_mutex);
+ }
+}
+
+/**
+ * Try to cache certificates queued for caching
+ */
+static void cache_queue(private_credential_manager_t *this)
+{
+ credential_set_t *set;
+ certificate_t *cert;
+ enumerator_t *enumerator;
+
+ this->queue_mutex->lock(this->queue_mutex);
+ if (this->cache_queue->get_count(this->cache_queue) > 0 &&
+ this->lock->try_write_lock(this->lock))
+ {
+ while (this->cache_queue->remove_last(this->cache_queue,
+ (void**)&cert) == SUCCESS)
+ {
+ enumerator = this->sets->create_enumerator(this->sets);
+ while (enumerator->enumerate(enumerator, &set))
+ {
+ set->cache_cert(set, cert);
+ }
+ enumerator->destroy(enumerator);
+ cert->destroy(cert);
+ }
+ this->lock->unlock(this->lock);
+ }
+ this->queue_mutex->unlock(this->queue_mutex);
+}
+
+/**
+ * forward declaration
+ */
+static enumerator_t *create_trusted_enumerator(private_credential_manager_t *this,
+ key_type_t type, identification_t *id, bool crl, bool ocsp);
+
+/**
+ * Do an OCSP request
+ */
+static certificate_t *fetch_ocsp(private_credential_manager_t *this, char *url,
+ certificate_t *subject, certificate_t *issuer)
+{
+ certificate_t *request, *response;
+ chunk_t send, receive;
+
+ /* TODO: requestor name, signature */
+ request = lib->creds->create(lib->creds,
+ CRED_CERTIFICATE, CERT_X509_OCSP_REQUEST,
+ BUILD_CA_CERT, issuer,
+ BUILD_CERT, subject, BUILD_END);
+ if (!request)
+ {
+ DBG1(DBG_CFG, "generating ocsp request failed");
+ return NULL;
+ }
+
+ send = request->get_encoding(request);
+ request->destroy(request);
+
+ DBG1(DBG_CFG, " requesting ocsp status from '%s' ...", url);
+ if (lib->fetcher->fetch(lib->fetcher, url, &receive,
+ FETCH_REQUEST_DATA, send,
+ FETCH_REQUEST_TYPE, "application/ocsp-request",
+ FETCH_END) != SUCCESS)
+ {
+ DBG1(DBG_CFG, "ocsp request to %s failed", url);
+ chunk_free(&send);
+ return NULL;
+ }
+ chunk_free(&send);
+
+ response = lib->creds->create(lib->creds,
+ CRED_CERTIFICATE, CERT_X509_OCSP_RESPONSE,
+ BUILD_BLOB_ASN1_DER, receive, BUILD_END);
+ chunk_free(&receive);
+ if (!response)
+ {
+ DBG1(DBG_CFG, "parsing ocsp response failed");
+ return NULL;
+ }
+ return response;
+}
+
+/**
+ * check the signature of an OCSP response
+ */
+static bool verify_ocsp(private_credential_manager_t *this,
+ ocsp_response_t *response)
+{
+ certificate_t *issuer, *subject;
+ identification_t *responder;
+ ocsp_response_wrapper_t *wrapper;
+ enumerator_t *enumerator;
+ bool verified = FALSE;
+
+ wrapper = ocsp_response_wrapper_create((ocsp_response_t*)response);
+ add_local_set(this, &wrapper->set);
+
+ subject = &response->certificate;
+ responder = subject->get_issuer(subject);
+ enumerator = create_trusted_enumerator(this, KEY_ANY, responder, FALSE, FALSE);
+ while (enumerator->enumerate(enumerator, &issuer, NULL))
+ {
+ if (this->cache->issued_by(this->cache, subject, issuer))
+ {
+ DBG1(DBG_CFG, " ocsp response correctly signed by \"%Y\"",
+ issuer->get_subject(issuer));
+ verified = TRUE;
+ break;
+ }
+ }
+ enumerator->destroy(enumerator);
+
+ remove_local_set(this, &wrapper->set);
+ wrapper->destroy(wrapper);
+ return verified;
+}
+
+/**
+ * Get the better of two OCSP responses, and check for usable OCSP info
+ */
+static certificate_t *get_better_ocsp(private_credential_manager_t *this,
+ certificate_t *cand, certificate_t *best,
+ x509_t *subject, x509_t *issuer,
+ cert_validation_t *valid, bool cache)
+{
+ ocsp_response_t *response;
+ time_t revocation, this_update, next_update, valid_until;
+ crl_reason_t reason;
+ bool revoked = FALSE;
+
+ response = (ocsp_response_t*)cand;
+
+ /* check ocsp signature */
+ if (!verify_ocsp(this, response))
+ {
+ DBG1(DBG_CFG, "ocsp response verification failed");
+ cand->destroy(cand);
+ return best;
+ }
+ /* check if response contains our certificate */
+ switch (response->get_status(response, subject, issuer, &revocation, &reason,
+ &this_update, &next_update))
+ {
+ case VALIDATION_REVOKED:
+ /* subject has been revoked by a valid OCSP response */
+ DBG1(DBG_CFG, "certificate was revoked on %T, reason: %N",
+ &revocation, TRUE, crl_reason_names, reason);
+ revoked = TRUE;
+ break;
+ case VALIDATION_GOOD:
+ /* results in either good or stale */
+ break;
+ default:
+ case VALIDATION_FAILED:
+ /* candidate unusable, does not contain our cert */
+ DBG1(DBG_CFG, " ocsp response contains no status on our certificate");
+ cand->destroy(cand);
+ return best;
+ }
+
+ /* select the better of the two responses */
+ if (best == NULL || cand->is_newer(cand, best))
+ {
+ DESTROY_IF(best);
+ best = cand;
+ if (best->get_validity(best, NULL, NULL, &valid_until))
+ {
+ DBG1(DBG_CFG, " ocsp response is valid: until %T",
+ &valid_until, FALSE);
+ *valid = VALIDATION_GOOD;
+ if (cache)
+ { /* cache non-stale only, stale certs get refetched */
+ cache_cert(this, best);
+ }
+ }
+ else
+ {
+ DBG1(DBG_CFG, " ocsp response is stale: since %T",
+ &valid_until, FALSE);
+ *valid = VALIDATION_STALE;
+ }
+ }
+ else
+ {
+ *valid = VALIDATION_STALE;
+ cand->destroy(cand);
+ }
+ if (revoked)
+ { /* revoked always counts, even if stale */
+ *valid = VALIDATION_REVOKED;
+ }
+ return best;
+}
+
+/**
+ * validate a x509 certificate using OCSP
+ */
+static cert_validation_t check_ocsp(private_credential_manager_t *this,
+ x509_t *subject, x509_t *issuer,
+ auth_cfg_t *auth)
+{
+ enumerator_t *enumerator;
+ cert_validation_t valid = VALIDATION_SKIPPED;
+ certificate_t *best = NULL, *current;
+ identification_t *keyid = NULL;
+ public_key_t *public;
+ chunk_t chunk;
+ char *uri = NULL;
+
+ /** lookup cache for valid OCSP responses */
+ enumerator = create_cert_enumerator(this, CERT_X509_OCSP_RESPONSE,
+ KEY_ANY, NULL, FALSE);
+ while (enumerator->enumerate(enumerator, &current))
+ {
+ current->get_ref(current);
+ best = get_better_ocsp(this, current, best, subject, issuer,
+ &valid, FALSE);
+ if (best && valid != VALIDATION_STALE)
+ {
+ DBG1(DBG_CFG, " using cached ocsp response");
+ break;
+ }
+ }
+ enumerator->destroy(enumerator);
+
+ /* derive the authorityKeyIdentifier from the issuer's public key */
+ current = &issuer->interface;
+ public = current->get_public_key(current);
+ if (public && public->get_fingerprint(public, KEY_ID_PUBKEY_SHA1, &chunk))
+ {
+ keyid = identification_create_from_encoding(ID_KEY_ID, chunk);
+ }
+ /** fetch from configured OCSP responder URLs */
+ if (keyid && valid != VALIDATION_GOOD && valid != VALIDATION_REVOKED)
+ {
+ enumerator = create_cdp_enumerator(this, CERT_X509_OCSP_RESPONSE, keyid);
+ while (enumerator->enumerate(enumerator, &uri))
+ {
+ current = fetch_ocsp(this, uri, &subject->interface,
+ &issuer->interface);
+ if (current)
+ {
+ best = get_better_ocsp(this, current, best, subject, issuer,
+ &valid, TRUE);
+ if (best && valid != VALIDATION_STALE)
+ {
+ break;
+ }
+ }
+ }
+ enumerator->destroy(enumerator);
+ }
+ DESTROY_IF(public);
+ DESTROY_IF(keyid);
+
+ /* fallback to URL fetching from subject certificate's URIs */
+ if (valid != VALIDATION_GOOD && valid != VALIDATION_REVOKED)
+ {
+ enumerator = subject->create_ocsp_uri_enumerator(subject);
+ while (enumerator->enumerate(enumerator, &uri))
+ {
+ current = fetch_ocsp(this, uri, &subject->interface,
+ &issuer->interface);
+ if (current)
+ {
+ best = get_better_ocsp(this, current, best, subject, issuer,
+ &valid, TRUE);
+ if (best && valid != VALIDATION_STALE)
+ {
+ break;
+ }
+ }
+ }
+ enumerator->destroy(enumerator);
+ }
+ /* an uri was found, but no result. switch validation state to failed */
+ if (valid == VALIDATION_SKIPPED && uri)
+ {
+ valid = VALIDATION_FAILED;
+ }
+ if (auth)
+ {
+ auth->add(auth, AUTH_RULE_OCSP_VALIDATION, valid);
+ if (valid == VALIDATION_GOOD)
+ { /* successful OCSP check fulfills also CRL constraint */
+ auth->add(auth, AUTH_RULE_CRL_VALIDATION, VALIDATION_GOOD);
+ }
+ }
+ DESTROY_IF(best);
+ return valid;
+}
+
+/**
+ * fetch a CRL from an URL
+ */
+static certificate_t* fetch_crl(private_credential_manager_t *this, char *url)
+{
+ certificate_t *crl;
+ chunk_t chunk;
+
+ DBG1(DBG_CFG, " fetching crl from '%s' ...", url);
+ if (lib->fetcher->fetch(lib->fetcher, url, &chunk, FETCH_END) != SUCCESS)
+ {
+ DBG1(DBG_CFG, "crl fetching failed");
+ return NULL;
+ }
+ crl = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509_CRL,
+ BUILD_BLOB_ASN1_DER, chunk, BUILD_END);
+ chunk_free(&chunk);
+ if (!crl)
+ {
+ DBG1(DBG_CFG, "crl fetched successfully but parsing failed");
+ return NULL;
+ }
+ return crl;
+}
+
+/**
+ * check the signature of an CRL
+ */
+static bool verify_crl(private_credential_manager_t *this, certificate_t *crl)
+{
+ certificate_t *issuer;
+ enumerator_t *enumerator;
+ bool verified = FALSE;
+
+ enumerator = create_trusted_enumerator(this, KEY_ANY, crl->get_issuer(crl),
+ FALSE, FALSE);
+ while (enumerator->enumerate(enumerator, &issuer, NULL))
+ {
+ if (this->cache->issued_by(this->cache, crl, issuer))
+ {
+ DBG1(DBG_CFG, " crl correctly signed by \"%Y\"",
+ issuer->get_subject(issuer));
+ verified = TRUE;
+ break;
+ }
+ }
+ enumerator->destroy(enumerator);
+
+ return verified;
+}
+
+/**
+ * Get the better of two CRLs, and check for usable CRL info
+ */
+static certificate_t *get_better_crl(private_credential_manager_t *this,
+ certificate_t *cand, certificate_t *best,
+ x509_t *subject, x509_t *issuer,
+ cert_validation_t *valid, bool cache)
+{
+ enumerator_t *enumerator;
+ time_t revocation, valid_until;
+ crl_reason_t reason;
+ chunk_t serial;
+ crl_t *crl;
+
+ /* check CRL signature */
+ if (!verify_crl(this, cand))
+ {
+ DBG1(DBG_CFG, "crl response verification failed");
+ cand->destroy(cand);
+ return best;
+ }
+
+ crl = (crl_t*)cand;
+ enumerator = crl->create_enumerator(crl);
+ while (enumerator->enumerate(enumerator, &serial, &revocation, &reason))
+ {
+ if (chunk_equals(serial, subject->get_serial(subject)))
+ {
+ DBG1(DBG_CFG, "certificate was revoked on %T, reason: %N",
+ &revocation, TRUE, crl_reason_names, reason);
+ *valid = VALIDATION_REVOKED;
+ enumerator->destroy(enumerator);
+ DESTROY_IF(best);
+ return cand;
+ }
+ }
+ enumerator->destroy(enumerator);
+
+ /* select the better of the two CRLs */
+ if (best == NULL || cand->is_newer(cand, best))
+ {
+ DESTROY_IF(best);
+ best = cand;
+ if (best->get_validity(best, NULL, NULL, &valid_until))
+ {
+ DBG1(DBG_CFG, " crl is valid: until %T", &valid_until, FALSE);
+ *valid = VALIDATION_GOOD;
+ if (cache)
+ { /* we cache non-stale crls only, as a stale crls are refetched */
+ cache_cert(this, best);
+ }
+ }
+ else
+ {
+ DBG1(DBG_CFG, " crl is stale: since %T", &valid_until, FALSE);
+ *valid = VALIDATION_STALE;
+ }
+ }
+ else
+ {
+ *valid = VALIDATION_STALE;
+ cand->destroy(cand);
+ }
+ return best;
+}
+
+/**
+ * validate a x509 certificate using CRL
+ */
+static cert_validation_t check_crl(private_credential_manager_t *this,
+ x509_t *subject, x509_t *issuer,
+ auth_cfg_t *auth)
+{
+ cert_validation_t valid = VALIDATION_SKIPPED;
+ identification_t *keyid = NULL;
+ certificate_t *best = NULL;
+ certificate_t *current;
+ public_key_t *public;
+ enumerator_t *enumerator;
+ chunk_t chunk;
+ char *uri = NULL;
+
+ /* derive the authorityKeyIdentifier from the issuer's public key */
+ current = &issuer->interface;
+ public = current->get_public_key(current);
+ if (public && public->get_fingerprint(public, KEY_ID_PUBKEY_SHA1, &chunk))
+ {
+ keyid = identification_create_from_encoding(ID_KEY_ID, chunk);
+
+ /* find a cached crl by authorityKeyIdentifier */
+ enumerator = create_cert_enumerator(this, CERT_X509_CRL, KEY_ANY,
+ keyid, FALSE);
+ while (enumerator->enumerate(enumerator, &current))
+ {
+ current->get_ref(current);
+ best = get_better_crl(this, current, best, subject, issuer,
+ &valid, FALSE);
+ if (best && valid != VALIDATION_STALE)
+ {
+ DBG1(DBG_CFG, " using cached crl");
+ break;
+ }
+ }
+ enumerator->destroy(enumerator);
+
+ /* fallback to fetching crls from credential sets cdps */
+ if (valid != VALIDATION_GOOD && valid != VALIDATION_REVOKED)
+ {
+ enumerator = create_cdp_enumerator(this, CERT_X509_CRL, keyid);
+
+ while (enumerator->enumerate(enumerator, &uri))
+ {
+ current = fetch_crl(this, uri);
+ if (current)
+ {
+ best = get_better_crl(this, current, best, subject, issuer,
+ &valid, TRUE);
+ if (best && valid != VALIDATION_STALE)
+ {
+ break;
+ }
+ }
+ }
+ enumerator->destroy(enumerator);
+ }
+ keyid->destroy(keyid);
+ }
+ DESTROY_IF(public);
+
+ /* fallback to fetching crls from cdps from subject's certificate */
+ if (valid != VALIDATION_GOOD && valid != VALIDATION_REVOKED)
+ {
+ enumerator = subject->create_crl_uri_enumerator(subject);
+
+ while (enumerator->enumerate(enumerator, &uri))
+ {
+ current = fetch_crl(this, uri);
+ if (current)
+ {
+ best = get_better_crl(this, current, best, subject, issuer,
+ &valid, TRUE);
+ if (best && valid != VALIDATION_STALE)
+ {
+ break;
+ }
+ }
+ }
+ enumerator->destroy(enumerator);
+ }
+
+ /* an uri was found, but no result. switch validation state to failed */
+ if (valid == VALIDATION_SKIPPED && uri)
+ {
+ valid = VALIDATION_FAILED;
+ }
+ if (auth)
+ {
+ if (valid == VALIDATION_SKIPPED)
+ { /* if we skipped CRL validation, we use the result of OCSP for
+ * constraint checking */
+ auth->add(auth, AUTH_RULE_CRL_VALIDATION,
+ auth->get(auth, AUTH_RULE_OCSP_VALIDATION));
+ }
+ else
+ {
+ auth->add(auth, AUTH_RULE_CRL_VALIDATION, valid);
+ }
+ }
+ DESTROY_IF(best);
+ return valid;
+}
+
+/**
+ * check a certificate for optional IP address block constraints
+ */
+static bool check_ip_addr_block_constraints(x509_t *subject, x509_t *issuer)
+{
+ bool subject_constraint = subject->get_flags(subject) & X509_IP_ADDR_BLOCKS;
+ bool issuer_constraint = issuer->get_flags(issuer) & X509_IP_ADDR_BLOCKS;
+ bool contained = TRUE;
+
+ enumerator_t *subject_enumerator, *issuer_enumerator;
+ traffic_selector_t *subject_ts, *issuer_ts;
+
+ if (!subject_constraint && !issuer_constraint)
+ {
+ return TRUE;
+ }
+ if (!subject_constraint)
+ {
+ DBG1(DBG_CFG, "subject certficate lacks ipAddrBlocks extension");
+ return FALSE;
+ }
+ if (!issuer_constraint)
+ {
+ DBG1(DBG_CFG, "issuer certficate lacks ipAddrBlocks extension");
+ return FALSE;
+ }
+ subject_enumerator = subject->create_ipAddrBlock_enumerator(subject);
+ while (subject_enumerator->enumerate(subject_enumerator, &subject_ts))
+ {
+ contained = FALSE;
+
+ issuer_enumerator = issuer->create_ipAddrBlock_enumerator(issuer);
+ while (issuer_enumerator->enumerate(issuer_enumerator, &issuer_ts))
+ {
+ if (subject_ts->is_contained_in(subject_ts, issuer_ts))
+ {
+ DBG2(DBG_CFG, " subject address block %R is contained in "
+ "issuer address block %R", subject_ts, issuer_ts);
+ contained = TRUE;
+ break;
+ }
+ }
+ issuer_enumerator->destroy(issuer_enumerator);
+ if (!contained)
+ {
+ DBG1(DBG_CFG, "subject address block %R is not contained in any "
+ "issuer address block", subject_ts);
+ break;
+ }
+ }
+ subject_enumerator->destroy(subject_enumerator);
+ return contained;
+}
+
+/**
+ * check a certificate for its lifetime
+ */
+static bool check_certificate(private_credential_manager_t *this,
+ certificate_t *subject, certificate_t *issuer,
+ bool crl, bool ocsp, auth_cfg_t *auth)
+{
+ time_t not_before, not_after;
+
+ if (!subject->get_validity(subject, NULL, &not_before, &not_after))
+ {
+ DBG1(DBG_CFG, "subject certificate invalid (valid from %T to %T)",
+ &not_before, FALSE, &not_after, FALSE);
+ return FALSE;
+ }
+ if (!issuer->get_validity(issuer, NULL, &not_before, &not_after))
+ {
+ DBG1(DBG_CFG, "issuer certificate invalid (valid from %T to %T)",
+ &not_before, FALSE, &not_after, FALSE);
+ return FALSE;
+ }
+ if (issuer->get_type(issuer) == CERT_X509 &&
+ subject->get_type(subject) == CERT_X509)
+ {
+ if (!check_ip_addr_block_constraints((x509_t*)subject, (x509_t*)issuer))
+ {
+ return FALSE;
+ }
+ if (ocsp || crl)
+ {
+ DBG1(DBG_CFG, "checking certificate status of \"%Y\"",
+ subject->get_subject(subject));
+ }
+ if (ocsp)
+ {
+ switch (check_ocsp(this, (x509_t*)subject, (x509_t*)issuer, auth))
+ {
+ case VALIDATION_GOOD:
+ DBG1(DBG_CFG, "certificate status is good");
+ return TRUE;
+ case VALIDATION_REVOKED:
+ /* has already been logged */
+ return FALSE;
+ case VALIDATION_SKIPPED:
+ DBG2(DBG_CFG, "ocsp check skipped, no ocsp found");
+ break;
+ case VALIDATION_STALE:
+ DBG1(DBG_CFG, "ocsp information stale, fallback to crl");
+ break;
+ case VALIDATION_FAILED:
+ DBG1(DBG_CFG, "ocsp check failed, fallback to crl");
+ break;
+ }
+ }
+ if (crl)
+ {
+ switch (check_crl(this, (x509_t*)subject, (x509_t*)issuer, auth))
+ {
+ case VALIDATION_GOOD:
+ DBG1(DBG_CFG, "certificate status is good");
+ return TRUE;
+ case VALIDATION_REVOKED:
+ /* has already been logged */
+ return FALSE;
+ case VALIDATION_FAILED:
+ case VALIDATION_SKIPPED:
+ DBG1(DBG_CFG, "certificate status is not available");
+ break;
+ case VALIDATION_STALE:
+ DBG1(DBG_CFG, "certificate status is unknown, crl is stale");
+ break;
+ }
+ }
+ }
+ return TRUE;
+}
+
+/**
+ * Get a trusted certificate from a credential set
+ */
+static certificate_t *get_pretrusted_cert(private_credential_manager_t *this,
+ key_type_t type, identification_t *id)
+{
+ certificate_t *subject;
+ public_key_t *public;
+
+ subject = get_cert(this, CERT_ANY, type, id, TRUE);
+ if (!subject)
+ {
+ return NULL;
+ }
+ public = subject->get_public_key(subject);
+ if (!public)
+ {
+ subject->destroy(subject);
+ return NULL;
+ }
+ public->destroy(public);
+ return subject;
+}
+
+/**
+ * Get the issuing certificate of a subject certificate
+ */
+static certificate_t *get_issuer_cert(private_credential_manager_t *this,
+ certificate_t *subject, bool trusted)
+{
+ enumerator_t *enumerator;
+ certificate_t *issuer = NULL, *candidate;
+
+ enumerator = create_cert_enumerator(this, subject->get_type(subject), KEY_ANY,
+ subject->get_issuer(subject), trusted);
+ while (enumerator->enumerate(enumerator, &candidate))
+ {
+ if (this->cache->issued_by(this->cache, subject, candidate))
+ {
+ issuer = candidate->get_ref(candidate);
+ break;
+ }
+ }
+ enumerator->destroy(enumerator);
+ return issuer;
+}
+
+/**
+ * try to verify the trust chain of subject, return TRUE if trusted
+ */
+static bool verify_trust_chain(private_credential_manager_t *this,
+ certificate_t *subject, auth_cfg_t *result,
+ bool trusted, bool crl, bool ocsp)
+{
+ certificate_t *current, *issuer;
+ x509_t *x509;
+ auth_cfg_t *auth;
+ int pathlen, pathlen_constraint;
+
+ auth = auth_cfg_create();
+ current = subject->get_ref(subject);
+
+ for (pathlen = 0; pathlen <= X509_MAX_PATH_LEN; pathlen++)
+ {
+ issuer = get_issuer_cert(this, current, TRUE);
+ if (issuer)
+ {
+ /* accept only self-signed CAs as trust anchor */
+ if (this->cache->issued_by(this->cache, issuer, issuer))
+ {
+ auth->add(auth, AUTH_RULE_CA_CERT, issuer->get_ref(issuer));
+ DBG1(DBG_CFG, " using trusted ca certificate \"%Y\"",
+ issuer->get_subject(issuer));
+ trusted = TRUE;
+ }
+ else
+ {
+ auth->add(auth, AUTH_RULE_IM_CERT, issuer->get_ref(issuer));
+ DBG1(DBG_CFG, " using trusted intermediate ca certificate "
+ "\"%Y\"", issuer->get_subject(issuer));
+ }
+ }
+ else
+ {
+ issuer = get_issuer_cert(this, current, FALSE);
+ if (issuer)
+ {
+ if (current->equals(current, issuer))
+ {
+ DBG1(DBG_CFG, " self-signed certificate \"%Y\" is not trusted",
+ current->get_subject(current));
+ issuer->destroy(issuer);
+ break;
+ }
+ auth->add(auth, AUTH_RULE_IM_CERT, issuer->get_ref(issuer));
+ DBG1(DBG_CFG, " using untrusted intermediate certificate "
+ "\"%Y\"", issuer->get_subject(issuer));
+ }
+ else
+ {
+ DBG1(DBG_CFG, "no issuer certificate found for \"%Y\"",
+ current->get_subject(current));
+ break;
+ }
+ }
+ if (!check_certificate(this, current, issuer, crl, ocsp,
+ current == subject ? auth : NULL))
+ {
+ trusted = FALSE;
+ issuer->destroy(issuer);
+ break;
+ }
+
+ /* check path length constraint */
+ x509 = (x509_t*)issuer;
+ pathlen_constraint = x509->get_pathLenConstraint(x509);
+ if (pathlen_constraint != X509_NO_PATH_LEN_CONSTRAINT &&
+ pathlen > pathlen_constraint)
+ {
+ DBG1(DBG_CFG, "path length of %d violates constraint of %d",
+ pathlen, pathlen_constraint);
+ trusted = FALSE;
+ issuer->destroy(issuer);
+ break;
+ }
+ current->destroy(current);
+ current = issuer;
+ if (trusted)
+ {
+ DBG1(DBG_CFG, " reached self-signed root ca with a path length of %d",
+ pathlen);
+ break;
+ }
+ }
+ current->destroy(current);
+ if (pathlen > X509_MAX_PATH_LEN)
+ {
+ DBG1(DBG_CFG, "maximum path length of %d exceeded", X509_MAX_PATH_LEN);
+ }
+ if (trusted)
+ {
+ result->merge(result, auth, FALSE);
+ }
+ auth->destroy(auth);
+ return trusted;
+}
+
+/**
+ * enumerator for trusted certificates
+ */
+typedef struct {
+ /** implements enumerator_t interface */
+ enumerator_t public;
+ /** enumerator over candidate peer certificates */
+ enumerator_t *candidates;
+ /** reference to the credential_manager */
+ private_credential_manager_t *this;
+ /** type of the requested key */
+ key_type_t type;
+ /** identity the requested key belongs to */
+ identification_t *id;
+ /** TRUE to do CRL checking */
+ bool crl;
+ /** TRUE to do OCSP checking */
+ bool ocsp;
+ /** pretrusted certificate we have served at first invocation */
+ certificate_t *pretrusted;
+ /** currently enumerating auth config */
+ auth_cfg_t *auth;
+} trusted_enumerator_t;
+
+/**
+ * Implements trusted_enumerator_t.enumerate
+ */
+static bool trusted_enumerate(trusted_enumerator_t *this,
+ certificate_t **cert, auth_cfg_t **auth)
+{
+ certificate_t *current;
+
+ DESTROY_IF(this->auth);
+ this->auth = auth_cfg_create();
+
+ if (!this->candidates)
+ {
+ /* first invocation, build enumerator for next one */
+ this->candidates = create_cert_enumerator(this->this, CERT_ANY,
+ this->type, this->id, FALSE);
+ /* check if we have a trusted certificate for that peer */
+ this->pretrusted = get_pretrusted_cert(this->this, this->type, this->id);
+ if (this->pretrusted)
+ {
+ /* if we find a trusted self signed certificate, we just accept it.
+ * However, in order to fulfill authorization rules, we try to build
+ * the trust chain if it is not self signed */
+ if (this->this->cache->issued_by(this->this->cache,
+ this->pretrusted, this->pretrusted) ||
+ verify_trust_chain(this->this, this->pretrusted, this->auth,
+ TRUE, this->crl, this->ocsp))
+ {
+ this->auth->add(this->auth, AUTH_RULE_SUBJECT_CERT,
+ this->pretrusted->get_ref(this->pretrusted));
+ DBG1(DBG_CFG, " using trusted certificate \"%Y\"",
+ this->pretrusted->get_subject(this->pretrusted));
+ *cert = this->pretrusted;
+ if (auth)
+ {
+ *auth = this->auth;
+ }
+ return TRUE;
+ }
+ }
+ }
+ /* try to verify the trust chain for each certificate found */
+ while (this->candidates->enumerate(this->candidates, &current))
+ {
+ if (this->pretrusted &&
+ this->pretrusted->equals(this->pretrusted, current))
+ { /* skip pretrusted certificate we already served */
+ continue;
+ }
+
+ DBG1(DBG_CFG, " using certificate \"%Y\"",
+ current->get_subject(current));
+ if (verify_trust_chain(this->this, current, this->auth, FALSE,
+ this->crl, this->ocsp))
+ {
+ *cert = current;
+ if (auth)
+ {
+ *auth = this->auth;
+ }
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+/**
+ * Implements trusted_enumerator_t.destroy
+ */
+static void trusted_destroy(trusted_enumerator_t *this)
+{
+ DESTROY_IF(this->pretrusted);
+ DESTROY_IF(this->auth);
+ DESTROY_IF(this->candidates);
+ free(this);
+}
+
+/**
+ * create an enumerator over trusted certificates and their trustchain
+ */
+static enumerator_t *create_trusted_enumerator(private_credential_manager_t *this,
+ key_type_t type, identification_t *id, bool crl, bool ocsp)
+{
+ trusted_enumerator_t *enumerator = malloc_thing(trusted_enumerator_t);
+
+ enumerator->public.enumerate = (void*)trusted_enumerate;
+ enumerator->public.destroy = (void*)trusted_destroy;
+
+ enumerator->candidates = NULL;
+ enumerator->this = this;
+ enumerator->type = type;
+ enumerator->id = id;
+ enumerator->crl = crl;
+ enumerator->ocsp = ocsp;
+ enumerator->pretrusted = NULL;
+ enumerator->auth = NULL;
+
+ return &enumerator->public;
+}
+
+/**
+ * enumerator for public keys
+ */
+typedef struct {
+ /** implements enumerator_t interface */
+ enumerator_t public;
+ /** enumerator over candidate peer certificates */
+ enumerator_t *inner;
+ /** reference to the credential_manager */
+ private_credential_manager_t *this;
+ /** currently enumerating key */
+ public_key_t *current;
+ /** credset wrapper around auth config */
+ auth_cfg_wrapper_t *wrapper;
+} public_enumerator_t;
+
+/**
+ * Implements public_enumerator_t.enumerate
+ */
+static bool public_enumerate(public_enumerator_t *this,
+ public_key_t **key, auth_cfg_t **auth)
+{
+ certificate_t *cert;
+
+ while (this->inner->enumerate(this->inner, &cert, auth))
+ {
+ DESTROY_IF(this->current);
+ this->current = cert->get_public_key(cert);
+ if (this->current)
+ {
+ *key = this->current;
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+/**
+ * Implements public_enumerator_t.destroy
+ */
+static void public_destroy(public_enumerator_t *this)
+{
+ DESTROY_IF(this->current);
+ this->inner->destroy(this->inner);
+ if (this->wrapper)
+ {
+ remove_local_set(this->this, &this->wrapper->set);
+ this->wrapper->destroy(this->wrapper);
+ }
+ this->this->lock->unlock(this->this->lock);
+
+ /* check for delayed certificate cache queue */
+ cache_queue(this->this);
+ free(this);
+}
+
+/**
+ * Implementation of credential_manager_t.create_public_enumerator.
+ */
+static enumerator_t* create_public_enumerator(private_credential_manager_t *this,
+ key_type_t type, identification_t *id, auth_cfg_t *auth)
+{
+ public_enumerator_t *enumerator = malloc_thing(public_enumerator_t);
+
+ enumerator->public.enumerate = (void*)public_enumerate;
+ enumerator->public.destroy = (void*)public_destroy;
+ enumerator->inner = create_trusted_enumerator(this, type, id, TRUE, TRUE);
+ enumerator->this = this;
+ enumerator->current = NULL;
+ enumerator->wrapper = NULL;
+ if (auth)
+ {
+ enumerator->wrapper = auth_cfg_wrapper_create(auth);
+ add_local_set(this, &enumerator->wrapper->set);
+ }
+ this->lock->read_lock(this->lock);
+ return &enumerator->public;
+}
+
+/**
+ * Check if a certificate's keyid is contained in the auth helper
+ */
+static bool auth_contains_cacert(auth_cfg_t *auth, certificate_t *cert)
+{
+ enumerator_t *enumerator;
+ identification_t *value;
+ auth_rule_t type;
+ bool found = FALSE;
+
+ enumerator = auth->create_enumerator(auth);
+ while (enumerator->enumerate(enumerator, &type, &value))
+ {
+ if (type == AUTH_RULE_CA_CERT &&
+ cert->equals(cert, (certificate_t*)value))
+ {
+ found = TRUE;
+ break;
+ }
+ }
+ enumerator->destroy(enumerator);
+ return found;
+}
+
+/**
+ * build a trustchain from subject up to a trust anchor in trusted
+ */
+static auth_cfg_t *build_trustchain(private_credential_manager_t *this,
+ certificate_t *subject, auth_cfg_t *auth)
+{
+ certificate_t *issuer, *current;
+ auth_cfg_t *trustchain;
+ int pathlen = 0;
+
+ trustchain = auth_cfg_create();
+
+ current = auth->get(auth, AUTH_RULE_CA_CERT);
+ if (!current)
+ {
+ /* no trust anchor specified, return this cert only */
+ trustchain->add(trustchain, AUTH_RULE_SUBJECT_CERT,
+ subject->get_ref(subject));
+ return trustchain;
+ }
+ current = subject->get_ref(subject);
+ while (TRUE)
+ {
+ if (auth_contains_cacert(auth, current))
+ {
+ trustchain->add(trustchain, AUTH_RULE_CA_CERT, current);
+ return trustchain;
+ }
+ if (subject == current)
+ {
+ trustchain->add(trustchain, AUTH_RULE_SUBJECT_CERT, current);
+ }
+ else
+ {
+ trustchain->add(trustchain, AUTH_RULE_IM_CERT, current);
+ }
+ issuer = get_issuer_cert(this, current, FALSE);
+ if (!issuer || issuer->equals(issuer, current) ||
+ pathlen > X509_MAX_PATH_LEN)
+ {
+ DESTROY_IF(issuer);
+ break;
+ }
+ current = issuer;
+ pathlen++;
+ }
+ trustchain->destroy(trustchain);
+ return NULL;
+}
+
+/**
+ * find a private key of a give certificate
+ */
+static private_key_t *get_private_by_cert(private_credential_manager_t *this,
+ certificate_t *cert, key_type_t type)
+{
+ private_key_t *private = NULL;
+ identification_t *keyid;
+ chunk_t chunk;
+ public_key_t *public;
+
+ public = cert->get_public_key(cert);
+ if (public)
+ {
+ if (public->get_fingerprint(public, KEY_ID_PUBKEY_SHA1, &chunk))
+ {
+ keyid = identification_create_from_encoding(ID_KEY_ID, chunk);
+ private = get_private_by_keyid(this, type, keyid);
+ keyid->destroy(keyid);
+ }
+ public->destroy(public);
+ }
+ return private;
+}
+
+/**
+ * Implementation of credential_manager_t.get_private.
+ */
+static private_key_t *get_private(private_credential_manager_t *this,
+ key_type_t type, identification_t *id,
+ auth_cfg_t *auth)
+{
+ enumerator_t *enumerator;
+ certificate_t *cert;
+ private_key_t *private = NULL;
+ auth_cfg_t *trustchain;
+
+ /* check if this is a lookup by key ID, and do it if so */
+ if (id && id->get_type(id) == ID_KEY_ID)
+ {
+ private = get_private_by_keyid(this, type, id);
+ if (private)
+ {
+ return private;
+ }
+ }
+
+ /* if a specific certificate is preferred, check for a matching key */
+ cert = auth->get(auth, AUTH_RULE_SUBJECT_CERT);
+ if (cert)
+ {
+ private = get_private_by_cert(this, cert, type);
+ if (private)
+ {
+ trustchain = build_trustchain(this, cert, auth);
+ if (trustchain)
+ {
+ auth->merge(auth, trustchain, FALSE);
+ trustchain->destroy(trustchain);
+ }
+ return private;
+ }
+ }
+
+ /* try to build a trust chain for each certificate found */
+ enumerator = create_cert_enumerator(this, CERT_ANY, type, id, FALSE);
+ while (enumerator->enumerate(enumerator, &cert))
+ {
+ private = get_private_by_cert(this, cert, type);
+ if (private)
+ {
+ trustchain = build_trustchain(this, cert, auth);
+ if (trustchain)
+ {
+ auth->merge(auth, trustchain, FALSE);
+ trustchain->destroy(trustchain);
+ break;
+ }
+ private->destroy(private);
+ private = NULL;
+ }
+ }
+ enumerator->destroy(enumerator);
+
+ /* if no valid trustchain was found, fall back to the first usable cert */
+ if (!private)
+ {
+ enumerator = create_cert_enumerator(this, CERT_ANY, type, id, FALSE);
+ while (enumerator->enumerate(enumerator, &cert))
+ {
+ private = get_private_by_cert(this, cert, type);
+ if (private)
+ {
+ auth->add(auth, AUTH_RULE_SUBJECT_CERT, cert->get_ref(cert));
+ break;
+ }
+ }
+ enumerator->destroy(enumerator);
+ }
+ return private;
+}
+
+/**
+ * Implementation of credential_manager_t.flush_cache.
+ */
+static void flush_cache(private_credential_manager_t *this,
+ certificate_type_t type)
+{
+ this->cache->flush(this->cache, type);
+}
+
+/**
+ * Implementation of credential_manager_t.add_set.
+ */
+static void add_set(private_credential_manager_t *this,
+ credential_set_t *set)
+{
+ this->lock->write_lock(this->lock);
+ this->sets->insert_last(this->sets, set);
+ this->lock->unlock(this->lock);
+}
+
+/**
+ * Implementation of credential_manager_t.remove_set.
+ */
+static void remove_set(private_credential_manager_t *this, credential_set_t *set)
+{
+ this->lock->write_lock(this->lock);
+ this->sets->remove(this->sets, set, NULL);
+ this->lock->unlock(this->lock);
+}
+
+/**
+ * Implementation of credential_manager_t.destroy
+ */
+static void destroy(private_credential_manager_t *this)
+{
+ cache_queue(this);
+ this->cache_queue->destroy(this->cache_queue);
+ this->sets->remove(this->sets, this->cache, NULL);
+ this->sets->destroy(this->sets);
+ this->local_sets->destroy(this->local_sets);
+ this->cache->destroy(this->cache);
+ this->lock->destroy(this->lock);
+ this->queue_mutex->destroy(this->queue_mutex);
+ free(this);
+}
+
+/*
+ * see header file
+ */
+credential_manager_t *credential_manager_create()
+{
+ private_credential_manager_t *this = malloc_thing(private_credential_manager_t);
+
+ this->public.create_cert_enumerator = (enumerator_t *(*)(credential_manager_t *this,certificate_type_t cert, key_type_t key,identification_t *id,bool))create_cert_enumerator;
+ this->public.create_shared_enumerator = (enumerator_t *(*)(credential_manager_t *this, shared_key_type_t type,identification_t *me, identification_t *other))create_shared_enumerator;
+ this->public.create_cdp_enumerator = (enumerator_t *(*)(credential_manager_t*, certificate_type_t type, identification_t *id))create_cdp_enumerator;
+ this->public.get_cert = (certificate_t *(*)(credential_manager_t *this,certificate_type_t cert, key_type_t key,identification_t *, bool))get_cert;
+ this->public.get_shared = (shared_key_t *(*)(credential_manager_t *this,shared_key_type_t type,identification_t *me, identification_t *other))get_shared;
+ this->public.get_private = (private_key_t*(*)(credential_manager_t*, key_type_t type, identification_t *, auth_cfg_t*))get_private;
+ this->public.create_public_enumerator = (enumerator_t*(*)(credential_manager_t*, key_type_t type, identification_t *id, auth_cfg_t *aut))create_public_enumerator;
+ this->public.flush_cache = (void(*)(credential_manager_t*, certificate_type_t type))flush_cache;
+ this->public.cache_cert = (void(*)(credential_manager_t*, certificate_t *cert))cache_cert;
+ this->public.add_set = (void(*)(credential_manager_t*, credential_set_t *set))add_set;
+ this->public.remove_set = (void(*)(credential_manager_t*, credential_set_t *set))remove_set;
+ this->public.destroy = (void(*)(credential_manager_t*))destroy;
+
+ this->sets = linked_list_create();
+ this->local_sets = thread_value_create((thread_cleanup_t)this->sets->destroy);
+ this->cache = cert_cache_create();
+ this->cache_queue = linked_list_create();
+ this->sets->insert_first(this->sets, this->cache);
+ this->lock = rwlock_create(RWLOCK_TYPE_DEFAULT);
+ this->queue_mutex = mutex_create(MUTEX_TYPE_DEFAULT);
+
+ return &this->public;
+}
+
diff --git a/src/libcharon/credentials/credential_manager.h b/src/libcharon/credentials/credential_manager.h
new file mode 100644
index 000000000..0448da992
--- /dev/null
+++ b/src/libcharon/credentials/credential_manager.h
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2007-2009 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * 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.
+ */
+
+/**
+ * @defgroup credential_manager credential_manager
+ * @{ @ingroup ccredentials
+ */
+
+#ifndef CREDENTIAL_MANAGER_H_
+#define CREDENTIAL_MANAGER_H_
+
+#include <utils/identification.h>
+#include <utils/enumerator.h>
+#include <config/auth_cfg.h>
+#include <credentials/credential_set.h>
+#include <credentials/keys/private_key.h>
+#include <credentials/keys/shared_key.h>
+#include <credentials/certificates/certificate.h>
+
+typedef struct credential_manager_t credential_manager_t;
+
+/**
+ * Manages credentials using credential_sets.
+ *
+ * The credential manager is the entry point of the credential framework. It
+ * uses so called "sets" to access credentials in a modular fashion, these
+ * are implemented through the credential_set_t interface.
+ * The manager additionally does trust chain verification and trust status
+ * chaching. A set may call the managers methods if it needs credentials itself,
+ * the manager uses recursive locking.
+ *
+ * @verbatim
+
+ +-------+ +----------------+
+ | A | | | +------------------+
+ | u | -----> | | ------> | +------------------+
+ | t | | credential- | | | +------------------+
+ | h | -----> | manager | ------> +--| | credential- | => IPC
+ | e | | | +--| sets |
+ | n | +--> | | ------> +------------------+
+ | t | | | | |
+ | i | | | | |
+ | c | | +----------------+ |
+ | a | | |
+ | t | +----------------------------------------------+
+ | o | may be recursive
+ | r |
+ +-------+
+
+ @endverbatim
+ *
+ * The credential manager uses rwlocks for performance reasons, credential
+ * sets must be fully thread save.
+ */
+struct credential_manager_t {
+
+ /**
+ * Create an enumerator over all certificates.
+ *
+ * @param cert kind of certificate
+ * @param key kind of key in certificate
+ * @param id subject this certificate belongs to
+ * @param trusted TRUE to list trusted certificates only
+ * @return enumerator over the certificates
+ */
+ enumerator_t *(*create_cert_enumerator)(credential_manager_t *this,
+ certificate_type_t cert, key_type_t key,
+ identification_t *id, bool trusted);
+ /**
+ * Create an enumerator over all shared keys.
+ *
+ * The enumerator enumerates over:
+ * shared_key_t*, id_match_t me, id_match_t other
+ * But must accepts values for the id_matches.
+ *
+ * @param type kind of requested shared key
+ * @param first first subject between key is shared
+ * @param second second subject between key is shared
+ * @return enumerator over shared keys
+ */
+ enumerator_t *(*create_shared_enumerator)(credential_manager_t *this,
+ shared_key_type_t type,
+ identification_t *first, identification_t *second);
+ /**
+ * Create an enumerator over all Certificate Distribution Points.
+ *
+ * @param type kind of certificate the point distributes
+ * @param id identification of the distributed certificate
+ * @return enumerator of CDPs as char*
+ */
+ enumerator_t *(*create_cdp_enumerator)(credential_manager_t *this,
+ certificate_type_t type, identification_t *id);
+ /**
+ * Get a trusted or untrusted certificate.
+ *
+ * @param cert kind of certificate
+ * @param key kind of key in certificate
+ * @param id subject this certificate belongs to
+ * @param trusted TRUE to get a trusted certificate only
+ * @return certificate, if found, NULL otherwise
+ */
+ certificate_t *(*get_cert)(credential_manager_t *this,
+ certificate_type_t cert, key_type_t key,
+ identification_t *id, bool trusted);
+ /**
+ * Get the best matching shared key for two IDs.
+ *
+ * @param type kind of requested shared key
+ * @param me own identity
+ * @param other peers identity
+ * @return shared_key_t, NULL if none found
+ */
+ shared_key_t *(*get_shared)(credential_manager_t *this, shared_key_type_t type,
+ identification_t *me, identification_t *other);
+ /**
+ * Get a private key to create a signature.
+ *
+ * The get_private() method gets a secret private key identified by either
+ * the keyid itself or an id the key belongs to.
+ * The auth parameter contains additional information, such as receipients
+ * trusted CA certs. Auth gets filled with subject and CA certificates
+ * needed to validate a created signature.
+ *
+ * @param type type of the key to get
+ * @param id identification the key belongs to
+ * @param auth auth config, including trusted CA certificates
+ * @return private_key_t, NULL if none found
+ */
+ private_key_t* (*get_private)(credential_manager_t *this, key_type_t type,
+ identification_t *id, auth_cfg_t *auth);
+
+ /**
+ * Create an enumerator over trusted public keys.
+ *
+ * This method gets a an enumerator over trusted public keys to verify a
+ * signature created by id. The auth parameter contains additional
+ * authentication infos, e.g. peer and intermediate certificates.
+ * The resulting enumerator enumerates over public_key_t *, auth_cfg_t *,
+ * where the auth config helper contains rules for constraint checks.
+ *
+ * @param type type of the key to get
+ * @param id owner of the key, signer of the signature
+ * @param auth authentication infos
+ * @return enumerator
+ */
+ enumerator_t* (*create_public_enumerator)(credential_manager_t *this,
+ key_type_t type, identification_t *id, auth_cfg_t *auth);
+
+ /**
+ * Cache a certificate by invoking cache_cert() on all registerd sets.
+ *
+ * @param cert certificate to cache
+ */
+ void (*cache_cert)(credential_manager_t *this, certificate_t *cert);
+
+ /**
+ * Flush the certificate cache.
+ *
+ * Only the managers local cache is flushed, but not the sets cache filled
+ * by the cache_cert() method.
+ *
+ * @param type type of certificate to flush, or CERT_ANY
+ */
+ void (*flush_cache)(credential_manager_t *this, certificate_type_t type);
+
+ /**
+ * Register a credential set to the manager.
+ *
+ * @param set set to register
+ */
+ void (*add_set)(credential_manager_t *this, credential_set_t *set);
+
+ /**
+ * Unregister a credential set from the manager.
+ *
+ * @param set set to unregister
+ */
+ void (*remove_set)(credential_manager_t *this, credential_set_t *set);
+
+ /**
+ * Destroy a credential_manager instance.
+ */
+ void (*destroy)(credential_manager_t *this);
+};
+
+/**
+ * Create a credential_manager instance.
+ */
+credential_manager_t *credential_manager_create();
+
+#endif /** CREDENTIAL_MANAGER_H_ @}*/
diff --git a/src/libcharon/credentials/credential_set.h b/src/libcharon/credentials/credential_set.h
new file mode 100644
index 000000000..274eb3feb
--- /dev/null
+++ b/src/libcharon/credentials/credential_set.h
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2007 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * 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.
+ */
+
+/**
+ * @defgroup credential_set credential_set
+ * @{ @ingroup ccredentials
+ */
+
+#ifndef CREDENTIAL_SET_H_
+#define CREDENTIAL_SET_H_
+
+#include <credentials/keys/public_key.h>
+#include <credentials/keys/shared_key.h>
+#include <credentials/certificates/certificate.h>
+
+typedef struct credential_set_t credential_set_t;
+
+/**
+ * A set of credentials.
+ *
+ * Contains private keys, shared keys and different kinds of certificates.
+ * Enumerators are used because queries might return multiple matches.
+ * Filter parameters restrict enumeration over specific items only.
+ * See credential_manager_t for an overview of the credential framework.
+ *
+ * A credential set enumerator may not block the credential set, i.e. multiple
+ * threads must be able to hold multiple enumerators, as the credential manager
+ * is higly parallelized. The best way to achieve this is by using shared
+ * read locks for the enumerators only. Otherwiese deadlocks will occur.
+ * The writing cache_cert() routine is called by the manager only if no
+ * enumerator is alive, so it is save to use a write lock there.
+ */
+struct credential_set_t {
+
+ /**
+ * Create an enumerator over private keys (private_key_t).
+ *
+ * The id is either a key identifier of the requested key, or an identity
+ * of the key owner.
+ *
+ * @param type type of requested private key
+ * @param id key identifier/owner
+ * @return enumerator over private_key_t's.
+ */
+ enumerator_t *(*create_private_enumerator)(credential_set_t *this,
+ key_type_t type, identification_t *id);
+ /**
+ * Create an enumerator over certificates (certificate_t).
+ *
+ * @param cert kind of certificate
+ * @param key kind of key in certificate
+ * @param id identity (subject) this certificate belongs to
+ * @param trusted whether the certificate must be trustworthy
+ * @return enumerator as described above
+ */
+ enumerator_t *(*create_cert_enumerator)(credential_set_t *this,
+ certificate_type_t cert, key_type_t key,
+ identification_t *id, bool trusted);
+ /**
+ * Create an enumerator over shared keys (shared_key_t).
+ *
+ * The enumerator enumerates over:
+ * shared_key_t*, id_match_t me, id_match_t other
+ * But must accept NULL values for the id_matches.
+ *
+ * @param type kind of requested shared key
+ * @param me own identity
+ * @param other other identity who owns that secret
+ * @return enumerator as described above
+ */
+ enumerator_t *(*create_shared_enumerator)(credential_set_t *this,
+ shared_key_type_t type,
+ identification_t *me, identification_t *other);
+
+ /**
+ * Create an enumerator over certificate distribution points.
+ *
+ * @param type type of the certificate to get a CDP
+ * @param id identification of the distributed certificate
+ * @return an enumerator over CDPs as char*
+ */
+ enumerator_t *(*create_cdp_enumerator)(credential_set_t *this,
+ certificate_type_t type, identification_t *id);
+
+ /**
+ * Cache a certificate in the credential set.
+ *
+ * The caching policy is implementation dependent, the sets may cache the
+ * certificate in-memory, persistent on disk or not at all.
+ *
+ * @param cert certificate to cache
+ */
+ void (*cache_cert)(credential_set_t *this, certificate_t *cert);
+};
+
+#endif /** CREDENTIAL_SET_H_ @}*/
diff --git a/src/libcharon/credentials/sets/auth_cfg_wrapper.c b/src/libcharon/credentials/sets/auth_cfg_wrapper.c
new file mode 100644
index 000000000..82e33d283
--- /dev/null
+++ b/src/libcharon/credentials/sets/auth_cfg_wrapper.c
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2008-2009 Martin Willi
+ * Copyright (C) 2008 Tobias Brunner
+ * Hochschule fuer Technik Rapperswil
+ *
+ * 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.
+ */
+
+#include <daemon.h>
+
+#include "auth_cfg_wrapper.h"
+
+typedef struct private_auth_cfg_wrapper_t private_auth_cfg_wrapper_t;
+
+/**
+ * private data of auth_cfg_wrapper
+ */
+struct private_auth_cfg_wrapper_t {
+
+ /**
+ * public functions
+ */
+ auth_cfg_wrapper_t public;
+
+ /**
+ * wrapped auth info
+ */
+ auth_cfg_t *auth;
+};
+
+/**
+ * enumerator for auth_cfg_wrapper_t.create_cert_enumerator()
+ */
+typedef struct {
+ /** implements enumerator_t */
+ enumerator_t public;
+ /** inner enumerator from auth_cfg */
+ enumerator_t *inner;
+ /** wrapped auth round */
+ auth_cfg_t *auth;
+ /** enumerated cert type */
+ certificate_type_t cert;
+ /** enumerated key type */
+ key_type_t key;
+ /** enumerated id */
+ identification_t *id;
+} wrapper_enumerator_t;
+
+/**
+ * Tries to fetch a certificate that was supplied as "Hash and URL"
+ * (replaces rule type and value in place).
+ */
+static bool fetch_cert(wrapper_enumerator_t *enumerator,
+ auth_rule_t *rule, void **value)
+{
+ char *url = (char*)*value;
+ if (!url)
+ {
+ /* fetching the certificate previously failed */
+ return FALSE;
+ }
+
+ chunk_t data;
+ certificate_t *cert;
+
+ DBG1(DBG_CFG, " fetching certificate from '%s' ...", url);
+ if (lib->fetcher->fetch(lib->fetcher, url, &data, FETCH_END) != SUCCESS)
+ {
+ DBG1(DBG_CFG, " fetching certificate failed");
+ /* we set the item to NULL, so we can skip it */
+ enumerator->auth->replace(enumerator->auth, enumerator->inner,
+ *rule, NULL);
+ return FALSE;
+ }
+
+ cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509,
+ BUILD_BLOB_ASN1_DER, data, BUILD_END);
+ free(data.ptr);
+
+ if (!cert)
+ {
+ DBG1(DBG_CFG, " parsing fetched certificate failed");
+ /* we set the item to NULL, so we can skip it */
+ enumerator->auth->replace(enumerator->auth, enumerator->inner,
+ *rule, NULL);
+ return FALSE;
+ }
+
+ DBG1(DBG_CFG, " fetched certificate \"%Y\"", cert->get_subject(cert));
+ charon->credentials->cache_cert(charon->credentials, cert);
+
+ if (*rule == AUTH_HELPER_IM_HASH_URL)
+ {
+ *rule = AUTH_HELPER_IM_CERT;
+ }
+ else
+ {
+ *rule = AUTH_HELPER_SUBJECT_CERT;
+ }
+ *value = cert;
+ enumerator->auth->replace(enumerator->auth, enumerator->inner,
+ *rule, cert->get_ref(cert));
+ return TRUE;
+}
+
+/**
+ * enumerate function for wrapper_enumerator_t
+ */
+static bool enumerate(wrapper_enumerator_t *this, certificate_t **cert)
+{
+ auth_rule_t rule;
+ certificate_t *current;
+ public_key_t *public;
+
+ while (this->inner->enumerate(this->inner, &rule, &current))
+ {
+ if (rule == AUTH_HELPER_IM_HASH_URL ||
+ rule == AUTH_HELPER_SUBJECT_HASH_URL)
+ { /* on-demand fetching of hash and url certificates */
+ if (!fetch_cert(this, &rule, (void**)&current))
+ {
+ continue;
+ }
+ }
+ else if (rule != AUTH_HELPER_SUBJECT_CERT &&
+ rule != AUTH_HELPER_IM_CERT)
+ { /* handle only HELPER certificates */
+ continue;
+ }
+ if (this->cert != CERT_ANY && this->cert != current->get_type(current))
+ { /* CERT type requested, but does not match */
+ continue;
+ }
+ public = current->get_public_key(current);
+ if (this->key != KEY_ANY && !public)
+ { /* key type requested, but no public key */
+ DESTROY_IF(public);
+ continue;
+ }
+ if (this->key != KEY_ANY && public && this->key != public->get_type(public))
+ { /* key type requested, but public key has another type */
+ DESTROY_IF(public);
+ continue;
+ }
+ DESTROY_IF(public);
+ if (this->id && !current->has_subject(current, this->id))
+ { /* subject requested, but does not match */
+ continue;
+ }
+ *cert = current;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/**
+ * destroy function for wrapper_enumerator_t
+ */
+static void wrapper_enumerator_destroy(wrapper_enumerator_t *this)
+{
+ this->inner->destroy(this->inner);
+ free(this);
+}
+
+/**
+ * implementation of auth_cfg_wrapper_t.set.create_cert_enumerator
+ */
+static enumerator_t *create_enumerator(private_auth_cfg_wrapper_t *this,
+ certificate_type_t cert, key_type_t key,
+ identification_t *id, bool trusted)
+{
+ wrapper_enumerator_t *enumerator;
+
+ if (trusted)
+ {
+ return NULL;
+ }
+ enumerator = malloc_thing(wrapper_enumerator_t);
+ enumerator->auth = this->auth;
+ enumerator->cert = cert;
+ enumerator->key = key;
+ enumerator->id = id;
+ enumerator->inner = this->auth->create_enumerator(this->auth);
+ enumerator->public.enumerate = (void*)enumerate;
+ enumerator->public.destroy = (void*)wrapper_enumerator_destroy;
+ return &enumerator->public;
+}
+
+/**
+ * Implementation of auth_cfg_wrapper_t.destroy
+ */
+static void destroy(private_auth_cfg_wrapper_t *this)
+{
+ free(this);
+}
+
+/*
+ * see header file
+ */
+auth_cfg_wrapper_t *auth_cfg_wrapper_create(auth_cfg_t *auth)
+{
+ private_auth_cfg_wrapper_t *this = malloc_thing(private_auth_cfg_wrapper_t);
+
+ this->public.set.create_private_enumerator = (void*)return_null;
+ this->public.set.create_cert_enumerator = (void*)create_enumerator;
+ this->public.set.create_shared_enumerator = (void*)return_null;
+ this->public.set.create_cdp_enumerator = (void*)return_null;
+ this->public.set.cache_cert = (void*)nop;
+ this->public.destroy = (void(*)(auth_cfg_wrapper_t*))destroy;
+
+ this->auth = auth;
+
+ return &this->public;
+}
+
diff --git a/src/libcharon/credentials/sets/auth_cfg_wrapper.h b/src/libcharon/credentials/sets/auth_cfg_wrapper.h
new file mode 100644
index 000000000..7653fcdbf
--- /dev/null
+++ b/src/libcharon/credentials/sets/auth_cfg_wrapper.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2008-2009 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * 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.
+ */
+
+/**
+ * @defgroup auth_cfg_wrapper auth_cfg_wrapper
+ * @{ @ingroup sets
+ */
+
+#ifndef AUTH_CFG_WRAPPER_H_
+#define AUTH_CFG_WRAPPER_H_
+
+#include <config/auth_cfg.h>
+#include <credentials/credential_set.h>
+
+typedef struct auth_cfg_wrapper_t auth_cfg_wrapper_t;
+
+/**
+ * A wrapper around auth_cfg_t to handle it as a credential set.
+ */
+struct auth_cfg_wrapper_t {
+
+ /**
+ * implements credential_set_t
+ */
+ credential_set_t set;
+
+ /**
+ * Destroy a auth_cfg_wrapper instance.
+ */
+ void (*destroy)(auth_cfg_wrapper_t *this);
+};
+
+/**
+ * Create a auth_cfg_wrapper instance.
+ *
+ * @param auth the wrapped auth info
+ * @return wrapper around auth
+ */
+auth_cfg_wrapper_t *auth_cfg_wrapper_create(auth_cfg_t *auth);
+
+#endif /** AUTH_CFG_WRAPPER_H_ @}*/
diff --git a/src/libcharon/credentials/sets/cert_cache.c b/src/libcharon/credentials/sets/cert_cache.c
new file mode 100644
index 000000000..176accce2
--- /dev/null
+++ b/src/libcharon/credentials/sets/cert_cache.c
@@ -0,0 +1,390 @@
+/*
+ * Copyright (C) 2008 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * 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.
+ */
+
+#include "cert_cache.h"
+
+#include <time.h>
+#include <sched.h>
+
+#include <daemon.h>
+#include <threading/rwlock.h>
+#include <utils/linked_list.h>
+
+/** cache size, a power of 2 for fast modulo */
+#define CACHE_SIZE 32
+
+/** attempts to acquire a cache lock */
+#define REPLACE_TRIES 5
+
+typedef struct private_cert_cache_t private_cert_cache_t;
+typedef struct relation_t relation_t;
+
+/**
+ * A trusted relation between subject and issuer
+ */
+struct relation_t {
+
+ /**
+ * subject of this relation
+ */
+ certificate_t *subject;
+
+ /**
+ * issuer of this relation
+ */
+ certificate_t *issuer;
+
+ /**
+ * Cache hits
+ */
+ u_int hits;
+
+ /**
+ * Lock for this relation
+ */
+ rwlock_t *lock;
+};
+
+/**
+ * private data of cert_cache
+ */
+struct private_cert_cache_t {
+
+ /**
+ * public functions
+ */
+ cert_cache_t public;
+
+ /**
+ * array of trusted subject-issuer relations
+ */
+ relation_t relations[CACHE_SIZE];
+};
+
+/**
+ * Cache relation in a free slot/replace an other
+ */
+static void cache(private_cert_cache_t *this,
+ certificate_t *subject, certificate_t *issuer)
+{
+ relation_t *rel;
+ int i, offset, try;
+ u_int total_hits = 0;
+
+ /* check for a unused relation slot first */
+ for (i = 0; i < CACHE_SIZE; i++)
+ {
+ rel = &this->relations[i];
+
+ if (!rel->subject && rel->lock->try_write_lock(rel->lock))
+ {
+ /* double-check having lock */
+ if (!rel->subject)
+ {
+ rel->subject = subject->get_ref(subject);
+ rel->issuer = issuer->get_ref(issuer);
+ return rel->lock->unlock(rel->lock);
+ }
+ rel->lock->unlock(rel->lock);
+ }
+ total_hits += rel->hits;
+ }
+ /* run several attempts to replace a random slot, never block. */
+ for (try = 0; try < REPLACE_TRIES; try++)
+ {
+ /* replace a random relation */
+ offset = random();
+ for (i = 0; i < CACHE_SIZE; i++)
+ {
+ rel = &this->relations[(i + offset) % CACHE_SIZE];
+
+ if (rel->hits > total_hits / CACHE_SIZE)
+ { /* skip often used slots */
+ continue;
+ }
+ if (rel->lock->try_write_lock(rel->lock))
+ {
+ if (rel->subject)
+ {
+ rel->subject->destroy(rel->subject);
+ rel->issuer->destroy(rel->issuer);
+ }
+ rel->subject = subject->get_ref(subject);
+ rel->issuer = issuer->get_ref(issuer);
+ rel->hits = 0;
+ return rel->lock->unlock(rel->lock);
+ }
+ }
+ /* give other threads a chance to release locks */
+ sched_yield();
+ }
+}
+
+/**
+ * Implementation of cert_cache_t.issued_by.
+ */
+static bool issued_by(private_cert_cache_t *this,
+ certificate_t *subject, certificate_t *issuer)
+{
+ relation_t *found = NULL, *current;
+ int i;
+
+ for (i = 0; i < CACHE_SIZE; i++)
+ {
+ current = &this->relations[i];
+
+ current->lock->read_lock(current->lock);
+ if (current->subject)
+ {
+ /* check for equal issuer */
+ if (issuer->equals(issuer, current->issuer))
+ {
+ /* reuse issuer instance in cache() */
+ issuer = current->issuer;
+ if (subject->equals(subject, current->subject))
+ {
+ /* write hit counter is not locked, but not critical */
+ current->hits++;
+ found = current;
+ }
+ }
+ }
+ current->lock->unlock(current->lock);
+ if (found)
+ {
+ return TRUE;
+ }
+ }
+ /* no cache hit, check and cache signature */
+ if (subject->issued_by(subject, issuer))
+ {
+ cache(this, subject, issuer);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/**
+ * certificate enumerator implemenation
+ */
+typedef struct {
+ /** implements enumerator_t interface */
+ enumerator_t public;
+ /** type of requested certificate */
+ certificate_type_t cert;
+ /** type of requested key */
+ key_type_t key;
+ /** ID to get a cert for */
+ identification_t *id;
+ /** cache */
+ relation_t *relations;
+ /** current position in array cache */
+ int index;
+ /** currently locked relation */
+ int locked;
+} cert_enumerator_t;
+
+/**
+ * filter function for certs enumerator
+ */
+static bool cert_enumerate(cert_enumerator_t *this, certificate_t **out)
+{
+ public_key_t *public;
+ relation_t *rel;
+
+ if (this->locked >= 0)
+ {
+ rel = &this->relations[this->locked];
+ rel->lock->unlock(rel->lock);
+ this->locked = -1;
+ }
+
+ while (++this->index < CACHE_SIZE)
+ {
+ rel = &this->relations[this->index];
+ rel->lock->read_lock(rel->lock);
+ this->locked = this->index;
+ if (rel->subject)
+ {
+ /* CRL lookup is done using issuer/authkeyidentifier */
+ if (this->key == KEY_ANY && this->id &&
+ (this->cert == CERT_ANY || this->cert == CERT_X509_CRL) &&
+ rel->subject->get_type(rel->subject) == CERT_X509_CRL &&
+ rel->subject->has_issuer(rel->subject, this->id))
+ {
+ *out = rel->subject;
+ return TRUE;
+ }
+ if ((this->cert == CERT_ANY ||
+ rel->subject->get_type(rel->subject) == this->cert) &&
+ (!this->id || rel->subject->has_subject(rel->subject, this->id)))
+ {
+ if (this->key == KEY_ANY)
+ {
+ *out = rel->subject;
+ return TRUE;
+ }
+ public = rel->subject->get_public_key(rel->subject);
+ if (public)
+ {
+ if (public->get_type(public) == this->key)
+ {
+ public->destroy(public);
+ *out = rel->subject;
+ return TRUE;
+ }
+ public->destroy(public);
+ }
+ }
+ }
+ this->locked = -1;
+ rel->lock->unlock(rel->lock);
+ }
+ return FALSE;
+}
+
+/**
+ * clean up enumeration data
+ */
+static void cert_enumerator_destroy(cert_enumerator_t *this)
+{
+ relation_t *rel;
+
+ if (this->locked >= 0)
+ {
+ rel = &this->relations[this->locked];
+ rel->lock->unlock(rel->lock);
+ }
+ free(this);
+}
+
+/**
+ * implementation of credential_set_t.create_cert_enumerator
+ */
+static enumerator_t *create_enumerator(private_cert_cache_t *this,
+ certificate_type_t cert, key_type_t key,
+ identification_t *id, bool trusted)
+{
+ cert_enumerator_t *enumerator;
+
+ if (trusted)
+ {
+ return NULL;
+ }
+ enumerator = malloc_thing(cert_enumerator_t);
+ enumerator->public.enumerate = (void*)cert_enumerate;
+ enumerator->public.destroy = (void*)cert_enumerator_destroy;
+ enumerator->cert = cert;
+ enumerator->key = key;
+ enumerator->id = id;
+ enumerator->relations = this->relations;
+ enumerator->index = -1;
+ enumerator->locked = -1;
+
+ return &enumerator->public;
+}
+
+/**
+ * Implementation of cert_cache_t.flush.
+ */
+static void flush(private_cert_cache_t *this, certificate_type_t type)
+{
+ relation_t *rel;
+ int i;
+
+ for (i = 0; i < CACHE_SIZE; i++)
+ {
+ rel = &this->relations[i];
+ if (!rel->subject)
+ {
+ continue;
+ }
+ /* check with cheap read lock first */
+ if (type != CERT_ANY)
+ {
+ rel->lock->read_lock(rel->lock);
+ if (!rel->subject || type != rel->subject->get_type(rel->subject))
+ {
+ rel->lock->unlock(rel->lock);
+ continue;
+ }
+ rel->lock->unlock(rel->lock);
+ }
+ /* double check in write lock */
+ rel->lock->write_lock(rel->lock);
+ if (rel->subject)
+ {
+ if (type == CERT_ANY || type == rel->subject->get_type(rel->subject))
+ {
+ rel->subject->destroy(rel->subject);
+ rel->issuer->destroy(rel->issuer);
+ rel->subject = NULL;
+ rel->issuer = NULL;
+ rel->hits = 0;
+ }
+ }
+ rel->lock->unlock(rel->lock);
+ }
+}
+
+/**
+ * Implementation of cert_cache_t.destroy
+ */
+static void destroy(private_cert_cache_t *this)
+{
+ relation_t *rel;
+ int i;
+
+ for (i = 0; i < CACHE_SIZE; i++)
+ {
+ rel = &this->relations[i];
+ if (rel->subject)
+ {
+ rel->subject->destroy(rel->subject);
+ rel->issuer->destroy(rel->issuer);
+ }
+ rel->lock->destroy(rel->lock);
+ }
+ free(this);
+}
+
+/*
+ * see header file
+ */
+cert_cache_t *cert_cache_create()
+{
+ private_cert_cache_t *this;
+ int i;
+
+ this = malloc_thing(private_cert_cache_t);
+ this->public.set.create_private_enumerator = (void*)return_null;
+ this->public.set.create_cert_enumerator = (void*)create_enumerator;
+ this->public.set.create_shared_enumerator = (void*)return_null;
+ this->public.set.create_cdp_enumerator = (void*)return_null;
+ this->public.set.cache_cert = (void*)nop;
+ this->public.issued_by = (bool(*)(cert_cache_t*, certificate_t *subject, certificate_t *issuer))issued_by;
+ this->public.flush = (void(*)(cert_cache_t*, certificate_type_t type))flush;
+ this->public.destroy = (void(*)(cert_cache_t*))destroy;
+
+ for (i = 0; i < CACHE_SIZE; i++)
+ {
+ this->relations[i].subject = NULL;
+ this->relations[i].issuer = NULL;
+ this->relations[i].hits = 0;
+ this->relations[i].lock = rwlock_create(RWLOCK_TYPE_DEFAULT);
+ }
+ return &this->public;
+}
+
diff --git a/src/libcharon/credentials/sets/cert_cache.h b/src/libcharon/credentials/sets/cert_cache.h
new file mode 100644
index 000000000..d2721866e
--- /dev/null
+++ b/src/libcharon/credentials/sets/cert_cache.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2008 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * 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.
+ */
+
+/**
+ * @defgroup cert_cache cert_cache
+ * @{ @ingroup sets
+ */
+
+#ifndef CERT_CACHE_H_
+#define CERT_CACHE_H_
+
+#include <credentials/credential_set.h>
+
+typedef struct cert_cache_t cert_cache_t;
+
+/**
+ * Certificate signature verification and certificate cache.
+ *
+ * This cache serves all certificates seen in its issued_by method
+ * and serves them as untrusted through the credential set interface. Further,
+ * it caches valid subject-issuer relationships to speed up the issued_by
+ * method.
+ */
+struct cert_cache_t {
+
+ /**
+ * Implements credential_set_t.
+ */
+ credential_set_t set;
+
+ /**
+ * Caching wrapper around certificate_t.issued_by.
+ *
+ * @param subject certificate to verify
+ * @param issuer issuing certificate to verify subject
+ * @return TRUE if subject issued by issuer
+ */
+ bool (*issued_by)(cert_cache_t *this,
+ certificate_t *subject, certificate_t *issuer);
+
+ /**
+ * Flush the certificate cache.
+ *
+ * @param type type of certificate to flush, or CERT_ANY
+ */
+ void (*flush)(cert_cache_t *this, certificate_type_t type);
+
+ /**
+ * Destroy a cert_cache instance.
+ */
+ void (*destroy)(cert_cache_t *this);
+};
+
+/**
+ * Create a cert_cache instance.
+ */
+cert_cache_t *cert_cache_create();
+
+#endif /** CERT_CACHE_H_ @}*/
diff --git a/src/libcharon/credentials/sets/ocsp_response_wrapper.c b/src/libcharon/credentials/sets/ocsp_response_wrapper.c
new file mode 100644
index 000000000..82079209a
--- /dev/null
+++ b/src/libcharon/credentials/sets/ocsp_response_wrapper.c
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2008 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * 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.
+ */
+
+#include "ocsp_response_wrapper.h"
+
+typedef struct private_ocsp_response_wrapper_t private_ocsp_response_wrapper_t;
+
+/**
+ * private data of ocsp_response_wrapper
+ */
+struct private_ocsp_response_wrapper_t {
+
+ /**
+ * public functions
+ */
+ ocsp_response_wrapper_t public;
+
+ /**
+ * wrapped OCSP response
+ */
+ ocsp_response_t *response;
+};
+
+/**
+ * enumerator for ocsp_response_wrapper_t.create_cert_enumerator()
+ */
+typedef struct {
+ /** implements enumerator_t */
+ enumerator_t public;
+ /** enumerator over ocsp response */
+ enumerator_t *inner;
+ /** type of cert */
+ certificate_type_t cert;
+ /** type of key */
+ key_type_t key;
+ /** filtering identity */
+ identification_t *id;
+} wrapper_enumerator_t;
+
+/**
+ * enumerate function wrapper_enumerator_t
+ */
+static bool enumerate(wrapper_enumerator_t *this, certificate_t **cert)
+{
+ certificate_t *current;
+ public_key_t *public;
+
+ while (this->inner->enumerate(this->inner, &current))
+ {
+ if (this->cert != CERT_ANY && this->cert != current->get_type(current))
+ { /* CERT type requested, but does not match */
+ continue;
+ }
+ public = current->get_public_key(current);
+ if (this->key != KEY_ANY && !public)
+ { /* key type requested, but no public key */
+ DESTROY_IF(public);
+ continue;
+ }
+ if (this->key != KEY_ANY && public && this->key != public->get_type(public))
+ { /* key type requested, but public key has another type */
+ DESTROY_IF(public);
+ continue;
+ }
+ DESTROY_IF(public);
+ if (this->id && !current->has_subject(current, this->id))
+ { /* subject requested, but does not match */
+ continue;
+ }
+ *cert = current;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/**
+ * destroy function for wrapper_enumerator_t
+ */
+static void enumerator_destroy(wrapper_enumerator_t *this)
+{
+ this->inner->destroy(this->inner);
+ free(this);
+}
+
+/**
+ * implementation of ocsp_response_wrapper_t.set.create_cert_enumerator
+ */
+static enumerator_t *create_enumerator(private_ocsp_response_wrapper_t *this,
+ certificate_type_t cert, key_type_t key,
+ identification_t *id, bool trusted)
+{
+ wrapper_enumerator_t *enumerator;
+
+ if (trusted)
+ {
+ return NULL;
+ }
+
+ enumerator = malloc_thing(wrapper_enumerator_t);
+ enumerator->cert = cert;
+ enumerator->key = key;
+ enumerator->id = id;
+ enumerator->inner = this->response->create_cert_enumerator(this->response);
+ enumerator->public.enumerate = (void*)enumerate;
+ enumerator->public.destroy = (void*)enumerator_destroy;
+ return &enumerator->public;
+}
+
+/**
+ * Implementation of ocsp_response_wrapper_t.destroy
+ */
+static void destroy(private_ocsp_response_wrapper_t *this)
+{
+ free(this);
+}
+
+/*
+ * see header file
+ */
+ocsp_response_wrapper_t *ocsp_response_wrapper_create(ocsp_response_t *response)
+{
+ private_ocsp_response_wrapper_t *this = malloc_thing(private_ocsp_response_wrapper_t);
+
+ this->public.set.create_private_enumerator = (void*)return_null;
+ this->public.set.create_cert_enumerator = (void*)create_enumerator;
+ this->public.set.create_shared_enumerator = (void*)return_null;
+ this->public.set.create_cdp_enumerator = (void*)return_null;
+ this->public.set.cache_cert = (void*)nop;
+ this->public.destroy = (void(*)(ocsp_response_wrapper_t*))destroy;
+
+ this->response = response;
+
+ return &this->public;
+}
+
diff --git a/src/libcharon/credentials/sets/ocsp_response_wrapper.h b/src/libcharon/credentials/sets/ocsp_response_wrapper.h
new file mode 100644
index 000000000..dc4b451df
--- /dev/null
+++ b/src/libcharon/credentials/sets/ocsp_response_wrapper.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2008 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * 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.
+ */
+
+/**
+ * @defgroup ocsp_response_wrapper ocsp_response_wrapper
+ * @{ @ingroup sets
+ */
+
+#ifndef OCSP_RESPONSE_WRAPPER_H_
+#define OCSP_RESPONSE_WRAPPER_H_
+
+#include <credentials/credential_set.h>
+#include <credentials/certificates/ocsp_response.h>
+
+typedef struct ocsp_response_wrapper_t ocsp_response_wrapper_t;
+
+/**
+ * A wrapper around ocsp_response_t to handle it like a credential set.
+ */
+struct ocsp_response_wrapper_t {
+
+ /**
+ * implements credential_set_t
+ */
+ credential_set_t set;
+
+ /**
+ * Destroy a ocsp_response_wrapper instance.
+ */
+ void (*destroy)(ocsp_response_wrapper_t *this);
+};
+
+/**
+ * Create a ocsp_response_wrapper instance.
+ *
+ * @param response the wrapped OCSP response
+ * @return wrapper around response
+ */
+ocsp_response_wrapper_t *ocsp_response_wrapper_create(ocsp_response_t *response);
+
+#endif /** OCSP_RESPONSE_WRAPPER_H_ @}*/