diff options
Diffstat (limited to 'src/charon/credentials')
-rw-r--r-- | src/charon/credentials/auth_info.c | 571 | ||||
-rw-r--r-- | src/charon/credentials/auth_info.h | 185 | ||||
-rw-r--r-- | src/charon/credentials/credential_manager.c | 1540 | ||||
-rw-r--r-- | src/charon/credentials/credential_manager.h | 207 | ||||
-rw-r--r-- | src/charon/credentials/credential_set.h | 103 | ||||
-rw-r--r-- | src/charon/credentials/sets/auth_info_wrapper.c | 215 | ||||
-rw-r--r-- | src/charon/credentials/sets/auth_info_wrapper.h | 55 | ||||
-rw-r--r-- | src/charon/credentials/sets/cert_cache.c | 332 | ||||
-rw-r--r-- | src/charon/credentials/sets/cert_cache.h | 73 | ||||
-rw-r--r-- | src/charon/credentials/sets/ocsp_response_wrapper.c | 149 | ||||
-rw-r--r-- | src/charon/credentials/sets/ocsp_response_wrapper.h | 55 |
11 files changed, 3485 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..cd748bc97 --- /dev/null +++ b/src/charon/credentials/auth_info.c @@ -0,0 +1,571 @@ +/* + * Copyright (C) 2008 Tobias Brunner + * 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: auth_info.c 3838 2008-04-18 11:24:45Z tobias $ + */ + + +#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_CA_CERT_KEYID", + "AUTHN_CA_CERT_NAME", + "AUTHN_IM_CERT", + "AUTHN_SUBJECT_CERT", + "AUTHN_IM_HASH_URL", + "AUTHN_SUBJECT_HASH_URL", + "AUTHZ_PUBKEY", + "AUTHZ_PSK", + "AUTHZ_EAP", + "AUTHZ_CA_CERT", + "AUTHZ_CA_CERT_NAME", + "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; +}; + +/** + * enumerator for auth_info_wrapper_t.create_cert_enumerator() + */ +typedef struct { + /** implements enumerator_t */ + enumerator_t public; + /** inner enumerator from linked_list_t */ + enumerator_t *inner; + /** the current item */ + item_t *item; +} item_enumerator_t; + +/** + * enumerate function for item_enumerator_t + */ +static bool enumerate(item_enumerator_t *this, auth_item_t *type, void **value) +{ + if (this->inner->enumerate(this->inner, &this->item)) + { + *type = this->item->type; + *value = this->item->value; + return TRUE; + } + return FALSE; +} + +/** + * destroy function for item_enumerator_t + */ +static void item_enumerator_destroy(item_enumerator_t *this) +{ + this->inner->destroy(this->inner); + free(this); +} + +/** + * Implementation of auth_info_t.create_item_enumerator. + */ +static enumerator_t* create_item_enumerator(private_auth_info_t *this) +{ + item_enumerator_t *enumerator; + + enumerator = malloc_thing(item_enumerator_t); + enumerator->item = NULL; + enumerator->inner = this->items->create_enumerator(this->items); + enumerator->public.enumerate = (void*)enumerate; + enumerator->public.destroy = (void*)item_enumerator_destroy; + return &enumerator->public; +} + +static void destroy_item_value(item_t *item); + +/** + * Implementation of auth_info_t.replace_item. + */ +static void replace_item(item_enumerator_t *enumerator, auth_item_t type, void *value) +{ + destroy_item_value(enumerator->item); + enumerator->item->type = type; + enumerator->item->value = value; +} + +/** + * 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_IM_HASH_URL: + case AUTHN_SUBJECT_HASH_URL: + { + item->value = strdup(value); + 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 AUTHN_CA_CERT_KEYID: + case AUTHN_CA_CERT_NAME: + case AUTHZ_CA_CERT_NAME: + 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 t1, t2; + void *value; + + enumerator = constraints->create_item_enumerator(constraints); + while (enumerator->enumerate(enumerator, &t1, &value)) + { + switch (t1) + { + case AUTHN_CA_CERT_KEYID: + case AUTHN_CA_CERT: + case AUTHN_CA_CERT_NAME: + case AUTHN_IM_CERT: + case AUTHN_SUBJECT_CERT: + case AUTHN_IM_HASH_URL: + case AUTHN_SUBJECT_HASH_URL: + { /* 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, t1, (void**)&valid) && + t1 == 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, t1, + 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, t1, + cert_validation_names, *valid, + cert_validation_names, *(cert_validation_t*)value); + success = FALSE; + break; + } + break; + } + case AUTHZ_CA_CERT: + { + enumerator_t *enumerator; + certificate_t *c1, *c2; + + c1 = (certificate_t*)value; + + success = FALSE; + enumerator = create_item_enumerator(this); + while (enumerator->enumerate(enumerator, &t2, &c2)) + { + if ((t2 == AUTHZ_CA_CERT || t2 == AUTHZ_IM_CERT) && + c1->equals(c1, c2)) + { + success = TRUE; + } + } + enumerator->destroy(enumerator); + if (!success) + { + DBG1(DBG_CFG, "constraint check failed: peer not " + "authenticated by CA '%D'.", c1->get_subject(c1)); + } + break; + } + case AUTHZ_CA_CERT_NAME: + { + enumerator_t *enumerator; + certificate_t *cert; + identification_t *id; + + id = (identification_t*)value; + success = FALSE; + enumerator = create_item_enumerator(this); + while (enumerator->enumerate(enumerator, &t2, &cert)) + { + if ((t2 == AUTHZ_CA_CERT || t2 == AUTHZ_IM_CERT) && + cert->has_subject(cert, id)) + { + success = TRUE; + } + } + enumerator->destroy(enumerator); + if (!success) + { + DBG1(DBG_CFG, "constraint check failed: peer not " + "authenticated by CA '%D'.", id); + } + 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, t1); + 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.equals. + */ +static bool equals(private_auth_info_t *this, private_auth_info_t *other) +{ + enumerator_t *e1, *e2; + item_t *i1, *i2; + bool equal = TRUE, found; + + e1 = this->items->create_enumerator(this->items); + while (e1->enumerate(e1, &i1)) + { + found = FALSE; + e2 = other->items->create_enumerator(other->items); + while (e2->enumerate(e2, &i2)) + { + if (i1->type == i2->type) + { + switch (i1->type) + { + case AUTHZ_CRL_VALIDATION: + case AUTHZ_OCSP_VALIDATION: + { + cert_validation_t c1, c2; + + c1 = *(cert_validation_t*)i1->value; + c2 = *(cert_validation_t*)i2->value; + + if (c1 == c2) + { + found = TRUE; + break; + } + continue; + } + case AUTHN_IM_HASH_URL: + case AUTHN_SUBJECT_HASH_URL: + { + if (streq(i1->value, i2->value)) + { + found = TRUE; + break; + } + continue; + } + 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 *c1, *c2; + + c1 = (certificate_t*)i1->value; + c2 = (certificate_t*)i2->value; + + if (c1->equals(c1, c2)) + { + found = TRUE; + break; + } + continue; + } + case AUTHN_CA_CERT_KEYID: + case AUTHN_CA_CERT_NAME: + case AUTHZ_CA_CERT_NAME: + { + identification_t *c1, *c2; + + c1 = (identification_t*)i1->value; + c2 = (identification_t*)i2->value; + + if (c1->equals(c1, c2)) + { + found = TRUE; + break; + } + continue; + } + case AUTHZ_PUBKEY: + case AUTHZ_PSK: + case AUTHZ_EAP: + case AUTHZ_AC_GROUP: + /* TODO: implement value comparison */ + break; + } + break; + } + } + e2->destroy(e2); + if (!found) + { + equal = FALSE; + break; + } + } + e1->destroy(e1); + return equal; +} + +/** + * Destroy the value associated with an item + */ +static void destroy_item_value(item_t *item) +{ + 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 AUTHN_IM_HASH_URL: + case AUTHN_SUBJECT_HASH_URL: + case AUTHZ_CRL_VALIDATION: + case AUTHZ_OCSP_VALIDATION: + case AUTHZ_EAP: + { + free(item->value); + break; + } + case AUTHN_CA_CERT_KEYID: + case AUTHN_CA_CERT_NAME: + case AUTHZ_CA_CERT_NAME: + case AUTHZ_AC_GROUP: + { + identification_t *id = (identification_t*)item->value; + id->destroy(id); + break; + } + } +} + +/** + * 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) + { + destroy_item_value(item); + 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.replace_item = (void(*)(enumerator_t*,auth_item_t,void*))replace_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.equals = (bool(*)(auth_info_t*, auth_info_t *other))equals; + 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..5fe2919f8 --- /dev/null +++ b/src/charon/credentials/auth_info.h @@ -0,0 +1,185 @@ +/* + * Copyright (C) 2008 Tobias Brunner + * 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, + /** Keyid of a CA certificate to use, value is identification_t* */ + AUTHN_CA_CERT_KEYID, + /** subject DN of a CA certificate to use, value is identification_t* */ + AUTHN_CA_CERT_NAME, + /** intermediate certificate, value is certificate_t* */ + AUTHN_IM_CERT, + /** certificate for trustchain verification, value is certificate_t* */ + AUTHN_SUBJECT_CERT, + /** intermediate certificate supplied as hash and url */ + AUTHN_IM_HASH_URL, + /** end-entity certificate supplied as hash and url */ + AUTHN_SUBJECT_HASH_URL, + + /* + * 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, + /** subject DN of a certificate authority, value is identification_t* */ + AUTHZ_CA_CERT_NAME, + /** 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); + + /** + * Replace an item. + * + * @param type new auth_info type + * @param value pointer to the new value + */ + void (*replace_item)(enumerator_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); + + /** + * Check two auth_infos for equality. + * + * @param other other item to compaire against this + * @return TRUE if auth infos identical + */ + bool (*equals)(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..251559141 --- /dev/null +++ b/src/charon/credentials/credential_manager.c @@ -0,0 +1,1540 @@ +/* + * 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: credential_manager.c 3953 2008-05-14 06:49:31Z martin $ + */ + +/* some clibs need it for rwlocks */ +#define _GNU_SOURCE +#include <pthread.h> + +#include "credential_manager.h" + +#include <daemon.h> +#include <utils/linked_list.h> +#include <credentials/sets/cert_cache.h> +#include <credentials/sets/auth_info_wrapper.h> +#include <credentials/sets/ocsp_response_wrapper.h> +#include <credentials/certificates/x509.h> +#include <credentials/certificates/crl.h> +#include <credentials/certificates/ocsp_request.h> +#include <credentials/certificates/ocsp_response.h> + +#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; + + /** + * thread local set of credentials, linked_list_t with credential_set_t's + */ + pthread_key_t local_sets; + + /** + * trust relationship and certificate cache + */ + cert_cache_t *cache; + + /** + * read-write lock to sets list + */ + pthread_rwlock_t lock; +}; + +/** data to pass to create_private_enumerator */ +typedef struct { + private_credential_manager_t *this; + key_type_t type; + identification_t* keyid; +} private_data_t; + +/** data to pass to create_cert_enumerator */ +typedef struct { + private_credential_manager_t *this; + certificate_type_t cert; + key_type_t key; + identification_t *id; + bool trusted; +} cert_data_t; + +/** data to pass to create_cdp_enumerator */ +typedef struct { + private_credential_manager_t *this; + certificate_type_t type; + identification_t *id; +} cdp_data_t; + +/** data to pass to create_shared_enumerator */ +typedef struct { + private_credential_manager_t *this; + shared_key_type_t type; + identification_t *me; + identification_t *other; +} shared_data_t; + +/** enumerator over local and global sets */ +typedef struct { + /** implements enumerator_t */ + enumerator_t public; + /** enumerator over global sets */ + enumerator_t *global; + /** enumerator over local sets */ + enumerator_t *local; +} sets_enumerator_t; + +/** + * destroy a sets_enumerator_t + */ +static void sets_enumerator_destroy(sets_enumerator_t *this) +{ + DESTROY_IF(this->global); + DESTROY_IF(this->local); + free(this); +} + +/** + * sets_enumerator_t.enumerate + */ +static bool sets_enumerator_enumerate(sets_enumerator_t *this, + credential_set_t **set) +{ + if (this->global) + { + if (this->global->enumerate(this->global, set)) + { + return TRUE; + } + /* end of global sets, look for local */ + this->global->destroy(this->global); + this->global = NULL; + } + if (this->local) + { + return this->local->enumerate(this->local, set); + } + return FALSE; +} + +/** + * create an enumerator over both, global and local sets + */ +static enumerator_t *create_sets_enumerator(private_credential_manager_t *this) +{ + linked_list_t *local; + sets_enumerator_t *enumerator = malloc_thing(sets_enumerator_t); + + enumerator->public.enumerate = (void*)sets_enumerator_enumerate; + enumerator->public.destroy = (void*)sets_enumerator_destroy; + enumerator->global = this->sets->create_enumerator(this->sets); + enumerator->local = NULL; + local = pthread_getspecific(this->local_sets); + if (local) + { + enumerator->local = local->create_enumerator(local); + } + return &enumerator->public; +} + +/** + * cleanup function for cert data + */ +static void destroy_cert_data(cert_data_t *data) +{ + pthread_rwlock_unlock(&data->this->lock); + free(data); +} + +/** + * enumerator constructor for certificates + */ +static enumerator_t *create_cert(credential_set_t *set, cert_data_t *data) +{ + return set->create_cert_enumerator(set, data->cert, data->key, + data->id, data->trusted); +} + +/** + * Implementation of credential_manager_t.create_cert_enumerator. + */ +static enumerator_t *create_cert_enumerator(private_credential_manager_t *this, + certificate_type_t certificate, key_type_t key, + identification_t *id, bool trusted) +{ + cert_data_t *data = malloc_thing(cert_data_t); + data->this = this; + data->cert = certificate; + data->key = key; + data->id = id; + data->trusted = trusted; + + pthread_rwlock_rdlock(&this->lock); + return enumerator_create_nested(create_sets_enumerator(this), + (void*)create_cert, data, + (void*)destroy_cert_data); +} + +/** + * Implementation of credential_manager_t.get_cert. + */ +static certificate_t *get_cert(private_credential_manager_t *this, + certificate_type_t cert, key_type_t key, + identification_t *id, bool trusted) +{ + certificate_t *current, *found = NULL; + enumerator_t *enumerator; + + enumerator = create_cert_enumerator(this, cert, key, id, trusted); + if (enumerator->enumerate(enumerator, ¤t)) + { + /* TODO: best match? order by keyid, subject, sualtname */ + found = current->get_ref(current); + } + enumerator->destroy(enumerator); + return found; +} + + +/** + * cleanup function for cdp data + */ +static void destroy_cdp_data(cdp_data_t *data) +{ + pthread_rwlock_unlock(&data->this->lock); + free(data); +} + +/** + * enumerator constructor for CDPs + */ +static enumerator_t *create_cdp(credential_set_t *set, cdp_data_t *data) +{ + return set->create_cdp_enumerator(set, data->type, data->id); +} +/** + * Implementation of credential_manager_t.create_cdp_enumerator. + */ +static enumerator_t * create_cdp_enumerator(private_credential_manager_t *this, + certificate_type_t type, identification_t *id) +{ + cdp_data_t *data = malloc_thing(cdp_data_t); + data->this = this; + data->type = type; + data->id = id; + + pthread_rwlock_rdlock(&this->lock); + return enumerator_create_nested(create_sets_enumerator(this), + (void*)create_cdp, data, + (void*)destroy_cdp_data); +} + +/** + * cleanup function for private data + */ +static void destroy_private_data(private_data_t *data) +{ + pthread_rwlock_unlock(&data->this->lock); + free(data); +} + +/** + * enumerator constructor for private keys + */ +static enumerator_t *create_private(credential_set_t *set, private_data_t *data) +{ + return set->create_private_enumerator(set, data->type, data->keyid); +} + +/** + * Implementation of credential_manager_t.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; + pthread_rwlock_rdlock(&this->lock); + return enumerator_create_nested(create_sets_enumerator(this), + (void*)create_private, data, + (void*)destroy_private_data); +} + +/** + * Implementation of credential_manager_t.get_private_by_keyid. + */ +static private_key_t *get_private_by_keyid(private_credential_manager_t *this, + key_type_t key, identification_t *keyid) +{ + private_key_t *found = NULL; + enumerator_t *enumerator; + + enumerator = create_private_enumerator(this, key, keyid); + if (enumerator->enumerate(enumerator, &found)) + { + found->get_ref(found); + } + enumerator->destroy(enumerator); + return found; +} + +/** + * cleanup function for shared data + */ +static void destroy_shared_data(shared_data_t *data) +{ + pthread_rwlock_unlock(&data->this->lock); + free(data); +} + +/** + * enumerator constructor for shared keys + */ +static enumerator_t *create_shared(credential_set_t *set, shared_data_t *data) +{ + return set->create_shared_enumerator(set, data->type, data->me, data->other); +} + +/** + * Implementation of credential_manager_t.create_shared_enumerator. + */ +static enumerator_t *create_shared_enumerator(private_credential_manager_t *this, + shared_key_type_t type, + identification_t *me, identification_t *other) +{ + shared_data_t *data = malloc_thing(shared_data_t); + data->this = this; + data->type = type; + data->me = me; + data->other = other; + + pthread_rwlock_rdlock(&this->lock); + return enumerator_create_nested(create_sets_enumerator(this), + (void*)create_shared, data, + (void*)destroy_shared_data); +} + +/** + * Implementation of credential_manager_t.get_shared. + */ +static shared_key_t *get_shared(private_credential_manager_t *this, + shared_key_type_t type, identification_t *me, + identification_t *other) +{ + shared_key_t *current, *found = NULL; + id_match_t *best_me = ID_MATCH_NONE, *best_other = ID_MATCH_NONE; + id_match_t *match_me, *match_other; + enumerator_t *enumerator; + + enumerator = create_shared_enumerator(this, type, me, other); + while (enumerator->enumerate(enumerator, ¤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; +} + +/** + * add a credential set to the thread local list + */ +static void add_local_set(private_credential_manager_t *this, + credential_set_t *set) +{ + linked_list_t *sets; + + sets = pthread_getspecific(this->local_sets); + if (!sets) + { /* first invocation */ + sets = linked_list_create(); + pthread_setspecific(this->local_sets, sets); + } + sets->insert_last(sets, set); +} + +/** + * remove a credential set from the thread local list + */ +static void remove_local_set(private_credential_manager_t *this, + credential_set_t *set) +{ + linked_list_t *sets; + + sets = pthread_getspecific(this->local_sets); + sets->remove(sets, set, NULL); +} + +/** + * Implementation of credential_manager_t.cache_cert. + */ +static void cache_cert(private_credential_manager_t *this, certificate_t *cert) +{ + credential_set_t *set; + enumerator_t *enumerator; + + pthread_rwlock_rdlock(&this->lock); + enumerator = this->sets->create_enumerator(this->sets); + while (enumerator->enumerate(enumerator, &set)) + { + set->cache_cert(set, cert); + } + enumerator->destroy(enumerator); + pthread_rwlock_unlock(&this->lock); +} + +/** + * forward declaration + */ +static enumerator_t *create_trusted_enumerator(private_credential_manager_t *this, + key_type_t type, identification_t *id, bool crl, bool ocsp); + +/** + * Do an OCSP request + */ +static certificate_t *fetch_ocsp(private_credential_manager_t *this, char *url, + certificate_t *subject, certificate_t *issuer) +{ + certificate_t *request, *response; + chunk_t send, receive; + + /* TODO: requestor name, signature */ + request = lib->creds->create(lib->creds, + CRED_CERTIFICATE, CERT_X509_OCSP_REQUEST, + BUILD_CA_CERT, issuer->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); + + DBG1(DBG_CFG, " requesting ocsp status from '%s' ...", url); + if (lib->fetcher->fetch(lib->fetcher, url, &receive, + FETCH_REQUEST_DATA, send, + FETCH_REQUEST_TYPE, "application/ocsp-request", + FETCH_END) != SUCCESS) + { + DBG1(DBG_CFG, "ocsp request to %s failed", url); + chunk_free(&send); + return NULL; + } + chunk_free(&send); + + response = lib->creds->create(lib->creds, + CRED_CERTIFICATE, CERT_X509_OCSP_RESPONSE, + BUILD_BLOB_ASN1_DER, receive, BUILD_END); + if (!response) + { + DBG1(DBG_CFG, "parsing ocsp response failed"); + return NULL; + } + return response; +} + +/** + * check the signature of an OCSP response + */ +static bool verify_ocsp(private_credential_manager_t *this, + ocsp_response_t *response) +{ + certificate_t *issuer, *subject; + identification_t *responder; + ocsp_response_wrapper_t *wrapper; + enumerator_t *enumerator; + bool verified = FALSE; + + wrapper = ocsp_response_wrapper_create((ocsp_response_t*)response); + add_local_set(this, &wrapper->set); + + subject = &response->certificate; + responder = subject->get_issuer(subject); + enumerator = create_trusted_enumerator(this, KEY_ANY, responder, FALSE, FALSE); + while (enumerator->enumerate(enumerator, &issuer, NULL)) + { + if (this->cache->issued_by(this->cache, subject, issuer)) + { + DBG1(DBG_CFG, " ocsp response correctly signed by \"%D\"", + issuer->get_subject(issuer)); + verified = TRUE; + break; + } + } + enumerator->destroy(enumerator); + + remove_local_set(this, &wrapper->set); + wrapper->destroy(wrapper); + return verified; +} + +/** + * Get the better of two OCSP responses, and check for usable OCSP info + */ +static certificate_t *get_better_ocsp(private_credential_manager_t *this, + certificate_t *cand, certificate_t *best, + x509_t *subject, x509_t *issuer, + cert_validation_t *valid, bool cache) +{ + ocsp_response_t *response; + time_t revocation, this_update, next_update, valid_until; + crl_reason_t reason; + bool revoked = FALSE; + + response = (ocsp_response_t*)cand; + + /* check ocsp signature */ + if (!verify_ocsp(this, response)) + { + DBG1(DBG_CFG, "ocsp response verification failed"); + cand->destroy(cand); + return best; + } + /* check if response contains our certificate */ + switch (response->get_status(response, subject, issuer, &revocation, &reason, + &this_update, &next_update)) + { + case VALIDATION_REVOKED: + /* subject has been revoked by a valid OCSP response */ + DBG1(DBG_CFG, "certificate was revoked on %T, reason: %N", + &revocation, crl_reason_names, reason); + revoked = TRUE; + break; + case VALIDATION_GOOD: + /* results in either good or stale */ + break; + default: + case VALIDATION_FAILED: + /* candidate unusable, does not contain our cert */ + DBG1(DBG_CFG, " ocsp response contains no status on our certificate"); + cand->destroy(cand); + return best; + } + + /* select the better of the two responses */ + if (best == NULL || cand->is_newer(cand, best)) + { + DESTROY_IF(best); + best = cand; + if (best->get_validity(best, NULL, NULL, &valid_until)) + { + DBG1(DBG_CFG, " ocsp response is valid: until %#T", + &valid_until, FALSE); + *valid = VALIDATION_GOOD; + if (cache) + { /* cache non-stale only, stale certs get refetched */ + cache_cert(this, best); + } + } + else + { + DBG1(DBG_CFG, " ocsp response is stale: since %#T", + &valid_until, FALSE); + *valid = VALIDATION_STALE; + } + } + else + { + *valid = VALIDATION_STALE; + cand->destroy(cand); + } + if (revoked) + { /* revoked always counts, even if stale */ + *valid = VALIDATION_REVOKED; + } + return best; +} + +/** + * validate a x509 certificate using OCSP + */ +static cert_validation_t check_ocsp(private_credential_manager_t *this, + x509_t *subject, x509_t *issuer, + auth_info_t *auth) +{ + enumerator_t *enumerator; + cert_validation_t valid = VALIDATION_SKIPPED; + certificate_t *best = NULL, *current; + identification_t *keyid = NULL; + public_key_t *public; + char *uri = NULL; + + /** lookup cache for valid OCSP responses */ + enumerator = create_cert_enumerator(this, CERT_X509_OCSP_RESPONSE, + KEY_ANY, NULL, FALSE); + while (enumerator->enumerate(enumerator, ¤t)) + { + current->get_ref(current); + best = get_better_ocsp(this, current, best, subject, issuer, + &valid, FALSE); + if (best && valid != VALIDATION_STALE) + { + DBG1(DBG_CFG, " using cached ocsp response"); + break; + } + } + enumerator->destroy(enumerator); + + /* derive the authorityKeyIdentifier from the issuer's public key */ + current = &issuer->interface; + public = current->get_public_key(current); + if (public) + { + keyid = public->get_id(public, ID_PUBKEY_SHA1); + } + /** fetch from configured OCSP responder URLs */ + if (keyid && valid != VALIDATION_GOOD && valid != VALIDATION_REVOKED) + { + enumerator = create_cdp_enumerator(this, CERT_X509_OCSP_RESPONSE, keyid); + while (enumerator->enumerate(enumerator, &uri)) + { + current = fetch_ocsp(this, uri, &subject->interface, + &issuer->interface); + if (current) + { + best = get_better_ocsp(this, current, best, subject, issuer, + &valid, TRUE); + if (best && valid != VALIDATION_STALE) + { + break; + } + } + } + enumerator->destroy(enumerator); + } + DESTROY_IF(public); + + /* fallback to URL fetching from subject certificate's URIs */ + if (valid != VALIDATION_GOOD && valid != VALIDATION_REVOKED) + { + enumerator = subject->create_ocsp_uri_enumerator(subject); + while (enumerator->enumerate(enumerator, &uri)) + { + current = fetch_ocsp(this, uri, &subject->interface, + &issuer->interface); + if (current) + { + best = get_better_ocsp(this, current, best, subject, issuer, + &valid, TRUE); + if (best && valid != VALIDATION_STALE) + { + break; + } + } + } + enumerator->destroy(enumerator); + } + /* an uri was found, but no result. switch validation state to failed */ + if (valid == VALIDATION_SKIPPED && uri) + { + valid = VALIDATION_FAILED; + } + if (auth) + { + auth->add_item(auth, AUTHZ_OCSP_VALIDATION, &valid); + } + DESTROY_IF(best); + return valid; +} + +/** + * fetch a CRL from an URL + */ +static certificate_t* fetch_crl(private_credential_manager_t *this, char *url) +{ + certificate_t *crl; + chunk_t chunk; + + DBG1(DBG_CFG, " fetching crl from '%s' ...", url); + if (lib->fetcher->fetch(lib->fetcher, url, &chunk, FETCH_END) != SUCCESS) + { + DBG1(DBG_CFG, "crl fetching failed"); + return NULL; + } + crl = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509_CRL, + BUILD_BLOB_ASN1_DER, chunk, BUILD_END); + if (!crl) + { + DBG1(DBG_CFG, "crl fetched successfully but parsing failed"); + return NULL; + } + return crl; +} + +/** + * check the signature of an CRL + */ +static bool verify_crl(private_credential_manager_t *this, certificate_t *crl) +{ + certificate_t *issuer; + enumerator_t *enumerator; + bool verified = FALSE; + + enumerator = create_trusted_enumerator(this, KEY_ANY, crl->get_issuer(crl), + FALSE, FALSE); + while (enumerator->enumerate(enumerator, &issuer, NULL)) + { + if (this->cache->issued_by(this->cache, crl, issuer)) + { + DBG1(DBG_CFG, " crl correctly signed by \"%D\"", + issuer->get_subject(issuer)); + verified = TRUE; + break; + } + } + enumerator->destroy(enumerator); + + return verified; +} + +/** + * Get the better of two CRLs, and check for usable CRL info + */ +static certificate_t *get_better_crl(private_credential_manager_t *this, + certificate_t *cand, certificate_t *best, + x509_t *subject, x509_t *issuer, + cert_validation_t *valid, bool cache) +{ + enumerator_t *enumerator; + time_t revocation, valid_until; + crl_reason_t reason; + chunk_t serial; + crl_t *crl; + + /* check CRL signature */ + if (!verify_crl(this, cand)) + { + DBG1(DBG_CFG, "crl response verification failed"); + cand->destroy(cand); + return best; + } + + crl = (crl_t*)cand; + enumerator = crl->create_enumerator(crl); + while (enumerator->enumerate(enumerator, &serial, &revocation, &reason)) + { + if (chunk_equals(serial, subject->get_serial(subject))) + { + DBG1(DBG_CFG, "certificate was revoked on %T, reason: %N", + &revocation, crl_reason_names, reason); + *valid = VALIDATION_REVOKED; + enumerator->destroy(enumerator); + DESTROY_IF(best); + return cand; + } + } + enumerator->destroy(enumerator); + + /* select the better of the two CRLs */ + if (best == NULL || cand->is_newer(cand, best)) + { + DESTROY_IF(best); + best = cand; + if (best->get_validity(best, NULL, NULL, &valid_until)) + { + DBG1(DBG_CFG, " crl is valid: until %#T", &valid_until, FALSE); + *valid = VALIDATION_GOOD; + if (cache) + { /* we cache non-stale crls only, as a stale crls are refetched */ + cache_cert(this, best); + } + } + else + { + DBG1(DBG_CFG, " crl is stale: since %#T", &valid_until, FALSE); + *valid = VALIDATION_STALE; + } + } + else + { + *valid = VALIDATION_STALE; + cand->destroy(cand); + } + return best; +} + +/** + * validate a x509 certificate using CRL + */ +static cert_validation_t check_crl(private_credential_manager_t *this, + x509_t *subject, x509_t *issuer, + auth_info_t *auth) +{ + cert_validation_t valid = VALIDATION_SKIPPED; + identification_t *keyid = NULL; + certificate_t *best = NULL; + certificate_t *current; + public_key_t *public; + enumerator_t *enumerator; + char *uri; + + /* derive the authorityKeyIdentifier from the issuer's public key */ + current = &issuer->interface; + public = current->get_public_key(current); + if (public) + { + keyid = public->get_id(public, ID_PUBKEY_SHA1); + } + + /* find a cached crl by authorityKeyIdentifier */ + if (keyid) + { + enumerator = create_cert_enumerator(this, CERT_X509_CRL, KEY_ANY, + keyid, FALSE); + while (enumerator->enumerate(enumerator, ¤t)) + { + current->get_ref(current); + best = get_better_crl(this, current, best, subject, issuer, + &valid, FALSE); + if (best && valid != VALIDATION_STALE) + { + DBG1(DBG_CFG, " using cached crl"); + break; + } + } + enumerator->destroy(enumerator); + } + + /* fallback to fetching crls from credential sets cdps */ + if (keyid && valid != VALIDATION_GOOD && valid != VALIDATION_REVOKED) + { + enumerator = create_cdp_enumerator(this, CERT_X509_CRL, keyid); + + while (enumerator->enumerate(enumerator, &uri)) + { + current = fetch_crl(this, uri); + if (current) + { + best = get_better_crl(this, current, best, subject, issuer, + &valid, TRUE); + if (best && valid != VALIDATION_STALE) + { + break; + } + } + } + enumerator->destroy(enumerator); + } + DESTROY_IF(public); + + /* fallback to fetching crls from cdps from subject's certificate */ + if (valid != VALIDATION_GOOD && valid != VALIDATION_REVOKED) + { + enumerator = subject->create_crl_uri_enumerator(subject); + + while (enumerator->enumerate(enumerator, &uri)) + { + current = fetch_crl(this, uri); + if (current) + { + best = get_better_crl(this, current, best, subject, issuer, + &valid, TRUE); + if (best && valid != VALIDATION_STALE) + { + break; + } + } + } + enumerator->destroy(enumerator); + } + + /* an uri was found, but no result. switch validation state to failed */ + if (valid == VALIDATION_SKIPPED && uri) + { + valid = VALIDATION_FAILED; + } + if (auth) + { + auth->add_item(auth, AUTHZ_CRL_VALIDATION, &valid); + } + DESTROY_IF(best); + 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, "subject certificate invalid (valid from %T to %T)", + ¬_before, ¬_after); + return FALSE; + } + if (!issuer->get_validity(issuer, NULL, ¬_before, ¬_after)) + { + DBG1(DBG_CFG, "issuer certificate invalid (valid from %T to %T)", + ¬_before, ¬_after); + return FALSE; + } + if (issuer->get_type(issuer) == CERT_X509 && + subject->get_type(subject) == CERT_X509) + { + if (ocsp || crl) + { + DBG1(DBG_CFG, "checking certificate status of \"%D\"", + subject->get_subject(subject)); + } + if (ocsp) + { + switch (check_ocsp(this, (x509_t*)subject, (x509_t*)issuer, auth)) + { + case VALIDATION_GOOD: + DBG1(DBG_CFG, "certificate status is good"); + return TRUE; + case VALIDATION_REVOKED: + /* has already been logged */ + return FALSE; + case VALIDATION_SKIPPED: + DBG2(DBG_CFG, "ocsp check skipped, no ocsp found"); + break; + case VALIDATION_STALE: + DBG1(DBG_CFG, "ocsp information stale, fallback to crl"); + break; + case VALIDATION_FAILED: + DBG1(DBG_CFG, "ocsp check failed, fallback to crl"); + break; + } + } + if (crl) + { + switch (check_crl(this, (x509_t*)subject, (x509_t*)issuer, auth)) + { + case VALIDATION_GOOD: + DBG1(DBG_CFG, "certificate status is good"); + return TRUE; + case VALIDATION_REVOKED: + /* has already been logged */ + return FALSE; + case VALIDATION_FAILED: + case VALIDATION_SKIPPED: + DBG1(DBG_CFG, "certificate status is not available"); + break; + case VALIDATION_STALE: + DBG1(DBG_CFG, "certificate status is unknown, crl is stale"); + break; + } + } + } + return TRUE; +} + +/** + * Get a trusted certificate from a credential set + */ +static certificate_t *get_pretrusted_cert(private_credential_manager_t *this, + key_type_t type, identification_t *id) +{ + certificate_t *subject; + public_key_t *public; + + subject = get_cert(this, CERT_ANY, type, id, TRUE); + if (!subject) + { + return NULL; + } + public = subject->get_public_key(subject); + if (!public) + { + subject->destroy(subject); + return NULL; + } + public->destroy(public); + return subject; +} + +/** + * Get the issuing certificate of a subject certificate + */ +static certificate_t *get_issuer_cert(private_credential_manager_t *this, + certificate_t *subject, bool trusted) +{ + enumerator_t *enumerator; + certificate_t *issuer = NULL, *candidate; + + enumerator = create_cert_enumerator(this, subject->get_type(subject), KEY_ANY, + subject->get_issuer(subject), trusted); + while (enumerator->enumerate(enumerator, &candidate)) + { + if (this->cache->issued_by(this->cache, subject, candidate)) + { + issuer = candidate->get_ref(candidate); + break; + } + } + enumerator->destroy(enumerator); + return issuer; +} + +/** + * try to verify the trust chain of subject, return TRUE if trusted + */ +static bool verify_trust_chain(private_credential_manager_t *this, + certificate_t *subject, auth_info_t *result, + bool trusted, bool crl, bool ocsp) +{ + certificate_t *current, *issuer; + auth_info_t *auth; + u_int level = 0; + + auth = auth_info_create(); + current = subject->get_ref(subject); + while (level++ < MAX_CA_LEVELS) + { + issuer = get_issuer_cert(this, current, TRUE); + if (issuer) + { + /* accept only self-signed CAs as trust anchor */ + if (this->cache->issued_by(this->cache, issuer, issuer)) + { + auth->add_item(auth, AUTHZ_CA_CERT, issuer); + DBG1(DBG_CFG, " using trusted ca certificate \"%D\"", + issuer->get_subject(issuer)); + trusted = TRUE; + } + else + { + auth->add_item(auth, AUTHZ_IM_CERT, issuer); + DBG1(DBG_CFG, " using trusted intermediate ca certificate " + "\"%D\"", issuer->get_subject(issuer)); + } + } + else + { + issuer = get_issuer_cert(this, current, FALSE); + if (issuer) + { + if (current->equals(current, issuer)) + { + DBG1(DBG_CFG, " self-signed certificate \"%D\" is not trusted", + current->get_subject(current)); + issuer->destroy(issuer); + break; + } + auth->add_item(auth, AUTHZ_IM_CERT, issuer); + DBG1(DBG_CFG, " using untrusted intermediate certificate " + "\"%D\"", issuer->get_subject(issuer)); + } + else + { + DBG1(DBG_CFG, "no issuer certificate found for \"%D\"", + current->get_subject(current)); + break; + } + } + if (!check_certificate(this, current, issuer, crl, ocsp, + current == subject ? auth : NULL)) + { + trusted = FALSE; + issuer->destroy(issuer); + break; + } + current->destroy(current); + current = issuer; + if (trusted) + { + break; + } + } + current->destroy(current); + if (level > MAX_CA_LEVELS) + { + DBG1(DBG_CFG, "maximum ca path length of %d levels reached", level); + } + if (trusted) + { + result->merge(result, auth); + } + auth->destroy(auth); + return trusted; +} + +/** + * enumerator for trusted certificates + */ +typedef struct { + /** implements enumerator_t interface */ + enumerator_t public; + /** enumerator over candidate peer certificates */ + enumerator_t *candidates; + /** reference to the credential_manager */ + private_credential_manager_t *this; + /** type of the requested key */ + key_type_t type; + /** identity the requested key belongs to */ + identification_t *id; + /** TRUE to do CRL checking */ + bool crl; + /** TRUE to do OCSP checking */ + bool ocsp; + /** pretrusted certificate we have served at first invocation */ + certificate_t *pretrusted; + /** currently enumerating auth info */ + auth_info_t *auth; +} trusted_enumerator_t; + +/** + * Implements trusted_enumerator_t.enumerate + */ +static bool trusted_enumerate(trusted_enumerator_t *this, + certificate_t **cert, auth_info_t **auth) +{ + certificate_t *current; + + DESTROY_IF(this->auth); + this->auth = auth_info_create(); + + if (!this->candidates) + { + /* first invocation, build enumerator for next one */ + this->candidates = create_cert_enumerator(this->this, CERT_ANY, + this->type, this->id, FALSE); + /* check if we have a trusted certificate for that peer */ + this->pretrusted = get_pretrusted_cert(this->this, this->type, this->id); + if (this->pretrusted) + { + /* if we find a trusted self signed certificate, we just accept it. + * However, in order to fulfill authorization rules, we try to build + * the trust chain if it is not self signed */ + if (this->this->cache->issued_by(this->this->cache, + this->pretrusted, this->pretrusted) || + verify_trust_chain(this->this, this->pretrusted, this->auth, + TRUE, this->crl, this->ocsp)) + { + DBG1(DBG_CFG, " using trusted certificate \"%D\"", + this->pretrusted->get_subject(this->pretrusted)); + *cert = this->pretrusted; + if (auth) + { + *auth = this->auth; + } + return TRUE; + } + } + } + /* try to verify the trust chain for each certificate found */ + while (this->candidates->enumerate(this->candidates, ¤t)) + { + if (this->pretrusted && + this->pretrusted->equals(this->pretrusted, current)) + { /* skip pretrusted certificate we already served */ + continue; + } + + DBG1(DBG_CFG, " using certificate \"%D\"", + current->get_subject(current)); + if (verify_trust_chain(this->this, current, this->auth, FALSE, + this->crl, this->ocsp)) + { + *cert = current; + if (auth) + { + *auth = this->auth; + } + return TRUE; + } + } + return FALSE; +} + +/** + * Implements trusted_enumerator_t.destroy + */ +static void trusted_destroy(trusted_enumerator_t *this) +{ + DESTROY_IF(this->pretrusted); + DESTROY_IF(this->auth); + DESTROY_IF(this->candidates); + free(this); +} + +/** + * create an enumerator over trusted certificates and their trustchain + */ +static enumerator_t *create_trusted_enumerator(private_credential_manager_t *this, + key_type_t type, identification_t *id, bool crl, bool ocsp) +{ + trusted_enumerator_t *enumerator = malloc_thing(trusted_enumerator_t); + + enumerator->public.enumerate = (void*)trusted_enumerate; + enumerator->public.destroy = (void*)trusted_destroy; + + enumerator->candidates = NULL; + enumerator->this = this; + enumerator->type = type; + enumerator->id = id; + enumerator->crl = crl; + enumerator->ocsp = ocsp; + enumerator->pretrusted = NULL; + enumerator->auth = NULL; + + return &enumerator->public; +} + +/** + * enumerator for public keys + */ +typedef struct { + /** implements enumerator_t interface */ + enumerator_t public; + /** enumerator over candidate peer certificates */ + enumerator_t *inner; + /** reference to the credential_manager */ + private_credential_manager_t *this; + /** currently enumerating key */ + public_key_t *current; + /** credset wrapper around auth */ + auth_info_wrapper_t *wrapper; +} public_enumerator_t; + +/** + * Implements public_enumerator_t.enumerate + */ +static bool public_enumerate(public_enumerator_t *this, + public_key_t **key, auth_info_t **auth) +{ + certificate_t *cert; + + while (this->inner->enumerate(this->inner, &cert, auth)) + { + DESTROY_IF(this->current); + this->current = cert->get_public_key(cert); + if (this->current) + { + *key = this->current; + return TRUE; + } + } + return FALSE; +} + +/** + * Implements public_enumerator_t.destroy + */ +static void public_destroy(public_enumerator_t *this) +{ + DESTROY_IF(this->current); + this->inner->destroy(this->inner); + if (this->wrapper) + { + remove_local_set(this->this, &this->wrapper->set); + this->wrapper->destroy(this->wrapper); + } + pthread_rwlock_unlock(&this->this->lock); + free(this); +} + +/** + * Implementation of credential_manager_t.create_public_enumerator. + */ +static enumerator_t* create_public_enumerator(private_credential_manager_t *this, + key_type_t type, identification_t *id, auth_info_t *auth) +{ + public_enumerator_t *enumerator = malloc_thing(public_enumerator_t); + + enumerator->public.enumerate = (void*)public_enumerate; + enumerator->public.destroy = (void*)public_destroy; + enumerator->inner = create_trusted_enumerator(this, type, id, TRUE, TRUE); + enumerator->this = this; + enumerator->current = NULL; + enumerator->wrapper = NULL; + if (auth) + { + enumerator->wrapper = auth_info_wrapper_create(auth); + add_local_set(this, &enumerator->wrapper->set); + } + pthread_rwlock_rdlock(&this->lock); + return &enumerator->public; +} + +/** + * 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; + } + if (type == AUTHN_CA_CERT_KEYID) + { + public_key_t *public; + identification_t *certid, *keyid; + + public = cert->get_public_key(cert); + if (public) + { + keyid = (identification_t*)value; + certid = public->get_id(public, keyid->get_type(keyid)); + if (certid && certid->equals(certid, keyid)) + { + public->destroy(public); + found = TRUE; + break; + } + public->destroy(public); + } + } + } + enumerator->destroy(enumerator); + return found; +} + +/** + * build a trustchain from subject up to a trust anchor in trusted + */ +static auth_info_t *build_trustchain(private_credential_manager_t *this, + certificate_t *subject, auth_info_t *auth) +{ + certificate_t *issuer, *current; + auth_info_t *trustchain; + u_int level = 0; + + trustchain = auth_info_create(); + + if (!auth->get_item(auth, AUTHN_CA_CERT, (void**)¤t)) + { + /* no trust anchor specified, return this cert only */ + trustchain->add_item(trustchain, AUTHZ_SUBJECT_CERT, subject); + return trustchain; + } + current = subject->get_ref(subject); + while (TRUE) + { + if (auth_contains_cacert(auth, current)) + { + trustchain->add_item(trustchain, AUTHZ_CA_CERT, current); + current->destroy(current); + return trustchain; + } + if (subject == current) + { + trustchain->add_item(trustchain, AUTHZ_SUBJECT_CERT, current); + } + else + { + trustchain->add_item(trustchain, AUTHZ_IM_CERT, current); + } + issuer = get_issuer_cert(this, current, FALSE); + if (!issuer || issuer->equals(issuer, current) || level > MAX_CA_LEVELS) + { + DESTROY_IF(issuer); + current->destroy(current); + break; + } + current->destroy(current); + current = issuer; + level++; + } + trustchain->destroy(trustchain); + return NULL; +} + +/** + * find a private key of a give certificate + */ +static private_key_t *get_private_by_cert(private_credential_manager_t *this, + certificate_t *cert, key_type_t type) +{ + private_key_t *private = NULL; + identification_t* keyid; + public_key_t *public; + + public = cert->get_public_key(cert); + if (public) + { + keyid = public->get_id(public, ID_PUBKEY_INFO_SHA1); + if (keyid) + { + private = get_private_by_keyid(this, type, keyid); + } + public->destroy(public); + } + return private; +} + +/** + * Implementation of credential_manager_t.get_private. + */ +static private_key_t *get_private(private_credential_manager_t *this, + key_type_t type, identification_t *id, + auth_info_t *auth) +{ + enumerator_t *enumerator; + certificate_t *cert; + private_key_t *private = NULL; + auth_info_t *trustchain; + + /* 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: + case ID_KEY_ID: + return get_private_by_keyid(this, type, id); + default: + break; + } + } + + /* try to build a trustchain for each certificate found */ + enumerator = create_cert_enumerator(this, CERT_ANY, type, id, FALSE); + while (enumerator->enumerate(enumerator, &cert)) + { + private = get_private_by_cert(this, cert, type); + if (private) + { + trustchain = build_trustchain(this, cert, auth); + if (trustchain) + { + auth->merge(auth, trustchain); + trustchain->destroy(trustchain); + break; + } + private->destroy(private); + private = NULL; + } + } + enumerator->destroy(enumerator); + /* if no valid trustchain was found, fall back to the first usable cert */ + if (!private) + { + enumerator = create_cert_enumerator(this, CERT_ANY, type, id, FALSE); + while (enumerator->enumerate(enumerator, &cert)) + { + private = get_private_by_cert(this, cert, type); + if (private) + { + auth->add_item(auth, AUTHZ_SUBJECT_CERT, cert); + break; + } + } + enumerator->destroy(enumerator); + } + return private; +} + +/** + * Implementation of credential_manager_t.flush_cache. + */ +static void flush_cache(private_credential_manager_t *this, + certificate_type_t type) +{ + this->cache->flush(this->cache, type); +} + +/** + * Implementation of credential_manager_t.add_set. + */ +static void add_set(private_credential_manager_t *this, + credential_set_t *set) +{ + pthread_rwlock_wrlock(&this->lock); + this->sets->insert_last(this->sets, set); + pthread_rwlock_unlock(&this->lock); +} + +/** + * Implementation of credential_manager_t.remove_set. + */ +static void remove_set(private_credential_manager_t *this, credential_set_t *set) +{ + pthread_rwlock_wrlock(&this->lock); + this->sets->remove(this->sets, set, NULL); + pthread_rwlock_unlock(&this->lock); +} + +/** + * Implementation of credential_manager_t.destroy + */ +static void destroy(private_credential_manager_t *this) +{ + this->sets->remove(this->sets, this->cache, NULL); + this->sets->destroy(this->sets); + pthread_key_delete(this->local_sets); + this->cache->destroy(this->cache); + pthread_rwlock_destroy(&this->lock); + free(this); +} + +/* + * see header file + */ +credential_manager_t *credential_manager_create() +{ + private_credential_manager_t *this = malloc_thing(private_credential_manager_t); + + this->public.create_cert_enumerator = (enumerator_t *(*)(credential_manager_t *this,certificate_type_t cert, key_type_t key,identification_t *id,bool))create_cert_enumerator; + this->public.create_shared_enumerator = (enumerator_t *(*)(credential_manager_t *this, shared_key_type_t type,identification_t *me, identification_t *other))create_shared_enumerator; + this->public.create_cdp_enumerator = (enumerator_t *(*)(credential_manager_t*, certificate_type_t type, identification_t *id))create_cdp_enumerator; + this->public.get_cert = (certificate_t *(*)(credential_manager_t *this,certificate_type_t cert, key_type_t key,identification_t *, bool))get_cert; + this->public.get_shared = (shared_key_t *(*)(credential_manager_t *this,shared_key_type_t type,identification_t *me, identification_t *other))get_shared; + this->public.get_private = (private_key_t*(*)(credential_manager_t*, key_type_t type, identification_t *, auth_info_t*))get_private; + this->public.create_public_enumerator = (enumerator_t*(*)(credential_manager_t*, key_type_t type, identification_t *id, auth_info_t *aut))create_public_enumerator; + this->public.flush_cache = (void(*)(credential_manager_t*, certificate_type_t type))flush_cache; + this->public.cache_cert = (void(*)(credential_manager_t*, certificate_t *cert))cache_cert; + this->public.add_set = (void(*)(credential_manager_t*, credential_set_t *set))add_set; + this->public.remove_set = (void(*)(credential_manager_t*, credential_set_t *set))remove_set; + this->public.destroy = (void(*)(credential_manager_t*))destroy; + + this->sets = linked_list_create(); + pthread_key_create(&this->local_sets, (void*)this->sets->destroy); + this->cache = cert_cache_create(); + this->sets->insert_first(this->sets, this->cache); + pthread_rwlock_init(&this->lock, NULL); + + 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..3a64437e6 --- /dev/null +++ b/src/charon/credentials/credential_manager.h @@ -0,0 +1,207 @@ +/* + * 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: credential_manager.h 3835 2008-04-18 10:11:41Z tobias $ + */ + +/** + * @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 + * + * The credential manager uses rwlocks for performance reasons, credential + * sets must be fully thread save. + */ +struct credential_manager_t { + + /** + * Create an enumerator over all certificates. + * + * @param cert kind of certificate + * @param key kind of key in certificate + * @param id subject this certificate belongs to + * @param trusted TRUE to list trusted certificates only + * @return enumerator over the certificates + */ + enumerator_t *(*create_cert_enumerator)(credential_manager_t *this, + certificate_type_t cert, key_type_t key, + identification_t *id, bool trusted); + /** + * Create an enumerator over all shared keys. + * + * The enumerator enumerates over: + * shared_key_t*, id_match_t me, id_match_t other + * But must accepts values for the id_matches. + * + * @param type kind of requested shared key + * @param first first subject between key is shared + * @param second second subject between key is shared + * @return enumerator over shared keys + */ + enumerator_t *(*create_shared_enumerator)(credential_manager_t *this, + shared_key_type_t type, + identification_t *first, identification_t *second); + /** + * Create an enumerator over all Certificate Distribution Points. + * + * @param type kind of certificate the point distributes + * @param id identification of the distributed certificate + * @return enumerator of CDPs as char* + */ + enumerator_t *(*create_cdp_enumerator)(credential_manager_t *this, + certificate_type_t type, identification_t *id); + /** + * Get a trusted or untrusted certificate. + * + * @param cert kind of certificate + * @param key kind of key in certificate + * @param id subject this certificate belongs to + * @param trusted TRUE to get a trusted certificate only + * @return certificate, if found, NULL otherwise + */ + certificate_t *(*get_cert)(credential_manager_t *this, + certificate_type_t cert, key_type_t key, + identification_t *id, bool trusted); + /** + * Get the best matching shared key for two IDs. + * + * @param type kind of requested shared key + * @param me own identity + * @param other peers identity + * @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); + + /** + * Create an enumerator over trusted public keys. + * + * This method gets a an enumerator over trusted public keys to verify a + * signature created by id. The auth parameter contains additional + * authentication infos, e.g. peer and intermediate certificates. + * The resulting enumerator enumerates over public_key_t *, auth_info_t *, + * where the auth info contains gained privileges for the authorization + * process. + * + * @param type type of the key to get + * @param id owner of the key, signer of the signature + * @param auth authentication infos + * @return enumerator + */ + enumerator_t* (*create_public_enumerator)(credential_manager_t *this, + key_type_t type, identification_t *id, auth_info_t *auth); + + /** + * Cache a certificate by invoking cache_cert() on all registerd sets. + * + * @param cert certificate to cache + */ + void (*cache_cert)(credential_manager_t *this, certificate_t *cert); + + /** + * Flush the certificate cache. + * + * Only the managers local cache is flushed, but not the sets cache filled + * by the cache_cert() method. + * + * @param type type of certificate to flush, or CERT_ANY + */ + void (*flush_cache)(credential_manager_t *this, certificate_type_t type); + + /** + * Register a credential set to the manager. + * + * @param set set to register + */ + void (*add_set)(credential_manager_t *this, credential_set_t *set); + + /** + * Unregister a credential set from the manager. + * + * @param set set to unregister + */ + void (*remove_set)(credential_manager_t *this, credential_set_t *set); + + /** + * Destroy a credential_manager instance. + */ + void (*destroy)(credential_manager_t *this); +}; + +/** + * Create a credential_manager instance. + */ +credential_manager_t *credential_manager_create(); + +#endif /* CREDENTIAL_MANAGER_H_ @} */ diff --git a/src/charon/credentials/credential_set.h b/src/charon/credentials/credential_set.h new file mode 100644 index 000000000..b5f3b95cd --- /dev/null +++ b/src/charon/credentials/credential_set.h @@ -0,0 +1,103 @@ +/* + * 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: credential_set.h 3820 2008-04-17 11:22:37Z martin $ + */ + +/** + * @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); + + /** + * Cache a certificate in the credential set. + * + * The caching policy is implementation dependent, the sets may cache the + * certificate in-memory, persistent on disk or not at all. + * + * @param cert certificate to cache + */ + void (*cache_cert)(credential_set_t *this, certificate_t *cert); +}; + +#endif /* CREDENTIAL_SET_H_ @} */ diff --git a/src/charon/credentials/sets/auth_info_wrapper.c b/src/charon/credentials/sets/auth_info_wrapper.c new file mode 100644 index 000000000..32783ff93 --- /dev/null +++ b/src/charon/credentials/sets/auth_info_wrapper.c @@ -0,0 +1,215 @@ +/* + * Copyright (C) 2008 Tobias Brunner + * Copyright (C) 2008 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * $Id$ + */ + +#include <daemon.h> + +#include "auth_info_wrapper.h" + +typedef struct private_auth_info_wrapper_t private_auth_info_wrapper_t; + +/** + * private data of auth_info_wrapper + */ +struct private_auth_info_wrapper_t { + + /** + * public functions + */ + auth_info_wrapper_t public; + + /** + * wrapped auth info + */ + auth_info_t *auth; +}; + +/** + * enumerator for auth_info_wrapper_t.create_cert_enumerator() + */ +typedef struct { + /** implements enumerator_t */ + enumerator_t public; + /** inner enumerator from auth_info */ + enumerator_t *inner; + /** wrapped auth info */ + auth_info_t *auth; + /** enumerated cert type */ + certificate_type_t cert; + /** enumerated key type */ + key_type_t key; + /** enumerated id */ + identification_t *id; +} wrapper_enumerator_t; + +/** + * Tries to fetch a certificate that was supplied as "Hash and URL" (replaces the + * item's type and value in place). + */ +static bool fetch_cert(wrapper_enumerator_t *enumerator, auth_item_t *type, void **value) +{ + char *url = (char*)*value; + if (!url) + { + /* fetching the certificate previously failed */ + return FALSE; + } + + chunk_t data; + certificate_t *cert; + + DBG1(DBG_CFG, " fetching certificate from '%s' ...", url); + if (lib->fetcher->fetch(lib->fetcher, url, &data) != SUCCESS) + { + DBG1(DBG_CFG, " fetching certificate failed"); + /* we set the item to NULL, so we can skip it */ + enumerator->auth->replace_item(enumerator->inner, *type, NULL); + return FALSE; + } + + cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509, + BUILD_BLOB_ASN1_DER, data, BUILD_END); + + if (!cert) + { + DBG1(DBG_CFG, " parsing fetched certificate failed"); + /* we set the item to NULL, so we can skip it */ + enumerator->auth->replace_item(enumerator->inner, *type, NULL); + return FALSE; + } + + DBG1(DBG_CFG, " fetched certificate \"%D\"", cert->get_subject(cert)); + charon->credentials->cache_cert(charon->credentials, cert); + + *type = (*type == AUTHN_IM_HASH_URL) ? AUTHN_IM_CERT : AUTHN_SUBJECT_CERT; + *value = cert; + enumerator->auth->replace_item(enumerator->inner, *type, cert); + + return TRUE; +} + +/** + * enumerate function for wrapper_enumerator_t + */ +static bool enumerate(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_IM_HASH_URL || + type == AUTHN_SUBJECT_HASH_URL) + { + if (!fetch_cert(this, &type, (void**)¤t)) + { + continue; + } + } + else 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 wrapper_enumerator_t + */ +static void wrapper_enumerator_destroy(wrapper_enumerator_t *this) +{ + this->inner->destroy(this->inner); + free(this); +} + +/** + * implementation of auth_info_wrapper_t.set.create_cert_enumerator + */ +static enumerator_t *create_enumerator(private_auth_info_wrapper_t *this, + certificate_type_t cert, key_type_t key, + identification_t *id, bool trusted) +{ + wrapper_enumerator_t *enumerator; + + if (trusted) + { + return NULL; + } + enumerator = malloc_thing(wrapper_enumerator_t); + enumerator->auth = this->auth; + enumerator->cert = cert; + enumerator->key = key; + enumerator->id = id; + enumerator->inner = this->auth->create_item_enumerator(this->auth); + enumerator->public.enumerate = (void*)enumerate; + enumerator->public.destroy = (void*)wrapper_enumerator_destroy; + return &enumerator->public; +} + +/** + * Implementation of auth_info_wrapper_t.destroy + */ +static void destroy(private_auth_info_wrapper_t *this) +{ + free(this); +} + +/* + * see header file + */ +auth_info_wrapper_t *auth_info_wrapper_create(auth_info_t *auth) +{ + private_auth_info_wrapper_t *this = malloc_thing(private_auth_info_wrapper_t); + + this->public.set.create_private_enumerator = (void*)return_null; + this->public.set.create_cert_enumerator = (void*)create_enumerator; + this->public.set.create_shared_enumerator = (void*)return_null; + this->public.set.create_cdp_enumerator = (void*)return_null; + this->public.set.cache_cert = (void*)nop; + this->public.destroy = (void(*)(auth_info_wrapper_t*))destroy; + + this->auth = auth; + + return &this->public; +} + diff --git a/src/charon/credentials/sets/auth_info_wrapper.h b/src/charon/credentials/sets/auth_info_wrapper.h new file mode 100644 index 000000000..c382e9870 --- /dev/null +++ b/src/charon/credentials/sets/auth_info_wrapper.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2008 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * $Id$ + */ + +/** + * @defgroup auth_info_wrapper auth_info_wrapper + * @{ @ingroup sets + */ + +#ifndef AUTH_INFO_WRAPPER_H_ +#define AUTH_INFO_WRAPPER_H_ + +#include <credentials/credential_set.h> +#include <credentials/auth_info.h> + +typedef struct auth_info_wrapper_t auth_info_wrapper_t; + +/** + * A wrapper around auth_info_t to handle it like a credential set. + */ +struct auth_info_wrapper_t { + + /** + * implements credential_set_t + */ + credential_set_t set; + + /** + * Destroy a auth_info_wrapper instance. + */ + void (*destroy)(auth_info_wrapper_t *this); +}; + +/** + * Create a auth_info_wrapper instance. + * + * @param auth the wrapped auth info + * @return wrapper around auth + */ +auth_info_wrapper_t *auth_info_wrapper_create(auth_info_t *auth); + +#endif /* AUTH_INFO_WRAPPER_H_ @}*/ diff --git a/src/charon/credentials/sets/cert_cache.c b/src/charon/credentials/sets/cert_cache.c new file mode 100644 index 000000000..8af8bb619 --- /dev/null +++ b/src/charon/credentials/sets/cert_cache.c @@ -0,0 +1,332 @@ +/* + * Copyright (C) 2008 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * $Id$ + */ + +#include "cert_cache.h" + +#include <daemon.h> +#include <utils/linked_list.h> +#include <utils/mutex.h> + +#define CACHE_SIZE 30 + +typedef struct private_cert_cache_t private_cert_cache_t; +typedef struct relation_t relation_t; + +/** + * private data of cert_cache + */ +struct private_cert_cache_t { + + /** + * public functions + */ + cert_cache_t public; + + /** + * list of trusted subject-issuer relations, as relation_t + */ + linked_list_t *relations; + + /** + * do we have an active enumerator + */ + bool enumerating; + + /** + * have we increased the cache without a check_cache? + */ + bool check_required; + + /** + * mutex to lock relations list + */ + mutex_t *mutex; +}; + +/** + * A trusted relation between subject and issuer + */ +struct relation_t { + /** subject of this relation */ + certificate_t *subject; + /** issuer of this relation */ + certificate_t *issuer; + /** time of last use */ + time_t last_use; +}; + +/** + * destroy a relation_t structure + */ +static void relation_destroy(relation_t *this) +{ + this->subject->destroy(this->subject); + this->issuer->destroy(this->issuer); + free(this); +} + +/** + * check the cache for oversize + */ +static void check_cache(private_cert_cache_t *this) +{ + if (this->enumerating) + { + this->check_required = TRUE; + } + else + { + while (this->relations->get_count(this->relations) > CACHE_SIZE) + { + relation_t *oldest = NULL, *current; + enumerator_t *enumerator; + + enumerator = this->relations->create_enumerator(this->relations); + while (enumerator->enumerate(enumerator, ¤t)) + { + if (oldest == NULL || oldest->last_use <= current->last_use) + { + oldest = current; + } + } + enumerator->destroy(enumerator); + this->relations->remove(this->relations, oldest, NULL); + relation_destroy(oldest); + } + this->check_required = FALSE; + } +} + +/** + * Implementation of cert_cache_t.issued_by. + */ +static bool issued_by(private_cert_cache_t *this, + certificate_t *subject, certificate_t *issuer) +{ + relation_t *found = NULL, *current; + enumerator_t *enumerator; + + /* lookup cache */ + this->mutex->lock(this->mutex); + enumerator = this->relations->create_enumerator(this->relations); + while (enumerator->enumerate(enumerator, ¤t)) + { + bool match = FALSE; + + /* check for equal certificates */ + if (subject->equals(subject, current->subject)) + { + match = TRUE; + subject = current->subject; + } + if (issuer->equals(issuer, current->issuer)) + { + issuer = current->issuer; + /* if both certs match, we already have a relation */ + if (match) + { + current->last_use = time(NULL); + found = current; + break; + } + } + } + enumerator->destroy(enumerator); + this->mutex->unlock(this->mutex); + if (found) + { + return TRUE; + } + /* no cache hit, check signature */ + if (!subject->issued_by(subject, issuer)) + { + return FALSE; + } + /* cache if good, respect cache limit */ + found = malloc_thing(relation_t); + found->subject = subject->get_ref(subject); + found->issuer = issuer->get_ref(issuer); + found->last_use = time(NULL); + this->mutex->lock(this->mutex); + this->relations->insert_last(this->relations, found); + check_cache(this); + this->mutex->unlock(this->mutex); + return TRUE; +} + +/** + * data associated to a cert enumeration + */ +typedef struct { + /** type of requested certificate */ + certificate_type_t cert; + /** type of requested key */ + key_type_t key; + /** ID to get a cert from */ + identification_t *id; + /** reverse pointer to cache */ + private_cert_cache_t *this; +} cert_data_t; + +/** + * filter function for certs enumerator + */ +static bool certs_filter(cert_data_t *data, relation_t **in, certificate_t **out) +{ + public_key_t *public; + certificate_t *cert; + + cert = (*in)->subject; + if (data->key == KEY_ANY && data->id && + (data->cert == CERT_ANY || data->cert == CERT_X509_CRL) && + cert->get_type(cert) == CERT_X509_CRL) + { /* CRL lookup is done using issuer/authkeyidentifier */ + if (cert->has_issuer(cert, data->id)) + { + *out = cert; + return TRUE; + } + } + + if ((data->cert == CERT_ANY || cert->get_type(cert) == data->cert) && + (!data->id || cert->has_subject(cert, data->id))) + { + if (data->key == KEY_ANY) + { + *out = cert; + return TRUE; + } + public = cert->get_public_key(cert); + if (public) + { + if (public->get_type(public) == data->key) + { + public->destroy(public); + *out = cert; + return TRUE; + } + public->destroy(public); + } + } + return FALSE; +} + +/** + * clean up enumeration data + */ +static void certs_destroy(cert_data_t *data) +{ + data->this->enumerating--; + if (data->this->check_required) + { + check_cache(data->this); + } + data->this->mutex->unlock(data->this->mutex); + free(data); +} + +/** + * implementation of credential_set_t.create_cert_enumerator + */ +static enumerator_t *create_enumerator(private_cert_cache_t *this, + certificate_type_t cert, key_type_t key, + identification_t *id, bool trusted) +{ + cert_data_t *data; + + if (trusted) + { + return NULL; + } + data = malloc_thing(cert_data_t); + data->cert = cert; + data->key = key; + data->id = id; + data->this = this; + + this->mutex->lock(this->mutex); + this->enumerating++; + return enumerator_create_filter( + this->relations->create_enumerator(this->relations), + (void*)certs_filter, data, (void*)certs_destroy); +} + +/** + * Implementation of credential_set_t.cache_cert. + */ +static void cache_cert(private_cert_cache_t *this, certificate_t *cert) +{ + /* TODO: implement caching */ +} + +/** + * Implementation of cert_cache_t.flush. + */ +static void flush(private_cert_cache_t *this, certificate_type_t type) +{ + enumerator_t *enumerator; + relation_t *relation; + + this->mutex->lock(this->mutex); + enumerator = this->relations->create_enumerator(this->relations); + while (enumerator->enumerate(enumerator, &relation)) + { + if (type == CERT_ANY || + type == relation->subject->get_type(relation->subject)) + { + this->relations->remove_at(this->relations, enumerator); + relation_destroy(relation); + } + } + enumerator->destroy(enumerator); + this->mutex->unlock(this->mutex); +} + +/** + * Implementation of cert_cache_t.destroy + */ +static void destroy(private_cert_cache_t *this) +{ + this->relations->destroy_function(this->relations, (void*)relation_destroy); + this->mutex->destroy(this->mutex); + free(this); +} + +/* + * see header file + */ +cert_cache_t *cert_cache_create() +{ + private_cert_cache_t *this = malloc_thing(private_cert_cache_t); + + this->public.set.create_private_enumerator = (void*)return_null; + this->public.set.create_cert_enumerator = (void*)create_enumerator; + this->public.set.create_shared_enumerator = (void*)return_null; + this->public.set.create_cdp_enumerator = (void*)return_null; + this->public.set.cache_cert = (void*)cache_cert; + this->public.issued_by = (bool(*)(cert_cache_t*, certificate_t *subject, certificate_t *issuer))issued_by; + this->public.flush = (void(*)(cert_cache_t*, certificate_type_t type))flush; + this->public.destroy = (void(*)(cert_cache_t*))destroy; + + this->relations = linked_list_create(); + this->enumerating = FALSE; + this->check_required = FALSE; + this->mutex = mutex_create(MUTEX_RECURSIVE); + + return &this->public; +} + diff --git a/src/charon/credentials/sets/cert_cache.h b/src/charon/credentials/sets/cert_cache.h new file mode 100644 index 000000000..281189d53 --- /dev/null +++ b/src/charon/credentials/sets/cert_cache.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2008 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * $Id$ + */ + +/** + * @defgroup cert_cache cert_cache + * @{ @ingroup sets + */ + +#ifndef CERT_CACHE_H_ +#define CERT_CACHE_H_ + +#include <credentials/credential_set.h> + +typedef struct cert_cache_t cert_cache_t; + +/** + * Certificate signature verification and certificate cache. + * + * This cache serves all certificates seen in its issued_by method + * and serves them as untrusted through the credential set interface. Further, + * it caches valid subject-issuer relationships to speed up the issued_by + * method. + */ +struct cert_cache_t { + + /** + * Implements credential_set_t. + */ + credential_set_t set; + + /** + * Caching wrapper around certificate_t.issued_by. + * + * @param subject certificate to verify + * @param issuer issuing certificate to verify subject + * @return TRUE if subject issued by issuer + */ + bool (*issued_by)(cert_cache_t *this, + certificate_t *subject, certificate_t *issuer); + + /** + * Flush the certificate cache. + * + * @param type type of certificate to flush, or CERT_ANY + */ + void (*flush)(cert_cache_t *this, certificate_type_t type); + + /** + * Destroy a cert_cache instance. + */ + void (*destroy)(cert_cache_t *this); +}; + +/** + * Create a cert_cache instance. + */ +cert_cache_t *cert_cache_create(); + +#endif /* CERT_CACHE_H_ @}*/ diff --git a/src/charon/credentials/sets/ocsp_response_wrapper.c b/src/charon/credentials/sets/ocsp_response_wrapper.c new file mode 100644 index 000000000..c4d3a5b0f --- /dev/null +++ b/src/charon/credentials/sets/ocsp_response_wrapper.c @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2008 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * $Id$ + */ + +#include "ocsp_response_wrapper.h" + +typedef struct private_ocsp_response_wrapper_t private_ocsp_response_wrapper_t; + +/** + * private data of ocsp_response_wrapper + */ +struct private_ocsp_response_wrapper_t { + + /** + * public functions + */ + ocsp_response_wrapper_t public; + + /** + * wrapped OCSP response + */ + ocsp_response_t *response; +}; + +/** + * enumerator for ocsp_response_wrapper_t.create_cert_enumerator() + */ +typedef struct { + /** implements enumerator_t */ + enumerator_t public; + /** enumerator over ocsp response */ + enumerator_t *inner; + /** type of cert */ + certificate_type_t cert; + /** type of key */ + key_type_t key; + /** filtering identity */ + identification_t *id; +} wrapper_enumerator_t; + +/** + * enumerate function wrapper_enumerator_t + */ +static bool enumerate(wrapper_enumerator_t *this, certificate_t **cert) +{ + certificate_t *current; + public_key_t *public; + + while (this->inner->enumerate(this->inner, ¤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 wrapper_enumerator_t + */ +static void enumerator_destroy(wrapper_enumerator_t *this) +{ + this->inner->destroy(this->inner); + free(this); +} + +/** + * implementation of ocsp_response_wrapper_t.set.create_cert_enumerator + */ +static enumerator_t *create_enumerator(private_ocsp_response_wrapper_t *this, + certificate_type_t cert, key_type_t key, + identification_t *id, bool trusted) +{ + wrapper_enumerator_t *enumerator; + + if (trusted) + { + return NULL; + } + + enumerator = malloc_thing(wrapper_enumerator_t); + enumerator->cert = cert; + enumerator->key = key; + enumerator->id = id; + enumerator->inner = this->response->create_cert_enumerator(this->response); + enumerator->public.enumerate = (void*)enumerate; + enumerator->public.destroy = (void*)enumerator_destroy; + return &enumerator->public; +} + +/** + * Implementation of ocsp_response_wrapper_t.destroy + */ +static void destroy(private_ocsp_response_wrapper_t *this) +{ + free(this); +} + +/* + * see header file + */ +ocsp_response_wrapper_t *ocsp_response_wrapper_create(ocsp_response_t *response) +{ + private_ocsp_response_wrapper_t *this = malloc_thing(private_ocsp_response_wrapper_t); + + this->public.set.create_private_enumerator = (void*)return_null; + this->public.set.create_cert_enumerator = (void*)create_enumerator; + this->public.set.create_shared_enumerator = (void*)return_null; + this->public.set.create_cdp_enumerator = (void*)return_null; + this->public.set.cache_cert = (void*)nop; + this->public.destroy = (void(*)(ocsp_response_wrapper_t*))destroy; + + this->response = response; + + return &this->public; +} + diff --git a/src/charon/credentials/sets/ocsp_response_wrapper.h b/src/charon/credentials/sets/ocsp_response_wrapper.h new file mode 100644 index 000000000..6d32c2ca8 --- /dev/null +++ b/src/charon/credentials/sets/ocsp_response_wrapper.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2008 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * $Id$ + */ + +/** + * @defgroup ocsp_response_wrapper ocsp_response_wrapper + * @{ @ingroup sets + */ + +#ifndef OCSP_RESPONSE_WRAPPER_H_ +#define OCSP_RESPONSE_WRAPPER_H_ + +#include <credentials/credential_set.h> +#include <credentials/certificates/ocsp_response.h> + +typedef struct ocsp_response_wrapper_t ocsp_response_wrapper_t; + +/** + * A wrapper around ocsp_response_t to handle it like a credential set. + */ +struct ocsp_response_wrapper_t { + + /** + * implements credential_set_t + */ + credential_set_t set; + + /** + * Destroy a ocsp_response_wrapper instance. + */ + void (*destroy)(ocsp_response_wrapper_t *this); +}; + +/** + * Create a ocsp_response_wrapper instance. + * + * @param response the wrapped OCSP response + * @return wrapper around response + */ +ocsp_response_wrapper_t *ocsp_response_wrapper_create(ocsp_response_t *response); + +#endif /* OCSP_RESPONSE_WRAPPER_H_ @}*/ |