summaryrefslogtreecommitdiff
path: root/src/libstrongswan/credentials/credential_manager.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libstrongswan/credentials/credential_manager.c')
-rw-r--r--src/libstrongswan/credentials/credential_manager.c1097
1 files changed, 1097 insertions, 0 deletions
diff --git a/src/libstrongswan/credentials/credential_manager.c b/src/libstrongswan/credentials/credential_manager.c
new file mode 100644
index 000000000..46c36c941
--- /dev/null
+++ b/src/libstrongswan/credentials/credential_manager.c
@@ -0,0 +1,1097 @@
+/*
+ * 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 <library.h>
+#include <debug.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/certificates/x509.h>
+
+/**
+ * Maximum length of a certificate trust chain
+ */
+#define MAX_TRUST_PATH_LEN 7
+
+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;
+
+ /**
+ * list of certificate validators, cert_validator_t
+ */
+ linked_list_t *validators;
+
+ /**
+ * 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;
+
+
+METHOD(enumerator_t, sets_enumerate, bool,
+ 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;
+}
+
+METHOD(enumerator_t, sets_destroy, void,
+ sets_enumerator_t *this)
+{
+ DESTROY_IF(this->global);
+ DESTROY_IF(this->local);
+ free(this);
+}
+
+/**
+ * create an enumerator over both, global and local sets
+ */
+static enumerator_t *create_sets_enumerator(private_credential_manager_t *this)
+{
+ sets_enumerator_t *enumerator;
+ linked_list_t *local;
+
+ INIT(enumerator,
+ .public.enumerate = (void*)_sets_enumerate,
+ .public.destroy = _sets_destroy,
+ .global = this->sets->create_enumerator(this->sets),
+ );
+ 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);
+}
+
+METHOD(credential_manager_t, create_cert_enumerator, enumerator_t*,
+ 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);
+}
+
+METHOD(credential_manager_t, get_cert, certificate_t*,
+ 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);
+}
+
+METHOD(credential_manager_t, create_cdp_enumerator, enumerator_t*,
+ private_credential_manager_t *this, certificate_type_t type,
+ identification_t *id)
+{
+ cdp_data_t *data;
+
+ INIT(data,
+ .this = this,
+ .type = type,
+ .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);
+}
+
+/**
+ * Create an enumerator over private keys
+ */
+static enumerator_t *create_private_enumerator(
+ private_credential_manager_t *this, key_type_t key, identification_t *keyid)
+{
+ private_data_t *data;
+
+ INIT(data,
+ .this = this,
+ .type = key,
+ .keyid = keyid,
+ );
+ this->lock->read_lock(this->lock);
+ return enumerator_create_nested(create_sets_enumerator(this),
+ (void*)create_private, data,
+ (void*)destroy_private_data);
+}
+
+/**
+ * Look up a private key by its key identifier
+ */
+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);
+}
+
+METHOD(credential_manager_t, create_shared_enumerator, enumerator_t*,
+ private_credential_manager_t *this, shared_key_type_t type,
+ identification_t *me, identification_t *other)
+{
+ shared_data_t *data;
+
+ INIT(data,
+ .this = this,
+ .type = type,
+ .me = me,
+ .other = other,
+ );
+ this->lock->read_lock(this->lock);
+ return enumerator_create_nested(create_sets_enumerator(this),
+ (void*)create_shared, data,
+ (void*)destroy_shared_data);
+}
+
+METHOD(credential_manager_t, get_shared, shared_key_t*,
+ 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;
+}
+
+METHOD(credential_manager_t, add_local_set, void,
+ 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);
+}
+
+METHOD(credential_manager_t, remove_local_set, void,
+ 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);
+}
+
+METHOD(credential_manager_t, cache_cert, void,
+ 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);
+}
+
+/**
+ * check a certificate for its lifetime
+ */
+static bool check_certificate(private_credential_manager_t *this,
+ certificate_t *subject, certificate_t *issuer,
+ bool online, int pathlen, auth_cfg_t *auth)
+{
+ time_t not_before, not_after;
+ cert_validator_t *validator;
+ enumerator_t *enumerator;
+
+ 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)
+ {
+ int pathlen_constraint;
+ x509_t *x509;
+
+ /* 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);
+ return FALSE;
+ }
+ }
+
+ enumerator = this->validators->create_enumerator(this->validators);
+ while (enumerator->enumerate(enumerator, &validator))
+ {
+ if (!validator->validate(validator, subject, issuer,
+ online, pathlen, auth))
+ {
+ enumerator->destroy(enumerator);
+ return FALSE;
+ }
+ }
+ enumerator->destroy(enumerator);
+ 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 online)
+{
+ certificate_t *current, *issuer;
+ auth_cfg_t *auth;
+ int pathlen;
+
+ auth = auth_cfg_create();
+ current = subject->get_ref(subject);
+
+ for (pathlen = 0; pathlen <= MAX_TRUST_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, online, pathlen,
+ current == subject ? auth : NULL))
+ {
+ 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 > MAX_TRUST_PATH_LEN)
+ {
+ DBG1(DBG_CFG, "maximum path length of %d exceeded", MAX_TRUST_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/OCSP checking */
+ bool online;
+ /** pretrusted certificate we have served at first invocation */
+ certificate_t *pretrusted;
+ /** currently enumerating auth config */
+ auth_cfg_t *auth;
+} trusted_enumerator_t;
+
+METHOD(enumerator_t, trusted_enumerate, bool,
+ 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->online))
+ {
+ 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->online))
+ {
+ *cert = current;
+ if (auth)
+ {
+ *auth = this->auth;
+ }
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+METHOD(enumerator_t, trusted_destroy, void,
+ trusted_enumerator_t *this)
+{
+ DESTROY_IF(this->pretrusted);
+ DESTROY_IF(this->auth);
+ DESTROY_IF(this->candidates);
+ free(this);
+}
+
+METHOD(credential_manager_t, create_trusted_enumerator, enumerator_t*,
+ private_credential_manager_t *this, key_type_t type,
+ identification_t *id, bool online)
+{
+ trusted_enumerator_t *enumerator;
+
+ INIT(enumerator,
+ .public = {
+ .enumerate = (void*)_trusted_enumerate,
+ .destroy = _trusted_destroy,
+ },
+ .this = this,
+ .type = type,
+ .id = id,
+ .online = online,
+ );
+ 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;
+
+METHOD(enumerator_t, public_enumerate, bool,
+ 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;
+}
+
+METHOD(enumerator_t, public_destroy, void,
+ 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);
+}
+
+METHOD(credential_manager_t, create_public_enumerator, enumerator_t*,
+ private_credential_manager_t *this, key_type_t type, identification_t *id,
+ auth_cfg_t *auth)
+{
+ public_enumerator_t *enumerator;
+
+ INIT(enumerator,
+ .public = {
+ .enumerate = (void*)_public_enumerate,
+ .destroy = _public_destroy,
+ },
+ .inner = create_trusted_enumerator(this, type, id, TRUE),
+ .this = this,
+ );
+ 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 > MAX_TRUST_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, KEYID_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;
+}
+
+METHOD(credential_manager_t, get_private, private_key_t*,
+ 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;
+}
+
+METHOD(credential_manager_t, flush_cache, void,
+ private_credential_manager_t *this, certificate_type_t type)
+{
+ this->cache->flush(this->cache, type);
+}
+
+METHOD(credential_manager_t, issued_by, bool,
+ private_credential_manager_t *this, certificate_t *subject,
+ certificate_t *issuer)
+{
+ return this->cache->issued_by(this->cache, subject, issuer);
+}
+
+METHOD(credential_manager_t, add_set, void,
+ 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);
+}
+
+METHOD(credential_manager_t, remove_set, void,
+ 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);
+}
+
+METHOD(credential_manager_t, add_validator, void,
+ private_credential_manager_t *this, cert_validator_t *vdtr)
+{
+ this->lock->write_lock(this->lock);
+ this->sets->insert_last(this->validators, vdtr);
+ this->lock->unlock(this->lock);
+}
+
+METHOD(credential_manager_t, remove_validator, void,
+ private_credential_manager_t *this, cert_validator_t *vdtr)
+{
+ this->lock->write_lock(this->lock);
+ this->validators->remove(this->validators, vdtr, NULL);
+ this->lock->unlock(this->lock);
+}
+
+METHOD(credential_manager_t, destroy, void,
+ 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->validators->destroy(this->validators);
+ 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;
+
+ INIT(this,
+ .public = {
+ .create_cert_enumerator = _create_cert_enumerator,
+ .create_shared_enumerator = _create_shared_enumerator,
+ .create_cdp_enumerator = _create_cdp_enumerator,
+ .get_cert = _get_cert,
+ .get_shared = _get_shared,
+ .get_private = _get_private,
+ .create_trusted_enumerator = _create_trusted_enumerator,
+ .create_public_enumerator = _create_public_enumerator,
+ .flush_cache = _flush_cache,
+ .cache_cert = _cache_cert,
+ .issued_by = _issued_by,
+ .add_set = _add_set,
+ .remove_set = _remove_set,
+ .add_local_set = _add_local_set,
+ .remove_local_set = _remove_local_set,
+ .add_validator = _add_validator,
+ .remove_validator = _remove_validator,
+ .destroy = _destroy,
+ },
+ .sets = linked_list_create(),
+ .validators = linked_list_create(),
+ .cache = cert_cache_create(),
+ .cache_queue = linked_list_create(),
+ .lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
+ .queue_mutex = mutex_create(MUTEX_TYPE_DEFAULT),
+ );
+
+ this->local_sets = thread_value_create((thread_cleanup_t)this->sets->destroy);
+ this->sets->insert_first(this->sets, this->cache);
+
+ return &this->public;
+}