diff options
Diffstat (limited to 'src/charon/credentials')
-rw-r--r-- | src/charon/credentials/auth_info.c | 356 | ||||
-rw-r--r-- | src/charon/credentials/auth_info.h | 158 | ||||
-rw-r--r-- | src/charon/credentials/credential_manager.c | 1385 | ||||
-rw-r--r-- | src/charon/credentials/credential_manager.h | 187 | ||||
-rw-r--r-- | src/charon/credentials/credential_set.h | 93 |
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, ¤t_type, ¤t_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, ¤t)) + { + /* 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, ¤t, &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, ¤t)) + { + 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, ¬_before, ¬_after)) + { + DBG1(DBG_CFG, "certificate invalid (valid from %T to %T)", + ¬_before, ¬_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, ¤t)) + { + 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_ @} */ |