aboutsummaryrefslogtreecommitdiffstats
path: root/src/charon/credentials
diff options
context:
space:
mode:
Diffstat (limited to 'src/charon/credentials')
-rw-r--r--src/charon/credentials/auth_info.c356
-rw-r--r--src/charon/credentials/auth_info.h158
-rw-r--r--src/charon/credentials/credential_manager.c1385
-rw-r--r--src/charon/credentials/credential_manager.h187
-rw-r--r--src/charon/credentials/credential_set.h93
5 files changed, 2179 insertions, 0 deletions
diff --git a/src/charon/credentials/auth_info.c b/src/charon/credentials/auth_info.c
new file mode 100644
index 000000000..a0fc4c00f
--- /dev/null
+++ b/src/charon/credentials/auth_info.c
@@ -0,0 +1,356 @@
+/*
+ * 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.
+ *
+ * $Id$
+ */
+
+
+#include "auth_info.h"
+
+#include <daemon.h>
+#include <utils/linked_list.h>
+#include <utils/identification.h>
+#include <credentials/certificates/certificate.h>
+
+ENUM(auth_item_names, AUTHN_CA_CERT, AUTHZ_AC_GROUP,
+ "AUTHN_CA_CERT",
+ "AUTHN_IM_CERT",
+ "AUTHN_SUBJECT_CERT",
+ "AUTHZ_PUBKEY",
+ "AUTHZ_PSK",
+ "AUTHZ_EAP",
+ "AUTHZ_CA_CERT",
+ "AUTHZ_IM_CERT",
+ "AUTHZ_SUBJECT_CERT",
+ "AUTHZ_CRL_VALIDATION",
+ "AUTHZ_OCSP_VALIDATION",
+ "AUTHZ_AC_GROUP",
+);
+
+typedef struct private_auth_info_t private_auth_info_t;
+
+/**
+ * private data of item_set
+ */
+struct private_auth_info_t {
+
+ /**
+ * public functions
+ */
+ auth_info_t public;
+
+ /**
+ * list of item_t's
+ */
+ linked_list_t *items;
+};
+
+typedef struct item_t item_t;
+
+struct item_t {
+ /** type of this item */
+ auth_item_t type;
+ /** associated privlege value, if any */
+ void *value;
+};
+
+/**
+ * implements item_enumerator_t.enumerate
+ */
+static bool item_filter(void *data, item_t **item, auth_item_t *type,
+ void *unused, void **value)
+{
+ *type = (*item)->type;
+ *value = (*item)->value;
+ return TRUE;
+}
+
+/**
+ * Implementation of auth_info_t.create_item_enumerator.
+ */
+static enumerator_t* create_item_enumerator(private_auth_info_t *this)
+{
+ return enumerator_create_filter(this->items->create_enumerator(this->items),
+ (void*)item_filter, NULL, NULL);
+}
+
+/**
+ * Implementation of auth_info_t.get_item.
+ */
+static bool get_item(private_auth_info_t *this, auth_item_t type, void** value)
+{
+ enumerator_t *enumerator;
+ void *current_value;
+ auth_item_t current_type;
+ bool found = FALSE;
+
+ enumerator = create_item_enumerator(this);
+ while (enumerator->enumerate(enumerator, &current_type, &current_value))
+ {
+ if (type == current_type)
+ {
+ *value = current_value;
+ found = TRUE;
+ break;
+ }
+ }
+ enumerator->destroy(enumerator);
+ return found;
+}
+
+/**
+ * Implementation of auth_info_t.add_item.
+ */
+static void add_item(private_auth_info_t *this, auth_item_t type, void *value)
+{
+ item_t *item = malloc_thing(item_t);
+
+ item->type = type;
+ switch (type)
+ {
+ case AUTHZ_PUBKEY:
+ {
+ public_key_t *key = (public_key_t*)value;
+
+ item->value = key->get_ref(key);
+ break;
+ }
+ case AUTHZ_PSK:
+ {
+ shared_key_t *key = (shared_key_t*)value;
+
+ item->value = key->get_ref(key);
+ break;
+ }
+ case AUTHN_CA_CERT:
+ case AUTHN_IM_CERT:
+ case AUTHN_SUBJECT_CERT:
+ case AUTHZ_CA_CERT:
+ case AUTHZ_IM_CERT:
+ case AUTHZ_SUBJECT_CERT:
+ {
+ certificate_t *cert = (certificate_t*)value;
+
+ item->value = cert->get_ref(cert);
+ break;
+ }
+ case AUTHZ_CRL_VALIDATION:
+ case AUTHZ_OCSP_VALIDATION:
+ {
+ cert_validation_t *validation = malloc_thing(cert_validation_t);
+
+ *validation = *(cert_validation_t*)value;
+ item->value = validation;
+ break;
+ }
+ case AUTHZ_EAP:
+ {
+ eap_method_t *method = malloc_thing(eap_method_t);
+
+ *method = *(eap_method_t*)value;
+ item->value = method;
+ break;
+ }
+ case AUTHZ_AC_GROUP:
+ {
+ identification_t *id = (identification_t*)value;
+
+ item->value = id->clone(id);
+ break;
+ }
+ }
+ this->items->insert_last(this->items, item);
+}
+
+
+/**
+ * Implementation of auth_info_t.complies.
+ */
+static bool complies(private_auth_info_t *this, auth_info_t *constraints)
+{
+ enumerator_t *enumerator;
+ bool success = TRUE;
+ auth_item_t type;
+ void *value;
+
+ enumerator = constraints->create_item_enumerator(constraints);
+ while (enumerator->enumerate(enumerator, &type, &value))
+ {
+ switch (type)
+ {
+ case AUTHN_CA_CERT:
+ case AUTHN_IM_CERT:
+ case AUTHN_SUBJECT_CERT:
+ { /* skip non-authorization tokens */
+ continue;
+ }
+ case AUTHZ_CRL_VALIDATION:
+ case AUTHZ_OCSP_VALIDATION:
+ {
+ cert_validation_t *valid;
+
+ /* OCSP validation is also sufficient for CRL constraint, but
+ * not vice-versa */
+ if (!get_item(this, type, (void**)&valid) &&
+ type == AUTHZ_CRL_VALIDATION &&
+ !get_item(this, AUTHZ_OCSP_VALIDATION, (void**)&valid))
+ {
+ DBG1(DBG_CFG, "constraint check failed: %N requires at "
+ "least %N, but no check done", auth_item_names, type,
+ cert_validation_names, *(cert_validation_t*)value);
+ success = FALSE;
+ break;
+ }
+ switch (*(cert_validation_t*)value)
+ {
+ case VALIDATION_SKIPPED:
+ if (*valid == VALIDATION_SKIPPED)
+ {
+ break;
+ } /* FALL */
+ case VALIDATION_GOOD:
+ if (*valid == VALIDATION_GOOD)
+ {
+ break;
+ } /* FALL */
+ default:
+ DBG1(DBG_CFG, "constraint check failed: %N is %N, but "
+ "requires at least %N", auth_item_names, type,
+ cert_validation_names, *valid,
+ cert_validation_names, *(cert_validation_t*)value);
+ success = FALSE;
+ break;
+ }
+ break;
+ }
+ case AUTHZ_PUBKEY:
+ case AUTHZ_PSK:
+ case AUTHZ_IM_CERT:
+ case AUTHZ_SUBJECT_CERT:
+ case AUTHZ_EAP:
+ case AUTHZ_AC_GROUP:
+ DBG1(DBG_CFG, "constraint check %N not implemented!",
+ auth_item_names, type);
+ success = FALSE;
+ break;
+ case AUTHZ_CA_CERT:
+ {
+ certificate_t *cert;
+
+ if (!get_item(this, AUTHZ_CA_CERT, (void**)&cert) ||
+ !cert->equals(cert, (certificate_t*)value))
+ {
+ cert = (certificate_t*)value;
+ DBG1(DBG_CFG, "constraint check failed: peer not "
+ "authenticated by CA '%D'.", cert->get_issuer(cert));
+ success = FALSE;
+ }
+ break;
+ }
+ }
+ if (!success)
+ {
+ break;
+ }
+ }
+ enumerator->destroy(enumerator);
+ return success;
+}
+
+/**
+ * Implementation of auth_info_t.merge.
+ */
+static void merge(private_auth_info_t *this, private_auth_info_t *other)
+{
+ item_t *item;
+
+ while (other->items->remove_first(other->items, (void**)&item) == SUCCESS)
+ {
+ this->items->insert_last(this->items, item);
+ }
+}
+
+/**
+ * Implementation of auth_info_t.destroy
+ */
+static void destroy(private_auth_info_t *this)
+{
+ item_t *item;
+
+ while (this->items->remove_last(this->items, (void**)&item) == SUCCESS)
+ {
+ switch (item->type)
+ {
+ case AUTHZ_PUBKEY:
+ {
+ public_key_t *key = (public_key_t*)item->value;
+ key->destroy(key);
+ break;
+ }
+ case AUTHZ_PSK:
+ {
+ shared_key_t *key = (shared_key_t*)item->value;
+ key->destroy(key);
+ break;
+ }
+ case AUTHN_CA_CERT:
+ case AUTHN_IM_CERT:
+ case AUTHN_SUBJECT_CERT:
+ case AUTHZ_CA_CERT:
+ case AUTHZ_IM_CERT:
+ case AUTHZ_SUBJECT_CERT:
+ {
+ certificate_t *cert = (certificate_t*)item->value;
+ cert->destroy(cert);
+ break;
+ }
+ case AUTHZ_CRL_VALIDATION:
+ case AUTHZ_OCSP_VALIDATION:
+ case AUTHZ_EAP:
+ {
+ free(item->value);
+ break;
+ }
+ case AUTHZ_AC_GROUP:
+ {
+ identification_t *id = (identification_t*)item->value;
+ id->destroy(id);
+ break;
+ }
+ }
+ free(item);
+ }
+ this->items->destroy(this->items);
+ free(this);
+}
+
+/*
+ * see header file
+ */
+auth_info_t *auth_info_create()
+{
+ private_auth_info_t *this = malloc_thing(private_auth_info_t);
+
+ this->public.add_item = (void(*)(auth_info_t*, auth_item_t type, void *value))add_item;
+ this->public.get_item = (bool(*)(auth_info_t*, auth_item_t type, void **value))get_item;
+ this->public.create_item_enumerator = (enumerator_t*(*)(auth_info_t*))create_item_enumerator;
+ this->public.complies = (bool(*)(auth_info_t*, auth_info_t *))complies;
+ this->public.merge = (void(*)(auth_info_t*, auth_info_t *other))merge;
+ this->public.destroy = (void(*)(auth_info_t*))destroy;
+
+ this->items = linked_list_create();
+
+ return &this->public;
+}
+
diff --git a/src/charon/credentials/auth_info.h b/src/charon/credentials/auth_info.h
new file mode 100644
index 000000000..f8ba29df0
--- /dev/null
+++ b/src/charon/credentials/auth_info.h
@@ -0,0 +1,158 @@
+/*
+ * 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 auth_info auth_info
+ * @{ @ingroup ccredentials
+ */
+
+#ifndef AUTH_INFO_H_
+#define AUTH_INFO_H_
+
+#include <utils/enumerator.h>
+
+typedef struct auth_info_t auth_info_t;
+typedef enum auth_item_t auth_item_t;
+
+/**
+ * Authentication/Authorization process helper item.
+ *
+ * For the authentication process, further information may be needed. These
+ * items are defined as auth_item_t and have a AUTHN prefix.
+ * The authentication process returns important data for the authorization
+ * process, these items are defined with a AUTHZ prefix.
+ * Authentication uses AUTHN items and creates AUTHZ items during authentication,
+ * authorization reads AUTHZ values to give out privileges.
+ *
+ * +---+ +---------------------+
+ * | A | | A |
+ * | u | | u +-----------+ |
+ * | t | | t | Required | |
+ * | h | | h | auth_info | |
+ * | e | | o +-----------+ |
+ * | n | | r | |
+ * +-----------+ | t | | i | |
+ * | Provided | | i | | z V |
+ * | auth_info |--| c |-------------| a ----> match? ----|------->
+ * +-----------+ | a | | t |
+ * | t | | i |
+ * | i | | o |
+ * | o | | n |
+ * | n | | |
+ * +---+ +---------------------+
+ */
+enum auth_item_t {
+
+ /*
+ * items provided to authentication process
+ */
+
+ /** CA certificate to use for authentication, value is certificate_t* */
+ AUTHN_CA_CERT,
+ /** intermediate certificate, value is certificate_t* */
+ AUTHN_IM_CERT,
+ /** certificate for trustchain verification, value is certificate_t* */
+ AUTHN_SUBJECT_CERT,
+
+ /*
+ * item provided to authorization process
+ */
+
+ /** subject has been authenticated by public key, value is public_key_t* */
+ AUTHZ_PUBKEY,
+ /** subject has ben authenticated using preshared secrets, value is shared_key_t* */
+ AUTHZ_PSK,
+ /** subject has been authenticated using EAP, value is eap_method_t */
+ AUTHZ_EAP,
+ /** certificate authority, value is certificate_t* */
+ AUTHZ_CA_CERT,
+ /** intermediate certificate in trustchain, value is certificate_t* */
+ AUTHZ_IM_CERT,
+ /** subject certificate, value is certificate_t* */
+ AUTHZ_SUBJECT_CERT,
+ /** result of a CRL validation, value is cert_validation_t */
+ AUTHZ_CRL_VALIDATION,
+ /** result of a OCSP validation, value is cert_validation_t */
+ AUTHZ_OCSP_VALIDATION,
+ /** subject is in attribute certificate group, value is identification_t* */
+ AUTHZ_AC_GROUP,
+};
+
+
+/**
+ * enum name for auth_item_t.
+ */
+extern enum_name_t *auth_item_names;
+
+/**
+ * The auth_info class contains auth_item_t's used for AA.
+ *
+ * A auth_info allows the separation of authentication and authorization.
+ */
+struct auth_info_t {
+
+ /**
+ * Add an item to the set.
+ *
+ * @param type auth_info type
+ * @param value associated value to auth_info type, if any
+ */
+ void (*add_item)(auth_info_t *this, auth_item_t type, void *value);
+
+ /**
+ * Get an item.
+ *
+ * @param type auth_info type to get
+ * @param value pointer to a pointer receiving item
+ * @return bool if item has been found
+ */
+ bool (*get_item)(auth_info_t *this, auth_item_t type, void **value);
+
+ /**
+ * Create an enumerator over all items.
+ *
+ * @return enumerator over (auth_item_t type, void *value)
+ */
+ enumerator_t* (*create_item_enumerator)(auth_info_t *this);
+
+ /**
+ * Check if this fulfills a set of required constraints.
+ *
+ * @param constraints required authorization infos
+ * @return TRUE if this complies with constraints
+ */
+ bool (*complies)(auth_info_t *this, auth_info_t *constraints);
+
+ /**
+ * Merge items from other into this.
+ *
+ * Items do not get cloned, but moved from other to this.
+ *
+ * @param other items to read for merge
+ */
+ void (*merge)(auth_info_t *this, auth_info_t *other);
+
+ /**
+ * Destroy a auth_info instance with all associated values.
+ */
+ void (*destroy)(auth_info_t *this);
+};
+
+/**
+ * Create a auth_info instance.
+ */
+auth_info_t *auth_info_create();
+
+#endif /* AUTH_INFO_H_ @}*/
diff --git a/src/charon/credentials/credential_manager.c b/src/charon/credentials/credential_manager.c
new file mode 100644
index 000000000..e5db23524
--- /dev/null
+++ b/src/charon/credentials/credential_manager.c
@@ -0,0 +1,1385 @@
+/*
+ * 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.
+ *
+ * $Id$
+ */
+
+#include "credential_manager.h"
+
+#include <daemon.h>
+#include <utils/linked_list.h>
+#include <utils/mutex.h>
+#include <credentials/certificates/x509.h>
+#include <credentials/certificates/crl.h>
+#include <credentials/certificates/ocsp_request.h>
+#include <credentials/certificates/ocsp_response.h>
+
+#define MAX_CA_LEVELS 6
+
+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;
+
+ /**
+ * mutex to gain exclusive access
+ */
+ mutex_t *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;
+
+/**
+ * cleanup function for cert data
+ */
+static void destroy_cert_data(cert_data_t *data)
+{
+ data->this->mutex->unlock(data->this->mutex);
+ 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->mutex->lock(this->mutex);
+ return enumerator_create_nested(this->sets->create_enumerator(this->sets),
+ (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;
+
+ this->mutex->lock(this->mutex);
+ 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);
+ this->mutex->unlock(this->mutex);
+ return found;
+}
+
+
+/**
+ * cleanup function for cdp data
+ */
+static void destroy_cdp_data(cdp_data_t *data)
+{
+ data->this->mutex->unlock(data->this->mutex);
+ 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,
+ credential_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->mutex->lock(this->mutex);
+ return enumerator_create_nested(this->sets->create_enumerator(this->sets),
+ (void*)create_cdp, data,
+ (void*)destroy_cdp_data);
+}
+
+/**
+ * cleanup function for private data
+ */
+static void destroy_private_data(private_data_t *data)
+{
+ data->this->mutex->unlock(data->this->mutex);
+ 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.get_private_by_keyid.
+ */
+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->mutex->lock(this->mutex);
+ return enumerator_create_nested(this->sets->create_enumerator(this->sets),
+ (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->mutex->unlock(data->this->mutex);
+ 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->mutex->lock(this->mutex);
+ return enumerator_create_nested(this->sets->create_enumerator(this->sets),
+ (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;
+}
+
+/**
+ * forward declaration
+ */
+static certificate_t *get_trusted_cert(private_credential_manager_t *this,
+ key_type_t type, identification_t *id,
+ auth_info_t *auth, bool crl, bool ocsp);
+/**
+ * return null ;-)
+ */
+static void *return_null()
+{
+ return NULL;
+}
+
+/**
+ * credential_set_t implementation around an OCSP response
+ */
+typedef struct ocsp_wrapper_t {
+ credential_set_t set;
+ ocsp_response_t *response;
+} ocsp_wrapper_t;
+
+/**
+ * enumerator for ocsp_wrapper_t.create_cert_enumerator()
+ */
+typedef struct {
+ enumerator_t public;
+ enumerator_t *inner;
+ certificate_type_t cert;
+ key_type_t key;
+ identification_t *id;
+} ocsp_wrapper_enumerator_t;
+
+/**
+ * enumerate function for ocsp_wrapper_enumerator_t
+ */
+static bool ocsp_wrapper_enum_enumerate(ocsp_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 ocsp_wrapper_enumerator_t
+ */
+static void ocsp_wrapper_enum_destroy(ocsp_wrapper_enumerator_t *this)
+{
+ this->inner->destroy(this->inner);
+ free(this);
+}
+
+/**
+ * implementation of ocsp_wrapper_t.set.create_cert_enumerator
+ */
+static enumerator_t *ocsp_wrapper_create_enumerator(ocsp_wrapper_t *this,
+ certificate_type_t cert, key_type_t key,
+ identification_t *id, bool trusted)
+{
+ ocsp_wrapper_enumerator_t *enumerator;
+
+ if (trusted)
+ {
+ return NULL;
+ }
+
+ enumerator = malloc_thing(ocsp_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*)ocsp_wrapper_enum_enumerate;
+ enumerator->public.destroy = (void*)ocsp_wrapper_enum_destroy;
+ return &enumerator->public;
+}
+
+/**
+ * create credential_set wrapper around an OCSP response
+ */
+static ocsp_wrapper_t *ocsp_wrapper_create(ocsp_response_t *response)
+{
+ ocsp_wrapper_t *this = malloc_thing(ocsp_wrapper_t);
+
+ this->response = response;
+ this->set.create_private_enumerator = (void*)return_null;
+ this->set.create_cert_enumerator = (void*)ocsp_wrapper_create_enumerator;
+ this->set.create_shared_enumerator = (void*)return_null;
+ this->set.create_cdp_enumerator = (void*)return_null;
+
+ return this;
+}
+
+/**
+ * Do an OCSP request
+ */
+static ocsp_response_t *fetch_ocsp(private_credential_manager_t *this, char *url,
+ certificate_t *subject, certificate_t *issuer)
+{
+ certificate_t *request, *response, *issuer_cert;
+ chunk_t send, receive;
+ identification_t *responder;
+ auth_info_t *auth;
+ ocsp_wrapper_t *wrapper;
+
+ /* TODO: requestor name, signature */
+ request = lib->creds->create(lib->creds,
+ CRED_CERTIFICATE, CERT_X509_OCSP_REQUEST,
+ BUILD_CA_CERT, issuer->get_ref(issuer),
+ BUILD_CERT, subject->get_ref(subject), BUILD_END);
+ if (!request)
+ {
+ DBG1(DBG_CFG, "generating OCSP request failed");
+ return NULL;
+ }
+
+ send = request->get_encoding(request);
+ request->destroy(request);
+ 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);
+ if (!response)
+ {
+ DBG1(DBG_CFG, "parsing OCSP response from %s failed", url);
+ return NULL;
+ }
+
+ responder = response->get_issuer(response);
+ auth = auth_info_create();
+ wrapper = ocsp_wrapper_create((ocsp_response_t*)response);
+ this->sets->insert_first(this->sets, wrapper);
+ issuer_cert = get_trusted_cert(this, KEY_ANY, responder, auth, FALSE, FALSE);
+ this->sets->remove(this->sets, wrapper, NULL);
+ free(wrapper);
+ auth->destroy(auth);
+ if (!issuer_cert)
+ {
+ DBG1(DBG_CFG, "verifying OCSP response failed, no trusted "
+ "certificate found");
+ response->destroy(response);
+ return NULL;
+ }
+ if (!response->issued_by(response, issuer_cert, TRUE))
+ {
+ DBG1(DBG_CFG, "verifying OCSP response signature failed");
+ response->destroy(response);
+ issuer_cert->destroy(issuer_cert);
+ return NULL;
+ }
+ issuer_cert->destroy(issuer_cert);
+
+ /* TODO: cache response? */
+
+ return (ocsp_response_t*)response;
+}
+
+/**
+ * validate a x509 certificate using OCSP
+ */
+static cert_validation_t check_ocsp(private_credential_manager_t *this,
+ x509_t *subject, x509_t *issuer,
+ auth_info_t *auth)
+{
+ public_key_t *public;
+ enumerator_t *enumerator;
+ ocsp_response_t *response = NULL;
+ certificate_t *cert, *sub = (certificate_t*)subject;
+ cert_validation_t valid = VALIDATION_SKIPPED;
+ identification_t *keyid = NULL;
+ char *url;
+
+ cert = &issuer->interface;
+ public = cert->get_public_key(cert);
+ if (public)
+ {
+ keyid = public->get_id(public, ID_PUBKEY_INFO_SHA1);
+ }
+
+ /* find a OCSP response by Authority key identifier (cache) */
+ if (keyid)
+ {
+ time_t update, best_update = 0;
+
+ enumerator = create_cert_enumerator(this, CERT_X509_OCSP_RESPONSE,
+ KEY_ANY, keyid, TRUE);
+ while (enumerator->enumerate(enumerator, &cert))
+ { /* get newest valid response */
+ if (cert->has_subject(cert, sub->get_subject(sub)) &&
+ cert->get_validity(cert, NULL, &update, NULL) &&
+ update > best_update)
+ {
+ best_update = update;
+ DESTROY_IF(&response->certificate);
+ response = (ocsp_response_t*)cert;
+ valid = VALIDATION_FAILED;
+ }
+ }
+ enumerator->destroy(enumerator);
+ }
+ /* fallback to URL fetching from CDPs */
+ if (!response && keyid)
+ {
+ enumerator = create_cdp_enumerator(this, CERT_X509_OCSP_RESPONSE, keyid);
+ while (enumerator->enumerate(enumerator, &url))
+ {
+ valid = VALIDATION_FAILED;
+ response = fetch_ocsp(this, url, &subject->interface, &issuer->interface);
+ if (response)
+ {
+ break;
+ }
+ }
+ enumerator->destroy(enumerator);
+ }
+ /* fallback to URL fetching from subject certificate's URIs */
+ if (!response)
+ {
+ enumerator = subject->create_ocsp_uri_enumerator(subject);
+ while (enumerator->enumerate(enumerator, &url))
+ {
+ valid = VALIDATION_FAILED;
+ response = fetch_ocsp(this, url, &subject->interface, &issuer->interface);
+ if (response)
+ {
+ break;
+ }
+ }
+ enumerator->destroy(enumerator);
+ }
+ /* look for subject in response */
+ if (response)
+ {
+ time_t revocation, this_update, next_update;
+ crl_reason_t reason;
+
+ valid = response->get_status(response, subject, issuer, &revocation,
+ &reason, &this_update, &next_update);
+ switch (valid)
+ {
+ case VALIDATION_FAILED:
+ DBG1(DBG_CFG, "subject not found in OCSP response");
+ break;
+ case VALIDATION_REVOKED:
+ DBG1(DBG_CFG, "certificate %D revoked by OCSP at %T: %N",
+ cert->get_subject(cert), &revocation,
+ crl_reason_names, reason);
+ break;
+ case VALIDATION_GOOD:
+ break;
+ default:
+ break;
+ }
+ cert = (certificate_t*)response;
+ cert->destroy(cert);
+ }
+ DESTROY_IF(public);
+ if (auth)
+ {
+ auth->add_item(auth, AUTHZ_OCSP_VALIDATION, &valid);
+ }
+ return valid;
+}
+
+/**
+ * fetch a CRL from an URL
+ */
+static certificate_t* fetch_crl(private_credential_manager_t *this, char *url)
+{
+ certificate_t *crl_cert;
+ chunk_t chunk;
+
+ /* TODO: unlock the manager while fetching? */
+ 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_cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509_CRL,
+ BUILD_BLOB_ASN1_DER, chunk, BUILD_END);
+ if (!crl_cert)
+ {
+ DBG1(DBG_CFG, " crl fetched successfully but parsing failed");
+ return NULL;
+ }
+
+ /* verify the signature of the fetched crl */
+ {
+ bool ok;
+ identification_t *issuer = crl_cert->get_issuer(crl_cert);
+ auth_info_t *auth = auth_info_create();
+ certificate_t *issuer_cert = get_trusted_cert(this, KEY_ANY, issuer,
+ auth, FALSE, FALSE);
+ auth->destroy(auth);
+
+ if (!issuer_cert)
+ {
+ DBG1(DBG_CFG, " crl is untrusted: issuer certificate not found");
+ crl_cert->destroy(crl_cert);
+ return NULL;
+ }
+ ok = crl_cert->issued_by(crl_cert, issuer_cert, TRUE);
+ issuer_cert->destroy(issuer_cert);
+
+ DBG1(DBG_CFG, " crl is %strusted: %s signature",
+ ok ? "":"un", ok ? "good" : "bad");
+ if (!ok)
+ {
+ crl_cert->destroy(crl_cert);
+ return NULL;
+ }
+ }
+ return crl_cert;
+}
+
+/**
+ * validate a x509 certificate using CRL
+ */
+static cert_validation_t check_crl(private_credential_manager_t *this,
+ x509_t *subject, x509_t *issuer,
+ auth_info_t *auth)
+{
+ identification_t *keyid = NULL;
+ certificate_t *best_cert = NULL;
+ cert_validation_t valid = VALIDATION_SKIPPED;
+ bool stale = TRUE;
+
+ /* derive the authorityKeyIdentifier from the issuer's public key */
+ {
+ certificate_t *cert = &issuer->interface;
+ public_key_t *public = cert->get_public_key(cert);
+
+ if (public)
+ {
+ keyid = public->get_id(public, ID_PUBKEY_SHA1);
+ public->destroy(public);
+ }
+ }
+
+ /* find a local crl by authorityKeyIdentifier */
+ if (keyid)
+ {
+ enumerator_t *enumerator = create_cert_enumerator(this, CERT_X509_CRL,
+ KEY_ANY, keyid, TRUE);
+ certificate_t *cert;
+
+ while (enumerator->enumerate(enumerator, &cert))
+ {
+ crl_t *crl = (crl_t*)cert;
+ crl_t *best_crl = (crl_t*)best_cert;
+
+ /* select most recent crl */
+ if (best_cert == NULL || crl->is_newer(crl, best_crl))
+ {
+ DESTROY_IF(best_cert);
+ best_cert = cert->get_ref(cert);
+ }
+ }
+ enumerator->destroy(enumerator);
+ }
+
+ /* check the validity of the local crl if one was found */
+ if (best_cert)
+ {
+ stale = !best_cert->get_validity(best_cert, NULL, NULL, NULL);
+ DBG1(DBG_CFG, "locally-stored crl is %s", stale? "stale":"valid");
+ }
+ else
+ {
+ DBG1(DBG_CFG, "no locally-stored crl found");
+ }
+
+ /* fallback to fetching crls from cdps defined in ca info sections */
+ if (stale && keyid)
+ {
+ enumerator_t *enumerator = create_cdp_enumerator(this, CERT_X509_CRL,
+ keyid);
+ char *uri;
+
+ while (enumerator->enumerate(enumerator, &uri))
+ {
+ certificate_t *cert = fetch_crl(this, uri);
+
+ /* redefine default since we have at least one uri */
+ valid = VALIDATION_FAILED;
+
+ if (cert)
+ {
+ crl_t *crl = (crl_t*)cert;
+ crl_t *best_crl = (crl_t*)best_cert;
+
+ /* select most recent crl */
+ if (best_cert == NULL || crl->is_newer(crl, best_crl))
+ {
+ DESTROY_IF(best_cert);
+ best_cert = cert;
+ stale = !best_cert->get_validity(best_cert, NULL, NULL, NULL);
+ DBG1(DBG_CFG, "fetched crl is %s", stale? "stale":"valid");
+ if (!stale)
+ {
+ break;
+ }
+ }
+ else
+ {
+ cert->destroy(cert);
+ }
+ }
+ }
+ enumerator->destroy(enumerator);
+ }
+
+ /* fallback to fetching crls from cdps defined in the subject's certificate */
+ if (stale)
+ {
+ enumerator_t *enumerator = subject->create_crl_uri_enumerator(subject);
+ char *uri;
+
+ while (enumerator->enumerate(enumerator, &uri))
+ {
+ certificate_t *cert = fetch_crl(this, uri);
+
+ /* redefine default since we have at least one uri */
+ valid = VALIDATION_FAILED;
+
+ if (cert)
+ {
+ crl_t *crl = (crl_t*)cert;
+ crl_t *best_crl = (crl_t*)best_cert;
+
+ /* select most recent crl */
+ if (best_cert == NULL || crl->is_newer(crl, best_crl))
+ {
+ DESTROY_IF(best_cert);
+ best_cert = cert;
+ stale = !best_cert->get_validity(best_cert, NULL, NULL, NULL);
+ DBG1(DBG_CFG, "fetched crl is %s", stale? "stale":"valid");
+ if (!stale)
+ {
+ break;
+ }
+ }
+ else
+ {
+ cert->destroy(cert);
+ }
+ }
+ }
+ enumerator->destroy(enumerator);
+ }
+
+ /* if we have a crl, check the revocation status */
+ if (best_cert)
+ {
+ chunk_t serial;
+ time_t revocation;
+ crl_reason_t reason;
+ crl_t *crl = (crl_t*)best_cert;
+ enumerator_t *enumerator = crl->create_enumerator(crl);
+
+ /* redefine default */
+ valid = stale ? VALIDATION_UNKNOWN : VALIDATION_GOOD;
+
+ 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, crl_reason_names, reason);
+ valid = VALIDATION_REVOKED;
+ break;
+ }
+ }
+ enumerator->destroy(enumerator);
+ best_cert->destroy(best_cert);
+ }
+
+ if (auth)
+ {
+ auth->add_item(auth, AUTHZ_CRL_VALIDATION, &valid);
+ }
+ return valid;
+}
+
+/**
+ * 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_info_t *auth)
+{
+ time_t not_before, not_after;
+
+ if (!subject->get_validity(subject, NULL, &not_before, &not_after))
+ {
+ DBG1(DBG_CFG, "certificate invalid (valid from %T to %T)",
+ &not_before, &not_after);
+ return FALSE;
+ }
+ if (issuer && !subject->issued_by(subject, issuer, TRUE))
+ {
+ DBG1(DBG_CFG, "certificate %D not issued by %D",
+ subject->get_subject(subject), issuer->get_subject(issuer));
+ return FALSE;
+ }
+ if (issuer && issuer->get_type(issuer) == CERT_X509 &&
+ subject->get_type(subject) == CERT_X509)
+ {
+ if (ocsp)
+ {
+ switch (check_ocsp(this, (x509_t*)subject, (x509_t*)issuer, auth))
+ {
+ case VALIDATION_GOOD:
+ DBG1(DBG_CFG, "certificate %D validated by OCSP",
+ subject->get_subject(subject));
+ return TRUE;
+ case VALIDATION_REVOKED:
+ return FALSE;
+ case VALIDATION_SKIPPED:
+ DBG2(DBG_CFG, "OCSP check skipped, no OCSP URI found");
+ 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");
+ break;
+ case VALIDATION_REVOKED:
+ /* has already been logged */
+ break;
+ case VALIDATION_UNKNOWN:
+ DBG1(DBG_CFG, "certificate status is unknown");
+ break;
+ case VALIDATION_FAILED:
+ case VALIDATION_SKIPPED:
+ DBG1(DBG_CFG, "certificate status is not available");
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ return TRUE;
+}
+
+/**
+ * credential_set_t implementation around a auth_info_t
+ */
+typedef struct auth_wrapper_t {
+ credential_set_t set;
+ auth_info_t *auth;
+} auth_wrapper_t;
+
+/**
+ * enumerator for auth_wrapper_t.create_cert_enumerator()
+ */
+typedef struct {
+ enumerator_t public;
+ enumerator_t *inner;
+ certificate_type_t cert;
+ key_type_t key;
+ identification_t *id;
+} auth_wrapper_enumerator_t;
+
+/**
+ * enumerate function for auth_wrapper_enumerator_t
+ */
+static bool auth_wrapper_enum_enumerate(auth_wrapper_enumerator_t *this,
+ certificate_t **cert)
+{
+ auth_item_t type;
+ certificate_t *current;
+ public_key_t *public;
+
+ while (this->inner->enumerate(this->inner, &type, &current))
+ {
+ if (type != AUTHN_SUBJECT_CERT &&
+ type != AUTHN_IM_CERT)
+ {
+ 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 auth_wrapper_enumerator_t
+ */
+static void auth_wrapper_enum_destroy(auth_wrapper_enumerator_t *this)
+{
+ this->inner->destroy(this->inner);
+ free(this);
+}
+
+/**
+ * implementation of auth_wrapper_t.set.create_cert_enumerator
+ */
+static enumerator_t *auth_wrapper_create_enumerator(auth_wrapper_t *this,
+ certificate_type_t cert, key_type_t key,
+ identification_t *id, bool trusted)
+{
+ auth_wrapper_enumerator_t *enumerator;
+
+ if (trusted)
+ {
+ return NULL;
+ }
+
+ enumerator = malloc_thing(auth_wrapper_enumerator_t);
+ enumerator->cert = cert;
+ enumerator->key = key;
+ enumerator->id = id;
+ enumerator->inner = this->auth->create_item_enumerator(this->auth);
+ enumerator->public.enumerate = (void*)auth_wrapper_enum_enumerate;
+ enumerator->public.destroy = (void*)auth_wrapper_enum_destroy;
+ return &enumerator->public;
+}
+
+/**
+ * create credential_set wrapper around auth_info_t
+ */
+static auth_wrapper_t *auth_wrapper_create(auth_info_t *auth)
+{
+ auth_wrapper_t *this = malloc_thing(auth_wrapper_t);
+
+ this->auth = auth;
+ this->set.create_private_enumerator = (void*)return_null;
+ this->set.create_cert_enumerator = (void*)auth_wrapper_create_enumerator;
+ this->set.create_shared_enumerator = (void*)return_null;
+ this->set.create_cdp_enumerator = (void*)return_null;
+
+ return this;
+}
+
+/**
+ * Get a trusted certificate
+ */
+static certificate_t *get_trusted_cert(private_credential_manager_t *this,
+ key_type_t type, identification_t *id,
+ auth_info_t *auth, bool crl, bool ocsp)
+{
+ enumerator_t *enumerator;
+ auth_wrapper_t *wrapper;
+ certificate_t *subject, *issuer, *candidate;
+ public_key_t *public;
+ bool trusted = FALSE;
+ auth_info_t *auth1, *auth2;
+ u_int level = 0;
+
+ this->mutex->lock(this->mutex);
+ wrapper = auth_wrapper_create(auth);
+ this->sets->insert_first(this->sets, wrapper);
+
+ /* check if we have a trusted certificate for that peer */
+ auth1 = auth_info_create();
+ subject = get_cert(this, CERT_ANY, type, id, TRUE);
+ if (subject)
+ {
+ if (check_certificate(this, subject, NULL, crl, ocsp, auth1))
+ {
+ public = subject->get_public_key(subject);
+ if (public)
+ {
+ DBG2(DBG_CFG, "using trusted certificate %D",
+ subject->get_subject(subject));
+ this->sets->remove(this->sets, wrapper, NULL);
+ free(wrapper);
+ this->mutex->unlock(this->mutex);
+ auth->add_item(auth1, AUTHZ_SUBJECT_CERT, subject);
+ public->destroy(public);
+ auth->merge(auth, auth1);
+ auth1->destroy(auth1);
+ return subject;
+ }
+ }
+ subject->destroy(subject);
+ }
+ auth1->destroy(auth1);
+
+ /* check for an untrusted certificate */
+ auth1 = auth_info_create();
+ subject = get_cert(this, CERT_ANY, type, id, FALSE);
+ if (!subject)
+ {
+ DBG1(DBG_CFG, "no end entity certificate found for %D", id);
+ }
+ else
+ {
+ issuer = subject;
+ do
+ {
+ /* look for a trusted certificate */
+ auth2 = auth_info_create();
+ enumerator = create_cert_enumerator(this, issuer->get_type(issuer),
+ KEY_ANY, issuer->get_issuer(issuer), TRUE);
+ while (enumerator->enumerate(enumerator, &candidate))
+ {
+ if (check_certificate(this, issuer, candidate, crl, ocsp,
+ issuer == subject ? auth2 : NULL) &&
+ check_certificate(this, candidate, NULL, crl, ocsp, NULL))
+ {
+ DBG2(DBG_CFG, "using trusted root CA certificate %D",
+ candidate->get_subject(candidate));
+ issuer = candidate;
+ trusted = TRUE;
+ auth1->merge(auth1, auth2);
+ auth1->add_item(auth1, AUTHZ_CA_CERT, candidate);
+ break;
+ }
+ }
+ enumerator->destroy(enumerator);
+ auth2->destroy(auth2);
+ if (trusted)
+ {
+ break;
+ }
+
+ /* no trusted certificate found, look for an untrusted */
+ enumerator = create_cert_enumerator(this, issuer->get_type(issuer),
+ KEY_ANY, issuer->get_issuer(issuer), FALSE);
+ while (enumerator->enumerate(enumerator, &candidate))
+ {
+ auth2 = auth_info_create();
+ if (check_certificate(this, issuer, candidate, crl, ocsp,
+ issuer == subject ? auth2 : NULL))
+ {
+ if (issuer != subject)
+ {
+ DBG2(DBG_CFG, "using intermediate CA certificate %D",
+ candidate->get_subject(candidate));
+ auth1->add_item(auth1, AUTHZ_IM_CERT, candidate);
+ }
+ else
+ {
+ DBG2(DBG_CFG, "using end entity certificate %D",
+ candidate->get_subject(candidate));
+ }
+ issuer = candidate;
+ auth1->merge(auth1, auth2);
+ auth2->destroy(auth2);
+ /* check next level */
+ break;
+ }
+ auth2->destroy(auth2);
+ }
+ enumerator->destroy(enumerator);
+ }
+ while (++level < MAX_CA_LEVELS);
+
+ if (!trusted)
+ {
+ subject->destroy(subject);
+ subject = NULL;
+ }
+ }
+ this->sets->remove(this->sets, wrapper, NULL);
+ free(wrapper);
+ this->mutex->unlock(this->mutex);
+ if (subject)
+ {
+ auth->add_item(auth, AUTHZ_SUBJECT_CERT, subject);
+ auth->merge(auth, auth1);
+ auth1->destroy(auth1);
+ return subject;
+ }
+ auth1->destroy(auth1);
+ return NULL;
+}
+
+/**
+ * Implementation of credential_manager_t.get_public.
+ */
+static public_key_t *get_public(private_credential_manager_t *this,
+ key_type_t type, identification_t *id,
+ auth_info_t *auth)
+{
+ public_key_t *public;
+ certificate_t *cert;
+
+ cert = get_trusted_cert(this, type, id, auth, TRUE, TRUE);
+ if (cert)
+ {
+ public = cert->get_public_key(cert);
+ cert->destroy(cert);
+ return public;
+ }
+ return NULL;
+}
+
+/**
+ * Get the issuing certificate of a subject certificate
+ */
+static certificate_t *get_issuer_cert(private_credential_manager_t *this,
+ certificate_t *subject)
+{
+ enumerator_t *enumerator;
+ certificate_t *issuer = NULL, *candidate;
+
+ enumerator = create_cert_enumerator(this, subject->get_type(subject), KEY_ANY,
+ subject->get_issuer(subject), FALSE);
+ while (enumerator->enumerate(enumerator, &candidate))
+ {
+ if (subject->issued_by(subject, candidate, FALSE))
+ {
+ issuer = candidate->get_ref(candidate);
+ break;
+ }
+ }
+ enumerator->destroy(enumerator);
+ return issuer;
+}
+
+/**
+ * Check if a certificate's keyid is contained in the auth helper
+ */
+static bool auth_contains_cacert(auth_info_t *auth, certificate_t *cert)
+{
+ enumerator_t *enumerator;
+ identification_t *value;
+ auth_item_t type;
+ bool found = FALSE;
+
+ enumerator = auth->create_item_enumerator(auth);
+ while (enumerator->enumerate(enumerator, &type, &value))
+ {
+ if (type == AUTHN_CA_CERT && cert->equals(cert, (certificate_t*)value))
+ {
+ found = TRUE;
+ break;
+ }
+ }
+ enumerator->destroy(enumerator);
+ return found;
+}
+
+/**
+ * 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_info_t *auth)
+{
+ enumerator_t *enumerator;
+ private_key_t *private;
+ public_key_t *public;
+ certificate_t *subject, *issuer, *candidate;
+ auth_info_t *cand_auth;
+ identification_t* keyid;
+ bool match = FALSE;
+
+ /* check if this is a lookup by key ID, and do it if so */
+ if (id)
+ {
+ switch (id->get_type(id))
+ {
+ case ID_PUBKEY_SHA1:
+ case ID_PUBKEY_INFO_SHA1:
+ return get_private_by_keyid(this, type, id);
+ default:
+ break;
+ }
+ }
+
+ this->mutex->lock(this->mutex);
+ /* Check if peer has included its trust anchors.
+ * If not we fall back to our trust anchors */
+ if (!auth->get_item(auth, AUTHN_CA_CERT, (void**)&issuer))
+ {
+ enumerator = create_cert_enumerator(this, CERT_ANY, type, NULL, TRUE);
+ while (enumerator->enumerate(enumerator, &issuer))
+ {
+ auth->add_item(auth, AUTHN_CA_CERT, issuer);
+ }
+ enumerator->destroy(enumerator);
+ }
+ DBG2(DBG_CFG, "finding private key with certificate the peer trusts");
+
+ /* get all available end entity certificates for us... */
+ enumerator = create_cert_enumerator(this, CERT_ANY, type, id, FALSE);
+ while (enumerator->enumerate(enumerator, &subject))
+ { /* ... check for public ... */
+ public = subject->get_public_key(subject);
+ if (public)
+ { /* ... and private keys for that certificate, ... */
+ /* TODO: check other keyid types? */
+ keyid = public->get_id(public, ID_PUBKEY_INFO_SHA1);
+ if (keyid)
+ {
+ private = get_private_by_keyid(this, type, keyid);
+ if (private)
+ {
+ u_int level = 0;
+ bool bad_path = FALSE;
+ issuer = NULL;
+
+ match = TRUE;
+ DBG2(DBG_CFG, " checking end entity cert %D",
+ subject->get_subject(subject));
+ cand_auth = auth_info_create();
+ /* .. check for a trust-path up to a peer-trusted CA */
+ candidate = subject->get_ref(subject);
+ while (!auth_contains_cacert(auth, candidate))
+ {
+ cand_auth->add_item(cand_auth, subject == candidate ?
+ AUTHZ_SUBJECT_CERT : AUTHZ_IM_CERT, candidate);
+ issuer = get_issuer_cert(this, candidate);
+ /* check if we have an issuing certificate */
+ if (!issuer)
+ {
+ DBG2(DBG_CFG, " no issuer, checking next cert");
+ bad_path = TRUE;
+ break;
+ }
+ /* and it is not self-issued */
+ if (issuer->equals(issuer, candidate) ||
+ level > MAX_CA_LEVELS)
+ {
+ issuer->destroy(issuer);
+ issuer = NULL;
+ bad_path = TRUE;
+ DBG2(DBG_CFG, " cert is self-signed, skipped");
+ break;
+ }
+ DBG2(DBG_CFG, " checking issuer cert %D",
+ issuer->get_subject(issuer));
+ candidate->destroy(candidate);
+ candidate = issuer;
+ level++;
+ }
+ if (bad_path)
+ { /* no issuer cert found peer trusts, try another path */
+ cand_auth->destroy(cand_auth);
+ private->destroy(private);
+ public->destroy(public);
+ continue;
+ }
+ if (issuer)
+ {
+ DBG2(DBG_CFG, " peer trusts issuer %D",
+ issuer->get_subject(issuer));
+ }
+ else
+ {
+ candidate->destroy(candidate);
+ }
+ auth->merge(auth, cand_auth);
+ cand_auth->destroy(cand_auth);
+ DESTROY_IF(issuer);
+ public->destroy(public);
+ enumerator->destroy(enumerator);
+ this->mutex->unlock(this->mutex);
+ return private;
+ }
+ }
+ public->destroy(public);
+ }
+ }
+ this->mutex->unlock(this->mutex);
+ if (match)
+ {
+ DBG1(DBG_CFG, "found a private key/cert for %D, but none which the "
+ "peer trusts", id);
+ }
+ else
+ {
+ DBG1(DBG_CFG, "no private key found for %D", id);
+ }
+ /* no trusted path found, unable to sign */
+ return NULL;
+}
+
+/**
+ * Implementation of credential_manager_t.add_set.
+ */
+static void add_set(private_credential_manager_t *this,
+ credential_set_t *set)
+{
+ this->mutex->lock(this->mutex);
+ this->sets->insert_last(this->sets, set);
+ this->mutex->unlock(this->mutex);
+}
+/**
+ * Implementation of credential_manager_t.remove_set.
+ */
+static void remove_set(private_credential_manager_t *this, credential_set_t *set)
+{
+ this->mutex->lock(this->mutex);
+ this->sets->remove(this->sets, set, NULL);
+ this->mutex->unlock(this->mutex);
+}
+
+/**
+ * Implementation of credential_manager_t.destroy
+ */
+static void destroy(private_credential_manager_t *this)
+{
+ this->sets->destroy(this->sets);
+ this->mutex->destroy(this->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*, credential_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_info_t*))get_private;
+ this->public.get_public = (public_key_t*(*)(credential_manager_t*, key_type_t type, identification_t *, auth_info_t*))get_public;
+ 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->mutex = mutex_create(MUTEX_RECURSIVE);
+
+ return &this->public;
+}
+
diff --git a/src/charon/credentials/credential_manager.h b/src/charon/credentials/credential_manager.h
new file mode 100644
index 000000000..816b9028e
--- /dev/null
+++ b/src/charon/credentials/credential_manager.h
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2007-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.
+ *
+ * $Id$
+ */
+
+/**
+ * @defgroup credential_manager credential_manager
+ * @{ @ingroup ccredentials
+ */
+
+#ifndef CREDENTIAL_MANAGER_H_
+#define CREDENTIAL_MANAGER_H_
+
+#include <utils/identification.h>
+#include <utils/enumerator.h>
+#include <credentials/auth_info.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
+ *
+ * Synchronization is done completely in the manager, so the sets don't have
+ * to worry about it. The locking mechanism is reentrant save, so sets can
+ * call the manager.
+ */
+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,
+ credential_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
+ * @param auth auth_info helper
+ * @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_info helper, 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_info_t *auth);
+ /**
+ * Get a public key to verify a signature.
+ *
+ * The get_public() method gets a trusted public key to verify a signature
+ * of id. The auth parameter contains additional authentication infos,
+ * e.g. peer and intermediate certificates.
+ *
+ * @param type type of key to get
+ * @param id identification the key belongs to
+ * @param auth auth_info helper, including certificates to verify key
+ * @return public_key_t, NULL if none found
+ */
+ public_key_t* (*get_public)(credential_manager_t *this, key_type_t type,
+ identification_t *id, auth_info_t *auth);
+
+ /**
+ * 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/charon/credentials/credential_set.h b/src/charon/credentials/credential_set.h
new file mode 100644
index 000000000..a4e891a84
--- /dev/null
+++ b/src/charon/credentials/credential_set.h
@@ -0,0 +1,93 @@
+/*
+ * 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.
+ *
+ * $Id$
+ */
+
+/**
+ * @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.
+ */
+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);
+};
+
+#endif /* CREDENTIAL_SET_H_ @} */