diff options
Diffstat (limited to 'src/libstrongswan/credentials')
27 files changed, 3930 insertions, 296 deletions
diff --git a/src/libstrongswan/credentials/auth_cfg.c b/src/libstrongswan/credentials/auth_cfg.c new file mode 100644 index 000000000..2573d0327 --- /dev/null +++ b/src/libstrongswan/credentials/auth_cfg.c @@ -0,0 +1,830 @@ +/* + * Copyright (C) 2007-2009 Martin Willi + * Copyright (C) 2008 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "auth_cfg.h" + +#include <library.h> +#include <debug.h> +#include <utils/linked_list.h> +#include <utils/identification.h> +#include <credentials/certificates/certificate.h> + +ENUM(auth_class_names, AUTH_CLASS_ANY, AUTH_CLASS_EAP, + "any", + "public key", + "pre-shared key", + "EAP", +); + +ENUM_BEGIN(eap_type_names, EAP_IDENTITY, EAP_GTC, + "EAP_IDENTITY", + "EAP_NOTIFICATION", + "EAP_NAK", + "EAP_MD5", + "EAP_OTP", + "EAP_GTC"); +ENUM_NEXT(eap_type_names, EAP_SIM, EAP_SIM, EAP_GTC, + "EAP_SIM"); +ENUM_NEXT(eap_type_names, EAP_AKA, EAP_AKA, EAP_SIM, + "EAP_AKA"); +ENUM_NEXT(eap_type_names, EAP_MSCHAPV2, EAP_MSCHAPV2, EAP_AKA, + "EAP_MSCHAPV2"); +ENUM_NEXT(eap_type_names, EAP_RADIUS, EAP_EXPERIMENTAL, EAP_MSCHAPV2, + "EAP_RADIUS", + "EAP_EXPANDED", + "EAP_EXPERIMENTAL"); +ENUM_END(eap_type_names, EAP_EXPERIMENTAL); + +ENUM_BEGIN(eap_type_short_names, EAP_IDENTITY, EAP_GTC, + "ID", + "NTF", + "NAK", + "MD5", + "OTP", + "GTC"); +ENUM_NEXT(eap_type_short_names, EAP_SIM, EAP_SIM, EAP_GTC, + "SIM"); +ENUM_NEXT(eap_type_short_names, EAP_AKA, EAP_AKA, EAP_SIM, + "AKA"); +ENUM_NEXT(eap_type_short_names, EAP_MSCHAPV2, EAP_MSCHAPV2, EAP_AKA, + "MSCHAPV2"); +ENUM_NEXT(eap_type_short_names, EAP_RADIUS, EAP_EXPERIMENTAL, EAP_MSCHAPV2, + "RAD", + "EXP", + "XP"); +ENUM_END(eap_type_short_names, EAP_EXPERIMENTAL); + +ENUM(auth_rule_names, AUTH_RULE_IDENTITY, AUTH_HELPER_SUBJECT_HASH_URL, + "RULE_IDENTITY", + "RULE_AUTH_CLASS", + "RULE_EAP_IDENTITY", + "RULE_EAP_TYPE", + "RULE_EAP_VENDOR", + "RULE_CA_CERT", + "RULE_IM_CERT", + "RULE_SUBJECT_CERT", + "RULE_CRL_VALIDATION", + "RULE_OCSP_VALIDATION", + "RULE_GROUP", + "HELPER_IM_CERT", + "HELPER_SUBJECT_CERT", + "HELPER_IM_HASH_URL", + "HELPER_SUBJECT_HASH_URL", +); + +typedef struct private_auth_cfg_t private_auth_cfg_t; + +/** + * private data of item_set + */ +struct private_auth_cfg_t { + + /** + * public functions + */ + auth_cfg_t public; + + /** + * list of entry_t + */ + linked_list_t *entries; +}; + +typedef struct entry_t entry_t; + +struct entry_t { + /** rule type */ + auth_rule_t type; + /** associated value */ + void *value; +}; + +/** + * enumerator for auth_cfg_t.create_enumerator() + */ +typedef struct { + /** implements enumerator_t */ + enumerator_t public; + /** inner enumerator from linked_list_t */ + enumerator_t *inner; + /** current entry */ + entry_t *current; +} entry_enumerator_t; + +/** + * enumerate function for item_enumerator_t + */ +static bool enumerate(entry_enumerator_t *this, auth_rule_t *type, void **value) +{ + entry_t *entry; + + if (this->inner->enumerate(this->inner, &entry)) + { + this->current = entry; + *type = entry->type; + *value = entry->value; + return TRUE; + } + return FALSE; +} + +/** + * destroy function for item_enumerator_t + */ +static void entry_enumerator_destroy(entry_enumerator_t *this) +{ + this->inner->destroy(this->inner); + free(this); +} + +/** + * Implementation of auth_cfg_t.create_enumerator. + */ +static enumerator_t* create_enumerator(private_auth_cfg_t *this) +{ + entry_enumerator_t *enumerator; + + enumerator = malloc_thing(entry_enumerator_t); + enumerator->inner = this->entries->create_enumerator(this->entries); + enumerator->public.enumerate = (void*)enumerate; + enumerator->public.destroy = (void*)entry_enumerator_destroy; + enumerator->current = NULL; + return &enumerator->public; +} + +/** + * Destroy the value associated with an entry + */ +static void destroy_entry_value(entry_t *entry) +{ + switch (entry->type) + { + case AUTH_RULE_IDENTITY: + case AUTH_RULE_EAP_IDENTITY: + case AUTH_RULE_GROUP: + { + identification_t *id = (identification_t*)entry->value; + id->destroy(id); + break; + } + case AUTH_RULE_CA_CERT: + case AUTH_RULE_IM_CERT: + case AUTH_RULE_SUBJECT_CERT: + case AUTH_HELPER_IM_CERT: + case AUTH_HELPER_SUBJECT_CERT: + { + certificate_t *cert = (certificate_t*)entry->value; + cert->destroy(cert); + break; + } + case AUTH_HELPER_IM_HASH_URL: + case AUTH_HELPER_SUBJECT_HASH_URL: + { + free(entry->value); + break; + } + case AUTH_RULE_AUTH_CLASS: + case AUTH_RULE_EAP_TYPE: + case AUTH_RULE_EAP_VENDOR: + case AUTH_RULE_CRL_VALIDATION: + case AUTH_RULE_OCSP_VALIDATION: + break; + } +} + +/** + * Implementation of auth_cfg_t.replace. + */ +static void replace(auth_cfg_t *this, entry_enumerator_t *enumerator, + auth_rule_t type, ...) +{ + if (enumerator->current) + { + va_list args; + + va_start(args, type); + + destroy_entry_value(enumerator->current); + enumerator->current->type = type; + switch (type) + { + case AUTH_RULE_AUTH_CLASS: + case AUTH_RULE_EAP_TYPE: + case AUTH_RULE_EAP_VENDOR: + case AUTH_RULE_CRL_VALIDATION: + case AUTH_RULE_OCSP_VALIDATION: + /* integer type */ + enumerator->current->value = (void*)(uintptr_t)va_arg(args, u_int); + break; + case AUTH_RULE_IDENTITY: + case AUTH_RULE_EAP_IDENTITY: + case AUTH_RULE_GROUP: + case AUTH_RULE_CA_CERT: + case AUTH_RULE_IM_CERT: + case AUTH_RULE_SUBJECT_CERT: + case AUTH_HELPER_IM_CERT: + case AUTH_HELPER_SUBJECT_CERT: + case AUTH_HELPER_IM_HASH_URL: + case AUTH_HELPER_SUBJECT_HASH_URL: + /* pointer type */ + enumerator->current->value = va_arg(args, void*); + break; + } + va_end(args); + } +} + +/** + * Implementation of auth_cfg_t.get. + */ +static void* get(private_auth_cfg_t *this, auth_rule_t type) +{ + enumerator_t *enumerator; + void *current_value, *best_value = NULL; + auth_rule_t current_type; + bool found = FALSE; + + enumerator = create_enumerator(this); + while (enumerator->enumerate(enumerator, ¤t_type, ¤t_value)) + { + if (type == current_type) + { + if (type == AUTH_RULE_CRL_VALIDATION || + type == AUTH_RULE_OCSP_VALIDATION) + { /* for CRL/OCSP validation, always get() the highest value */ + if (!found || current_value > best_value) + { + best_value = current_value; + } + found = TRUE; + continue; + } + best_value = current_value; + found = TRUE; + break; + } + } + enumerator->destroy(enumerator); + if (found) + { + return best_value; + } + switch (type) + { + /* use some sane defaults if we don't find an entry */ + case AUTH_RULE_AUTH_CLASS: + return (void*)AUTH_CLASS_ANY; + case AUTH_RULE_EAP_TYPE: + return (void*)EAP_NAK; + case AUTH_RULE_EAP_VENDOR: + return (void*)0; + case AUTH_RULE_CRL_VALIDATION: + case AUTH_RULE_OCSP_VALIDATION: + return (void*)VALIDATION_FAILED; + case AUTH_RULE_IDENTITY: + case AUTH_RULE_EAP_IDENTITY: + case AUTH_RULE_GROUP: + case AUTH_RULE_CA_CERT: + case AUTH_RULE_IM_CERT: + case AUTH_RULE_SUBJECT_CERT: + case AUTH_HELPER_IM_CERT: + case AUTH_HELPER_SUBJECT_CERT: + case AUTH_HELPER_IM_HASH_URL: + case AUTH_HELPER_SUBJECT_HASH_URL: + default: + return NULL; + } +} + +/** + * Implementation of auth_cfg_t.add. + */ +static void add(private_auth_cfg_t *this, auth_rule_t type, ...) +{ + entry_t *entry = malloc_thing(entry_t); + va_list args; + + va_start(args, type); + entry->type = type; + switch (type) + { + case AUTH_RULE_AUTH_CLASS: + case AUTH_RULE_EAP_TYPE: + case AUTH_RULE_EAP_VENDOR: + case AUTH_RULE_CRL_VALIDATION: + case AUTH_RULE_OCSP_VALIDATION: + /* integer type */ + entry->value = (void*)(uintptr_t)va_arg(args, u_int); + break; + case AUTH_RULE_IDENTITY: + case AUTH_RULE_EAP_IDENTITY: + case AUTH_RULE_GROUP: + case AUTH_RULE_CA_CERT: + case AUTH_RULE_IM_CERT: + case AUTH_RULE_SUBJECT_CERT: + case AUTH_HELPER_IM_CERT: + case AUTH_HELPER_SUBJECT_CERT: + case AUTH_HELPER_IM_HASH_URL: + case AUTH_HELPER_SUBJECT_HASH_URL: + /* pointer type */ + entry->value = va_arg(args, void*); + break; + } + va_end(args); + this->entries->insert_last(this->entries, entry); +} + +/** + * Implementation of auth_cfg_t.complies. + */ +static bool complies(private_auth_cfg_t *this, auth_cfg_t *constraints, + bool log_error) +{ + enumerator_t *e1, *e2; + bool success = TRUE, has_group = FALSE, group_match = FALSE; + auth_rule_t t1, t2; + void *value; + + e1 = constraints->create_enumerator(constraints); + while (e1->enumerate(e1, &t1, &value)) + { + switch (t1) + { + case AUTH_RULE_CA_CERT: + case AUTH_RULE_IM_CERT: + { + certificate_t *c1, *c2; + + c1 = (certificate_t*)value; + + success = FALSE; + e2 = create_enumerator(this); + while (e2->enumerate(e2, &t2, &c2)) + { + if ((t2 == AUTH_RULE_CA_CERT || t2 == AUTH_RULE_IM_CERT) && + c1->equals(c1, c2)) + { + success = TRUE; + } + } + e2->destroy(e2); + if (!success && log_error) + { + DBG1(DBG_CFG, "constraint check failed: peer not " + "authenticated by CA '%Y'.", c1->get_subject(c1)); + } + break; + } + case AUTH_RULE_SUBJECT_CERT: + { + certificate_t *c1, *c2; + + c1 = (certificate_t*)value; + c2 = get(this, AUTH_RULE_SUBJECT_CERT); + if (!c2 || !c1->equals(c1, c2)) + { + success = FALSE; + if (log_error) + { + DBG1(DBG_CFG, "constraint check failed: peer not " + "authenticated with peer cert '%Y'.", + c1->get_subject(c1)); + } + } + break; + } + case AUTH_RULE_CRL_VALIDATION: + case AUTH_RULE_OCSP_VALIDATION: + { + cert_validation_t validated, required; + + required = (uintptr_t)value; + validated = (uintptr_t)get(this, t1); + switch (required) + { + case VALIDATION_FAILED: + /* no constraint */ + break; + case VALIDATION_SKIPPED: + if (validated == VALIDATION_SKIPPED) + { + break; + } + /* FALL */ + case VALIDATION_GOOD: + if (validated == VALIDATION_GOOD) + { + break; + } + /* FALL */ + default: + success = FALSE; + if (log_error) + { + DBG1(DBG_CFG, "constraint check failed: %N is %N, " + "but requires at least %N", auth_rule_names, + t1, cert_validation_names, validated, + cert_validation_names, required); + } + break; + } + break; + } + case AUTH_RULE_IDENTITY: + case AUTH_RULE_EAP_IDENTITY: + { + identification_t *id1, *id2; + + id1 = (identification_t*)value; + id2 = get(this, t1); + if (!id2 || !id2->matches(id2, id1)) + { + success = FALSE; + if (log_error) + { + DBG1(DBG_CFG, "constraint check failed: %sidentity '%Y'" + " required ", t1 == AUTH_RULE_IDENTITY ? "" : + "EAP ", id1); + } + } + break; + } + case AUTH_RULE_AUTH_CLASS: + { + if ((uintptr_t)value != AUTH_CLASS_ANY && + (uintptr_t)value != (uintptr_t)get(this, t1)) + { + success = FALSE; + if (log_error) + { + DBG1(DBG_CFG, "constraint requires %N authentication, " + "but %N was used", auth_class_names, (uintptr_t)value, + auth_class_names, (uintptr_t)get(this, t1)); + } + } + break; + } + case AUTH_RULE_EAP_TYPE: + { + if ((uintptr_t)value != (uintptr_t)get(this, t1)) + { + success = FALSE; + if (log_error) + { + DBG1(DBG_CFG, "constraint requires %N, " + "but %N was used", eap_type_names, (uintptr_t)value, + eap_type_names, (uintptr_t)get(this, t1)); + } + } + break; + } + case AUTH_RULE_EAP_VENDOR: + { + if ((uintptr_t)value != (uintptr_t)get(this, t1)) + { + success = FALSE; + if (log_error) + { + DBG1(DBG_CFG, "constraint requires EAP vendor %d, " + "but %d was used", (uintptr_t)value, + (uintptr_t)get(this, t1)); + } + } + break; + } + case AUTH_RULE_GROUP: + { + identification_t *id1, *id2; + + /* for groups, a match of a single group is sufficient */ + has_group = TRUE; + id1 = (identification_t*)value; + e2 = create_enumerator(this); + while (e2->enumerate(e2, &t2, &id2)) + { + if (t2 == AUTH_RULE_GROUP && id2->matches(id2, id1)) + { + group_match = TRUE; + } + } + e2->destroy(e2); + break; + } + case AUTH_HELPER_IM_CERT: + case AUTH_HELPER_SUBJECT_CERT: + case AUTH_HELPER_IM_HASH_URL: + case AUTH_HELPER_SUBJECT_HASH_URL: + /* skip helpers */ + continue; + } + if (!success) + { + break; + } + } + e1->destroy(e1); + + if (has_group && !group_match) + { + if (log_error) + { + DBG1(DBG_CFG, "constraint check failed: group membership required"); + } + return FALSE; + } + return success; +} + +/** + * Implementation of auth_cfg_t.merge. + */ +static void merge(private_auth_cfg_t *this, private_auth_cfg_t *other, bool copy) +{ + if (!other) + { /* nothing to merge */ + return; + } + if (copy) + { + enumerator_t *enumerator; + auth_rule_t type; + void *value; + + enumerator = create_enumerator(other); + while (enumerator->enumerate(enumerator, &type, &value)) + { + switch (type) + { + case AUTH_RULE_CA_CERT: + case AUTH_RULE_IM_CERT: + case AUTH_RULE_SUBJECT_CERT: + case AUTH_HELPER_IM_CERT: + case AUTH_HELPER_SUBJECT_CERT: + { + certificate_t *cert = (certificate_t*)value; + + add(this, type, cert->get_ref(cert)); + break; + } + case AUTH_RULE_CRL_VALIDATION: + case AUTH_RULE_OCSP_VALIDATION: + case AUTH_RULE_AUTH_CLASS: + case AUTH_RULE_EAP_TYPE: + case AUTH_RULE_EAP_VENDOR: + { + add(this, type, (uintptr_t)value); + break; + } + case AUTH_RULE_IDENTITY: + case AUTH_RULE_EAP_IDENTITY: + case AUTH_RULE_GROUP: + { + identification_t *id = (identification_t*)value; + + add(this, type, id->clone(id)); + break; + } + case AUTH_HELPER_IM_HASH_URL: + case AUTH_HELPER_SUBJECT_HASH_URL: + { + add(this, type, strdup((char*)value)); + break; + } + } + } + enumerator->destroy(enumerator); + } + else + { + entry_t *entry; + + while (other->entries->remove_first(other->entries, + (void**)&entry) == SUCCESS) + { + this->entries->insert_last(this->entries, entry); + } + } +} + +/** + * Implementation of auth_cfg_t.equals. + */ +static bool equals(private_auth_cfg_t *this, private_auth_cfg_t *other) +{ + enumerator_t *e1, *e2; + entry_t *i1, *i2; + bool equal = TRUE, found; + + if (this->entries->get_count(this->entries) != + other->entries->get_count(other->entries)) + { + return FALSE; + } + e1 = this->entries->create_enumerator(this->entries); + while (e1->enumerate(e1, &i1)) + { + found = FALSE; + e2 = other->entries->create_enumerator(other->entries); + while (e2->enumerate(e2, &i2)) + { + if (i1->type == i2->type) + { + switch (i1->type) + { + case AUTH_RULE_AUTH_CLASS: + case AUTH_RULE_EAP_TYPE: + case AUTH_RULE_EAP_VENDOR: + case AUTH_RULE_CRL_VALIDATION: + case AUTH_RULE_OCSP_VALIDATION: + { + if (i1->value == i2->value) + { + found = TRUE; + break; + } + continue; + } + case AUTH_RULE_CA_CERT: + case AUTH_RULE_IM_CERT: + case AUTH_RULE_SUBJECT_CERT: + case AUTH_HELPER_IM_CERT: + case AUTH_HELPER_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 AUTH_RULE_IDENTITY: + case AUTH_RULE_EAP_IDENTITY: + case AUTH_RULE_GROUP: + { + identification_t *id1, *id2; + + id1 = (identification_t*)i1->value; + id2 = (identification_t*)i2->value; + + if (id1->equals(id1, id2)) + { + found = TRUE; + break; + } + continue; + } + case AUTH_HELPER_IM_HASH_URL: + case AUTH_HELPER_SUBJECT_HASH_URL: + { + if (streq(i1->value, i2->value)) + { + found = TRUE; + break; + } + continue; + } + } + break; + } + } + e2->destroy(e2); + if (!found) + { + equal = FALSE; + break; + } + } + e1->destroy(e1); + return equal; +} + +/** + * Implementation of auth_cfg_t.purge + */ +static void purge(private_auth_cfg_t *this, bool keep_ca) +{ + entry_t *entry; + linked_list_t *cas; + + cas = linked_list_create(); + while (this->entries->remove_last(this->entries, (void**)&entry) == SUCCESS) + { + if (keep_ca && entry->type == AUTH_RULE_CA_CERT) + { + cas->insert_first(cas, entry); + } + else + { + destroy_entry_value(entry); + free(entry); + } + } + while (cas->remove_last(cas, (void**)&entry) == SUCCESS) + { + this->entries->insert_first(this->entries, entry); + } + cas->destroy(cas); +} + +/** + * Implementation of auth_cfg_t.clone + */ +static auth_cfg_t* clone_(private_auth_cfg_t *this) +{ + enumerator_t *enumerator; + auth_cfg_t *clone; + entry_t *entry; + + clone = auth_cfg_create(); + enumerator = this->entries->create_enumerator(this->entries); + while (enumerator->enumerate(enumerator, &entry)) + { + switch (entry->type) + { + case AUTH_RULE_IDENTITY: + case AUTH_RULE_EAP_IDENTITY: + case AUTH_RULE_GROUP: + { + identification_t *id = (identification_t*)entry->value; + clone->add(clone, entry->type, id->clone(id)); + break; + } + case AUTH_RULE_CA_CERT: + case AUTH_RULE_IM_CERT: + case AUTH_RULE_SUBJECT_CERT: + case AUTH_HELPER_IM_CERT: + case AUTH_HELPER_SUBJECT_CERT: + { + certificate_t *cert = (certificate_t*)entry->value; + clone->add(clone, entry->type, cert->get_ref(cert)); + break; + } + case AUTH_HELPER_IM_HASH_URL: + case AUTH_HELPER_SUBJECT_HASH_URL: + { + clone->add(clone, entry->type, strdup(entry->value)); + break; + } + case AUTH_RULE_AUTH_CLASS: + case AUTH_RULE_EAP_TYPE: + case AUTH_RULE_EAP_VENDOR: + case AUTH_RULE_CRL_VALIDATION: + case AUTH_RULE_OCSP_VALIDATION: + clone->add(clone, entry->type, (uintptr_t)entry->value); + break; + } + } + enumerator->destroy(enumerator); + return clone; +} + +/** + * Implementation of auth_cfg_t.destroy + */ +static void destroy(private_auth_cfg_t *this) +{ + purge(this, FALSE); + this->entries->destroy(this->entries); + free(this); +} + +/* + * see header file + */ +auth_cfg_t *auth_cfg_create() +{ + private_auth_cfg_t *this = malloc_thing(private_auth_cfg_t); + + this->public.add = (void(*)(auth_cfg_t*, auth_rule_t type, ...))add; + this->public.get = (void*(*)(auth_cfg_t*, auth_rule_t type))get; + this->public.create_enumerator = (enumerator_t*(*)(auth_cfg_t*))create_enumerator; + this->public.replace = (void(*)(auth_cfg_t*,enumerator_t*,auth_rule_t,...))replace; + this->public.complies = (bool(*)(auth_cfg_t*, auth_cfg_t *,bool))complies; + this->public.merge = (void(*)(auth_cfg_t*, auth_cfg_t *other,bool))merge; + this->public.purge = (void(*)(auth_cfg_t*,bool))purge; + this->public.equals = (bool(*)(auth_cfg_t*, auth_cfg_t *other))equals; + this->public.clone = (auth_cfg_t*(*)(auth_cfg_t*))clone_; + this->public.destroy = (void(*)(auth_cfg_t*))destroy; + + this->entries = linked_list_create(); + + return &this->public; +} diff --git a/src/libstrongswan/credentials/auth_cfg.h b/src/libstrongswan/credentials/auth_cfg.h new file mode 100644 index 000000000..713e16372 --- /dev/null +++ b/src/libstrongswan/credentials/auth_cfg.h @@ -0,0 +1,255 @@ +/* + * Copyright (C) 2007-2009 Martin Willi + * Copyright (C) 2008 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/** + * @defgroup auth_cfg auth_cfg + * @{ @ingroup credentials + */ + +#ifndef AUTH_CFG_H_ +#define AUTH_CFG_H_ + +#include <utils/enumerator.h> + +typedef struct auth_cfg_t auth_cfg_t; +typedef enum auth_rule_t auth_rule_t; +typedef enum auth_class_t auth_class_t; +typedef enum eap_type_t eap_type_t; + +/** + * Class of authentication to use. This is different to auth_method_t in that + * it does not specify a method, but a class of acceptable methods. The found + * certificate finally dictates wich method is used. + */ +enum auth_class_t { + /** any class acceptable */ + AUTH_CLASS_ANY = 0, + /** authentication using public keys (RSA, ECDSA) */ + AUTH_CLASS_PUBKEY = 1, + /** authentication using a pre-shared secrets */ + AUTH_CLASS_PSK = 2, + /** authentication using EAP */ + AUTH_CLASS_EAP = 3, +}; + +/** + * enum strings for auth_class_t + */ +extern enum_name_t *auth_class_names; + +/** + * EAP types, defines the EAP method implementation + */ +enum eap_type_t { + EAP_IDENTITY = 1, + EAP_NOTIFICATION = 2, + EAP_NAK = 3, + EAP_MD5 = 4, + EAP_OTP = 5, + EAP_GTC = 6, + EAP_SIM = 18, + EAP_AKA = 23, + EAP_MSCHAPV2 = 26, + /** not a method, but an implementation providing different methods */ + EAP_RADIUS = 253, + EAP_EXPANDED = 254, + EAP_EXPERIMENTAL = 255, +}; + +/** + * enum names for eap_type_t. + */ +extern enum_name_t *eap_type_names; + +/** + * short string enum names for eap_type_t. + */ +extern enum_name_t *eap_type_short_names; + +/** + * Authentication config to use during authentication process. + * + * Each authentication config contains a set of rules. These rule-sets are used + * in two ways: + * - For configs specifying local authentication behavior, the rules define + * which authentication method in which way. + * - For configs specifying remote peer authentication, the rules define + * constraints the peer has to fullfill. + * + * Additionally to the rules, there is a set of helper items. These are used + * to transport credentials during the authentication process. + */ +enum auth_rule_t { + + /** identity to use for IKEv2 authentication exchange, identification_t* */ + AUTH_RULE_IDENTITY, + /** authentication class, auth_class_t */ + AUTH_RULE_AUTH_CLASS, + /** EAP identity to use within EAP-Identity exchange, identification_t* */ + AUTH_RULE_EAP_IDENTITY, + /** EAP type to propose for peer authentication, eap_type_t */ + AUTH_RULE_EAP_TYPE, + /** EAP vendor for vendor specific type, u_int32_t */ + AUTH_RULE_EAP_VENDOR, + /** certificate authority, certificate_t* */ + AUTH_RULE_CA_CERT, + /** intermediate certificate in trustchain, certificate_t* */ + AUTH_RULE_IM_CERT, + /** subject certificate, certificate_t* */ + AUTH_RULE_SUBJECT_CERT, + /** result of a CRL validation, cert_validation_t */ + AUTH_RULE_CRL_VALIDATION, + /** result of a OCSP validation, cert_validation_t */ + AUTH_RULE_OCSP_VALIDATION, + /** subject is member of a group, identification_t* + * The group membership constraint is fulfilled if the subject is member of + * one group defined in the constraints. */ + AUTH_RULE_GROUP, + + /** intermediate certificate, certificate_t* */ + AUTH_HELPER_IM_CERT, + /** subject certificate, certificate_t* */ + AUTH_HELPER_SUBJECT_CERT, + /** Hash and URL of a intermediate certificate, char* */ + AUTH_HELPER_IM_HASH_URL, + /** Hash and URL of a end-entity certificate, char* */ + AUTH_HELPER_SUBJECT_HASH_URL, +}; + +/** + * enum name for auth_rule_t. + */ +extern enum_name_t *auth_rule_names; + +/** + * Authentication/Authorization round. + * + * RFC4739 defines multiple authentication rounds. This class defines such + * a round from a configuration perspective, either for the local or the remote + * peer. Local config are called "rulesets", as they define how we authenticate. + * Remote peer configs are called "constraits", they define what is needed to + * complete the authentication round successfully. + * + * @verbatim + + [Repeat for each configuration] + +--------------------------------------------------+ + | | + | | + | +----------+ IKE_AUTH +--------- + | + | | config | -----------> | | | + | | ruleset | | | | + | +----------+ [ <----------- ] | | | + | [ optional EAP ] | Peer | | + | +----------+ [ -----------> ] | | | + | | config | | | | + | | constr. | <----------- | | | + | +----------+ IKE_AUTH +--------- + | + | | + | | + +--------------------------------------------------+ + + @endverbatim + * + * Values for each items are either pointers (casted to void*) or short + * integers (use uintptr_t cast). + */ +struct auth_cfg_t { + + /** + * Add an rule to the set. + * + * @param rule rule type + * @param ... associated value to rule + */ + void (*add)(auth_cfg_t *this, auth_rule_t rule, ...); + + /** + * Get an rule value. + * + * @param rule rule type + * @return bool if item has been found + */ + void* (*get)(auth_cfg_t *this, auth_rule_t rule); + + /** + * Create an enumerator over added rules. + * + * @return enumerator over (auth_rule_t, union{void*,uintpr_t}) + */ + enumerator_t* (*create_enumerator)(auth_cfg_t *this); + + /** + * Replace an rule at enumerator position. + * + * @param pos enumerator position position + * @param rule rule type + * @param ... associated value to rule + */ + void (*replace)(auth_cfg_t *this, enumerator_t *pos, + auth_rule_t rule, ...); + + /** + * Check if a used config fulfills a set of configured constraints. + * + * @param constraints required authorization rules + * @param log_error wheter to log compliance errors + * @return TRUE if this complies with constraints + */ + bool (*complies)(auth_cfg_t *this, auth_cfg_t *constraints, bool log_error); + + /** + * Merge items from other into this. + * + * @param other items to read for merge + * @param copy TRUE to copy items, FALSE to move them + */ + void (*merge)(auth_cfg_t *this, auth_cfg_t *other, bool copy); + + /** + * Purge all rules in a config. + * + * @param keep_ca wheter to keep AUTH_RULE_CA_CERT entries + */ + void (*purge)(auth_cfg_t *this, bool keep_ca); + + /** + * Check two configs for equality. + * + * @param other other config to compaire against this + * @return TRUE if auth infos identical + */ + bool (*equals)(auth_cfg_t *this, auth_cfg_t *other); + + /** + * Clone a authentication config, including all rules. + * + * @return cloned configuration + */ + auth_cfg_t* (*clone)(auth_cfg_t *this); + + /** + * Destroy a config with all associated rules/values. + */ + void (*destroy)(auth_cfg_t *this); +}; + +/** + * Create a authentication config. + */ +auth_cfg_t *auth_cfg_create(); + +#endif /** AUTH_CFG_H_ @}*/ diff --git a/src/libstrongswan/credentials/builder.c b/src/libstrongswan/credentials/builder.c index 8be1c1576..cfb708e33 100644 --- a/src/libstrongswan/credentials/builder.c +++ b/src/libstrongswan/credentials/builder.c @@ -44,6 +44,7 @@ ENUM(builder_part_names, BUILD_FROM_FILE, BUILD_END, "BUILD_OCSP_ACCESS_LOCATIONS", "BUILD_PATHLEN", "BUILD_X509_FLAG", + "BUILD_REVOKED_ENUMERATOR", "BUILD_SMARTCARD_KEYID", "BUILD_SMARTCARD_PIN", "BUILD_RSA_MODULUS", diff --git a/src/libstrongswan/credentials/builder.h b/src/libstrongswan/credentials/builder.h index 62a6ffaaf..ffb09f72a 100644 --- a/src/libstrongswan/credentials/builder.h +++ b/src/libstrongswan/credentials/builder.h @@ -101,6 +101,8 @@ enum builder_part_t { BUILD_PATHLEN, /** enforce an additional X509 flag, x509_flag_t */ BUILD_X509_FLAG, + /** enumerator_t over (chunk_t serial, time_t date, crl_reason_t reason) */ + BUILD_REVOKED_ENUMERATOR, /** key ID of a key on a smartcard, null terminated char* ([slot:]keyid) */ BUILD_SMARTCARD_KEYID, /** pin to access a key on a smartcard, null terminated char* */ diff --git a/src/libstrongswan/credentials/cert_validator.h b/src/libstrongswan/credentials/cert_validator.h new file mode 100644 index 000000000..1e67c23ab --- /dev/null +++ b/src/libstrongswan/credentials/cert_validator.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2010 Martin Willi + * Copyright (C) 2010 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/** + * @defgroup cert_validator cert_validator + * @{ @ingroup credentials + */ + +#ifndef CERT_VALIDATOR_H_ +#define CERT_VALIDATOR_H_ + +typedef struct cert_validator_t cert_validator_t; + +#include <library.h> + +/** + * Certificate validator interface. + * + * A certificate validator checks constraints or revocation in a certificate + * or its issuing CA certificate. The interface allows plugins to do + * revocation checking or similar tasks. + */ +struct cert_validator_t { + + /** + * Validate a subject certificate in relation to its issuer. + * + * @param subject subject certificate to check + * @param issuer issuer of subject + * @param online wheter to do online revocation checking + * @param pathlen the current length of the path up to the root CA + * @param auth container for resulting authentication info + */ + bool (*validate)(cert_validator_t *this, certificate_t *subject, + certificate_t *issuer, bool online, int pathlen, + auth_cfg_t *auth); +}; + +#endif /** CERT_VALIDATOR_H_ @}*/ diff --git a/src/libstrongswan/credentials/certificates/certificate.c b/src/libstrongswan/credentials/certificates/certificate.c index 156d12358..661b69e36 100644 --- a/src/libstrongswan/credentials/certificates/certificate.c +++ b/src/libstrongswan/credentials/certificates/certificate.c @@ -15,6 +15,7 @@ #include "certificate.h" +#include <debug.h> #include <credentials/certificates/x509.h> ENUM(certificate_type_names, CERT_ANY, CERT_PLUTO_CRL, @@ -40,3 +41,24 @@ ENUM(cert_validation_names, VALIDATION_GOOD, VALIDATION_REVOKED, "REVOKED", ); +/** + * See header + */ +bool certificate_is_newer(certificate_t *this, certificate_t *other) +{ + time_t this_update, that_update; + char *type = "certificate"; + bool newer; + + if (this->get_type(this) == CERT_X509_CRL) + { + type = "crl"; + } + this->get_validity(this, NULL, &this_update, NULL); + other->get_validity(other, NULL, &that_update, NULL); + newer = this_update > that_update; + DBG1(DBG_LIB, " %s from %T is %s - existing %s from %T %s", + type, &this_update, FALSE, newer ? "newer" : "not newer", + type, &that_update, FALSE, newer ? "replaced" : "retained"); + return newer; +} diff --git a/src/libstrongswan/credentials/certificates/certificate.h b/src/libstrongswan/credentials/certificates/certificate.h index a4f9aa3e0..43bfe3dc1 100644 --- a/src/libstrongswan/credentials/certificates/certificate.h +++ b/src/libstrongswan/credentials/certificates/certificate.h @@ -28,6 +28,7 @@ typedef enum cert_validation_t cert_validation_t; #include <library.h> #include <utils/identification.h> #include <credentials/keys/public_key.h> +#include <credentials/cred_encoding.h> /** * Kind of a certificate_t @@ -163,18 +164,14 @@ struct certificate_t { time_t *not_before, time_t *not_after); /** - * Is this newer than that? + * Get the certificate in an encoded form as a chunk. * - * @return TRUE if newer, FALSE otherwise + * @param type type of the encoding, one of CERT_* + * @param encoding encoding of the key, allocated + * @return TRUE if encoding supported */ - bool (*is_newer)(certificate_t *this, certificate_t *that); - - /** - * Get the certificate in an encoded form. - * - * @return allocated chunk of encoded cert - */ - chunk_t (*get_encoding)(certificate_t *this); + bool (*get_encoding)(certificate_t *this, cred_encoding_type_t type, + chunk_t *encoding); /** * Check if two certificates are equal. @@ -197,4 +194,13 @@ struct certificate_t { void (*destroy)(certificate_t *this); }; +/** + * Generic check if a given certificate is newer than another. + * + * @param this first certificate to check + * @param other second certificate + * @return TRUE if this newer than other + */ +bool certificate_is_newer(certificate_t *this, certificate_t *other); + #endif /** CERTIFICATE_H_ @}*/ diff --git a/src/libstrongswan/credentials/certificates/crl.c b/src/libstrongswan/credentials/certificates/crl.c index 085ad16cc..69bd80b84 100644 --- a/src/libstrongswan/credentials/certificates/crl.c +++ b/src/libstrongswan/credentials/certificates/crl.c @@ -16,6 +16,8 @@ #include "crl.h" +#include <debug.h> + ENUM(crl_reason_names, CRL_REASON_UNSPECIFIED, CRL_REASON_REMOVE_FROM_CRL, "unspecified", "key compromise", @@ -27,3 +29,29 @@ ENUM(crl_reason_names, CRL_REASON_UNSPECIFIED, CRL_REASON_REMOVE_FROM_CRL, "reason #7", "remove from crl", ); + +/** + * Check if this CRL is newer + */ +bool crl_is_newer(crl_t *this, crl_t *other) +{ + chunk_t this_num, other_num; + bool newer; + + this_num = this->get_serial(this); + other_num = other->get_serial(other); + + /* compare crlNumbers if available - otherwise use generic cert compare */ + if (this_num.ptr != NULL && other_num.ptr != NULL) + { + newer = chunk_compare(this_num, other_num) > 0; + DBG1(DBG_LIB, " crl #%#B is %s - existing crl #%#B %s", + &this_num, newer ? "newer" : "not newer", + &other_num, newer ? "replaced" : "retained"); + } + else + { + newer = certificate_is_newer(&this->certificate, &other->certificate); + } + return newer; +} diff --git a/src/libstrongswan/credentials/certificates/crl.h b/src/libstrongswan/credentials/certificates/crl.h index 4b612390c..9425311fb 100644 --- a/src/libstrongswan/credentials/certificates/crl.h +++ b/src/libstrongswan/credentials/certificates/crl.h @@ -80,7 +80,15 @@ struct crl_t { * @return enumerator over revoked certificates. */ enumerator_t* (*create_enumerator)(crl_t *this); - }; +/** + * Generic check if a given CRL is newer than another. + * + * @param this first CRL to check + * @param other second CRL + * @return TRUE if this newer than other + */ +bool crl_is_newer(crl_t *this, crl_t *other); + #endif /** CRL_H_ @}*/ diff --git a/src/libstrongswan/credentials/certificates/x509.h b/src/libstrongswan/credentials/certificates/x509.h index 172bd9696..6e0a5002a 100644 --- a/src/libstrongswan/credentials/certificates/x509.h +++ b/src/libstrongswan/credentials/certificates/x509.h @@ -25,7 +25,6 @@ #include <credentials/certificates/certificate.h> #define X509_NO_PATH_LEN_CONSTRAINT -1 -#define X509_MAX_PATH_LEN 7 typedef struct x509_t x509_t; typedef enum x509_flag_t x509_flag_t; diff --git a/src/libstrongswan/credentials/keys/key_encoding.c b/src/libstrongswan/credentials/cred_encoding.c index 89b25226c..edd76205b 100644 --- a/src/libstrongswan/credentials/keys/key_encoding.c +++ b/src/libstrongswan/credentials/cred_encoding.c @@ -13,7 +13,7 @@ * for more details. */ -#include "key_encoding.h" +#include "cred_encoding.h" #include <stdint.h> @@ -21,25 +21,25 @@ #include <utils/hashtable.h> #include <threading/rwlock.h> -typedef struct private_key_encoding_t private_key_encoding_t; +typedef struct private_cred_encoding_t private_cred_encoding_t; /** - * Private data of an key_encoding_t object. + * Private data of an cred_encoding_t object. */ -struct private_key_encoding_t { +struct private_cred_encoding_t { /** - * Public key_encoding_t interface. + * Public cred_encoding_t interface. */ - key_encoding_t public; + cred_encoding_t public; /** * cached encodings, a table for each encoding_type_t, containing chunk_t* */ - hashtable_t *cache[KEY_ENCODING_MAX]; + hashtable_t *cache[CRED_ENCODING_MAX]; /** - * Registered encoding fuctions, key_encoder_t + * Registered encoding fuctions, cred_encoder_t */ linked_list_t *encoders; @@ -52,7 +52,7 @@ struct private_key_encoding_t { /** * See header. */ -bool key_encoding_args(va_list args, ...) +bool cred_encoding_args(va_list args, ...) { va_list parts, copy; bool failed = FALSE; @@ -61,12 +61,12 @@ bool key_encoding_args(va_list args, ...) while (!failed) { - key_encoding_part_t current, target; + cred_encoding_part_t current, target; chunk_t *out, data; /* get the part we are looking for */ - target = va_arg(parts, key_encoding_part_t); - if (target == KEY_PART_END) + target = va_arg(parts, cred_encoding_part_t); + if (target == CRED_PART_END) { break; } @@ -75,8 +75,8 @@ bool key_encoding_args(va_list args, ...) va_copy(copy, args); while (!failed) { - current = va_arg(copy, key_encoding_part_t); - if (current == KEY_PART_END) + current = va_arg(copy, cred_encoding_part_t); + if (current == CRED_PART_END) { failed = TRUE; break; @@ -111,14 +111,14 @@ static bool equals(void *key1, void *key2) } /** - * Implementation of key_encoding_t.get_cache + * Implementation of cred_encoding_t.get_cache */ -static bool get_cache(private_key_encoding_t *this, key_encoding_type_t type, +static bool get_cache(private_cred_encoding_t *this, cred_encoding_type_t type, void *cache, chunk_t *encoding) { chunk_t *chunk; - if (type >= KEY_ENCODING_MAX || type < 0) + if (type >= CRED_ENCODING_MAX || type < 0) { return FALSE; } @@ -133,18 +133,18 @@ static bool get_cache(private_key_encoding_t *this, key_encoding_type_t type, } /** - * Implementation of key_encoding_t.encode + * Implementation of cred_encoding_t.encode */ -static bool encode(private_key_encoding_t *this, key_encoding_type_t type, +static bool encode(private_cred_encoding_t *this, cred_encoding_type_t type, void *cache, chunk_t *encoding, ...) { enumerator_t *enumerator; va_list args, copy; - key_encoder_t encode; + cred_encoder_t encode; bool success = FALSE; chunk_t *chunk; - if (type >= KEY_ENCODING_MAX || type < 0) + if (type >= CRED_ENCODING_MAX || type < 0) { return FALSE; } @@ -168,32 +168,33 @@ static bool encode(private_key_encoding_t *this, key_encoding_type_t type, va_end(copy); if (success) { - if (cache) - { - chunk = malloc_thing(chunk_t); - *chunk = *encoding; - this->lock->unlock(this->lock); - this->lock->write_lock(this->lock); - this->cache[type]->put(this->cache[type], cache, chunk); - } break; } } enumerator->destroy(enumerator); - va_end(args); this->lock->unlock(this->lock); + va_end(args); + + if (success && cache) + { + chunk = malloc_thing(chunk_t); + *chunk = *encoding; + this->lock->write_lock(this->lock); + this->cache[type]->put(this->cache[type], cache, chunk); + this->lock->unlock(this->lock); + } return success; } /** - * Implementation of key_encoding_t.cache + * Implementation of cred_encoding_t.cache */ -static void cache(private_key_encoding_t *this, key_encoding_type_t type, +static void cache(private_cred_encoding_t *this, cred_encoding_type_t type, void *cache, chunk_t encoding) { chunk_t *chunk; - if (type >= KEY_ENCODING_MAX || type < 0) + if (type >= CRED_ENCODING_MAX || type < 0) { return free(encoding.ptr); } @@ -211,15 +212,15 @@ static void cache(private_key_encoding_t *this, key_encoding_type_t type, } /** - * Implementation of key_encoding_t.clear_cache + * Implementation of cred_encoding_t.clear_cache */ -static void clear_cache(private_key_encoding_t *this, void *cache) +static void clear_cache(private_cred_encoding_t *this, void *cache) { - key_encoding_type_t type; + cred_encoding_type_t type; chunk_t *chunk; this->lock->write_lock(this->lock); - for (type = 0; type < KEY_ENCODING_MAX; type++) + for (type = 0; type < CRED_ENCODING_MAX; type++) { chunk = this->cache[type]->remove(this->cache[type], cache); if (chunk) @@ -232,9 +233,9 @@ static void clear_cache(private_key_encoding_t *this, void *cache) } /** - * Implementation of key_encoding_t.add_encoder + * Implementation of cred_encoding_t.add_encoder */ -static void add_encoder(private_key_encoding_t *this, key_encoder_t encoder) +static void add_encoder(private_cred_encoding_t *this, cred_encoder_t encoder) { this->lock->write_lock(this->lock); this->encoders->insert_last(this->encoders, encoder); @@ -242,9 +243,9 @@ static void add_encoder(private_key_encoding_t *this, key_encoder_t encoder) } /** - * Implementation of key_encoding_t.remove_encoder + * Implementation of cred_encoding_t.remove_encoder */ -static void remove_encoder(private_key_encoding_t *this, key_encoder_t encoder) +static void remove_encoder(private_cred_encoding_t *this, cred_encoder_t encoder) { this->lock->write_lock(this->lock); this->encoders->remove(this->encoders, encoder, NULL); @@ -252,18 +253,18 @@ static void remove_encoder(private_key_encoding_t *this, key_encoder_t encoder) } /** - * Implementation of key_encoder_t.destroy. + * Implementation of cred_encoder_t.destroy. */ -static void destroy(private_key_encoding_t *this) +static void destroy(private_cred_encoding_t *this) { - key_encoding_type_t type; + cred_encoding_type_t type; - for (type = 0; type < KEY_ENCODING_MAX; type++) + for (type = 0; type < CRED_ENCODING_MAX; type++) { - /* We explicitly do not free remaining encodings. All keys should + /* We explicitly do not free remaining encodings. All creds should * have gone now, and they are responsible for cleaning out their * cache entries. Not flushing here allows the leak detective to - * complain if a key did not flush cached encodings. */ + * complain if a credential did not flush cached encodings. */ this->cache[type]->destroy(this->cache[type]); } this->encoders->destroy(this->encoders); @@ -274,20 +275,20 @@ static void destroy(private_key_encoding_t *this) /** * See header */ -key_encoding_t *key_encoding_create() +cred_encoding_t *cred_encoding_create() { - private_key_encoding_t *this = malloc_thing(private_key_encoding_t); - key_encoding_type_t type; - - this->public.encode = (bool(*)(key_encoding_t*, key_encoding_type_t type, void *cache, chunk_t *encoding, ...))encode; - this->public.get_cache = (bool(*)(key_encoding_t*, key_encoding_type_t type, void *cache, chunk_t *encoding))get_cache; - this->public.cache = (void(*)(key_encoding_t*, key_encoding_type_t type, void *cache, chunk_t encoding))cache; - this->public.clear_cache = (void(*)(key_encoding_t*, void *cache))clear_cache; - this->public.add_encoder = (void(*)(key_encoding_t*, key_encoder_t encoder))add_encoder; - this->public.remove_encoder = (void(*)(key_encoding_t*, key_encoder_t encoder))remove_encoder; - this->public.destroy = (void(*)(key_encoding_t*))destroy; - - for (type = 0; type < KEY_ENCODING_MAX; type++) + private_cred_encoding_t *this = malloc_thing(private_cred_encoding_t); + cred_encoding_type_t type; + + this->public.encode = (bool(*)(cred_encoding_t*, cred_encoding_type_t type, void *cache, chunk_t *encoding, ...))encode; + this->public.get_cache = (bool(*)(cred_encoding_t*, cred_encoding_type_t type, void *cache, chunk_t *encoding))get_cache; + this->public.cache = (void(*)(cred_encoding_t*, cred_encoding_type_t type, void *cache, chunk_t encoding))cache; + this->public.clear_cache = (void(*)(cred_encoding_t*, void *cache))clear_cache; + this->public.add_encoder = (void(*)(cred_encoding_t*, cred_encoder_t encoder))add_encoder; + this->public.remove_encoder = (void(*)(cred_encoding_t*, cred_encoder_t encoder))remove_encoder; + this->public.destroy = (void(*)(cred_encoding_t*))destroy; + + for (type = 0; type < CRED_ENCODING_MAX; type++) { this->cache[type] = hashtable_create(hash, equals, 8); } diff --git a/src/libstrongswan/credentials/cred_encoding.h b/src/libstrongswan/credentials/cred_encoding.h new file mode 100644 index 000000000..e2d69691e --- /dev/null +++ b/src/libstrongswan/credentials/cred_encoding.h @@ -0,0 +1,224 @@ +/* + * Copyright (C) 2009 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/** + * @defgroup cred_encoding cred_encoding + * @{ @ingroup credentials + */ + +#ifndef CRED_ENCODING_H_ +#define CRED_ENCODING_H_ + +typedef struct cred_encoding_t cred_encoding_t; +typedef enum cred_encoding_type_t cred_encoding_type_t; +typedef enum cred_encoding_part_t cred_encoding_part_t; + +#include <library.h> + +/** + * Credential encoder function implementing encoding/fingerprinting. + * + * The variable argument list takes cred_encoding_part_t, followed by part + * specific arguments, terminated by KEY_PART_END. + * + * @param type format to encode the credential to + * @param args list of (cred_encoding_part_t, data) + * @param encoding encoding result, allocated + * @return TRUE if encoding successful + */ +typedef bool (*cred_encoder_t)(cred_encoding_type_t type, chunk_t *encoding, + va_list args); + +/** + * Helper function for cred_encoder_t implementations to parse argument list. + * + * Credential encoder functions get a variable argument list to parse. To + * simplify the job, this function reads the arguments and returns chunks for + * each part. + * The argument list of this function takes a cred_encoding_part_t, followed + * by a data pointer receiving the value, terminated by CRED_PART_END. + * + * @param args argument list passed to credential encoder function + * @param ... list of (cred_encoding_part_t, data*) + * @return TRUE if all parts found, FALSE otherwise + */ +bool cred_encoding_args(va_list args, ...); + +/** + * Encoding type of a fingerprint/credential. + * + * Fingerprints have have the KEYID_*, public keys the PUBKEY_* and + * private keys the PRIVKEY_* prefix. + */ +enum cred_encoding_type_t { + /** SHA1 fingerprint over subjectPublicKeyInfo */ + KEYID_PUBKEY_INFO_SHA1 = 0, + /** SHA1 fingerprint over subjectPublicKey */ + KEYID_PUBKEY_SHA1, + /** PGPv3 fingerprint */ + KEYID_PGPV3, + /** PGPv4 fingerprint */ + KEYID_PGPV4, + + KEYID_MAX, + + /** PKCS#1 and similar ASN.1 key encoding */ + PUBKEY_ASN1_DER, + PRIVKEY_ASN1_DER, + /** subjectPublicKeyInfo encoding */ + PUBKEY_SPKI_ASN1_DER, + /** PEM encoded PKCS#1 key */ + PUBKEY_PEM, + PRIVKEY_PEM, + /** PGP key encoding */ + PUBKEY_PGP, + PRIVKEY_PGP, + + /** ASN.1 DER encoded certificate */ + CERT_ASN1_DER, + /** PEM encoded certificate */ + CERT_PEM, + /** PGP Packet encoded certificate */ + CERT_PGP_PKT, + + CRED_ENCODING_MAX, +}; + +/** + * Parts of a credential to encode. + */ +enum cred_encoding_part_t { + /** modulus of a RSA key, n */ + CRED_PART_RSA_MODULUS, + /** public exponent of a RSA key, e */ + CRED_PART_RSA_PUB_EXP, + /** private exponent of a RSA key, d */ + CRED_PART_RSA_PRIV_EXP, + /** prime1 a RSA key, p */ + CRED_PART_RSA_PRIME1, + /** prime2 a RSA key, q */ + CRED_PART_RSA_PRIME2, + /** exponent1 a RSA key, exp1 */ + CRED_PART_RSA_EXP1, + /** exponent1 a RSA key, exp2 */ + CRED_PART_RSA_EXP2, + /** coefficient of RSA key, coeff */ + CRED_PART_RSA_COEFF, + /** a DER encoded RSA public key */ + CRED_PART_RSA_PUB_ASN1_DER, + /** a DER encoded RSA private key */ + CRED_PART_RSA_PRIV_ASN1_DER, + /** a DER encoded ECDSA public key */ + CRED_PART_ECDSA_PUB_ASN1_DER, + /** a DER encoded ECDSA private key */ + CRED_PART_ECDSA_PRIV_ASN1_DER, + /** a DER encoded X509 certificate */ + CRED_PART_X509_ASN1_DER, + /** a DER encoded X509 CRL */ + CRED_PART_X509_CRL_ASN1_DER, + /** a DER encoded X509 OCSP request */ + CRED_PART_X509_OCSP_REQ_ASN1_DER, + /** a DER encoded X509 OCSP response */ + CRED_PART_X509_OCSP_RES_ASN1_DER, + /** a DER encoded X509 attribute certificate */ + CRED_PART_X509_AC_ASN1_DER, + /** a DER encoded PKCS10 certificate request */ + CRED_PART_PKCS10_ASN1_DER, + /** a PGP encoded certificate */ + CRED_PART_PGP_CERT, + + CRED_PART_END, +}; + +/** + * Credential encoding and fingerprinting facility. + */ +struct cred_encoding_t { + + /** + * Encode a credential in a format using several parts, optional caching. + * + * The variable argument list takes cred_encoding_part_t, followed by part + * specific arguments, terminated by CRED_PART_END. + * If a cache key is given, the returned encoding points to internal data: + * do not free or modify. If no cache key is given, the encoding is + * allocated and must be freed by the caller. + * + * @param type format the credential should be encoded to + * @param cache key to use for caching, NULL to not cache + * @param encoding encoding result, allocated if caching disabled + * @param ... list of (cred_encoding_part_t, data) + * @return TRUE if encoding successful + */ + bool (*encode)(cred_encoding_t *this, cred_encoding_type_t type, void *cache, + chunk_t *encoding, ...); + + /** + * Clear all cached encodings of a given cache key. + * + * @param cache key used in encode() for caching + */ + void (*clear_cache)(cred_encoding_t *this, void *cache); + + /** + * Check for a cached encoding. + * + * @param type format of the credential encoding + * @param cache key to use for caching, as given to encode() + * @param encoding encoding result, internal data + * @return TRUE if cache entry found + */ + bool (*get_cache)(cred_encoding_t *this, cred_encoding_type_t type, + void *cache, chunk_t *encoding); + + /** + * Cache a credential encoding created externally. + * + * After calling cache(), the passed encoding is owned by the cred encoding + * facility. + * + * @param type format of the credential encoding + * @param cache key to use for caching, as given to encode() + * @param encoding encoding to cache, gets owned by this + */ + void (*cache)(cred_encoding_t *this, cred_encoding_type_t type, void *cache, + chunk_t encoding); + + /** + * Register a credential encoder function. + * + * @param encoder credential encoder function to add + */ + void (*add_encoder)(cred_encoding_t *this, cred_encoder_t encoder); + + /** + * Unregister a previously registered credential encoder function. + * + * @param encoder credential encoder function to remove + */ + void (*remove_encoder)(cred_encoding_t *this, cred_encoder_t encoder); + + /** + * Destroy a cred_encoding_t. + */ + void (*destroy)(cred_encoding_t *this); +}; + +/** + * Create a cred_encoding instance. + */ +cred_encoding_t *cred_encoding_create(); + +#endif /** CRED_ENCODING_H_ @}*/ diff --git a/src/libstrongswan/credentials/credential_manager.c b/src/libstrongswan/credentials/credential_manager.c new file mode 100644 index 000000000..46c36c941 --- /dev/null +++ b/src/libstrongswan/credentials/credential_manager.c @@ -0,0 +1,1097 @@ +/* + * Copyright (C) 2007 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "credential_manager.h" + +#include <library.h> +#include <debug.h> +#include <threading/thread_value.h> +#include <threading/mutex.h> +#include <threading/rwlock.h> +#include <utils/linked_list.h> +#include <credentials/sets/cert_cache.h> +#include <credentials/sets/auth_cfg_wrapper.h> +#include <credentials/certificates/x509.h> + +/** + * Maximum length of a certificate trust chain + */ +#define MAX_TRUST_PATH_LEN 7 + +typedef struct private_credential_manager_t private_credential_manager_t; + +/** + * private data of credential_manager + */ +struct private_credential_manager_t { + + /** + * public functions + */ + credential_manager_t public; + + /** + * list of credential sets + */ + linked_list_t *sets; + + /** + * thread local set of credentials, linked_list_t with credential_set_t's + */ + thread_value_t *local_sets; + + /** + * trust relationship and certificate cache + */ + cert_cache_t *cache; + + /** + * certificates queued for persistent caching + */ + linked_list_t *cache_queue; + + /** + * list of certificate validators, cert_validator_t + */ + linked_list_t *validators; + + /** + * read-write lock to sets list + */ + rwlock_t *lock; + + /** + * mutex for cache queue + */ + mutex_t *queue_mutex; +}; + +/** data to pass to create_private_enumerator */ +typedef struct { + private_credential_manager_t *this; + key_type_t type; + identification_t* keyid; +} private_data_t; + +/** data to pass to create_cert_enumerator */ +typedef struct { + private_credential_manager_t *this; + certificate_type_t cert; + key_type_t key; + identification_t *id; + bool trusted; +} cert_data_t; + +/** data to pass to create_cdp_enumerator */ +typedef struct { + private_credential_manager_t *this; + certificate_type_t type; + identification_t *id; +} cdp_data_t; + +/** data to pass to create_shared_enumerator */ +typedef struct { + private_credential_manager_t *this; + shared_key_type_t type; + identification_t *me; + identification_t *other; +} shared_data_t; + +/** enumerator over local and global sets */ +typedef struct { + /** implements enumerator_t */ + enumerator_t public; + /** enumerator over global sets */ + enumerator_t *global; + /** enumerator over local sets */ + enumerator_t *local; +} sets_enumerator_t; + + +METHOD(enumerator_t, sets_enumerate, bool, + sets_enumerator_t *this, credential_set_t **set) +{ + if (this->global) + { + if (this->global->enumerate(this->global, set)) + { + return TRUE; + } + /* end of global sets, look for local */ + this->global->destroy(this->global); + this->global = NULL; + } + if (this->local) + { + return this->local->enumerate(this->local, set); + } + return FALSE; +} + +METHOD(enumerator_t, sets_destroy, void, + sets_enumerator_t *this) +{ + DESTROY_IF(this->global); + DESTROY_IF(this->local); + free(this); +} + +/** + * create an enumerator over both, global and local sets + */ +static enumerator_t *create_sets_enumerator(private_credential_manager_t *this) +{ + sets_enumerator_t *enumerator; + linked_list_t *local; + + INIT(enumerator, + .public.enumerate = (void*)_sets_enumerate, + .public.destroy = _sets_destroy, + .global = this->sets->create_enumerator(this->sets), + ); + local = this->local_sets->get(this->local_sets); + if (local) + { + enumerator->local = local->create_enumerator(local); + } + return &enumerator->public; +} + +/** + * cleanup function for cert data + */ +static void destroy_cert_data(cert_data_t *data) +{ + data->this->lock->unlock(data->this->lock); + free(data); +} + +/** + * enumerator constructor for certificates + */ +static enumerator_t *create_cert(credential_set_t *set, cert_data_t *data) +{ + return set->create_cert_enumerator(set, data->cert, data->key, + data->id, data->trusted); +} + +METHOD(credential_manager_t, create_cert_enumerator, enumerator_t*, + private_credential_manager_t *this, certificate_type_t certificate, + key_type_t key, identification_t *id, bool trusted) +{ + cert_data_t *data = malloc_thing(cert_data_t); + data->this = this; + data->cert = certificate; + data->key = key; + data->id = id; + data->trusted = trusted; + + this->lock->read_lock(this->lock); + return enumerator_create_nested(create_sets_enumerator(this), + (void*)create_cert, data, + (void*)destroy_cert_data); +} + +METHOD(credential_manager_t, get_cert, certificate_t*, + private_credential_manager_t *this, certificate_type_t cert, key_type_t key, + identification_t *id, bool trusted) +{ + certificate_t *current, *found = NULL; + enumerator_t *enumerator; + + enumerator = create_cert_enumerator(this, cert, key, id, trusted); + if (enumerator->enumerate(enumerator, ¤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) +{ + data->this->lock->unlock(data->this->lock); + free(data); +} + +/** + * enumerator constructor for CDPs + */ +static enumerator_t *create_cdp(credential_set_t *set, cdp_data_t *data) +{ + return set->create_cdp_enumerator(set, data->type, data->id); +} + +METHOD(credential_manager_t, create_cdp_enumerator, enumerator_t*, + private_credential_manager_t *this, certificate_type_t type, + identification_t *id) +{ + cdp_data_t *data; + + INIT(data, + .this = this, + .type = type, + .id = id, + ); + this->lock->read_lock(this->lock); + return enumerator_create_nested(create_sets_enumerator(this), + (void*)create_cdp, data, + (void*)destroy_cdp_data); +} + +/** + * cleanup function for private data + */ +static void destroy_private_data(private_data_t *data) +{ + data->this->lock->unlock(data->this->lock); + free(data); +} + +/** + * enumerator constructor for private keys + */ +static enumerator_t *create_private(credential_set_t *set, private_data_t *data) +{ + return set->create_private_enumerator(set, data->type, data->keyid); +} + +/** + * Create an enumerator over private keys + */ +static enumerator_t *create_private_enumerator( + private_credential_manager_t *this, key_type_t key, identification_t *keyid) +{ + private_data_t *data; + + INIT(data, + .this = this, + .type = key, + .keyid = keyid, + ); + this->lock->read_lock(this->lock); + return enumerator_create_nested(create_sets_enumerator(this), + (void*)create_private, data, + (void*)destroy_private_data); +} + +/** + * Look up a private key by its key identifier + */ +static private_key_t* get_private_by_keyid(private_credential_manager_t *this, + key_type_t key, identification_t *keyid) +{ + private_key_t *found = NULL; + enumerator_t *enumerator; + + enumerator = create_private_enumerator(this, key, keyid); + if (enumerator->enumerate(enumerator, &found)) + { + found->get_ref(found); + } + enumerator->destroy(enumerator); + return found; +} + +/** + * cleanup function for shared data + */ +static void destroy_shared_data(shared_data_t *data) +{ + data->this->lock->unlock(data->this->lock); + free(data); +} + +/** + * enumerator constructor for shared keys + */ +static enumerator_t *create_shared(credential_set_t *set, shared_data_t *data) +{ + return set->create_shared_enumerator(set, data->type, data->me, data->other); +} + +METHOD(credential_manager_t, create_shared_enumerator, enumerator_t*, + private_credential_manager_t *this, shared_key_type_t type, + identification_t *me, identification_t *other) +{ + shared_data_t *data; + + INIT(data, + .this = this, + .type = type, + .me = me, + .other = other, + ); + this->lock->read_lock(this->lock); + return enumerator_create_nested(create_sets_enumerator(this), + (void*)create_shared, data, + (void*)destroy_shared_data); +} + +METHOD(credential_manager_t, get_shared, shared_key_t*, + private_credential_manager_t *this, shared_key_type_t type, + identification_t *me, identification_t *other) +{ + shared_key_t *current, *found = NULL; + id_match_t *best_me = ID_MATCH_NONE, *best_other = ID_MATCH_NONE; + id_match_t *match_me, *match_other; + enumerator_t *enumerator; + + enumerator = create_shared_enumerator(this, type, me, other); + while (enumerator->enumerate(enumerator, ¤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; +} + +METHOD(credential_manager_t, add_local_set, void, + private_credential_manager_t *this, credential_set_t *set) +{ + linked_list_t *sets; + + sets = this->local_sets->get(this->local_sets); + if (!sets) + { /* first invocation */ + sets = linked_list_create(); + this->local_sets->set(this->local_sets, sets); + } + sets->insert_last(sets, set); +} + +METHOD(credential_manager_t, remove_local_set, void, + private_credential_manager_t *this, credential_set_t *set) +{ + linked_list_t *sets; + + sets = this->local_sets->get(this->local_sets); + sets->remove(sets, set, NULL); +} + +METHOD(credential_manager_t, cache_cert, void, + private_credential_manager_t *this, certificate_t *cert) +{ + credential_set_t *set; + enumerator_t *enumerator; + + if (this->lock->try_write_lock(this->lock)) + { + enumerator = this->sets->create_enumerator(this->sets); + while (enumerator->enumerate(enumerator, &set)) + { + set->cache_cert(set, cert); + } + enumerator->destroy(enumerator); + this->lock->unlock(this->lock); + } + else + { /* we can't cache now as other threads are active, queue for later */ + this->queue_mutex->lock(this->queue_mutex); + this->cache_queue->insert_last(this->cache_queue, cert->get_ref(cert)); + this->queue_mutex->unlock(this->queue_mutex); + } +} + +/** + * Try to cache certificates queued for caching + */ +static void cache_queue(private_credential_manager_t *this) +{ + credential_set_t *set; + certificate_t *cert; + enumerator_t *enumerator; + + this->queue_mutex->lock(this->queue_mutex); + if (this->cache_queue->get_count(this->cache_queue) > 0 && + this->lock->try_write_lock(this->lock)) + { + while (this->cache_queue->remove_last(this->cache_queue, + (void**)&cert) == SUCCESS) + { + enumerator = this->sets->create_enumerator(this->sets); + while (enumerator->enumerate(enumerator, &set)) + { + set->cache_cert(set, cert); + } + enumerator->destroy(enumerator); + cert->destroy(cert); + } + this->lock->unlock(this->lock); + } + this->queue_mutex->unlock(this->queue_mutex); +} + +/** + * check a certificate for its lifetime + */ +static bool check_certificate(private_credential_manager_t *this, + certificate_t *subject, certificate_t *issuer, + bool online, int pathlen, auth_cfg_t *auth) +{ + time_t not_before, not_after; + cert_validator_t *validator; + enumerator_t *enumerator; + + if (!subject->get_validity(subject, NULL, ¬_before, ¬_after)) + { + DBG1(DBG_CFG, "subject certificate invalid (valid from %T to %T)", + ¬_before, FALSE, ¬_after, FALSE); + return FALSE; + } + if (!issuer->get_validity(issuer, NULL, ¬_before, ¬_after)) + { + DBG1(DBG_CFG, "issuer certificate invalid (valid from %T to %T)", + ¬_before, FALSE, ¬_after, FALSE); + return FALSE; + } + if (issuer->get_type(issuer) == CERT_X509 && + subject->get_type(subject) == CERT_X509) + { + int pathlen_constraint; + x509_t *x509; + + /* check path length constraint */ + x509 = (x509_t*)issuer; + pathlen_constraint = x509->get_pathLenConstraint(x509); + if (pathlen_constraint != X509_NO_PATH_LEN_CONSTRAINT && + pathlen > pathlen_constraint) + { + DBG1(DBG_CFG, "path length of %d violates constraint of %d", + pathlen, pathlen_constraint); + return FALSE; + } + } + + enumerator = this->validators->create_enumerator(this->validators); + while (enumerator->enumerate(enumerator, &validator)) + { + if (!validator->validate(validator, subject, issuer, + online, pathlen, auth)) + { + enumerator->destroy(enumerator); + return FALSE; + } + } + enumerator->destroy(enumerator); + return TRUE; +} + +/** + * Get a trusted certificate from a credential set + */ +static certificate_t *get_pretrusted_cert(private_credential_manager_t *this, + key_type_t type, identification_t *id) +{ + certificate_t *subject; + public_key_t *public; + + subject = get_cert(this, CERT_ANY, type, id, TRUE); + if (!subject) + { + return NULL; + } + public = subject->get_public_key(subject); + if (!public) + { + subject->destroy(subject); + return NULL; + } + public->destroy(public); + return subject; +} + +/** + * Get the issuing certificate of a subject certificate + */ +static certificate_t *get_issuer_cert(private_credential_manager_t *this, + certificate_t *subject, bool trusted) +{ + enumerator_t *enumerator; + certificate_t *issuer = NULL, *candidate; + + enumerator = create_cert_enumerator(this, subject->get_type(subject), KEY_ANY, + subject->get_issuer(subject), trusted); + while (enumerator->enumerate(enumerator, &candidate)) + { + if (this->cache->issued_by(this->cache, subject, candidate)) + { + issuer = candidate->get_ref(candidate); + break; + } + } + enumerator->destroy(enumerator); + return issuer; +} + +/** + * try to verify the trust chain of subject, return TRUE if trusted + */ +static bool verify_trust_chain(private_credential_manager_t *this, + certificate_t *subject, auth_cfg_t *result, + bool trusted, bool online) +{ + certificate_t *current, *issuer; + auth_cfg_t *auth; + int pathlen; + + auth = auth_cfg_create(); + current = subject->get_ref(subject); + + for (pathlen = 0; pathlen <= MAX_TRUST_PATH_LEN; pathlen++) + { + issuer = get_issuer_cert(this, current, TRUE); + if (issuer) + { + /* accept only self-signed CAs as trust anchor */ + if (this->cache->issued_by(this->cache, issuer, issuer)) + { + auth->add(auth, AUTH_RULE_CA_CERT, issuer->get_ref(issuer)); + DBG1(DBG_CFG, " using trusted ca certificate \"%Y\"", + issuer->get_subject(issuer)); + trusted = TRUE; + } + else + { + auth->add(auth, AUTH_RULE_IM_CERT, issuer->get_ref(issuer)); + DBG1(DBG_CFG, " using trusted intermediate ca certificate " + "\"%Y\"", issuer->get_subject(issuer)); + } + } + else + { + issuer = get_issuer_cert(this, current, FALSE); + if (issuer) + { + if (current->equals(current, issuer)) + { + DBG1(DBG_CFG, " self-signed certificate \"%Y\" is not trusted", + current->get_subject(current)); + issuer->destroy(issuer); + break; + } + auth->add(auth, AUTH_RULE_IM_CERT, issuer->get_ref(issuer)); + DBG1(DBG_CFG, " using untrusted intermediate certificate " + "\"%Y\"", issuer->get_subject(issuer)); + } + else + { + DBG1(DBG_CFG, "no issuer certificate found for \"%Y\"", + current->get_subject(current)); + break; + } + } + if (!check_certificate(this, current, issuer, online, pathlen, + current == subject ? auth : NULL)) + { + trusted = FALSE; + issuer->destroy(issuer); + break; + } + current->destroy(current); + current = issuer; + if (trusted) + { + DBG1(DBG_CFG, " reached self-signed root ca with a path length of %d", + pathlen); + break; + } + } + current->destroy(current); + if (pathlen > MAX_TRUST_PATH_LEN) + { + DBG1(DBG_CFG, "maximum path length of %d exceeded", MAX_TRUST_PATH_LEN); + } + if (trusted) + { + result->merge(result, auth, FALSE); + } + auth->destroy(auth); + return trusted; +} + +/** + * enumerator for trusted certificates + */ +typedef struct { + /** implements enumerator_t interface */ + enumerator_t public; + /** enumerator over candidate peer certificates */ + enumerator_t *candidates; + /** reference to the credential_manager */ + private_credential_manager_t *this; + /** type of the requested key */ + key_type_t type; + /** identity the requested key belongs to */ + identification_t *id; + /** TRUE to do CRL/OCSP checking */ + bool online; + /** pretrusted certificate we have served at first invocation */ + certificate_t *pretrusted; + /** currently enumerating auth config */ + auth_cfg_t *auth; +} trusted_enumerator_t; + +METHOD(enumerator_t, trusted_enumerate, bool, + trusted_enumerator_t *this, certificate_t **cert, auth_cfg_t **auth) +{ + certificate_t *current; + + DESTROY_IF(this->auth); + this->auth = auth_cfg_create(); + + if (!this->candidates) + { + /* first invocation, build enumerator for next one */ + this->candidates = create_cert_enumerator(this->this, CERT_ANY, + this->type, this->id, FALSE); + /* check if we have a trusted certificate for that peer */ + this->pretrusted = get_pretrusted_cert(this->this, this->type, this->id); + if (this->pretrusted) + { + /* if we find a trusted self signed certificate, we just accept it. + * However, in order to fulfill authorization rules, we try to build + * the trust chain if it is not self signed */ + if (this->this->cache->issued_by(this->this->cache, + this->pretrusted, this->pretrusted) || + verify_trust_chain(this->this, this->pretrusted, this->auth, + TRUE, this->online)) + { + this->auth->add(this->auth, AUTH_RULE_SUBJECT_CERT, + this->pretrusted->get_ref(this->pretrusted)); + DBG1(DBG_CFG, " using trusted certificate \"%Y\"", + this->pretrusted->get_subject(this->pretrusted)); + *cert = this->pretrusted; + if (auth) + { + *auth = this->auth; + } + return TRUE; + } + } + } + /* try to verify the trust chain for each certificate found */ + while (this->candidates->enumerate(this->candidates, ¤t)) + { + if (this->pretrusted && + this->pretrusted->equals(this->pretrusted, current)) + { /* skip pretrusted certificate we already served */ + continue; + } + + DBG1(DBG_CFG, " using certificate \"%Y\"", + current->get_subject(current)); + if (verify_trust_chain(this->this, current, this->auth, FALSE, + this->online)) + { + *cert = current; + if (auth) + { + *auth = this->auth; + } + return TRUE; + } + } + return FALSE; +} + +METHOD(enumerator_t, trusted_destroy, void, + trusted_enumerator_t *this) +{ + DESTROY_IF(this->pretrusted); + DESTROY_IF(this->auth); + DESTROY_IF(this->candidates); + free(this); +} + +METHOD(credential_manager_t, create_trusted_enumerator, enumerator_t*, + private_credential_manager_t *this, key_type_t type, + identification_t *id, bool online) +{ + trusted_enumerator_t *enumerator; + + INIT(enumerator, + .public = { + .enumerate = (void*)_trusted_enumerate, + .destroy = _trusted_destroy, + }, + .this = this, + .type = type, + .id = id, + .online = online, + ); + return &enumerator->public; +} + +/** + * enumerator for public keys + */ +typedef struct { + /** implements enumerator_t interface */ + enumerator_t public; + /** enumerator over candidate peer certificates */ + enumerator_t *inner; + /** reference to the credential_manager */ + private_credential_manager_t *this; + /** currently enumerating key */ + public_key_t *current; + /** credset wrapper around auth config */ + auth_cfg_wrapper_t *wrapper; +} public_enumerator_t; + +METHOD(enumerator_t, public_enumerate, bool, + public_enumerator_t *this, public_key_t **key, auth_cfg_t **auth) +{ + certificate_t *cert; + + while (this->inner->enumerate(this->inner, &cert, auth)) + { + DESTROY_IF(this->current); + this->current = cert->get_public_key(cert); + if (this->current) + { + *key = this->current; + return TRUE; + } + } + return FALSE; +} + +METHOD(enumerator_t, public_destroy, void, + public_enumerator_t *this) +{ + DESTROY_IF(this->current); + this->inner->destroy(this->inner); + if (this->wrapper) + { + remove_local_set(this->this, &this->wrapper->set); + this->wrapper->destroy(this->wrapper); + } + this->this->lock->unlock(this->this->lock); + + /* check for delayed certificate cache queue */ + cache_queue(this->this); + free(this); +} + +METHOD(credential_manager_t, create_public_enumerator, enumerator_t*, + private_credential_manager_t *this, key_type_t type, identification_t *id, + auth_cfg_t *auth) +{ + public_enumerator_t *enumerator; + + INIT(enumerator, + .public = { + .enumerate = (void*)_public_enumerate, + .destroy = _public_destroy, + }, + .inner = create_trusted_enumerator(this, type, id, TRUE), + .this = this, + ); + if (auth) + { + enumerator->wrapper = auth_cfg_wrapper_create(auth); + add_local_set(this, &enumerator->wrapper->set); + } + this->lock->read_lock(this->lock); + return &enumerator->public; +} + +/** + * Check if a certificate's keyid is contained in the auth helper + */ +static bool auth_contains_cacert(auth_cfg_t *auth, certificate_t *cert) +{ + enumerator_t *enumerator; + identification_t *value; + auth_rule_t type; + bool found = FALSE; + + enumerator = auth->create_enumerator(auth); + while (enumerator->enumerate(enumerator, &type, &value)) + { + if (type == AUTH_RULE_CA_CERT && + cert->equals(cert, (certificate_t*)value)) + { + found = TRUE; + break; + } + } + enumerator->destroy(enumerator); + return found; +} + +/** + * build a trustchain from subject up to a trust anchor in trusted + */ +static auth_cfg_t *build_trustchain(private_credential_manager_t *this, + certificate_t *subject, auth_cfg_t *auth) +{ + certificate_t *issuer, *current; + auth_cfg_t *trustchain; + int pathlen = 0; + + trustchain = auth_cfg_create(); + + current = auth->get(auth, AUTH_RULE_CA_CERT); + if (!current) + { + /* no trust anchor specified, return this cert only */ + trustchain->add(trustchain, AUTH_RULE_SUBJECT_CERT, + subject->get_ref(subject)); + return trustchain; + } + current = subject->get_ref(subject); + while (TRUE) + { + if (auth_contains_cacert(auth, current)) + { + trustchain->add(trustchain, AUTH_RULE_CA_CERT, current); + return trustchain; + } + if (subject == current) + { + trustchain->add(trustchain, AUTH_RULE_SUBJECT_CERT, current); + } + else + { + trustchain->add(trustchain, AUTH_RULE_IM_CERT, current); + } + issuer = get_issuer_cert(this, current, FALSE); + if (!issuer || issuer->equals(issuer, current) || + pathlen > MAX_TRUST_PATH_LEN) + { + DESTROY_IF(issuer); + break; + } + current = issuer; + pathlen++; + } + trustchain->destroy(trustchain); + return NULL; +} + +/** + * find a private key of a give certificate + */ +static private_key_t *get_private_by_cert(private_credential_manager_t *this, + certificate_t *cert, key_type_t type) +{ + private_key_t *private = NULL; + identification_t *keyid; + chunk_t chunk; + public_key_t *public; + + public = cert->get_public_key(cert); + if (public) + { + if (public->get_fingerprint(public, KEYID_PUBKEY_SHA1, &chunk)) + { + keyid = identification_create_from_encoding(ID_KEY_ID, chunk); + private = get_private_by_keyid(this, type, keyid); + keyid->destroy(keyid); + } + public->destroy(public); + } + return private; +} + +METHOD(credential_manager_t, get_private, private_key_t*, + private_credential_manager_t *this, key_type_t type, identification_t *id, + auth_cfg_t *auth) +{ + enumerator_t *enumerator; + certificate_t *cert; + private_key_t *private = NULL; + auth_cfg_t *trustchain; + + /* check if this is a lookup by key ID, and do it if so */ + if (id && id->get_type(id) == ID_KEY_ID) + { + private = get_private_by_keyid(this, type, id); + if (private) + { + return private; + } + } + + /* if a specific certificate is preferred, check for a matching key */ + cert = auth->get(auth, AUTH_RULE_SUBJECT_CERT); + if (cert) + { + private = get_private_by_cert(this, cert, type); + if (private) + { + trustchain = build_trustchain(this, cert, auth); + if (trustchain) + { + auth->merge(auth, trustchain, FALSE); + trustchain->destroy(trustchain); + } + return private; + } + } + + /* try to build a trust chain for each certificate found */ + enumerator = create_cert_enumerator(this, CERT_ANY, type, id, FALSE); + while (enumerator->enumerate(enumerator, &cert)) + { + private = get_private_by_cert(this, cert, type); + if (private) + { + trustchain = build_trustchain(this, cert, auth); + if (trustchain) + { + auth->merge(auth, trustchain, FALSE); + trustchain->destroy(trustchain); + break; + } + private->destroy(private); + private = NULL; + } + } + enumerator->destroy(enumerator); + + /* if no valid trustchain was found, fall back to the first usable cert */ + if (!private) + { + enumerator = create_cert_enumerator(this, CERT_ANY, type, id, FALSE); + while (enumerator->enumerate(enumerator, &cert)) + { + private = get_private_by_cert(this, cert, type); + if (private) + { + auth->add(auth, AUTH_RULE_SUBJECT_CERT, cert->get_ref(cert)); + break; + } + } + enumerator->destroy(enumerator); + } + return private; +} + +METHOD(credential_manager_t, flush_cache, void, + private_credential_manager_t *this, certificate_type_t type) +{ + this->cache->flush(this->cache, type); +} + +METHOD(credential_manager_t, issued_by, bool, + private_credential_manager_t *this, certificate_t *subject, + certificate_t *issuer) +{ + return this->cache->issued_by(this->cache, subject, issuer); +} + +METHOD(credential_manager_t, add_set, void, + private_credential_manager_t *this, credential_set_t *set) +{ + this->lock->write_lock(this->lock); + this->sets->insert_last(this->sets, set); + this->lock->unlock(this->lock); +} + +METHOD(credential_manager_t, remove_set, void, + private_credential_manager_t *this, credential_set_t *set) +{ + this->lock->write_lock(this->lock); + this->sets->remove(this->sets, set, NULL); + this->lock->unlock(this->lock); +} + +METHOD(credential_manager_t, add_validator, void, + private_credential_manager_t *this, cert_validator_t *vdtr) +{ + this->lock->write_lock(this->lock); + this->sets->insert_last(this->validators, vdtr); + this->lock->unlock(this->lock); +} + +METHOD(credential_manager_t, remove_validator, void, + private_credential_manager_t *this, cert_validator_t *vdtr) +{ + this->lock->write_lock(this->lock); + this->validators->remove(this->validators, vdtr, NULL); + this->lock->unlock(this->lock); +} + +METHOD(credential_manager_t, destroy, void, + private_credential_manager_t *this) +{ + cache_queue(this); + this->cache_queue->destroy(this->cache_queue); + this->sets->remove(this->sets, this->cache, NULL); + this->sets->destroy(this->sets); + this->local_sets->destroy(this->local_sets); + this->cache->destroy(this->cache); + this->validators->destroy(this->validators); + this->lock->destroy(this->lock); + this->queue_mutex->destroy(this->queue_mutex); + free(this); +} + +/* + * see header file + */ +credential_manager_t *credential_manager_create() +{ + private_credential_manager_t *this; + + INIT(this, + .public = { + .create_cert_enumerator = _create_cert_enumerator, + .create_shared_enumerator = _create_shared_enumerator, + .create_cdp_enumerator = _create_cdp_enumerator, + .get_cert = _get_cert, + .get_shared = _get_shared, + .get_private = _get_private, + .create_trusted_enumerator = _create_trusted_enumerator, + .create_public_enumerator = _create_public_enumerator, + .flush_cache = _flush_cache, + .cache_cert = _cache_cert, + .issued_by = _issued_by, + .add_set = _add_set, + .remove_set = _remove_set, + .add_local_set = _add_local_set, + .remove_local_set = _remove_local_set, + .add_validator = _add_validator, + .remove_validator = _remove_validator, + .destroy = _destroy, + }, + .sets = linked_list_create(), + .validators = linked_list_create(), + .cache = cert_cache_create(), + .cache_queue = linked_list_create(), + .lock = rwlock_create(RWLOCK_TYPE_DEFAULT), + .queue_mutex = mutex_create(MUTEX_TYPE_DEFAULT), + ); + + this->local_sets = thread_value_create((thread_cleanup_t)this->sets->destroy); + this->sets->insert_first(this->sets, this->cache); + + return &this->public; +} diff --git a/src/libstrongswan/credentials/credential_manager.h b/src/libstrongswan/credentials/credential_manager.h new file mode 100644 index 000000000..04269cfbf --- /dev/null +++ b/src/libstrongswan/credentials/credential_manager.h @@ -0,0 +1,270 @@ +/* + * Copyright (C) 2007-2009 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/** + * @defgroup credential_manager credential_manager + * @{ @ingroup credentials + */ + +#ifndef CREDENTIAL_MANAGER_H_ +#define CREDENTIAL_MANAGER_H_ + +typedef struct credential_manager_t credential_manager_t; + +#include <utils/identification.h> +#include <utils/enumerator.h> +#include <credentials/auth_cfg.h> +#include <credentials/credential_set.h> +#include <credentials/keys/private_key.h> +#include <credentials/keys/shared_key.h> +#include <credentials/certificates/certificate.h> +#include <credentials/cert_validator.h> + +/** + * Manages credentials using credential_sets. + * + * The credential manager is the entry point of the credential framework. It + * uses so called "sets" to access credentials in a modular fashion, these + * are implemented through the credential_set_t interface. + * The manager additionally does trust chain verification and trust status + * chaching. A set may call the managers methods if it needs credentials itself, + * the manager uses recursive locking. + * + * @verbatim + + +-------+ +----------------+ + | A | | | +------------------+ + | u | -----> | | ------> | +------------------+ + | t | | credential- | | | +------------------+ + | h | -----> | manager | ------> +--| | credential- | => IPC + | e | | | +--| sets | + | n | +--> | | ------> +------------------+ + | t | | | | | + | i | | | | | + | c | | +----------------+ | + | a | | | + | t | +----------------------------------------------+ + | o | may be recursive + | r | + +-------+ + + @endverbatim + * + * The credential manager uses rwlocks for performance reasons, credential + * sets must be fully thread save. + */ +struct credential_manager_t { + + /** + * Create an enumerator over all certificates. + * + * @param cert kind of certificate + * @param key kind of key in certificate + * @param id subject this certificate belongs to + * @param trusted TRUE to list trusted certificates only + * @return enumerator over the certificates + */ + enumerator_t *(*create_cert_enumerator)(credential_manager_t *this, + certificate_type_t cert, key_type_t key, + identification_t *id, bool trusted); + /** + * Create an enumerator over all shared keys. + * + * The enumerator enumerates over: + * shared_key_t*, id_match_t me, id_match_t other + * But must accepts values for the id_matches. + * + * @param type kind of requested shared key + * @param first first subject between key is shared + * @param second second subject between key is shared + * @return enumerator over shared keys + */ + enumerator_t *(*create_shared_enumerator)(credential_manager_t *this, + shared_key_type_t type, + identification_t *first, identification_t *second); + /** + * Create an enumerator over all Certificate Distribution Points. + * + * @param type kind of certificate the point distributes + * @param id identification of the distributed certificate + * @return enumerator of CDPs as char* + */ + enumerator_t *(*create_cdp_enumerator)(credential_manager_t *this, + certificate_type_t type, identification_t *id); + /** + * Get a trusted or untrusted certificate. + * + * @param cert kind of certificate + * @param key kind of key in certificate + * @param id subject this certificate belongs to + * @param trusted TRUE to get a trusted certificate only + * @return certificate, if found, NULL otherwise + */ + certificate_t *(*get_cert)(credential_manager_t *this, + certificate_type_t cert, key_type_t key, + identification_t *id, bool trusted); + /** + * Get the best matching shared key for two IDs. + * + * @param type kind of requested shared key + * @param me own identity + * @param other peers identity + * @return shared_key_t, NULL if none found + */ + shared_key_t *(*get_shared)(credential_manager_t *this, shared_key_type_t type, + identification_t *me, identification_t *other); + /** + * Get a private key to create a signature. + * + * The get_private() method gets a secret private key identified by either + * the keyid itself or an id the key belongs to. + * The auth parameter contains additional information, such as receipients + * trusted CA certs. Auth gets filled with subject and CA certificates + * needed to validate a created signature. + * + * @param type type of the key to get + * @param id identification the key belongs to + * @param auth auth config, including trusted CA certificates + * @return private_key_t, NULL if none found + */ + private_key_t* (*get_private)(credential_manager_t *this, key_type_t type, + identification_t *id, auth_cfg_t *auth); + + /** + * Create an enumerator over trusted certificates. + * + * This method creates an enumerator over trusted certificates. The auth + * parameter (if given) recevies the trustchain used to validate + * the certificate. The resulting enumerator enumerates over + * certificate_t*, auth_cfg_t*. + * If online is set, revocations are checked online for the whole + * trustchain. + * + * @param type type of the key we want a certificate for + * @param id subject of the certificate + * @param online whether revocations should be checked online + * @return enumerator + */ + enumerator_t* (*create_trusted_enumerator)(credential_manager_t *this, + key_type_t type, identification_t *id, bool online); + + /** + * Create an enumerator over trusted public keys. + * + * This method gets a an enumerator over trusted public keys to verify a + * signature created by id. The auth parameter contains additional + * authentication infos, e.g. peer and intermediate certificates. + * The resulting enumerator enumerates over public_key_t *, auth_cfg_t *, + * where the auth config helper contains rules for constraint checks. + * This function is very similar to create_trusted_enumerator(), but + * gets public keys directly. + * + * @param type type of the key to get + * @param id owner of the key, signer of the signature + * @param auth authentication infos + * @return enumerator + */ + enumerator_t* (*create_public_enumerator)(credential_manager_t *this, + key_type_t type, identification_t *id, auth_cfg_t *auth); + + /** + * Cache a certificate by invoking cache_cert() on all registerd sets. + * + * @param cert certificate to cache + */ + void (*cache_cert)(credential_manager_t *this, certificate_t *cert); + + /** + * Flush the certificate cache. + * + * Only the managers local cache is flushed, but not the sets cache filled + * by the cache_cert() method. + * + * @param type type of certificate to flush, or CERT_ANY + */ + void (*flush_cache)(credential_manager_t *this, certificate_type_t type); + + /** + * Check if a given subject certificate is issued by an issuer certificate. + * + * This operation does signature verification, but uses the credential + * managers cache for to speed up the operation. + * + * @param subject subject certificate to check + * @param issuer issuer certificate that potentially has signed subject + * @return TRUE if issuer signed subject + */ + bool (*issued_by)(credential_manager_t *this, + certificate_t *subject, certificate_t *issuer); + + /** + * 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); + + /** + * Register a thread local credential set to the manager. + * + * To add a credential set for the current trustchain verification + * operation, sets may be added for the calling thread only. This + * does not require a write lock and is therefore a much less expensive + * operation. + * + * @param set set to register + */ + void (*add_local_set)(credential_manager_t *this, credential_set_t *set); + + /** + * Unregister a thread local credential set from the manager. + * + * @param set set to unregister + */ + void (*remove_local_set)(credential_manager_t *this, credential_set_t *set); + + /** + * Register a certificate validator to the manager. + * + * @param vdtr validator to register + */ + void (*add_validator)(credential_manager_t *this, cert_validator_t *vdtr); + + /** + * Remove a certificate validator from the manager. + * + * @param vdtr validator to unregister + */ + void (*remove_validator)(credential_manager_t *this, cert_validator_t *vdtr); + + /** + * 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/libstrongswan/credentials/credential_set.h b/src/libstrongswan/credentials/credential_set.h new file mode 100644 index 000000000..0eee237cb --- /dev/null +++ b/src/libstrongswan/credentials/credential_set.h @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2007 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/** + * @defgroup credential_set credential_set + * @{ @ingroup credentials + */ + +#ifndef CREDENTIAL_SET_H_ +#define CREDENTIAL_SET_H_ + +typedef struct credential_set_t credential_set_t; + +#include <credentials/keys/public_key.h> +#include <credentials/keys/shared_key.h> +#include <credentials/certificates/certificate.h> + +/** + * A set of credentials. + * + * Contains private keys, shared keys and different kinds of certificates. + * Enumerators are used because queries might return multiple matches. + * Filter parameters restrict enumeration over specific items only. + * See credential_manager_t for an overview of the credential framework. + * + * A credential set enumerator may not block the credential set, i.e. multiple + * threads must be able to hold multiple enumerators, as the credential manager + * is higly parallelized. The best way to achieve this is by using shared + * read locks for the enumerators only. Otherwiese deadlocks will occur. + * The writing cache_cert() routine is called by the manager only if no + * enumerator is alive, so it is save to use a write lock there. + */ +struct credential_set_t { + + /** + * Create an enumerator over private keys (private_key_t). + * + * The id is either a key identifier of the requested key, or an identity + * of the key owner. + * + * @param type type of requested private key + * @param id key identifier/owner + * @return enumerator over private_key_t's. + */ + enumerator_t *(*create_private_enumerator)(credential_set_t *this, + key_type_t type, identification_t *id); + /** + * Create an enumerator over certificates (certificate_t). + * + * @param cert kind of certificate + * @param key kind of key in certificate + * @param id identity (subject) this certificate belongs to + * @param trusted whether the certificate must be trustworthy + * @return enumerator as described above + */ + enumerator_t *(*create_cert_enumerator)(credential_set_t *this, + certificate_type_t cert, key_type_t key, + identification_t *id, bool trusted); + /** + * Create an enumerator over shared keys (shared_key_t). + * + * The enumerator enumerates over: + * shared_key_t*, id_match_t me, id_match_t other + * But must accept NULL values for the id_matches. + * + * @param type kind of requested shared key + * @param me own identity + * @param other other identity who owns that secret + * @return enumerator as described above + */ + enumerator_t *(*create_shared_enumerator)(credential_set_t *this, + shared_key_type_t type, + identification_t *me, identification_t *other); + + /** + * Create an enumerator over certificate distribution points. + * + * @param type type of the certificate to get a CDP + * @param id identification of the distributed certificate + * @return an enumerator over CDPs as char* + */ + enumerator_t *(*create_cdp_enumerator)(credential_set_t *this, + certificate_type_t type, identification_t *id); + + /** + * Cache a certificate in the credential set. + * + * The caching policy is implementation dependent, the sets may cache the + * certificate in-memory, persistent on disk or not at all. + * + * @param cert certificate to cache + */ + void (*cache_cert)(credential_set_t *this, certificate_t *cert); +}; + +#endif /** CREDENTIAL_SET_H_ @}*/ diff --git a/src/libstrongswan/credentials/ietf_attributes/ietf_attributes.c b/src/libstrongswan/credentials/ietf_attributes/ietf_attributes.c index 71dcd28f2..de5b85bae 100644 --- a/src/libstrongswan/credentials/ietf_attributes/ietf_attributes.c +++ b/src/libstrongswan/credentials/ietf_attributes/ietf_attributes.c @@ -173,7 +173,7 @@ static char* get_string(private_ietf_attributes_t *this) break; } pos += written; - len -= written; + len -= written; } switch (attr->type) diff --git a/src/libstrongswan/credentials/keys/key_encoding.h b/src/libstrongswan/credentials/keys/key_encoding.h deleted file mode 100644 index d8435f4b4..000000000 --- a/src/libstrongswan/credentials/keys/key_encoding.h +++ /dev/null @@ -1,203 +0,0 @@ -/* - * Copyright (C) 2009 Martin Willi - * Hochschule fuer Technik Rapperswil - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * for more details. - */ - -/** - * @defgroup key_encoding key_encoding - * @{ @ingroup keys - */ - -#ifndef KEY_ENCODING_H_ -#define KEY_ENCODING_H_ - -typedef struct key_encoding_t key_encoding_t; -typedef enum key_encoding_type_t key_encoding_type_t; -typedef enum key_encoding_part_t key_encoding_part_t; - -#include <library.h> - -/** - * Key encoder function implementing encoding/fingerprinting. - * - * The variable argument list takes key_encoding_part_t, followed by part - * specific arguments, terminated by KEY_PART_END. - * - * @param type format to encode the key to - * @param args list of (key_encoding_part_t, data) - * @param encoding encoding result, allocated - * @return TRUE if encoding successful - */ -typedef bool (*key_encoder_t)(key_encoding_type_t type, chunk_t *encoding, - va_list args); - -/** - * Helper function for key_encoder_t implementations to parse argument list. - * - * Key encoder functions get a variable argument list to parse. To simplify - * the job, this function reads the arguments and returns chunks for each - * part. - * The argument list of this function takes a key_encoding_part_t, followed - * by a data pointer receiving the value, terminated by KEY_PART_END. - * - * @param args argument list passed to key encoder function - * @param ... list of (key_encoding_part_t, data*) - * @return TRUE if all parts found, FALSE otherwise - */ -bool key_encoding_args(va_list args, ...); - -/** - * Encoding type of a fingerprint/private-/public-key. - * - * Fingerprints have have the KEY_ID_*, public keys the KEY_PUB_* and - * private keys the KEY_PRIV_* prefix. - */ -enum key_encoding_type_t { - /** SHA1 fingerprint over subjectPublicKeyInfo */ - KEY_ID_PUBKEY_INFO_SHA1 = 0, - /** SHA1 fingerprint over subjectPublicKey */ - KEY_ID_PUBKEY_SHA1, - /** PGPv3 fingerprint */ - KEY_ID_PGPV3, - /** PGPv4 fingerprint */ - KEY_ID_PGPV4, - - KEY_ID_MAX, - - /** PKCS#1 and similar ASN.1 key encoding */ - KEY_PUB_ASN1_DER, - KEY_PRIV_ASN1_DER, - /** subjectPublicKeyInfo encoding */ - KEY_PUB_SPKI_ASN1_DER, - /** PEM encoded PKCS#1 key */ - KEY_PUB_PEM, - KEY_PRIV_PEM, - /** PGP key encoding */ - KEY_PUB_PGP, - KEY_PRIV_PGP, - - KEY_ENCODING_MAX, -}; - -/** - * Parts of a key to encode. - */ -enum key_encoding_part_t { - /** modulus of a RSA key, n */ - KEY_PART_RSA_MODULUS, - /** public exponent of a RSA key, e */ - KEY_PART_RSA_PUB_EXP, - /** private exponent of a RSA key, d */ - KEY_PART_RSA_PRIV_EXP, - /** prime1 a RSA key, p */ - KEY_PART_RSA_PRIME1, - /** prime2 a RSA key, q */ - KEY_PART_RSA_PRIME2, - /** exponent1 a RSA key, exp1 */ - KEY_PART_RSA_EXP1, - /** exponent1 a RSA key, exp2 */ - KEY_PART_RSA_EXP2, - /** coefficient of RSA key, coeff */ - KEY_PART_RSA_COEFF, - /** a DER encoded RSA public key */ - KEY_PART_RSA_PUB_ASN1_DER, - /** a DER encoded RSA private key */ - KEY_PART_RSA_PRIV_ASN1_DER, - /** a DER encoded ECDSA public key */ - KEY_PART_ECDSA_PUB_ASN1_DER, - /** a DER encoded ECDSA private key */ - KEY_PART_ECDSA_PRIV_ASN1_DER, - - KEY_PART_END, -}; - -/** - * Private/Public key encoding and fingerprinting facility. - */ -struct key_encoding_t { - - /** - * Encode a key into a format using several key parts, optional caching. - * - * The variable argument list takes key_encoding_part_t, followed by part - * specific arguments, terminated by KEY_PART_END. - * If a cache key is given, the returned encoding points to internal data: - * do not free or modify. If no cache key is given, the encoding is - * allocated and must be freed by the caller. - * - * @param type format the key should be encoded to - * @param cache key to use for caching, NULL to not cache - * @param encoding encoding result, allocated if caching disabled - * @param ... list of (key_encoding_part_t, data) - * @return TRUE if encoding successful - */ - bool (*encode)(key_encoding_t *this, key_encoding_type_t type, void *cache, - chunk_t *encoding, ...); - - /** - * Clear all cached encodings of a given cache key. - * - * @param cache key used in encode() for caching - */ - void (*clear_cache)(key_encoding_t *this, void *cache); - - /** - * Check for a cached encoding. - * - * @param type format of the key encoding - * @param cache key to use for caching, as given to encode() - * @param encoding encoding result, internal data - * @return TRUE if cache entry found - */ - bool (*get_cache)(key_encoding_t *this, key_encoding_type_t type, - void *cache, chunk_t *encoding); - - /** - * Cache a key encoding created externally. - * - * After calling cache(), the passed encoding is owned by the key encoding - * facility. - * - * @param type format of the key encoding - * @param cache key to use for caching, as given to encode() - * @param encoding encoding to cache, gets owned by this - */ - void (*cache)(key_encoding_t *this, key_encoding_type_t type, void *cache, - chunk_t encoding); - - /** - * Register a key encoder function. - * - * @param encoder key encoder function to add - */ - void (*add_encoder)(key_encoding_t *this, key_encoder_t encoder); - - /** - * Unregister a previously registered key encoder function. - * - * @param encoder key encoder function to remove - */ - void (*remove_encoder)(key_encoding_t *this, key_encoder_t encoder); - - /** - * Destroy a key_encoding_t. - */ - void (*destroy)(key_encoding_t *this); -}; - -/** - * Create a key_encoding instance. - */ -key_encoding_t *key_encoding_create(); - -#endif /** KEY_ENCODING_H_ @}*/ diff --git a/src/libstrongswan/credentials/keys/private_key.c b/src/libstrongswan/credentials/keys/private_key.c index c3b5ac55b..8292af495 100644 --- a/src/libstrongswan/credentials/keys/private_key.c +++ b/src/libstrongswan/credentials/keys/private_key.c @@ -20,7 +20,7 @@ */ bool private_key_equals(private_key_t *this, private_key_t *other) { - key_encoding_type_t type; + cred_encoding_type_t type; chunk_t a, b; if (this == other) @@ -28,7 +28,7 @@ bool private_key_equals(private_key_t *this, private_key_t *other) return TRUE; } - for (type = 0; type < KEY_ENCODING_MAX; type++) + for (type = 0; type < CRED_ENCODING_MAX; type++) { if (this->get_fingerprint(this, type, &a) && other->get_fingerprint(other, type, &b)) @@ -44,10 +44,10 @@ bool private_key_equals(private_key_t *this, private_key_t *other) */ bool private_key_belongs_to(private_key_t *private, public_key_t *public) { - key_encoding_type_t type; + cred_encoding_type_t type; chunk_t a, b; - for (type = 0; type < KEY_ENCODING_MAX; type++) + for (type = 0; type < CRED_ENCODING_MAX; type++) { if (private->get_fingerprint(private, type, &a) && public->get_fingerprint(public, type, &b)) @@ -63,10 +63,10 @@ bool private_key_belongs_to(private_key_t *private, public_key_t *public) */ bool private_key_has_fingerprint(private_key_t *private, chunk_t fingerprint) { - key_encoding_type_t type; + cred_encoding_type_t type; chunk_t current; - for (type = 0; type < KEY_ID_MAX; type++) + for (type = 0; type < KEYID_MAX; type++) { if (private->get_fingerprint(private, type, ¤t) && chunk_equals(current, fingerprint)) diff --git a/src/libstrongswan/credentials/keys/private_key.h b/src/libstrongswan/credentials/keys/private_key.h index d4517f296..27f4ab098 100644 --- a/src/libstrongswan/credentials/keys/private_key.h +++ b/src/libstrongswan/credentials/keys/private_key.h @@ -23,6 +23,7 @@ typedef struct private_key_t private_key_t; +#include <credentials/cred_encoding.h> #include <credentials/keys/public_key.h> /** @@ -89,11 +90,11 @@ struct private_key_t { /** * Get the fingerprint of the key. * - * @param type type of fingerprint, one of KEY_ID_* + * @param type type of fingerprint, one of KEYID_* * @param fp fingerprint, points to internal data * @return TRUE if fingerprint type supported */ - bool (*get_fingerprint)(private_key_t *this, key_encoding_type_t type, + bool (*get_fingerprint)(private_key_t *this, cred_encoding_type_t type, chunk_t *fp); /** @@ -107,11 +108,11 @@ struct private_key_t { /** * Get the key in an encoded form as a chunk. * - * @param type type of the encoding, one of KEY_PRIV_* + * @param type type of the encoding, one of PRIVKEY_* * @param encoding encoding of the key, allocated * @return TRUE if encoding supported */ - bool (*get_encoding)(private_key_t *this, key_encoding_type_t type, + bool (*get_encoding)(private_key_t *this, cred_encoding_type_t type, chunk_t *encoding); /** diff --git a/src/libstrongswan/credentials/keys/public_key.c b/src/libstrongswan/credentials/keys/public_key.c index ba3036793..ce342de33 100644 --- a/src/libstrongswan/credentials/keys/public_key.c +++ b/src/libstrongswan/credentials/keys/public_key.c @@ -47,7 +47,7 @@ ENUM(signature_scheme_names, SIGN_UNKNOWN, SIGN_ECDSA_521, */ bool public_key_equals(public_key_t *this, public_key_t *other) { - key_encoding_type_t type; + cred_encoding_type_t type; chunk_t a, b; if (this == other) @@ -55,7 +55,7 @@ bool public_key_equals(public_key_t *this, public_key_t *other) return TRUE; } - for (type = 0; type < KEY_ENCODING_MAX; type++) + for (type = 0; type < CRED_ENCODING_MAX; type++) { if (this->get_fingerprint(this, type, &a) && other->get_fingerprint(other, type, &b)) @@ -71,10 +71,10 @@ bool public_key_equals(public_key_t *this, public_key_t *other) */ bool public_key_has_fingerprint(public_key_t *public, chunk_t fingerprint) { - key_encoding_type_t type; + cred_encoding_type_t type; chunk_t current; - for (type = 0; type < KEY_ID_MAX; type++) + for (type = 0; type < KEYID_MAX; type++) { if (public->get_fingerprint(public, type, ¤t) && chunk_equals(current, fingerprint)) diff --git a/src/libstrongswan/credentials/keys/public_key.h b/src/libstrongswan/credentials/keys/public_key.h index a421e7b5b..ff827a189 100644 --- a/src/libstrongswan/credentials/keys/public_key.h +++ b/src/libstrongswan/credentials/keys/public_key.h @@ -23,12 +23,11 @@ typedef struct public_key_t public_key_t; typedef enum key_type_t key_type_t; -typedef enum key_id_type_t key_id_type_t; typedef enum signature_scheme_t signature_scheme_t; #include <library.h> #include <utils/identification.h> -#include <credentials/keys/key_encoding.h> +#include <credentials/cred_encoding.h> /** * Type of a key pair, the used crypto system @@ -147,11 +146,11 @@ struct public_key_t { /** * Get the fingerprint of the key. * - * @param type type of fingerprint, one of KEY_ID_* + * @param type type of fingerprint, one of KEYID_* * @param fp fingerprint, points to internal data * @return TRUE if fingerprint type supported */ - bool (*get_fingerprint)(public_key_t *this, key_encoding_type_t type, + bool (*get_fingerprint)(public_key_t *this, cred_encoding_type_t type, chunk_t *fp); /** @@ -165,11 +164,11 @@ struct public_key_t { /** * Get the key in an encoded form as a chunk. * - * @param type type of the encoding, one of KEY_PRIV_* + * @param type type of the encoding, one of PRIVKEY_* * @param encoding encoding of the key, allocated * @return TRUE if encoding supported */ - bool (*get_encoding)(public_key_t *this, key_encoding_type_t type, + bool (*get_encoding)(public_key_t *this, cred_encoding_type_t type, chunk_t *encoding); /** diff --git a/src/libstrongswan/credentials/sets/auth_cfg_wrapper.c b/src/libstrongswan/credentials/sets/auth_cfg_wrapper.c new file mode 100644 index 000000000..5e8458616 --- /dev/null +++ b/src/libstrongswan/credentials/sets/auth_cfg_wrapper.c @@ -0,0 +1,223 @@ +/* + * Copyright (C) 2008-2009 Martin Willi + * Copyright (C) 2008 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include <library.h> +#include <debug.h> + +#include "auth_cfg_wrapper.h" + +typedef struct private_auth_cfg_wrapper_t private_auth_cfg_wrapper_t; + +/** + * private data of auth_cfg_wrapper + */ +struct private_auth_cfg_wrapper_t { + + /** + * public functions + */ + auth_cfg_wrapper_t public; + + /** + * wrapped auth info + */ + auth_cfg_t *auth; +}; + +/** + * enumerator for auth_cfg_wrapper_t.create_cert_enumerator() + */ +typedef struct { + /** implements enumerator_t */ + enumerator_t public; + /** inner enumerator from auth_cfg */ + enumerator_t *inner; + /** wrapped auth round */ + auth_cfg_t *auth; + /** enumerated cert type */ + certificate_type_t cert; + /** enumerated key type */ + key_type_t key; + /** enumerated id */ + identification_t *id; +} wrapper_enumerator_t; + +/** + * Tries to fetch a certificate that was supplied as "Hash and URL" + * (replaces rule type and value in place). + */ +static bool fetch_cert(wrapper_enumerator_t *enumerator, + auth_rule_t *rule, void **value) +{ + char *url = (char*)*value; + if (!url) + { + /* fetching the certificate previously failed */ + return FALSE; + } + + chunk_t data; + certificate_t *cert; + + DBG1(DBG_CFG, " fetching certificate from '%s' ...", url); + if (lib->fetcher->fetch(lib->fetcher, url, &data, FETCH_END) != SUCCESS) + { + DBG1(DBG_CFG, " fetching certificate failed"); + /* we set the item to NULL, so we can skip it */ + enumerator->auth->replace(enumerator->auth, enumerator->inner, + *rule, NULL); + return FALSE; + } + + cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509, + BUILD_BLOB_ASN1_DER, data, BUILD_END); + free(data.ptr); + + if (!cert) + { + DBG1(DBG_CFG, " parsing fetched certificate failed"); + /* we set the item to NULL, so we can skip it */ + enumerator->auth->replace(enumerator->auth, enumerator->inner, + *rule, NULL); + return FALSE; + } + + DBG1(DBG_CFG, " fetched certificate \"%Y\"", cert->get_subject(cert)); + lib->credmgr->cache_cert(lib->credmgr, cert); + + if (*rule == AUTH_HELPER_IM_HASH_URL) + { + *rule = AUTH_HELPER_IM_CERT; + } + else + { + *rule = AUTH_HELPER_SUBJECT_CERT; + } + *value = cert; + enumerator->auth->replace(enumerator->auth, enumerator->inner, + *rule, cert->get_ref(cert)); + return TRUE; +} + +/** + * enumerate function for wrapper_enumerator_t + */ +static bool enumerate(wrapper_enumerator_t *this, certificate_t **cert) +{ + auth_rule_t rule; + certificate_t *current; + public_key_t *public; + + while (this->inner->enumerate(this->inner, &rule, ¤t)) + { + if (rule == AUTH_HELPER_IM_HASH_URL || + rule == AUTH_HELPER_SUBJECT_HASH_URL) + { /* on-demand fetching of hash and url certificates */ + if (!fetch_cert(this, &rule, (void**)¤t)) + { + continue; + } + } + else if (rule != AUTH_HELPER_SUBJECT_CERT && + rule != AUTH_HELPER_IM_CERT) + { /* handle only HELPER certificates */ + continue; + } + if (this->cert != CERT_ANY && this->cert != current->get_type(current)) + { /* CERT type requested, but does not match */ + continue; + } + public = current->get_public_key(current); + if (this->key != KEY_ANY && !public) + { /* key type requested, but no public key */ + DESTROY_IF(public); + continue; + } + if (this->key != KEY_ANY && public && this->key != public->get_type(public)) + { /* key type requested, but public key has another type */ + DESTROY_IF(public); + continue; + } + DESTROY_IF(public); + if (this->id && !current->has_subject(current, this->id)) + { /* subject requested, but does not match */ + continue; + } + *cert = current; + return TRUE; + } + return FALSE; +} + +/** + * destroy function for wrapper_enumerator_t + */ +static void wrapper_enumerator_destroy(wrapper_enumerator_t *this) +{ + this->inner->destroy(this->inner); + free(this); +} + +/** + * implementation of auth_cfg_wrapper_t.set.create_cert_enumerator + */ +static enumerator_t *create_enumerator(private_auth_cfg_wrapper_t *this, + certificate_type_t cert, key_type_t key, + identification_t *id, bool trusted) +{ + wrapper_enumerator_t *enumerator; + + if (trusted) + { + return NULL; + } + enumerator = malloc_thing(wrapper_enumerator_t); + enumerator->auth = this->auth; + enumerator->cert = cert; + enumerator->key = key; + enumerator->id = id; + enumerator->inner = this->auth->create_enumerator(this->auth); + enumerator->public.enumerate = (void*)enumerate; + enumerator->public.destroy = (void*)wrapper_enumerator_destroy; + return &enumerator->public; +} + +/** + * Implementation of auth_cfg_wrapper_t.destroy + */ +static void destroy(private_auth_cfg_wrapper_t *this) +{ + free(this); +} + +/* + * see header file + */ +auth_cfg_wrapper_t *auth_cfg_wrapper_create(auth_cfg_t *auth) +{ + private_auth_cfg_wrapper_t *this = malloc_thing(private_auth_cfg_wrapper_t); + + this->public.set.create_private_enumerator = (void*)return_null; + this->public.set.create_cert_enumerator = (void*)create_enumerator; + this->public.set.create_shared_enumerator = (void*)return_null; + this->public.set.create_cdp_enumerator = (void*)return_null; + this->public.set.cache_cert = (void*)nop; + this->public.destroy = (void(*)(auth_cfg_wrapper_t*))destroy; + + this->auth = auth; + + return &this->public; +} diff --git a/src/libstrongswan/credentials/sets/auth_cfg_wrapper.h b/src/libstrongswan/credentials/sets/auth_cfg_wrapper.h new file mode 100644 index 000000000..3a4b197ac --- /dev/null +++ b/src/libstrongswan/credentials/sets/auth_cfg_wrapper.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2008-2009 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/** + * @defgroup auth_cfg_wrapper auth_cfg_wrapper + * @{ @ingroup sets + */ + +#ifndef AUTH_CFG_WRAPPER_H_ +#define AUTH_CFG_WRAPPER_H_ + +#include <credentials/auth_cfg.h> +#include <credentials/credential_set.h> + +typedef struct auth_cfg_wrapper_t auth_cfg_wrapper_t; + +/** + * A wrapper around auth_cfg_t to handle it as a credential set. + */ +struct auth_cfg_wrapper_t { + + /** + * implements credential_set_t + */ + credential_set_t set; + + /** + * Destroy a auth_cfg_wrapper instance. + */ + void (*destroy)(auth_cfg_wrapper_t *this); +}; + +/** + * Create a auth_cfg_wrapper instance. + * + * @param auth the wrapped auth info + * @return wrapper around auth + */ +auth_cfg_wrapper_t *auth_cfg_wrapper_create(auth_cfg_t *auth); + +#endif /** AUTH_CFG_WRAPPER_H_ @}*/ diff --git a/src/libstrongswan/credentials/sets/cert_cache.c b/src/libstrongswan/credentials/sets/cert_cache.c new file mode 100644 index 000000000..7161ac9ac --- /dev/null +++ b/src/libstrongswan/credentials/sets/cert_cache.c @@ -0,0 +1,389 @@ +/* + * Copyright (C) 2008 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "cert_cache.h" + +#include <time.h> +#include <sched.h> + +#include <library.h> +#include <threading/rwlock.h> +#include <utils/linked_list.h> + +/** cache size, a power of 2 for fast modulo */ +#define CACHE_SIZE 32 + +/** attempts to acquire a cache lock */ +#define REPLACE_TRIES 5 + +typedef struct private_cert_cache_t private_cert_cache_t; +typedef struct relation_t relation_t; + +/** + * A trusted relation between subject and issuer + */ +struct relation_t { + + /** + * subject of this relation + */ + certificate_t *subject; + + /** + * issuer of this relation + */ + certificate_t *issuer; + + /** + * Cache hits + */ + u_int hits; + + /** + * Lock for this relation + */ + rwlock_t *lock; +}; + +/** + * private data of cert_cache + */ +struct private_cert_cache_t { + + /** + * public functions + */ + cert_cache_t public; + + /** + * array of trusted subject-issuer relations + */ + relation_t relations[CACHE_SIZE]; +}; + +/** + * Cache relation in a free slot/replace an other + */ +static void cache(private_cert_cache_t *this, + certificate_t *subject, certificate_t *issuer) +{ + relation_t *rel; + int i, offset, try; + u_int total_hits = 0; + + /* check for a unused relation slot first */ + for (i = 0; i < CACHE_SIZE; i++) + { + rel = &this->relations[i]; + + if (!rel->subject && rel->lock->try_write_lock(rel->lock)) + { + /* double-check having lock */ + if (!rel->subject) + { + rel->subject = subject->get_ref(subject); + rel->issuer = issuer->get_ref(issuer); + return rel->lock->unlock(rel->lock); + } + rel->lock->unlock(rel->lock); + } + total_hits += rel->hits; + } + /* run several attempts to replace a random slot, never block. */ + for (try = 0; try < REPLACE_TRIES; try++) + { + /* replace a random relation */ + offset = random(); + for (i = 0; i < CACHE_SIZE; i++) + { + rel = &this->relations[(i + offset) % CACHE_SIZE]; + + if (rel->hits > total_hits / CACHE_SIZE) + { /* skip often used slots */ + continue; + } + if (rel->lock->try_write_lock(rel->lock)) + { + if (rel->subject) + { + rel->subject->destroy(rel->subject); + rel->issuer->destroy(rel->issuer); + } + rel->subject = subject->get_ref(subject); + rel->issuer = issuer->get_ref(issuer); + rel->hits = 0; + return rel->lock->unlock(rel->lock); + } + } + /* give other threads a chance to release locks */ + sched_yield(); + } +} + +/** + * Implementation of cert_cache_t.issued_by. + */ +static bool issued_by(private_cert_cache_t *this, + certificate_t *subject, certificate_t *issuer) +{ + relation_t *found = NULL, *current; + int i; + + for (i = 0; i < CACHE_SIZE; i++) + { + current = &this->relations[i]; + + current->lock->read_lock(current->lock); + if (current->subject) + { + /* check for equal issuer */ + if (issuer->equals(issuer, current->issuer)) + { + /* reuse issuer instance in cache() */ + issuer = current->issuer; + if (subject->equals(subject, current->subject)) + { + /* write hit counter is not locked, but not critical */ + current->hits++; + found = current; + } + } + } + current->lock->unlock(current->lock); + if (found) + { + return TRUE; + } + } + /* no cache hit, check and cache signature */ + if (subject->issued_by(subject, issuer)) + { + cache(this, subject, issuer); + return TRUE; + } + return FALSE; +} + +/** + * certificate enumerator implemenation + */ +typedef struct { + /** implements enumerator_t interface */ + enumerator_t public; + /** type of requested certificate */ + certificate_type_t cert; + /** type of requested key */ + key_type_t key; + /** ID to get a cert for */ + identification_t *id; + /** cache */ + relation_t *relations; + /** current position in array cache */ + int index; + /** currently locked relation */ + int locked; +} cert_enumerator_t; + +/** + * filter function for certs enumerator + */ +static bool cert_enumerate(cert_enumerator_t *this, certificate_t **out) +{ + public_key_t *public; + relation_t *rel; + + if (this->locked >= 0) + { + rel = &this->relations[this->locked]; + rel->lock->unlock(rel->lock); + this->locked = -1; + } + + while (++this->index < CACHE_SIZE) + { + rel = &this->relations[this->index]; + rel->lock->read_lock(rel->lock); + this->locked = this->index; + if (rel->subject) + { + /* CRL lookup is done using issuer/authkeyidentifier */ + if (this->key == KEY_ANY && this->id && + (this->cert == CERT_ANY || this->cert == CERT_X509_CRL) && + rel->subject->get_type(rel->subject) == CERT_X509_CRL && + rel->subject->has_issuer(rel->subject, this->id)) + { + *out = rel->subject; + return TRUE; + } + if ((this->cert == CERT_ANY || + rel->subject->get_type(rel->subject) == this->cert) && + (!this->id || rel->subject->has_subject(rel->subject, this->id))) + { + if (this->key == KEY_ANY) + { + *out = rel->subject; + return TRUE; + } + public = rel->subject->get_public_key(rel->subject); + if (public) + { + if (public->get_type(public) == this->key) + { + public->destroy(public); + *out = rel->subject; + return TRUE; + } + public->destroy(public); + } + } + } + this->locked = -1; + rel->lock->unlock(rel->lock); + } + return FALSE; +} + +/** + * clean up enumeration data + */ +static void cert_enumerator_destroy(cert_enumerator_t *this) +{ + relation_t *rel; + + if (this->locked >= 0) + { + rel = &this->relations[this->locked]; + rel->lock->unlock(rel->lock); + } + free(this); +} + +/** + * implementation of credential_set_t.create_cert_enumerator + */ +static enumerator_t *create_enumerator(private_cert_cache_t *this, + certificate_type_t cert, key_type_t key, + identification_t *id, bool trusted) +{ + cert_enumerator_t *enumerator; + + if (trusted) + { + return NULL; + } + enumerator = malloc_thing(cert_enumerator_t); + enumerator->public.enumerate = (void*)cert_enumerate; + enumerator->public.destroy = (void*)cert_enumerator_destroy; + enumerator->cert = cert; + enumerator->key = key; + enumerator->id = id; + enumerator->relations = this->relations; + enumerator->index = -1; + enumerator->locked = -1; + + return &enumerator->public; +} + +/** + * Implementation of cert_cache_t.flush. + */ +static void flush(private_cert_cache_t *this, certificate_type_t type) +{ + relation_t *rel; + int i; + + for (i = 0; i < CACHE_SIZE; i++) + { + rel = &this->relations[i]; + if (!rel->subject) + { + continue; + } + /* check with cheap read lock first */ + if (type != CERT_ANY) + { + rel->lock->read_lock(rel->lock); + if (!rel->subject || type != rel->subject->get_type(rel->subject)) + { + rel->lock->unlock(rel->lock); + continue; + } + rel->lock->unlock(rel->lock); + } + /* double check in write lock */ + rel->lock->write_lock(rel->lock); + if (rel->subject) + { + if (type == CERT_ANY || type == rel->subject->get_type(rel->subject)) + { + rel->subject->destroy(rel->subject); + rel->issuer->destroy(rel->issuer); + rel->subject = NULL; + rel->issuer = NULL; + rel->hits = 0; + } + } + rel->lock->unlock(rel->lock); + } +} + +/** + * Implementation of cert_cache_t.destroy + */ +static void destroy(private_cert_cache_t *this) +{ + relation_t *rel; + int i; + + for (i = 0; i < CACHE_SIZE; i++) + { + rel = &this->relations[i]; + if (rel->subject) + { + rel->subject->destroy(rel->subject); + rel->issuer->destroy(rel->issuer); + } + rel->lock->destroy(rel->lock); + } + free(this); +} + +/* + * see header file + */ +cert_cache_t *cert_cache_create() +{ + private_cert_cache_t *this; + int i; + + this = malloc_thing(private_cert_cache_t); + this->public.set.create_private_enumerator = (void*)return_null; + this->public.set.create_cert_enumerator = (void*)create_enumerator; + this->public.set.create_shared_enumerator = (void*)return_null; + this->public.set.create_cdp_enumerator = (void*)return_null; + this->public.set.cache_cert = (void*)nop; + this->public.issued_by = (bool(*)(cert_cache_t*, certificate_t *subject, certificate_t *issuer))issued_by; + this->public.flush = (void(*)(cert_cache_t*, certificate_type_t type))flush; + this->public.destroy = (void(*)(cert_cache_t*))destroy; + + for (i = 0; i < CACHE_SIZE; i++) + { + this->relations[i].subject = NULL; + this->relations[i].issuer = NULL; + this->relations[i].hits = 0; + this->relations[i].lock = rwlock_create(RWLOCK_TYPE_DEFAULT); + } + return &this->public; +} diff --git a/src/libstrongswan/credentials/sets/cert_cache.h b/src/libstrongswan/credentials/sets/cert_cache.h new file mode 100644 index 000000000..d2721866e --- /dev/null +++ b/src/libstrongswan/credentials/sets/cert_cache.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2008 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/** + * @defgroup cert_cache cert_cache + * @{ @ingroup sets + */ + +#ifndef CERT_CACHE_H_ +#define CERT_CACHE_H_ + +#include <credentials/credential_set.h> + +typedef struct cert_cache_t cert_cache_t; + +/** + * Certificate signature verification and certificate cache. + * + * This cache serves all certificates seen in its issued_by method + * and serves them as untrusted through the credential set interface. Further, + * it caches valid subject-issuer relationships to speed up the issued_by + * method. + */ +struct cert_cache_t { + + /** + * Implements credential_set_t. + */ + credential_set_t set; + + /** + * Caching wrapper around certificate_t.issued_by. + * + * @param subject certificate to verify + * @param issuer issuing certificate to verify subject + * @return TRUE if subject issued by issuer + */ + bool (*issued_by)(cert_cache_t *this, + certificate_t *subject, certificate_t *issuer); + + /** + * Flush the certificate cache. + * + * @param type type of certificate to flush, or CERT_ANY + */ + void (*flush)(cert_cache_t *this, certificate_type_t type); + + /** + * Destroy a cert_cache instance. + */ + void (*destroy)(cert_cache_t *this); +}; + +/** + * Create a cert_cache instance. + */ +cert_cache_t *cert_cache_create(); + +#endif /** CERT_CACHE_H_ @}*/ diff --git a/src/libstrongswan/credentials/sets/ocsp_response_wrapper.c b/src/libstrongswan/credentials/sets/ocsp_response_wrapper.c new file mode 100644 index 000000000..4786495da --- /dev/null +++ b/src/libstrongswan/credentials/sets/ocsp_response_wrapper.c @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2008 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "ocsp_response_wrapper.h" + +typedef struct private_ocsp_response_wrapper_t private_ocsp_response_wrapper_t; + +/** + * private data of ocsp_response_wrapper + */ +struct private_ocsp_response_wrapper_t { + + /** + * public functions + */ + ocsp_response_wrapper_t public; + + /** + * wrapped OCSP response + */ + ocsp_response_t *response; +}; + +/** + * enumerator for ocsp_response_wrapper_t.create_cert_enumerator() + */ +typedef struct { + /** implements enumerator_t */ + enumerator_t public; + /** enumerator over ocsp response */ + enumerator_t *inner; + /** type of cert */ + certificate_type_t cert; + /** type of key */ + key_type_t key; + /** filtering identity */ + identification_t *id; +} wrapper_enumerator_t; + +/** + * enumerate function wrapper_enumerator_t + */ +static bool enumerate(wrapper_enumerator_t *this, certificate_t **cert) +{ + certificate_t *current; + public_key_t *public; + + while (this->inner->enumerate(this->inner, ¤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/libstrongswan/credentials/sets/ocsp_response_wrapper.h b/src/libstrongswan/credentials/sets/ocsp_response_wrapper.h new file mode 100644 index 000000000..dc4b451df --- /dev/null +++ b/src/libstrongswan/credentials/sets/ocsp_response_wrapper.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2008 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/** + * @defgroup ocsp_response_wrapper ocsp_response_wrapper + * @{ @ingroup sets + */ + +#ifndef OCSP_RESPONSE_WRAPPER_H_ +#define OCSP_RESPONSE_WRAPPER_H_ + +#include <credentials/credential_set.h> +#include <credentials/certificates/ocsp_response.h> + +typedef struct ocsp_response_wrapper_t ocsp_response_wrapper_t; + +/** + * A wrapper around ocsp_response_t to handle it like a credential set. + */ +struct ocsp_response_wrapper_t { + + /** + * implements credential_set_t + */ + credential_set_t set; + + /** + * Destroy a ocsp_response_wrapper instance. + */ + void (*destroy)(ocsp_response_wrapper_t *this); +}; + +/** + * Create a ocsp_response_wrapper instance. + * + * @param response the wrapped OCSP response + * @return wrapper around response + */ +ocsp_response_wrapper_t *ocsp_response_wrapper_create(ocsp_response_t *response); + +#endif /** OCSP_RESPONSE_WRAPPER_H_ @}*/ |