diff options
author | Rene Mayrhofer <rene@mayrhofer.eu.org> | 2007-04-12 20:41:31 +0000 |
---|---|---|
committer | Rene Mayrhofer <rene@mayrhofer.eu.org> | 2007-04-12 20:41:31 +0000 |
commit | 774a362e87feab25f1be16fbca08269ddc7121a4 (patch) | |
tree | cf71f4e7466468ac3edc2127125f333224a9acfb /src/charon/config | |
parent | c54a140a445bfe7aa66721f68bb0781f26add91c (diff) | |
download | vyos-strongswan-774a362e87feab25f1be16fbca08269ddc7121a4.tar.gz vyos-strongswan-774a362e87feab25f1be16fbca08269ddc7121a4.zip |
Major new upstream release, just ran svn-upgrade for now (and wrote some
debian/changelong entries).
Diffstat (limited to 'src/charon/config')
-rwxr-xr-x | src/charon/config/configuration.c | 162 | ||||
-rwxr-xr-x | src/charon/config/configuration.h | 102 | ||||
-rw-r--r-- | src/charon/config/connections/connection.c | 404 | ||||
-rw-r--r-- | src/charon/config/connections/connection.h | 292 | ||||
-rwxr-xr-x | src/charon/config/connections/connection_store.h | 118 | ||||
-rw-r--r-- | src/charon/config/connections/local_connection_store.c | 237 | ||||
-rw-r--r-- | src/charon/config/connections/local_connection_store.h | 62 | ||||
-rw-r--r-- | src/charon/config/credentials/local_credential_store.c | 1363 | ||||
-rw-r--r-- | src/charon/config/credentials/local_credential_store.h | 64 | ||||
-rw-r--r-- | src/charon/config/policies/local_policy_store.c | 282 | ||||
-rw-r--r-- | src/charon/config/policies/local_policy_store.h | 60 | ||||
-rw-r--r-- | src/charon/config/policies/policy.c | 635 | ||||
-rw-r--r-- | src/charon/config/policies/policy.h | 413 | ||||
-rwxr-xr-x | src/charon/config/policies/policy_store.h | 119 | ||||
-rw-r--r-- | src/charon/config/proposal.c | 641 | ||||
-rw-r--r-- | src/charon/config/proposal.h | 266 | ||||
-rw-r--r-- | src/charon/config/traffic_selector.c | 795 | ||||
-rw-r--r-- | src/charon/config/traffic_selector.h | 312 |
18 files changed, 6327 insertions, 0 deletions
diff --git a/src/charon/config/configuration.c b/src/charon/config/configuration.c new file mode 100755 index 000000000..488ba9a5e --- /dev/null +++ b/src/charon/config/configuration.c @@ -0,0 +1,162 @@ +/** + * @file configuration.c + * + * @brief Implementation of configuration_t. + * + */ + +/* + * Copyright (C) 2006 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 <stdlib.h> +#include <math.h> + +#include "configuration.h" + +#include <library.h> + +/** + * Timeout in milliseconds after that a half open IKE_SA gets deleted. + */ +#define HALF_OPEN_IKE_SA_TIMEOUT 30000 + +/** + * Retransmission uses a backoff algorithm. The timeout is calculated using + * TIMEOUT * (BASE ** try). + * When try reaches TRIES, retransmission is given up. + * + * Using an initial TIMEOUT of 4s, a BASE of 1.8, and 5 TRIES gives us: + * + * | relative | absolute + * --------------------------------------------------------- + * 4s * (1.8 ** (0 % 5)) = 4s 4s + * 4s * (1.8 ** (1 % 5)) = 7s 11s + * 4s * (1.8 ** (2 % 5)) = 13s 24s + * 4s * (1.8 ** (3 % 5)) = 23s 47s + * 4s * (1.8 ** (4 % 5)) = 42s 89s + * 4s * (1.8 ** (5 % 5)) = 76s 165s + * + * The peer is considered dead after 2min 45s when no reply comes in. + */ + +/** + * First retransmit timeout in milliseconds. + * Timeout value is increasing in each retransmit round. + */ +#define RETRANSMIT_TIMEOUT 4000 + +/** + * Base which is raised to the power of the retransmission count. + */ +#define RETRANSMIT_BASE 1.8 + +/** + * Number of retransmits done in a retransmit sequence + */ +#define RETRANSMIT_TRIES 5 + +/** + * Keepalive interval in seconds. + */ +#define KEEPALIVE_INTERVAL 20 + +/** + * retry interval in seconds. + */ +#define RETRY_INTERVAL 30 + +/** + * jitter to user for retrying + */ +#define RETRY_JITTER 20 + + +typedef struct private_configuration_t private_configuration_t; + +/** + * Private data of an configuration_t object. + */ +struct private_configuration_t { + + /** + * Public part of configuration_t object. + */ + configuration_t public; + +}; + +/** + * Implementation of configuration_t.get_retransmit_timeout. + */ +static u_int32_t get_retransmit_timeout (private_configuration_t *this, + u_int32_t retransmit_count) +{ + if (retransmit_count > RETRANSMIT_TRIES) + { + /* give up */ + return 0; + } + return (u_int32_t) + (RETRANSMIT_TIMEOUT * pow(RETRANSMIT_BASE, retransmit_count)); +} + +/** + * Implementation of configuration_t.get_half_open_ike_sa_timeout. + */ +static u_int32_t get_half_open_ike_sa_timeout (private_configuration_t *this) +{ + return HALF_OPEN_IKE_SA_TIMEOUT; +} + +/** + * Implementation of configuration_t.get_keepalive_interval. + */ +static u_int32_t get_keepalive_interval (private_configuration_t *this) +{ + return KEEPALIVE_INTERVAL; +} + +/** + * Implementation of configuration_t.get_retry_interval. + */ +static u_int32_t get_retry_interval (private_configuration_t *this) +{ + return RETRY_INTERVAL - (random() % RETRY_JITTER); +} + +/** + * Implementation of configuration_t.destroy. + */ +static void destroy(private_configuration_t *this) +{ + free(this); +} + +/* + * Described in header-file + */ +configuration_t *configuration_create() +{ + private_configuration_t *this = malloc_thing(private_configuration_t); + + /* public functions */ + this->public.destroy = (void(*)(configuration_t*))destroy; + this->public.get_retransmit_timeout = (u_int32_t (*) (configuration_t*,u_int32_t))get_retransmit_timeout; + this->public.get_half_open_ike_sa_timeout = (u_int32_t (*) (configuration_t*)) get_half_open_ike_sa_timeout; + this->public.get_keepalive_interval = (u_int32_t (*) (configuration_t*)) get_keepalive_interval; + this->public.get_retry_interval = (u_int32_t (*) (configuration_t*)) get_retry_interval; + + return (&this->public); +} diff --git a/src/charon/config/configuration.h b/src/charon/config/configuration.h new file mode 100755 index 000000000..c1207171d --- /dev/null +++ b/src/charon/config/configuration.h @@ -0,0 +1,102 @@ +/** + * @file configuration.h + * + * @brief Interface configuration_t. + * + */ + +/* + * Copyright (C) 2006 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. + */ + +#ifndef CONFIGURATION_H_ +#define CONFIGURATION_H_ + +typedef struct configuration_t configuration_t; + +#include <library.h> + +/** + * @brief The interface for various daemon related configs. + * + * @b Constructors: + * - configuration_create() + * + * @ingroup config + */ +struct configuration_t { + + /** + * @brief Returns the retransmit timeout. + * + * A return value of zero means the request should not be + * retransmitted again. + * + * @param this calling object + * @param retransmitted number of times a message was retransmitted so far + * @return time in milliseconds, when to do next retransmit + */ + u_int32_t (*get_retransmit_timeout) (configuration_t *this, + u_int32_t retransmitted); + + /** + * @brief Returns the timeout for an half open IKE_SA in ms. + * + * Half open means that the IKE_SA is still on a not established state + * + * @param this calling object + * @return timeout in milliseconds (ms) + */ + u_int32_t (*get_half_open_ike_sa_timeout) (configuration_t *this); + + /** + * @brief Returns the keepalive interval in s. + * + * The keepalive interval defines the idle time after which a + * NAT keepalive packet should be sent. + * + * @param this calling object + * @return interval in s + */ + u_int32_t (*get_keepalive_interval) (configuration_t *this); + + /** + * @brief Returns the interval to retry a failed action again. + * + * In some situations, the protocol may be in a state where processing + * is not possible and an action must be retried (e.g. rekeying). + * + * @param this calling object + * @return interval in s + */ + u_int32_t (*get_retry_interval) (configuration_t *this); + + /** + * @brief Destroys a configuration_t object. + * + * @param this calling object + */ + void (*destroy) (configuration_t *this); +}; + +/** + * @brief Creates a configuration backend. + * + * @return static_configuration_t object + * + * @ingroup config + */ +configuration_t *configuration_create(void); + +#endif /*CONFIGURATION_H_*/ diff --git a/src/charon/config/connections/connection.c b/src/charon/config/connections/connection.c new file mode 100644 index 000000000..ffe508992 --- /dev/null +++ b/src/charon/config/connections/connection.c @@ -0,0 +1,404 @@ +/** + * @file connection.c + * + * @brief Implementation of connection_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * 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 <string.h> + +#include <config/connections/connection.h> +#include <utils/linked_list.h> + +ENUM(cert_policy_names, CERT_ALWAYS_SEND, CERT_NEVER_SEND, + "CERT_ALWAYS_SEND", + "CERT_SEND_IF_ASKED", + "CERT_NEVER_SEND" +); + +typedef struct private_connection_t private_connection_t; + +/** + * Private data of an connection_t object + */ +struct private_connection_t { + + /** + * Public part + */ + connection_t public; + + /** + * Number of references hold by others to this connection + */ + refcount_t refcount; + + /** + * Name of the connection + */ + char *name; + + /** + * Does charon handle this connection? Or can he ignore it? + */ + bool ikev2; + + /** + * should we send a certificate request? + */ + cert_policy_t certreq_policy; + + /** + * should we send a certificates? + */ + cert_policy_t cert_policy; + + /** + * ID of us + */ + identification_t *my_id; + + /** + * Host information of my host. + */ + host_t *my_host; + + /** + * Host information of other host. + */ + host_t *other_host; + + /** + * Interval to send DPD liveness checks on inactivity + */ + u_int32_t dpd_delay; + + /** + * Number of retransmission sequences to send bevore giving up + */ + u_int32_t keyingtries; + + /** + * Supported proposals + */ + linked_list_t *proposals; + + /** + * Time before an SA gets invalid + */ + u_int32_t soft_lifetime; + + /** + * Time before an SA gets rekeyed + */ + u_int32_t hard_lifetime; + + /** + * Use full reauthentication instead of rekeying + */ + bool reauth; + + /** + * Time, which specifies the range of a random value + * substracted from soft_lifetime. + */ + u_int32_t jitter; +}; + +/** + * Implementation of connection_t.get_name. + */ +static char *get_name (private_connection_t *this) +{ + return this->name; +} + +/** + * Implementation of connection_t.is_ikev2. + */ +static bool is_ikev2 (private_connection_t *this) +{ + return this->ikev2; +} + +/** + * Implementation of connection_t.get_certreq_policy. + */ +static cert_policy_t get_certreq_policy (private_connection_t *this) +{ + return this->certreq_policy; +} + +/** + * Implementation of connection_t.get_cert_policy. + */ +static cert_policy_t get_cert_policy (private_connection_t *this) +{ + return this->cert_policy; +} + +/** + * Implementation of connection_t.get_my_host. + */ +static host_t *get_my_host (private_connection_t *this) +{ + return this->my_host; +} + +/** + * Implementation of connection_t.get_other_host. + */ +static host_t *get_other_host (private_connection_t *this) +{ + return this->other_host; +} + +/** + * Implementation of connection_t.get_proposals. + */ +static linked_list_t* get_proposals(private_connection_t *this) +{ + iterator_t *iterator; + proposal_t *current; + linked_list_t *proposals = linked_list_create(); + + iterator = this->proposals->create_iterator(this->proposals, TRUE); + while (iterator->iterate(iterator, (void**)¤t)) + { + current = current->clone(current); + proposals->insert_last(proposals, (void*)current); + } + iterator->destroy(iterator); + + return proposals; +} + +/** + * Implementation of connection_t.select_proposal. + */ +static proposal_t *select_proposal(private_connection_t *this, linked_list_t *proposals) +{ + iterator_t *stored_iter, *supplied_iter; + proposal_t *stored, *supplied, *selected; + + stored_iter = this->proposals->create_iterator(this->proposals, TRUE); + supplied_iter = proposals->create_iterator(proposals, TRUE); + + /* compare all stored proposals with all supplied. Stored ones are preferred. */ + while (stored_iter->iterate(stored_iter, (void**)&stored)) + { + supplied_iter->reset(supplied_iter); + + while (supplied_iter->iterate(supplied_iter, (void**)&supplied)) + { + selected = stored->select(stored, supplied); + if (selected) + { + /* they match, return */ + stored_iter->destroy(stored_iter); + supplied_iter->destroy(supplied_iter); + return selected; + } + } + } + /* no proposal match :-(, will result in a NO_PROPOSAL_CHOSEN... */ + stored_iter->destroy(stored_iter); + supplied_iter->destroy(supplied_iter); + + return NULL; +} + +/** + * Implementation of connection_t.add_proposal. + */ +static void add_proposal(private_connection_t *this, proposal_t *proposal) +{ + this->proposals->insert_last(this->proposals, proposal); +} + +/** + * Implementation of connection_t.get_dpd_delay. + */ +static u_int32_t get_dpd_delay(private_connection_t *this) +{ + return this->dpd_delay; +} + +/** + * Implementation of connection_t.get_keyingtries. + */ +static u_int32_t get_keyingtries(private_connection_t *this) +{ + return this->keyingtries; +} + +/** + * Implementation of connection_t.get_dh_group. + */ +static diffie_hellman_group_t get_dh_group(private_connection_t *this) +{ + iterator_t *iterator; + proposal_t *proposal; + algorithm_t *algo; + diffie_hellman_group_t dh_group = MODP_NONE; + + iterator = this->proposals->create_iterator(this->proposals, TRUE); + while (iterator->iterate(iterator, (void**)&proposal)) + { + if (proposal->get_algorithm(proposal, DIFFIE_HELLMAN_GROUP, &algo)) + { + dh_group = algo->algorithm; + break; + } + } + iterator->destroy(iterator); + return dh_group; +} + +/** + * Implementation of connection_t.check_dh_group. + */ +static bool check_dh_group(private_connection_t *this, diffie_hellman_group_t dh_group) +{ + iterator_t *prop_iter, *alg_iter; + proposal_t *proposal; + algorithm_t *algo; + + prop_iter = this->proposals->create_iterator(this->proposals, TRUE); + while (prop_iter->iterate(prop_iter, (void**)&proposal)) + { + alg_iter = proposal->create_algorithm_iterator(proposal, DIFFIE_HELLMAN_GROUP); + while (alg_iter->iterate(alg_iter, (void**)&algo)) + { + if (algo->algorithm == dh_group) + { + prop_iter->destroy(prop_iter); + alg_iter->destroy(alg_iter); + return TRUE; + } + } + alg_iter->destroy(alg_iter); + } + prop_iter->destroy(prop_iter); + return FALSE; +} +/** + * Implementation of connection_t.get_soft_lifetime + */ +static u_int32_t get_soft_lifetime(private_connection_t *this) +{ + if (this->jitter == 0) + { + return this->soft_lifetime ; + } + return this->soft_lifetime - (random() % this->jitter); +} + +/** + * Implementation of connection_t.get_hard_lifetime. + */ +static u_int32_t get_hard_lifetime(private_connection_t *this) +{ + return this->hard_lifetime; +} + +/** + * Implementation of connection_t.get_reauth. + */ +static bool get_reauth(private_connection_t *this) +{ + return this->reauth; +} + +/** + * Implementation of connection_t.get_ref. + */ +static void get_ref(private_connection_t *this) +{ + ref_get(&this->refcount); +} + +/** + * Implementation of connection_t.destroy. + */ +static void destroy(private_connection_t *this) +{ + if (ref_put(&this->refcount)) + { + this->proposals->destroy_offset(this->proposals, offsetof(proposal_t, destroy)); + this->my_host->destroy(this->my_host); + this->other_host->destroy(this->other_host); + free(this->name); + free(this); + } +} + +/** + * Described in header. + */ +connection_t * connection_create(char *name, bool ikev2, + cert_policy_t cert_policy, + cert_policy_t certreq_policy, + host_t *my_host, host_t *other_host, + u_int32_t dpd_delay, bool reauth, + u_int32_t keyingtries, + u_int32_t hard_lifetime, + u_int32_t soft_lifetime, u_int32_t jitter) +{ + private_connection_t *this = malloc_thing(private_connection_t); + + /* public functions */ + this->public.get_name = (char*(*)(connection_t*))get_name; + this->public.is_ikev2 = (bool(*)(connection_t*))is_ikev2; + this->public.get_cert_policy = (cert_policy_t(*)(connection_t*))get_cert_policy; + this->public.get_certreq_policy = (cert_policy_t(*)(connection_t*))get_certreq_policy; + this->public.get_my_host = (host_t*(*)(connection_t*))get_my_host; + this->public.get_other_host = (host_t*(*)(connection_t*))get_other_host; + this->public.get_proposals = (linked_list_t*(*)(connection_t*))get_proposals; + this->public.select_proposal = (proposal_t*(*)(connection_t*,linked_list_t*))select_proposal; + this->public.add_proposal = (void(*)(connection_t*, proposal_t*)) add_proposal; + this->public.get_dpd_delay = (u_int32_t(*)(connection_t*)) get_dpd_delay; + this->public.get_reauth = (bool(*)(connection_t*)) get_reauth; + this->public.get_keyingtries = (u_int32_t(*)(connection_t*)) get_keyingtries; + this->public.get_dh_group = (diffie_hellman_group_t(*)(connection_t*)) get_dh_group; + this->public.check_dh_group = (bool(*)(connection_t*,diffie_hellman_group_t)) check_dh_group; + this->public.get_soft_lifetime = (u_int32_t (*) (connection_t *))get_soft_lifetime; + this->public.get_hard_lifetime = (u_int32_t (*) (connection_t *))get_hard_lifetime; + this->public.get_ref = (void(*)(connection_t*))get_ref; + this->public.destroy = (void(*)(connection_t*))destroy; + + /* private variables */ + this->refcount = 1; + this->name = strdup(name); + this->ikev2 = ikev2; + this->cert_policy = cert_policy; + this->certreq_policy = certreq_policy; + this->my_host = my_host; + this->other_host = other_host; + this->dpd_delay = dpd_delay; + this->reauth = reauth; + this->keyingtries = keyingtries; + this->hard_lifetime = hard_lifetime; + this->soft_lifetime = soft_lifetime; + this->jitter = jitter; + + this->proposals = linked_list_create(); + + return &this->public; +} diff --git a/src/charon/config/connections/connection.h b/src/charon/config/connections/connection.h new file mode 100644 index 000000000..d0788876f --- /dev/null +++ b/src/charon/config/connections/connection.h @@ -0,0 +1,292 @@ +/** + * @file connection.h + * + * @brief Interface of connection_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * 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. + */ + +#ifndef CONNECTION_H_ +#define CONNECTION_H_ + +typedef enum cert_policy_t cert_policy_t; +typedef struct connection_t connection_t; + +#include <library.h> +#include <utils/host.h> +#include <utils/linked_list.h> +#include <utils/identification.h> +#include <config/proposal.h> +#include <crypto/diffie_hellman.h> + + +/** + * Certificate sending policy. This is also used for certificate + * requests when using this definition for the other peer. If + * it is CERT_NEVER_SEND, a certreq is omitted, otherwise its + * included. + * + * @ingroup config + * + * @warning These definitions must be the same as in pluto/starter, + * as they are sent over the stroke socket. + */ +enum cert_policy_t { + /** always send certificates, even when not requested */ + CERT_ALWAYS_SEND = 0, + /** send certificate upon cert request */ + CERT_SEND_IF_ASKED = 1, + /** never send a certificate, even when requested */ + CERT_NEVER_SEND = 2, +}; + +/** + * enum strings for cert_policy_t + * + * @ingroup config + */ +extern enum_name_t *cert_policy_names; + +/** + * @brief A connection_t defines the rules to set up an IKE_SA. + * + * @b Constructors: + * - connection_create() + * + * @ingroup config + */ +struct connection_t { + + /** + * @brief Get my address as host_t object. + * + * Object is NOT getting cloned. + * + * @param this calling object + * @return host information as host_t object + */ + host_t *(*get_my_host) (connection_t *this); + + /** + * @brief Get others address as host_t object. + * + * Object is NOT getting cloned. + * + * @param this calling object + * @return host information as host_t object + */ + host_t *(*get_other_host) (connection_t *this); + + /** + * @brief Returns a list of all supported proposals. + * + * Returned list and its proposals must be destroyed after usage. + * + * @param this calling object + * @return list containing all the proposals + */ + linked_list_t *(*get_proposals) (connection_t *this); + + /** + * @brief Adds a proposal to the list. + * + * The first added proposal has the highest priority, the last + * added the lowest. + * + * @param this calling object + * @param proposal proposal to add + */ + void (*add_proposal) (connection_t *this, proposal_t *proposal); + + /** + * @brief Select a proposed from suggested proposals. + * + * Returned proposal must be destroyed after usage. + * + * @param this calling object + * @param proposals list of proposals to select from + * @return selected proposal, or NULL if none matches. + */ + proposal_t *(*select_proposal) (connection_t *this, linked_list_t *proposals); + + /** + * @brief Get the DPD check interval. + * + * @param this calling object + * @return dpd_delay in seconds + */ + u_int32_t (*get_dpd_delay) (connection_t *this); + + /** + * @brief Should a full reauthentication be done instead of rekeying? + * + * @param this calling object + * @return TRUE to use full reauthentication + */ + bool (*get_reauth) (connection_t *this); + + /** + * @brief Get the max number of retransmission sequences. + * + * @param this calling object + * @return max number of retransmission sequences + */ + u_int32_t (*get_keyingtries) (connection_t *this); + + /** + * @brief Get the connection name. + * + * Name must not be freed, since it points to + * internal data. + * + * @param this calling object + * @return name of the connection + */ + char* (*get_name) (connection_t *this); + + /** + * @brief Check if the connection is marked as an IKEv2 connection. + * + * Since all connections (IKEv1+2) are loaded, but charon handles + * only those marked with IKEv2, this flag can tell us if we must + * ignore a connection on initiaton. Then pluto will do it for us. + * + * @param this calling object + * @return - TRUE, if this is an IKEv2 connection + */ + bool (*is_ikev2) (connection_t *this); + + /** + * @brief Should be sent a certificate request for this connection? + * + * A certificate request contains serials of our trusted CA certificates. + * This flag says if such a request is sent on connection setup to + * the peer. It should be omitted when CERT_SEND_NEVER, sended otherwise. + * + * @param this calling object + * @return certificate request sending policy + */ + cert_policy_t (*get_certreq_policy) (connection_t *this); + + /** + * @brief Should be sent a certificate for this connection? + * + * Return the policy used to send the certificate. + * + * @param this calling object + * @return certificate sending policy + */ + cert_policy_t (*get_cert_policy) (connection_t *this); + + /** + * @brief Get the DH group to use for connection initialization. + * + * @param this calling object + * @return dh group to use for initialization + */ + diffie_hellman_group_t (*get_dh_group) (connection_t *this); + + /** + * @brief Check if a suggested dh group is acceptable. + * + * If we guess a wrong DH group for IKE_SA_INIT, the other + * peer will send us a offer. But is this acceptable for us? + * + * @param this calling object + * @return TRUE if group acceptable + */ + bool (*check_dh_group) (connection_t *this, diffie_hellman_group_t dh_group); + + /** + * @brief Get the lifetime of a connection, before IKE_SA rekeying starts. + * + * A call to this function automatically adds a jitter to + * avoid simultanous rekeying. + * + * @param this calling object + * @return lifetime in seconds + */ + u_int32_t (*get_soft_lifetime) (connection_t *this); + + /** + * @brief Get the lifetime of a connection, before IKE_SA gets deleted. + * + * @param this calling object + * @return lifetime in seconds + */ + u_int32_t (*get_hard_lifetime) (connection_t *this); + + /** + * @brief Get a new reference to this connection. + * + * Get a new reference to this connection by increasing + * it's internal reference counter. + * Do not call get_ref or any other function until you + * already have a reference. Otherwise the object may get + * destroyed while calling get_ref(), + * + * @param this calling object + */ + void (*get_ref) (connection_t *this); + + /** + * @brief Destroys a connection_t object. + * + * Decrements the internal reference counter and + * destroys the connection when it reaches zero. + * + * @param this calling object + */ + void (*destroy) (connection_t *this); +}; + +/** + * @brief Creates a connection_t object. + * + * Supplied hosts become owned by connection, so + * do not modify or destroy them after a call to + * connection_create(). Name gets cloned internally. + * The retrasmit sequence number says how fast we give up when the peer + * does not respond. A high value may bridge-over temporary connection + * problems, a small value can detect dead peers faster. + * + * @param name connection identifier + * @param ikev2 TRUE if this is an IKEv2 connection + * @param cert_policy certificate send policy + * @param cert_req_policy certificate request send policy + * @param my_host host_t representing local address + * @param other_host host_t representing remote address + * @param dpd_delay interval of DPD liveness checks + * @param reauth use full reauthentication instead of rekeying + * @param keyingtries number of retransmit sequences to use + * @param hard_lifetime lifetime before deleting an IKE_SA + * @param soft_lifetime lifetime before rekeying an IKE_SA + * @param jitter range of randomization time + * @return connection_t object. + * + * @ingroup config + */ +connection_t * connection_create(char *name, bool ikev2, + cert_policy_t cert_pol, cert_policy_t req_pol, + host_t *my_host, host_t *other_host, + u_int32_t dpd_delay, bool reauth, + u_int32_t keyingtries, + u_int32_t hard_lifetime, u_int32_t soft_lifetime, + u_int32_t jitter); + +#endif /* CONNECTION_H_ */ diff --git a/src/charon/config/connections/connection_store.h b/src/charon/config/connections/connection_store.h new file mode 100755 index 000000000..70f209d3b --- /dev/null +++ b/src/charon/config/connections/connection_store.h @@ -0,0 +1,118 @@ +/** + * @file connection_store.h + * + * @brief Interface connection_store_t. + * + */ + +/* + * Copyright (C) 2006 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. + */ + +#ifndef CONNECTION_STORE_H_ +#define CONNECTION_STORE_H_ + +typedef struct connection_store_t connection_store_t; + +#include <library.h> +#include <config/connections/connection.h> +#include <utils/iterator.h> + +/** + * @brief The interface for a store of connection_t's. + * + * @b Constructors: + * - stroke_create() + * + * @ingroup config + */ +struct connection_store_t { + + /** + * @brief Returns a connection definition identified by two hosts. + * + * This call is usefull to get a connection identified by addresses. + * It may be used after kernel request for traffic protection. + * The returned connection gets created/cloned and therefore must + * be destroyed after usage. + * + * @param this calling object + * @param my_id own address of connection + * @param other_id others address of connection + * @return + * - connection_t, if found + * - NULL otherwise + */ + connection_t *(*get_connection_by_hosts)(connection_store_t *this, + host_t *my_host, host_t *other_host); + + /** + * @brief Returns a connection identified by its name. + * + * This call is usefull to get a connection identified its + * name, as on an connection setup. + * + * @param this calling object + * @param name name of the connection to get + * @return + * - connection_t, if found + * - NULL otherwise + */ + connection_t *(*get_connection_by_name) (connection_store_t *this, char *name); + + /** + * @brief Add a connection to the store. + * + * After a successful call, the connection is owned by the store and may + * not be manipulated nor destroyed. + * + * @param this calling object + * @param connection connection to add + * @return + * - SUCCESS, or + * - FAILED + */ + status_t (*add_connection) (connection_store_t *this, connection_t *connection); + + /** + * @brief Delete a connection from the store. + * + * Remove a connection from the connection store, identified + * by the connections name. + * + * @param this calling object + * @param name name of the connection to delete + * @return + * - SUCCESS, or + * - NOT_FOUND + */ + status_t (*delete_connection) (connection_store_t *this, char *name); + + /** + * @brief Get an iterator for the stored connections. + * + * @param this calling object + * @return iterator over all stored connections + */ + iterator_t* (*create_iterator) (connection_store_t *this); + + /** + * @brief Destroys a connection_store_t object. + * + * @param this calling object + */ + void (*destroy) (connection_store_t *this); +}; + +#endif /* CONNECTION_STORE_H_ */ diff --git a/src/charon/config/connections/local_connection_store.c b/src/charon/config/connections/local_connection_store.c new file mode 100644 index 000000000..df4ec230a --- /dev/null +++ b/src/charon/config/connections/local_connection_store.c @@ -0,0 +1,237 @@ +/** + * @file local_connection_store.c + * + * @brief Implementation of local_connection_store_t. + * + */ + +/* + * Copyright (C) 2006 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 <string.h> + +#include "local_connection_store.h" + +#include <daemon.h> +#include <utils/linked_list.h> + + +typedef struct private_local_connection_store_t private_local_connection_store_t; + +/** + * Private data of an local_connection_store_t object + */ +struct private_local_connection_store_t { + + /** + * Public part + */ + local_connection_store_t public; + + /** + * stored connection + */ + linked_list_t *connections; + + /** + * Mutex to exclusivly access connection list + */ + pthread_mutex_t mutex; +}; + + +/** + * Implementation of connection_store_t.get_connection_by_hosts. + */ +static connection_t *get_connection_by_hosts(private_local_connection_store_t *this, host_t *my_host, host_t *other_host) +{ + typedef enum { + PRIO_UNDEFINED= 0x00, + PRIO_ADDR_ANY= 0x01, + PRIO_ADDR_MATCH= 0x02 + } prio_t; + + prio_t best_prio = PRIO_UNDEFINED; + + iterator_t *iterator; + connection_t *candidate; + connection_t *found = NULL; + + DBG2(DBG_CFG, "looking for connection for host pair %H...%H", + my_host, other_host); + + pthread_mutex_lock(&(this->mutex)); + iterator = this->connections->create_iterator(this->connections, TRUE); + /* determine closest matching connection */ + while (iterator->iterate(iterator, (void**)&candidate)) + { + host_t *candidate_my_host; + host_t *candidate_other_host; + + candidate_my_host = candidate->get_my_host(candidate); + candidate_other_host = candidate->get_other_host(candidate); + + /* my_host addresses must match*/ + if (my_host->ip_equals(my_host, candidate_my_host)) + { + prio_t prio = PRIO_UNDEFINED; + + /* exact match of peer host address or wildcard address? */ + if (other_host->ip_equals(other_host, candidate_other_host)) + { + prio |= PRIO_ADDR_MATCH; + } + else if (candidate_other_host->is_anyaddr(candidate_other_host)) + { + prio |= PRIO_ADDR_ANY; + } + + DBG2(DBG_CFG, "candidate connection \"%s\": %H...%H (prio=%d)", + candidate->get_name(candidate), + candidate_my_host, candidate_other_host, prio); + + if (prio > best_prio) + { + found = candidate; + best_prio = prio; + } + } + } + iterator->destroy(iterator); + + if (found) + { + DBG2(DBG_CFG, "found matching connection \"%s\": %H...%H (prio=%d)", + found->get_name(found), found->get_my_host(found), + found->get_other_host(found), best_prio); + + /* give out a new reference to it */ + found->get_ref(found); + } + pthread_mutex_unlock(&(this->mutex)); + return found; +} + +/** + * Implementation of connection_store_t.get_connection_by_name. + */ +static connection_t *get_connection_by_name(private_local_connection_store_t *this, char *name) +{ + iterator_t *iterator; + connection_t *current, *found = NULL; + + pthread_mutex_lock(&(this->mutex)); + iterator = this->connections->create_iterator(this->connections, TRUE); + while (iterator->iterate(iterator, (void**)¤t)) + { + if (strcmp(name, current->get_name(current)) == 0) + { + found = current; + break; + } + } + iterator->destroy(iterator); + pthread_mutex_unlock(&(this->mutex)); + + if (found) + { + /* get a new reference for it */ + found->get_ref(found); + } + return found; +} + +/** + * Implementation of connection_store_t.delete_connection. + */ +static status_t delete_connection(private_local_connection_store_t *this, char *name) +{ + iterator_t *iterator; + connection_t *current; + bool found = FALSE; + + pthread_mutex_lock(&(this->mutex)); + iterator = this->connections->create_iterator(this->connections, TRUE); + while (iterator->iterate(iterator, (void **)¤t)) + { + if (strcmp(current->get_name(current), name) == 0) + { + /* remove connection from list, and destroy it */ + iterator->remove(iterator); + current->destroy(current); + found = TRUE; + break; + } + } + iterator->destroy(iterator); + pthread_mutex_unlock(&(this->mutex)); + if (found) + { + return SUCCESS; + } + return NOT_FOUND; +} + +/** + * Implementation of connection_store_t.add_connection. + */ +static status_t add_connection(private_local_connection_store_t *this, connection_t *connection) +{ + pthread_mutex_lock(&(this->mutex)); + this->connections->insert_last(this->connections, connection); + pthread_mutex_unlock(&(this->mutex)); + return SUCCESS; +} + +/** + * Implementation of connection_store_t.create_iterator. + */ +static iterator_t* create_iterator(private_local_connection_store_t *this) +{ + return this->connections->create_iterator_locked(this->connections, + &this->mutex); +} + +/** + * Implementation of connection_store_t.destroy. + */ +static void destroy (private_local_connection_store_t *this) +{ + pthread_mutex_lock(&(this->mutex)); + this->connections->destroy_offset(this->connections, offsetof(connection_t, destroy)); + pthread_mutex_unlock(&(this->mutex)); + free(this); +} + +/** + * Described in header. + */ +local_connection_store_t * local_connection_store_create(void) +{ + private_local_connection_store_t *this = malloc_thing(private_local_connection_store_t); + + this->public.connection_store.get_connection_by_hosts = (connection_t*(*)(connection_store_t*,host_t*,host_t*))get_connection_by_hosts; + this->public.connection_store.get_connection_by_name = (connection_t*(*)(connection_store_t*,char*))get_connection_by_name; + this->public.connection_store.delete_connection = (status_t(*)(connection_store_t*,char*))delete_connection; + this->public.connection_store.add_connection = (status_t(*)(connection_store_t*,connection_t*))add_connection; + this->public.connection_store.create_iterator = (iterator_t*(*)(connection_store_t*))create_iterator; + this->public.connection_store.destroy = (void(*)(connection_store_t*))destroy; + + /* private variables */ + this->connections = linked_list_create(); + pthread_mutex_init(&(this->mutex), NULL); + + return (&this->public); +} diff --git a/src/charon/config/connections/local_connection_store.h b/src/charon/config/connections/local_connection_store.h new file mode 100644 index 000000000..e78ed809a --- /dev/null +++ b/src/charon/config/connections/local_connection_store.h @@ -0,0 +1,62 @@ +/** + * @file local_connection_store.h + * + * @brief Interface of local_connection_store_t. + * + */ + +/* + * Copyright (C) 2006 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. + */ + +#ifndef LOCAL_CONNECTION_H_ +#define LOCAL_CONNECTION_H_ + +typedef struct local_connection_store_t local_connection_store_t; + +#include <library.h> +#include <config/connections/connection_store.h> + +/** + * @brief A connection_store_t implementation using a simple connection list. + * + * The local_connection_store_t class implements the connection_store_t interface + * as simple as possible. connection_t's are stored in an in-memory list. + * + * @b Constructors: + * - local_connection_store_create() + * + * @todo Make thread-save first + * @todo Add remove_connection method + * + * @ingroup config + */ +struct local_connection_store_t { + + /** + * Implements connection_store_t interface + */ + connection_store_t connection_store; +}; + +/** + * @brief Creates a local_connection_store_t instance. + * + * @return connection store instance. + * + * @ingroup config + */ +local_connection_store_t * local_connection_store_create(void); + +#endif /* LOCAL_CONNECTION_H_ */ diff --git a/src/charon/config/credentials/local_credential_store.c b/src/charon/config/credentials/local_credential_store.c new file mode 100644 index 000000000..b7b71b9e7 --- /dev/null +++ b/src/charon/config/credentials/local_credential_store.c @@ -0,0 +1,1363 @@ +/** + * @file local_credential_store.c + * + * @brief Implementation of local_credential_store_t. + * + */ + +/* + * Copyright (C) 2006 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 <sys/stat.h> +#include <dirent.h> +#include <string.h> +#include <pthread.h> + +#include <library.h> +#include <utils/lexparser.h> +#include <utils/linked_list.h> +#include <crypto/rsa/rsa_public_key.h> +#include <crypto/certinfo.h> +#include <crypto/x509.h> +#include <crypto/ca.h> +#include <crypto/crl.h> +#include <asn1/ttodata.h> + +#include "local_credential_store.h" + +#define PATH_BUF 256 +#define MAX_CA_PATH_LEN 7 + +typedef struct shared_key_t shared_key_t; + +/** + * Private date of a shared_key_t object + */ +struct shared_key_t { + + /** + * shared secret + */ + chunk_t secret; + + /** + * list of peer IDs + */ + linked_list_t *peers; +}; + + +/** + * Implementation of shared_key_t.destroy. + */ +static void shared_key_destroy(shared_key_t *this) +{ + this->peers->destroy_offset(this->peers, offsetof(identification_t, destroy)); + chunk_free(&this->secret); + free(this); +} + +/** + * @brief Creates a shared_key_t object. + * + * @param shared_key shared key value + * @return shared_key_t object + * + * @ingroup config + */ +static shared_key_t *shared_key_create(chunk_t secret) +{ + shared_key_t *this = malloc_thing(shared_key_t); + + /* private data */ + this->secret = chunk_clone(secret); + this->peers = linked_list_create(); + + return (this); +} + +/* ------------------------------------------------------------------------ * + * the ca_info_t object as a central control element + ++--------------------------------------------------------+ +| local_credential_store_t | ++--------------------------------------------------------+ + | | ++---------------------------+ +-------------------------+ +| linked_list_t *auth_certs | | linked_list_t *ca_infos | ++---------------------------+ +-------------------------+ + | | + | +------------------------- + + | | ca_info_t | + | +--------------------------+ ++---------------+ | char *name | +| x509_t |<--| x509_t *cacert | +----------------------+ ++---------------+ | linked_list_t *certinfos |-->| certinfo_t | +| chunk_t keyid | | linked_list_t *ocspuris | +----------------------+ ++---------------+ | crl_t *crl | | chunk_t serialNumber | + | | linked_list_t *crluris | | cert_status_t status | + | | pthread_mutex_t mutex | | time_t thisUpdate | ++---------------+ +--------------------------+ | time_t nextUpdate | +| x509_t | | | bool once | ++---------------+ | +----------------------+ +| chunk_t keyid | | | ++---------------+ +------------------------- + +----------------------+ + | | ca_info_t | | certinfo_t | + | +--------------------------+ +----------------------+ ++---------------+ | char *name | | chunk_t serialNumber | +| x509_t |<--| x509_t *cacert | | cert_status_t status | ++---------------+ | linked_list_t *certinfos | | time_t thisUpdate | +| chunk_t keyid | | linked_list_t *ocspuris | | time_t nextUpdate | ++---------------+ | crl_t *crl | | bool once | + | | linked_list_t *crluris | +----------------------+ + | | pthread_mutex_t mutex; | | + | +--------------------------+ + | | + + * ------------------------------------------------------------------------ */ + +typedef struct private_local_credential_store_t private_local_credential_store_t; + +/** + * Private data of an local_credential_store_t object + */ +struct private_local_credential_store_t { + + /** + * Public part + */ + local_credential_store_t public; + + /** + * list of shared keys + */ + linked_list_t *shared_keys; + + /** + * list of EAP keys + */ + linked_list_t *eap_keys; + + /** + * list of key_entry_t's with private keys + */ + linked_list_t *private_keys; + + /** + * list of X.509 certificates with public keys + */ + linked_list_t *certs; + + /** + * list of X.509 authority certificates with public keys + */ + linked_list_t *auth_certs; + + /** + * list of X.509 CA information records + */ + linked_list_t *ca_infos; + + /** + * enforce strict crl policy + */ + bool strict; +}; + + +/** + * Get a key from a list with shared_key_t's + */ +static status_t get_key(linked_list_t *keys, + identification_t *my_id, + identification_t *other_id, chunk_t *secret) +{ + typedef enum { + PRIO_UNDEFINED= 0x00, + PRIO_ANY_MATCH= 0x01, + PRIO_MY_MATCH= 0x02, + PRIO_OTHER_MATCH= 0x04, + } prio_t; + + prio_t best_prio = PRIO_UNDEFINED; + chunk_t found = chunk_empty; + shared_key_t *shared_key; + + iterator_t *iterator = keys->create_iterator(keys, TRUE); + + while (iterator->iterate(iterator, (void**)&shared_key)) + { + iterator_t *peer_iterator; + identification_t *peer_id; + prio_t prio = PRIO_UNDEFINED; + + peer_iterator = shared_key->peers->create_iterator(shared_key->peers, TRUE); + + if (peer_iterator->get_count(peer_iterator) == 0) + { + /* this is a wildcard shared key */ + prio = PRIO_ANY_MATCH; + } + else + { + while (peer_iterator->iterate(peer_iterator, (void**)&peer_id)) + { + if (my_id->equals(my_id, peer_id)) + { + prio |= PRIO_MY_MATCH; + } + if (other_id->equals(other_id, peer_id)) + { + prio |= PRIO_OTHER_MATCH; + } + } + } + peer_iterator->destroy(peer_iterator); + + if (prio > best_prio) + { + best_prio = prio; + found = shared_key->secret; + } + } + iterator->destroy(iterator); + + if (best_prio == PRIO_UNDEFINED) + { + return NOT_FOUND; + } + else + { + *secret = chunk_clone(found); + return SUCCESS; + } +} + + +/** + * Implementation of local_credential_store_t.get_shared_key. + */ +static status_t get_shared_key(private_local_credential_store_t *this, + identification_t *my_id, + identification_t *other_id, chunk_t *secret) +{ + return get_key(this->shared_keys, my_id, other_id, secret); +} + +/** + * Implementation of local_credential_store_t.get_eap_key. + */ +static status_t get_eap_key(private_local_credential_store_t *this, + identification_t *my_id, + identification_t *other_id, chunk_t *secret) +{ + return get_key(this->eap_keys, my_id, other_id, secret); +} + +/** + * Implementation of credential_store_t.get_certificate. + */ +static x509_t* get_certificate(private_local_credential_store_t *this, + identification_t *id) +{ + x509_t *found = NULL; + x509_t *current_cert; + + iterator_t *iterator = this->certs->create_iterator(this->certs, TRUE); + + while (iterator->iterate(iterator, (void**)¤t_cert)) + { + if (id->equals(id, current_cert->get_subject(current_cert)) || + current_cert->equals_subjectAltName(current_cert, id)) + { + found = current_cert; + break; + } + } + iterator->destroy(iterator); + return found; +} + +/** + * Implementation of local_credential_store_t.get_rsa_public_key. + */ +static rsa_public_key_t *get_rsa_public_key(private_local_credential_store_t *this, + identification_t *id) +{ + x509_t *cert = get_certificate(this, id); + + return (cert == NULL)? NULL:cert->get_public_key(cert); +} + +/** + * Implementation of local_credential_store_t.get_trusted_public_key. + */ +static rsa_public_key_t *get_trusted_public_key(private_local_credential_store_t *this, + identification_t *id) +{ + cert_status_t status; + err_t ugh; + + x509_t *cert = get_certificate(this, id); + + if (cert == NULL) + return NULL; + + ugh = cert->is_valid(cert, NULL); + if (ugh != NULL) + { + DBG1(DBG_CFG, "certificate %s", ugh); + return NULL; + } + + status = cert->get_status(cert); + if (status == CERT_REVOKED || status == CERT_UNTRUSTED || (this->strict && status != CERT_GOOD)) + { + DBG1(DBG_CFG, "certificate status: %N", cert_status_names, status); + return NULL; + } + if (status == CERT_GOOD && cert->get_until(cert) < time(NULL)) + { + DBG1(DBG_CFG, "certificate is good but crl is stale"); + return NULL; + } + + return cert->get_public_key(cert); +} + +/** + * Implementation of local_credential_store_t.get_rsa_private_key. + */ +static rsa_private_key_t *get_rsa_private_key(private_local_credential_store_t *this, + rsa_public_key_t *pubkey) +{ + rsa_private_key_t *found = NULL, *current; + + iterator_t *iterator = this->private_keys->create_iterator(this->private_keys, TRUE); + + while (iterator->iterate(iterator, (void**)¤t)) + { + if (current->belongs_to(current, pubkey)) + { + found = current->clone(current); + break; + } + } + iterator->destroy(iterator); + return found; +} + +/** + * Implementation of local_credential_store_t.has_rsa_private_key. + */ +static bool has_rsa_private_key(private_local_credential_store_t *this, rsa_public_key_t *pubkey) +{ + bool found = FALSE; + rsa_private_key_t *current; + + iterator_t *iterator = this->private_keys->create_iterator(this->private_keys, TRUE); + + while (iterator->iterate(iterator, (void**)¤t)) + { + if (current->belongs_to(current, pubkey)) + { + found = TRUE; + break; + } + } + iterator->destroy(iterator); + return found; +} + +/** + * Implementation of credential_store_t.get_auth_certificate. + */ +static x509_t* get_auth_certificate(private_local_credential_store_t *this, + u_int auth_flags, + identification_t *id) +{ + x509_t *found = NULL; + x509_t *current_cert; + + iterator_t *iterator = this->auth_certs->create_iterator(this->auth_certs, TRUE); + + while (iterator->iterate(iterator, (void**)¤t_cert)) + { + if (current_cert->has_authority_flag(current_cert, auth_flags) + && id->equals(id, current_cert->get_subject(current_cert))) + { + found = current_cert; + break; + } + } + iterator->destroy(iterator); + + return found; +} + +/** + * Implementation of credential_store_t.get_ca_certificate_by_keyid. + */ +static x509_t* get_ca_certificate_by_keyid(private_local_credential_store_t *this, + chunk_t keyid) +{ + x509_t *found = NULL; + x509_t *current_cert; + + iterator_t *iterator = this->auth_certs->create_iterator(this->auth_certs, TRUE); + + while (iterator->iterate(iterator, (void**)¤t_cert)) + { + rsa_public_key_t *pubkey = current_cert->get_public_key(current_cert); + + if (current_cert->has_authority_flag(current_cert, AUTH_CA) + && chunk_equals(keyid, pubkey->get_keyid(pubkey))) + { + found = current_cert; + break; + } + } + iterator->destroy(iterator); + + return found; +} + +/** + * Implementation of credential_store_t.get_issuer. + */ +static ca_info_t* get_issuer(private_local_credential_store_t *this, const x509_t *cert) +{ + ca_info_t *found = NULL; + ca_info_t *ca_info; + + iterator_t *iterator = this->ca_infos->create_iterator(this->ca_infos, TRUE); + + while (iterator->iterate(iterator, (void**)&ca_info)) + { + if (ca_info->is_cert_issuer(ca_info, cert)) + { + found = ca_info; + break; + } + } + iterator->destroy(iterator); + + return found; +} + +/** + * Find an exact copy of a certificate in a linked list + */ +static x509_t* find_certificate(linked_list_t *certs, x509_t *cert) +{ + x509_t *found_cert = NULL, *current_cert; + + iterator_t *iterator = certs->create_iterator(certs, TRUE); + + while (iterator->iterate(iterator, (void**)¤t_cert)) + { + if (cert->equals(cert, current_cert)) + { + found_cert = current_cert; + break; + } + } + iterator->destroy(iterator); + + return found_cert; +} + +/** + * Adds crl and ocsp uris to the corresponding issuer info record + */ +static void add_uris(ca_info_t *issuer, x509_t *cert) +{ + iterator_t *iterator; + identification_t *uri; + + /* add any crl distribution points to the issuer ca info record */ + iterator = cert->create_crluri_iterator(cert); + + while (iterator->iterate(iterator, (void**)&uri)) + { + issuer->add_crluri(issuer, uri->get_encoding(uri)); + } + iterator->destroy(iterator); + + /* add any ocsp access points to the issuer ca info record */ + iterator = cert->create_ocspuri_iterator(cert); + + while (iterator->iterate(iterator, (void**)&uri)) + { + issuer->add_ocspuri(issuer, uri->get_encoding(uri)); + } + iterator->destroy(iterator); +} + +/** + * Implementation of credential_store_t.is_trusted + */ +static bool is_trusted(private_local_credential_store_t *this, x509_t *cert) +{ + int pathlen; + time_t until = UNDEFINED_TIME; + x509_t *cert_to_be_trusted = cert; + + DBG2(DBG_CFG, "establishing trust in certificate:"); + + for (pathlen = 0; pathlen < MAX_CA_PATH_LEN; pathlen++) + { + err_t ugh = NULL; + ca_info_t *issuer; + x509_t *issuer_cert; + rsa_public_key_t *issuer_public_key; + bool valid_signature; + + DBG2(DBG_CFG, "subject: '%D'", cert->get_subject(cert)); + DBG2(DBG_CFG, "issuer: '%D'", cert->get_issuer(cert)); + + ugh = cert->is_valid(cert, &until); + if (ugh != NULL) + { + DBG1(DBG_CFG, "certificate %s", ugh); + return FALSE; + } + DBG2(DBG_CFG, "certificate is valid"); + + issuer = get_issuer(this, cert); + if (issuer == NULL) + { + DBG1(DBG_CFG, "issuer not found"); + return FALSE; + } + DBG2(DBG_CFG, "issuer found"); + + issuer_cert = issuer->get_certificate(issuer); + issuer_public_key = issuer_cert->get_public_key(issuer_cert); + valid_signature = cert->verify(cert, issuer_public_key); + + if (!valid_signature) + { + DBG1(DBG_CFG, "certificate signature is invalid"); + return FALSE; + } + DBG2(DBG_CFG, "certificate signature is valid"); + + /* check if cert is a self-signed root ca */ + if (pathlen > 0 && cert->is_self_signed(cert)) + { + DBG2(DBG_CFG, "reached self-signed root ca"); + cert_to_be_trusted->set_until(cert_to_be_trusted, until); + cert_to_be_trusted->set_status(cert_to_be_trusted, CERT_GOOD); + return TRUE; + } + else + { + /* go up one step in the trust chain */ + cert = issuer_cert; + } + } + DBG1(DBG_CFG, "maximum ca path length of %d levels exceeded", MAX_CA_PATH_LEN); + return FALSE; +} + +/** + * Implementation of credential_store_t.verify. + */ +static bool verify(private_local_credential_store_t *this, x509_t *cert, bool *found) +{ + int pathlen; + time_t until = UNDEFINED_TIME; + + x509_t *end_cert = cert; + x509_t *cert_copy = find_certificate(this->certs, end_cert); + + DBG2(DBG_CFG, "verifying end entity certificate:"); + + *found = (cert_copy != NULL); + if (*found) + { + DBG2(DBG_CFG, + "end entitity certificate is already in credential store"); + } + + for (pathlen = 0; pathlen < MAX_CA_PATH_LEN; pathlen++) + { + err_t ugh = NULL; + ca_info_t *issuer; + x509_t *issuer_cert; + rsa_public_key_t *issuer_public_key; + bool valid_signature; + + DBG1(DBG_CFG, "subject: '%D'", cert->get_subject(cert)); + DBG1(DBG_CFG, "issuer: '%D'", cert->get_issuer(cert)); + + ugh = cert->is_valid(cert, &until); + if (ugh != NULL) + { + DBG1(DBG_CFG, "certificate %s", ugh); + return FALSE; + } + DBG2(DBG_CFG, "certificate is valid"); + + issuer = get_issuer(this, cert); + if (issuer == NULL) + { + DBG1(DBG_CFG, "issuer not found"); + return FALSE; + } + DBG2(DBG_CFG, "issuer found"); + + issuer_cert = issuer->get_certificate(issuer); + issuer_public_key = issuer_cert->get_public_key(issuer_cert); + valid_signature = cert->verify(cert, issuer_public_key); + + if (!valid_signature) + { + DBG1(DBG_CFG, "certificate signature is invalid"); + return FALSE; + } + DBG2(DBG_CFG, "certificate signature is valid"); + + /* check if cert is a self-signed root ca */ + if (pathlen > 0 && cert->is_self_signed(cert)) + { + DBG1(DBG_CFG, "reached self-signed root ca"); + + /* set the definite status and trust interval of the end entity certificate */ + end_cert->set_until(end_cert, until); + if (cert_copy) + { + cert_copy->set_status(cert_copy, end_cert->get_status(end_cert)); + cert_copy->set_until(cert_copy, until); + } + return TRUE; + } + else + { + time_t nextUpdate; + cert_status_t status; + certinfo_t *certinfo = certinfo_create(cert->get_serialNumber(cert)); + + certinfo->set_nextUpdate(certinfo, until); + + if (pathlen == 0) + { + /* add any crl and ocsp uris contained in the certificate under test */ + add_uris(issuer, cert); + } + + /* first check certificate revocation using ocsp */ + status = issuer->verify_by_ocsp(issuer, certinfo, &this->public.credential_store); + + /* if ocsp service is not available then fall back to crl */ + if ((status == CERT_UNDEFINED) || (status == CERT_UNKNOWN && this->strict)) + { + status = issuer->verify_by_crl(issuer, certinfo, CRL_DIR); + } + + nextUpdate = certinfo->get_nextUpdate(certinfo); + cert->set_status(cert, status); + + switch (status) + { + case CERT_GOOD: + /* set nextUpdate */ + cert->set_until(cert, nextUpdate); + + /* if status information is stale */ + if (this->strict && nextUpdate < time(NULL)) + { + DBG2(DBG_CFG, "certificate is good but status is stale"); + certinfo->destroy(certinfo); + return FALSE; + } + DBG1(DBG_CFG, "certificate is good"); + + /* with strict crl policy the public key must have the same + * lifetime as the validity of the ocsp status or crl lifetime + */ + if (this->strict && nextUpdate < until) + until = nextUpdate; + break; + case CERT_REVOKED: + { + time_t revocationTime = certinfo->get_revocationTime(certinfo); + DBG1(DBG_CFG, + "certificate was revoked on %T, reason: %N", + &revocationTime, crl_reason_names, + certinfo->get_revocationReason(certinfo)); + + /* set revocationTime */ + cert->set_until(cert, revocationTime); + + /* update status of end certificate in the credential store */ + if (cert_copy) + { + if (pathlen > 0) + { + cert_copy->set_status(cert_copy, CERT_UNTRUSTED); + } + else + { + cert_copy->set_status(cert_copy, CERT_REVOKED); + cert_copy->set_until(cert_copy, + certinfo->get_revocationTime(certinfo)); + } + } + certinfo->destroy(certinfo); + return FALSE; + } + case CERT_UNKNOWN: + case CERT_UNDEFINED: + default: + DBG1(DBG_CFG, "certificate status unknown"); + if (this->strict) + { + /* update status of end certificate in the credential store */ + if (cert_copy) + { + cert_copy->set_status(cert_copy, CERT_UNTRUSTED); + } + certinfo->destroy(certinfo); + return FALSE; + } + break; + } + certinfo->destroy(certinfo); + } + /* go up one step in the trust chain */ + cert = issuer_cert; + } + DBG1(DBG_CFG, "maximum ca path length of %d levels exceeded", MAX_CA_PATH_LEN); + return FALSE; +} + +/** + * Add a unique certificate to a linked list + */ +static x509_t* add_certificate(linked_list_t *certs, x509_t *cert) +{ + x509_t *found_cert = find_certificate(certs, cert); + + if (found_cert) + { + /* add the authority flags */ + found_cert->add_authority_flags(found_cert, cert->get_authority_flags(cert)); + + cert->destroy(cert); + return found_cert; + } + else + { + certs->insert_last(certs, (void*)cert); + return cert; + } +} + +/** + * Add a unique ca info record to a linked list + */ +static void add_ca_info(private_local_credential_store_t *this, ca_info_t *ca_info) +{ + ca_info_t *current_ca_info; + ca_info_t *found_ca_info = NULL; + + iterator_t *iterator = this->ca_infos->create_iterator(this->ca_infos, TRUE); + + while (iterator->iterate(iterator, (void**)¤t_ca_info)) + { + if (current_ca_info->equals(current_ca_info, ca_info)) + { + found_ca_info = current_ca_info; + break; + } + } + iterator->destroy(iterator); + + if (found_ca_info) + { + current_ca_info->add_info(current_ca_info, ca_info); + ca_info->destroy(ca_info); + } + else + { + this->ca_infos->insert_last(this->ca_infos, (void*)ca_info); + } +} + +/** + * Release ca info record of a given name + */ +static status_t release_ca_info(private_local_credential_store_t *this, const char *name) +{ + status_t status = NOT_FOUND; + ca_info_t *ca_info; + + iterator_t *iterator = this->ca_infos->create_iterator(this->ca_infos, TRUE); + + while (iterator->iterate(iterator, (void**)&ca_info)) + { + if (ca_info->equals_name_release_info(ca_info, name)) + { + status = SUCCESS; + break; + } + } + iterator->destroy(iterator); + + return status; +} + +/** + * Implements local_credential_store_t.add_end_certificate + */ +static x509_t* add_end_certificate(private_local_credential_store_t *this, x509_t *cert) +{ + x509_t *ret_cert = add_certificate(this->certs, cert); + + /* add crl and ocsp uris the first time the certificate is added */ + if (ret_cert == cert) + { + ca_info_t *issuer = get_issuer(this, cert); + + if (issuer) + { + add_uris(issuer, cert); + } + } + return ret_cert; +} + +/** + * Implements local_credential_store_t.add_auth_certificate + */ +static x509_t* add_auth_certificate(private_local_credential_store_t *this, x509_t *cert, u_int auth_flags) +{ + cert->add_authority_flags(cert, auth_flags); + return add_certificate(this->auth_certs, cert); +} + +/** + * Implements local_credential_store_t.create_cert_iterator + */ +static iterator_t* create_cert_iterator(private_local_credential_store_t *this) +{ + return this->certs->create_iterator(this->certs, TRUE); +} + +/** + * Implements local_credential_store_t.create_cacert_iterator + */ +static iterator_t* create_auth_cert_iterator(private_local_credential_store_t *this) +{ + return this->auth_certs->create_iterator(this->auth_certs, TRUE); +} + +/** + * Implements local_credential_store_t.create_cainfo_iterator + */ +static iterator_t* create_cainfo_iterator(private_local_credential_store_t *this) +{ + return this->ca_infos->create_iterator(this->ca_infos, TRUE); +} + +/** + * Implements local_credential_store_t.load_auth_certificates + */ +static void load_auth_certificates(private_local_credential_store_t *this, + u_int auth_flag, + const char* label, + const char* path) +{ + struct dirent* entry; + struct stat stb; + DIR* dir; + + DBG1(DBG_CFG, "loading %s certificates from '%s/'", label, path); + + dir = opendir(path); + if (dir == NULL) + { + DBG1(DBG_CFG, "error opening %s certs directory %s'", label, path); + return; + } + + while ((entry = readdir(dir)) != NULL) + { + char file[PATH_BUF]; + + snprintf(file, sizeof(file), "%s/%s", path, entry->d_name); + + if (stat(file, &stb) == -1) + { + continue; + } + /* try to parse all regular files */ + if (stb.st_mode & S_IFREG) + { + x509_t *cert = x509_create_from_file(file, label); + + if (cert) + { + err_t ugh = cert->is_valid(cert, NULL); + + if (ugh != NULL) + { + DBG1(DBG_CFG, "warning: %s certificate %s", label, ugh); + } + + if (auth_flag == AUTH_CA && !cert->is_ca(cert)) + { + DBG1(DBG_CFG, " CA basic constraints flag not set, cert discarded"); + cert->destroy(cert); + } + else + { + x509_t *ret_cert; + + cert->add_authority_flags(cert, auth_flag); + + ret_cert = add_certificate(this->auth_certs, cert); + + if (auth_flag == AUTH_CA && ret_cert == cert) + { + ca_info_t *ca_info = ca_info_create(NULL, cert); + + add_ca_info(this, ca_info); + } + } + } + } + } + closedir(dir); +} + +/** + * Implements local_credential_store_t.load_ca_certificates + */ +static void load_ca_certificates(private_local_credential_store_t *this) +{ + load_auth_certificates(this, AUTH_CA, "ca", CA_CERTIFICATE_DIR); + + /* add any crl and ocsp uris found in the ca certificates to the + * corresponding issuer info record. We can do this only after all + * ca certificates have been loaded and the ca hierarchy is known. + */ + { + iterator_t *iterator = this->ca_infos->create_iterator(this->ca_infos, TRUE); + ca_info_t *ca_info; + + while (iterator->iterate(iterator, (void **)&ca_info)) + { + x509_t *cacert = ca_info->get_certificate(ca_info); + ca_info_t *issuer = get_issuer(this, cacert); + + if (issuer) + { + add_uris(issuer, cacert); + } + } + iterator->destroy(iterator); + } +} + +/** + * Implements local_credential_store_t.load_ocsp_certificates + */ +static void load_ocsp_certificates(private_local_credential_store_t *this) +{ + load_auth_certificates(this, AUTH_OCSP, "ocsp", OCSP_CERTIFICATE_DIR); +} + +/** + * Add the latest crl to the issuing ca + */ +static void add_crl(private_local_credential_store_t *this, crl_t *crl, const char *path) +{ + iterator_t *iterator = this->ca_infos->create_iterator(this->ca_infos, TRUE); + ca_info_t *ca_info; + bool found = FALSE; + + while (iterator->iterate(iterator, (void**)&ca_info)) + { + if (ca_info->is_crl_issuer(ca_info, crl)) + { + char buffer[BUF_LEN]; + chunk_t uri = { buffer, 7 + strlen(path) }; + + ca_info->add_crl(ca_info, crl); + if (uri.len < BUF_LEN) + { + snprintf(buffer, BUF_LEN, "file://%s", path); + ca_info->add_crluri(ca_info, uri); + } + found = TRUE; + break; + } + } + iterator->destroy(iterator); + + if (!found) + { + crl->destroy(crl); + DBG2(DBG_CFG, " no issuing ca found for this crl - discarded"); + } +} + +/** + * Implements local_credential_store_t.load_crls + */ +static void load_crls(private_local_credential_store_t *this) +{ + struct dirent* entry; + struct stat stb; + DIR* dir; + crl_t *crl; + + DBG1(DBG_CFG, "loading crls from '%s/'", CRL_DIR); + + dir = opendir(CRL_DIR); + if (dir == NULL) + { + DBG1(DBG_CFG, "error opening crl directory %s'", CRL_DIR); + return; + } + + while ((entry = readdir(dir)) != NULL) + { + char file[PATH_BUF]; + + snprintf(file, sizeof(file), "%s/%s", CRL_DIR, entry->d_name); + + if (stat(file, &stb) == -1) + { + continue; + } + /* try to parse all regular files */ + if (stb.st_mode & S_IFREG) + { + crl = crl_create_from_file(file); + if (crl) + { + DBG1(DBG_CFG, " crl is %s", crl->is_valid(crl)? "valid":"stale"); + add_crl(this, crl, file); + } + } + } + closedir(dir); +} + +/** + * Convert a string of characters into a binary secret + * A string between single or double quotes is treated as ASCII characters + * A string prepended by 0x is treated as HEX and prepended by 0s as Base64 + */ +static err_t extract_secret(chunk_t *secret, chunk_t *line) +{ + chunk_t raw_secret; + char delimiter = ' '; + bool quotes = FALSE; + + if (!eat_whitespace(line)) + { + return "missing secret"; + } + + if (*line->ptr == '\'' || *line->ptr == '"') + { + quotes = TRUE; + delimiter = *line->ptr; + line->ptr++; line->len--; + } + + if (!extract_token(&raw_secret, delimiter, line)) + { + if (delimiter == ' ') + { + raw_secret = *line; + } + else + { + return "missing second delimiter"; + } + } + + if (quotes) + { /* treat as an ASCII string */ + if (raw_secret.len > secret->len) + return "secret larger than buffer"; + memcpy(secret->ptr, raw_secret.ptr, raw_secret.len); + secret->len = raw_secret.len; + } + else + { /* convert from HEX or Base64 to binary */ + size_t len; + err_t ugh = ttodata(raw_secret.ptr, raw_secret.len, 0, secret->ptr, secret->len, &len); + + if (ugh != NULL) + return ugh; + if (len > secret->len) + return "secret larger than buffer"; + secret->len = len; + } + return NULL; +} + +/** + * Implements local_credential_store_t.load_secrets + */ +static void load_secrets(private_local_credential_store_t *this) +{ + FILE *fd = fopen(SECRETS_FILE, "r"); + + if (fd) + { + int bytes; + int line_nr = 0; + chunk_t chunk, src, line; + + DBG1(DBG_CFG, "loading secrets from \"%s\"", SECRETS_FILE); + + fseek(fd, 0, SEEK_END); + chunk.len = ftell(fd); + rewind(fd); + chunk.ptr = malloc(chunk.len); + bytes = fread(chunk.ptr, 1, chunk.len, fd); + fclose(fd); + + src = chunk; + + while (fetchline(&src, &line)) + { + chunk_t ids, token; + bool is_eap = FALSE; + + line_nr++; + + if (!eat_whitespace(&line)) + { + continue; + } + if (!extract_token(&ids, ':', &line)) + { + DBG1(DBG_CFG, "line %d: missing ':' separator", line_nr); + goto error; + } + /* NULL terminate the ids string by replacing the : separator */ + *(ids.ptr + ids.len) = '\0'; + + if (!eat_whitespace(&line) || !extract_token(&token, ' ', &line)) + { + DBG1(DBG_CFG, "line %d: missing token", line_nr); + goto error; + } + if (match("RSA", &token)) + { + char path[PATH_BUF]; + chunk_t filename; + + char buf[BUF_LEN]; + chunk_t secret = { buf, BUF_LEN }; + chunk_t *passphrase = NULL; + + rsa_private_key_t *key; + + err_t ugh = extract_value(&filename, &line); + + if (ugh != NULL) + { + DBG1(DBG_CFG, "line %d: %s", line_nr, ugh); + goto error; + } + if (filename.len == 0) + { + DBG1(DBG_CFG, "line %d: empty filename", line_nr); + goto error; + } + if (*filename.ptr == '/') + { + /* absolute path name */ + snprintf(path, sizeof(path), "%.*s", filename.len, filename.ptr); + } + else + { + /* relative path name */ + snprintf(path, sizeof(path), "%s/%.*s", PRIVATE_KEY_DIR, + filename.len, filename.ptr); + } + + /* check for optional passphrase */ + if (eat_whitespace(&line)) + { + ugh = extract_secret(&secret, &line); + if (ugh != NULL) + { + DBG1(DBG_CFG, "line %d: malformed passphrase: %s", line_nr, ugh); + goto error; + } + if (secret.len > 0) + passphrase = &secret; + } + key = rsa_private_key_create_from_file(path, passphrase); + if (key) + { + this->private_keys->insert_last(this->private_keys, (void*)key); + } + } + else if ( match("PSK", &token) || + ((match("EAP", &token) || match("XAUTH", &token)) && (is_eap = TRUE))) + { + shared_key_t *shared_key; + + char buf[BUF_LEN]; + chunk_t secret = { buf, BUF_LEN }; + + err_t ugh = extract_secret(&secret, &line); + if (ugh != NULL) + { + DBG1(DBG_CFG, "line %d: malformed secret: %s", line_nr, ugh); + goto error; + } + + DBG1(DBG_CFG, " loading %s key for %s", + is_eap ? "EAP" : "shared", + ids.len > 0 ? (char*)ids.ptr : "%any"); + + DBG4(DBG_CFG, " secret:", secret); + + shared_key = shared_key_create(secret); + if (shared_key) + { + if (is_eap) + { + this->eap_keys->insert_last(this->eap_keys, (void*)shared_key); + } + else + { + this->shared_keys->insert_last(this->shared_keys, (void*)shared_key); + } + } + while (ids.len > 0) + { + chunk_t id; + identification_t *peer_id; + + ugh = extract_value(&id, &ids); + if (ugh != NULL) + { + DBG1(DBG_CFG, "line %d: %s", line_nr, ugh); + goto error; + } + if (id.len == 0) + { + continue; + } + + /* NULL terminate the ID string */ + *(id.ptr + id.len) = '\0'; + + peer_id = identification_create_from_string(id.ptr); + if (peer_id == NULL) + { + DBG1(DBG_CFG, "line %d: malformed ID: %s", line_nr, id.ptr); + goto error; + } + + if (peer_id->get_type(peer_id) == ID_ANY) + { + peer_id->destroy(peer_id); + continue; + } + shared_key->peers->insert_last(shared_key->peers, (void*)peer_id); + } + } + else if (match("PIN", &token)) + { + + } + else + { + DBG1(DBG_CFG, "line %d: token must be either " + "RSA, PSK, EAP, or PIN", line_nr, token.len); + goto error; + } + } +error: + free(chunk.ptr); + } + else + { + DBG1(DBG_CFG, "could not open file '%s'", SECRETS_FILE); + } +} + +/** + * Implementation of local_credential_store_t.destroy. + */ +static void destroy(private_local_credential_store_t *this) +{ + this->certs->destroy_offset(this->certs, offsetof(x509_t, destroy)); + this->auth_certs->destroy_offset(this->auth_certs, offsetof(x509_t, destroy)); + this->ca_infos->destroy_offset(this->ca_infos, offsetof(ca_info_t, destroy)); + this->private_keys->destroy_offset(this->private_keys, offsetof(rsa_private_key_t, destroy)); + this->shared_keys->destroy_function(this->shared_keys, (void*)shared_key_destroy); + this->eap_keys->destroy_function(this->eap_keys, (void*)shared_key_destroy); + free(this); +} + +/** + * Described in header. + */ +local_credential_store_t * local_credential_store_create(bool strict) +{ + private_local_credential_store_t *this = malloc_thing(private_local_credential_store_t); + + this->public.credential_store.get_shared_key = (status_t (*) (credential_store_t*,identification_t*,identification_t*,chunk_t*))get_shared_key; + this->public.credential_store.get_eap_key = (status_t (*) (credential_store_t*,identification_t*,identification_t*,chunk_t*))get_eap_key; + this->public.credential_store.get_rsa_public_key = (rsa_public_key_t*(*)(credential_store_t*,identification_t*))get_rsa_public_key; + this->public.credential_store.get_rsa_private_key = (rsa_private_key_t* (*) (credential_store_t*,rsa_public_key_t*))get_rsa_private_key; + this->public.credential_store.has_rsa_private_key = (bool (*) (credential_store_t*,rsa_public_key_t*))has_rsa_private_key; + this->public.credential_store.get_trusted_public_key = (rsa_public_key_t*(*)(credential_store_t*,identification_t*))get_trusted_public_key; + this->public.credential_store.get_certificate = (x509_t* (*) (credential_store_t*,identification_t*))get_certificate; + this->public.credential_store.get_auth_certificate = (x509_t* (*) (credential_store_t*,u_int,identification_t*))get_auth_certificate; + this->public.credential_store.get_ca_certificate_by_keyid = (x509_t* (*) (credential_store_t*,chunk_t))get_ca_certificate_by_keyid; + this->public.credential_store.get_issuer = (ca_info_t* (*) (credential_store_t*,const x509_t*))get_issuer; + this->public.credential_store.is_trusted = (bool (*) (credential_store_t*,x509_t*))is_trusted; + this->public.credential_store.verify = (bool (*) (credential_store_t*,x509_t*,bool*))verify; + this->public.credential_store.add_end_certificate = (x509_t* (*) (credential_store_t*,x509_t*))add_end_certificate; + this->public.credential_store.add_auth_certificate = (x509_t* (*) (credential_store_t*,x509_t*,u_int))add_auth_certificate; + this->public.credential_store.add_ca_info = (void (*) (credential_store_t*,ca_info_t*))add_ca_info; + this->public.credential_store.release_ca_info = (status_t (*) (credential_store_t*,const char*))release_ca_info; + this->public.credential_store.create_cert_iterator = (iterator_t* (*) (credential_store_t*))create_cert_iterator; + this->public.credential_store.create_auth_cert_iterator = (iterator_t* (*) (credential_store_t*))create_auth_cert_iterator; + this->public.credential_store.create_cainfo_iterator = (iterator_t* (*) (credential_store_t*))create_cainfo_iterator; + this->public.credential_store.load_ca_certificates = (void (*) (credential_store_t*))load_ca_certificates; + this->public.credential_store.load_ocsp_certificates = (void (*) (credential_store_t*))load_ocsp_certificates; + this->public.credential_store.load_crls = (void (*) (credential_store_t*))load_crls; + this->public.credential_store.load_secrets = (void (*) (credential_store_t*))load_secrets; + this->public.credential_store.destroy = (void (*) (credential_store_t*))destroy; + + /* private variables */ + this->shared_keys = linked_list_create(); + this->eap_keys = linked_list_create(); + this->private_keys = linked_list_create(); + this->certs = linked_list_create(); + this->auth_certs = linked_list_create(); + this->ca_infos = linked_list_create(); + this->strict = strict; + + return (&this->public); +} diff --git a/src/charon/config/credentials/local_credential_store.h b/src/charon/config/credentials/local_credential_store.h new file mode 100644 index 000000000..88a94d6f9 --- /dev/null +++ b/src/charon/config/credentials/local_credential_store.h @@ -0,0 +1,64 @@ +/** + * @file local_credential_store.h + * + * @brief Interface of local_credential_store_t. + * + */ + +/* + * Copyright (C) 2006 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. + */ + +#ifndef LOCAL_CREDENTIAL_H_ +#define LOCAL_CREDENTIAL_H_ + +typedef struct local_credential_store_t local_credential_store_t; + +#include <library.h> +#include <credential_store.h> +#include <daemon.h> + + +/** + * @brief A credential_store_t implementation using simple credentail lists. + * + * The local_credential_store_t class implements the credential_store_t interface + * as simple as possible. The credentials are stored in lists, and are loaded from + * files on the disk. + * Shared secret are not handled yet, so get_shared_secret always returns NOT_FOUND. + * + * @b Constructors: + * - local_credential_store_create(bool strict) + * + * @ingroup config + */ +struct local_credential_store_t { + + /** + * Implements credential_store_t interface + */ + credential_store_t credential_store; +}; + +/** + * @brief Creates a local_credential_store_t instance. + * + * @param strict enforce a strict crl policy + * @return credential store instance. + * + * @ingroup config + */ +local_credential_store_t *local_credential_store_create(bool strict); + +#endif /* LOCAL_CREDENTIAL_H_ */ diff --git a/src/charon/config/policies/local_policy_store.c b/src/charon/config/policies/local_policy_store.c new file mode 100644 index 000000000..dd22b43a0 --- /dev/null +++ b/src/charon/config/policies/local_policy_store.c @@ -0,0 +1,282 @@ +/** + * @file local_policy_store.c + * + * @brief Implementation of local_policy_store_t. + * + */ + +/* + * Copyright (C) 2006 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 <string.h> + +#include "local_policy_store.h" + +#include <daemon.h> +#include <utils/linked_list.h> + + +typedef struct private_local_policy_store_t private_local_policy_store_t; + +/** + * Private data of an local_policy_store_t object + */ +struct private_local_policy_store_t { + + /** + * Public part + */ + local_policy_store_t public; + + /** + * list of policy_t's + */ + linked_list_t *policies; + + /** + * Mutex to exclusivly access list + */ + pthread_mutex_t mutex; +}; + +/** + * Implementation of policy_store_t.add_policy. + */ +static void add_policy(private_local_policy_store_t *this, policy_t *policy) +{ + pthread_mutex_lock(&(this->mutex)); + this->policies->insert_last(this->policies, (void*)policy); + pthread_mutex_unlock(&(this->mutex)); +} + +/** + * Check if a policy contains traffic selectors + */ +static bool contains_traffic_selectors(policy_t *policy, bool mine, + linked_list_t *ts, host_t *host) +{ + linked_list_t *selected; + bool contains = FALSE; + + if (mine) + { + selected = policy->select_my_traffic_selectors(policy, ts, host); + } + else + { + selected = policy->select_other_traffic_selectors(policy, ts, host); + } + if (selected->get_count(selected)) + { + contains = TRUE; + } + selected->destroy_offset(selected, offsetof(traffic_selector_t, destroy)); + return contains; +} + +/** + * Implementation of policy_store_t.get_policy. + */ +static policy_t *get_policy(private_local_policy_store_t *this, + identification_t *my_id, identification_t *other_id, + linked_list_t *my_ts, linked_list_t *other_ts, + host_t *my_host, host_t *other_host) +{ + typedef enum { + PRIO_UNDEFINED = 0x00, + PRIO_TS_MISMATCH = 0x01, + PRIO_ID_ANY = 0x02, + PRIO_ID_MATCH = PRIO_ID_ANY + MAX_WILDCARDS, + } prio_t; + + prio_t best_prio = PRIO_UNDEFINED; + + iterator_t *iterator; + policy_t *candidate; + policy_t *found = NULL; + traffic_selector_t *ts; + + DBG1(DBG_CFG, "searching policy for '%D'...'%D'", my_id, other_id); + iterator = my_ts->create_iterator(my_ts, TRUE); + while (iterator->iterate(iterator, (void**)&ts)) + { + DBG1(DBG_CFG, " local TS: %R", ts); + } + iterator->destroy(iterator); + iterator = other_ts->create_iterator(other_ts, TRUE); + while (iterator->iterate(iterator, (void**)&ts)) + { + DBG1(DBG_CFG, " remote TS: %R", ts); + } + iterator->destroy(iterator); + + pthread_mutex_lock(&(this->mutex)); + iterator = this->policies->create_iterator(this->policies, TRUE); + + /* determine closest matching policy */ + while (iterator->iterate(iterator, (void**)&candidate)) + { + identification_t *candidate_my_id; + identification_t *candidate_other_id; + int wildcards; + + candidate_my_id = candidate->get_my_id(candidate); + candidate_other_id = candidate->get_other_id(candidate); + + /* my_id is either %any or if set must match exactly */ + if (candidate_my_id->matches(candidate_my_id, my_id, &wildcards)) + { + prio_t prio = PRIO_UNDEFINED; + + /* wildcard match for other_id */ + if (!other_id->matches(other_id, candidate_other_id, &wildcards)) + { + continue; + } + prio = PRIO_ID_MATCH - wildcards; + + /* only accept if traffic selectors match */ + if (!contains_traffic_selectors(candidate, TRUE, my_ts, my_host) || + !contains_traffic_selectors(candidate, FALSE, other_ts, other_host)) + { + DBG2(DBG_CFG, "candidate '%s' inacceptable due traffic " + "selector mismatch", candidate->get_name(candidate)); + prio = PRIO_TS_MISMATCH; + } + + DBG2(DBG_CFG, "candidate policy '%s': '%D'...'%D' (prio=%d)", + candidate->get_name(candidate), + candidate_my_id, candidate_other_id, prio); + + if (prio > best_prio) + { + found = candidate; + best_prio = prio; + } + } + } + iterator->destroy(iterator); + + if (found) + { + DBG1(DBG_CFG, "found matching policy '%s': '%D'...'%D' (prio=%d)", + found->get_name(found), found->get_my_id(found), + found->get_other_id(found), best_prio); + /* give out a new reference to it */ + found->get_ref(found); + } + pthread_mutex_unlock(&(this->mutex)); + return found; +} + +/** + * Implementation of policy_store_t.get_policy_by_name. + */ +static policy_t *get_policy_by_name(private_local_policy_store_t *this, char *name) +{ + iterator_t *iterator; + policy_t *current, *found = NULL; + + DBG2(DBG_CFG, "looking for policy '%s'", name); + + pthread_mutex_lock(&(this->mutex)); + iterator = this->policies->create_iterator(this->policies, TRUE); + while (iterator->iterate(iterator, (void **)¤t)) + { + if (strcmp(current->get_name(current), name) == 0) + { + found = current; + } + } + iterator->destroy(iterator); + pthread_mutex_unlock(&(this->mutex)); + + /* give out a new reference */ + found->get_ref(found); + return found; +} + +/** + * Implementation of policy_store_t.delete_policy. + */ +static status_t delete_policy(private_local_policy_store_t *this, char *name) +{ + iterator_t *iterator; + policy_t *current; + bool found = FALSE; + + pthread_mutex_lock(&(this->mutex)); + iterator = this->policies->create_iterator(this->policies, TRUE); + while (iterator->iterate(iterator, (void **)¤t)) + { + if (strcmp(current->get_name(current), name) == 0) + { + /* remove policy from list, and destroy it */ + iterator->remove(iterator); + current->destroy(current); + found = TRUE; + /* we do not break here, as there may be multipe policies */ + } + } + iterator->destroy(iterator); + pthread_mutex_unlock(&(this->mutex)); + if (found) + { + return SUCCESS; + } + return NOT_FOUND; +} + +/** + * Implementation of policy_store_t.create_iterator. + */ +static iterator_t* create_iterator(private_local_policy_store_t *this) +{ + return this->policies->create_iterator_locked(this->policies, + &this->mutex); +} + +/** + * Implementation of policy_store_t.destroy. + */ +static void destroy(private_local_policy_store_t *this) +{ + pthread_mutex_lock(&(this->mutex)); + this->policies->destroy_offset(this->policies, offsetof(policy_t, destroy)); + pthread_mutex_unlock(&(this->mutex)); + free(this); +} + +/** + * Described in header. + */ +local_policy_store_t *local_policy_store_create(void) +{ + private_local_policy_store_t *this = malloc_thing(private_local_policy_store_t); + + this->public.policy_store.add_policy = (void (*) (policy_store_t*,policy_t*))add_policy; + this->public.policy_store.get_policy = (policy_t* (*) (policy_store_t*,identification_t*,identification_t*, + linked_list_t*,linked_list_t*,host_t*,host_t*))get_policy; + this->public.policy_store.get_policy_by_name = (policy_t* (*) (policy_store_t*,char*))get_policy_by_name; + this->public.policy_store.delete_policy = (status_t (*) (policy_store_t*,char*))delete_policy; + this->public.policy_store.create_iterator = (iterator_t* (*) (policy_store_t*))create_iterator; + this->public.policy_store.destroy = (void (*) (policy_store_t*))destroy; + + /* private variables */ + this->policies = linked_list_create(); + pthread_mutex_init(&(this->mutex), NULL); + + return (&this->public); +} diff --git a/src/charon/config/policies/local_policy_store.h b/src/charon/config/policies/local_policy_store.h new file mode 100644 index 000000000..01d5d2d60 --- /dev/null +++ b/src/charon/config/policies/local_policy_store.h @@ -0,0 +1,60 @@ +/** + * @file local_policy_store.h + * + * @brief Interface of local_policy_store_t. + * + */ + +/* + * Copyright (C) 2006 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. + */ + +#ifndef LOCAL_POLICY_STORE_H_ +#define LOCAL_POLICY_STORE_H_ + +typedef struct local_policy_store_t local_policy_store_t; + +#include <library.h> +#include <config/policies/policy_store.h> + + +/** + * @brief A policy_store_t implementation using a simple policy lists. + * + * The local_policy_store_t class implements the policy_store_t interface + * as simple as possible. The policies are stored in a in-memory list. + * + * @b Constructors: + * - local_policy_store_create() + * + * @ingroup config + */ +struct local_policy_store_t { + + /** + * Implements policy_store_t interface + */ + policy_store_t policy_store; +}; + +/** + * @brief Creates a local_policy_store_t instance. + * + * @return policy store instance. + * + * @ingroup config + */ +local_policy_store_t *local_policy_store_create(void); + +#endif /* LOCAL_POLICY_STORE_H_ */ diff --git a/src/charon/config/policies/policy.c b/src/charon/config/policies/policy.c new file mode 100644 index 000000000..363d1609f --- /dev/null +++ b/src/charon/config/policies/policy.c @@ -0,0 +1,635 @@ +/** + * @file policy.c + * + * @brief Implementation of policy_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * 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 <time.h> +#include <string.h> +#include <unistd.h> + +#include "policy.h" + +#include <daemon.h> +#include <utils/linked_list.h> +#include <utils/identification.h> + +ENUM(dpd_action_names, DPD_NONE, DPD_RESTART, + "DPD_NONE", + "DPD_CLEAR", + "DPD_ROUTE", + "DPD_RESTART" +); + +ENUM(mode_names, MODE_TRANSPORT, MODE_BEET, + "TRANSPORT", + "TUNNEL", + "2", + "3", + "BEET" +); + +typedef struct private_policy_t private_policy_t; + +/** + * Private data of an policy_t object + */ +struct private_policy_t { + + /** + * Public part + */ + policy_t public; + + /** + * Number of references hold by others to this policy + */ + refcount_t refcount; + + /** + * Name of the policy, used to query it + */ + char *name; + + /** + * id to use to identify us + */ + identification_t *my_id; + + /** + * allowed id for other + */ + identification_t *other_id; + + /** + * virtual IP to use locally + */ + host_t *my_virtual_ip; + + /** + * virtual IP to use remotly + */ + host_t *other_virtual_ip; + + /** + * Method to use for own authentication data + */ + auth_method_t auth_method; + + /** + * EAP type to use for peer authentication + */ + eap_type_t eap_type; + + /** + * we have a cert issued by this CA + */ + identification_t *my_ca; + + /** + * we require the other end to have a cert issued by this CA + */ + identification_t *other_ca; + + /** + * updown script + */ + char *updown; + + /** + * allow host access + */ + bool hostaccess; + + /** + * list for all proposals + */ + linked_list_t *proposals; + + /** + * list for traffic selectors for my site + */ + linked_list_t *my_ts; + + /** + * list for traffic selectors for others site + */ + linked_list_t *other_ts; + + /** + * Time before an SA gets invalid + */ + u_int32_t soft_lifetime; + + /** + * Time before an SA gets rekeyed + */ + u_int32_t hard_lifetime; + + /** + * Time, which specifies the range of a random value + * substracted from soft_lifetime. + */ + u_int32_t jitter; + + /** + * What to do with an SA when other peer seams to be dead? + */ + bool dpd_action; + + /** + * Mode to propose for a initiated CHILD: tunnel/transport + */ + mode_t mode; +}; + +/** + * Implementation of policy_t.get_name + */ +static char *get_name(private_policy_t *this) +{ + return this->name; +} + +/** + * Implementation of policy_t.get_my_id + */ +static identification_t *get_my_id(private_policy_t *this) +{ + return this->my_id; +} + +/** + * Implementation of policy_t.get_other_id + */ +static identification_t *get_other_id(private_policy_t *this) +{ + return this->other_id; +} + +/** + * Implementation of policy_t.get_my_ca + */ +static identification_t *get_my_ca(private_policy_t *this) +{ + return this->my_ca; +} + +/** + * Implementation of policy_t.get_other_ca + */ +static identification_t *get_other_ca(private_policy_t *this) +{ + return this->other_ca; +} + +/** + * Implementation of connection_t.auth_method_t. + */ +static auth_method_t get_auth_method(private_policy_t *this) +{ + return this->auth_method; +} + +/** + * Implementation of connection_t.get_eap_type. + */ +static eap_type_t get_eap_type(private_policy_t *this) +{ + return this->eap_type; +} + +/** + * Get traffic selectors, with wildcard-address update + */ +static linked_list_t *get_traffic_selectors(private_policy_t *this, + linked_list_t *list, host_t *host) +{ + iterator_t *iterator; + traffic_selector_t *current; + linked_list_t *result = linked_list_create(); + + iterator = list->create_iterator(list, TRUE); + + while (iterator->iterate(iterator, (void**)¤t)) + { + /* we make a copy of the TS, this allows us to update wildcard + * addresses in it. We won't pollute the shared policy. */ + current = current->clone(current); + if (host) + { + current->set_address(current, host); + } + + result->insert_last(result, (void*)current); + } + iterator->destroy(iterator); + return result; +} + +/** + * Implementation of policy_t.get_my_traffic_selectors + */ +static linked_list_t *get_my_traffic_selectors(private_policy_t *this, host_t *me) +{ + return get_traffic_selectors(this, this->my_ts, me); +} + +/** + * Implementation of policy_t.get_other_traffic_selectors + */ +static linked_list_t *get_other_traffic_selectors(private_policy_t *this, host_t *other) +{ + return get_traffic_selectors(this, this->other_ts, other); +} + +/** + * Narrow traffic selectors, with wildcard-address update in "stored". + */ +static linked_list_t *select_traffic_selectors(private_policy_t *this, + linked_list_t *stored, + linked_list_t *supplied, + host_t *host) +{ + iterator_t *supplied_iter, *stored_iter, *i1, *i2; + traffic_selector_t *supplied_ts, *stored_ts, *selected_ts, *ts1, *ts2; + linked_list_t *selected = linked_list_create(); + + DBG2(DBG_CFG, "selecting traffic selectors"); + + stored_iter = stored->create_iterator(stored, TRUE); + supplied_iter = supplied->create_iterator(supplied, TRUE); + + /* iterate over all stored selectors */ + while (stored_iter->iterate(stored_iter, (void**)&stored_ts)) + { + /* we make a copy of the TS, this allows us to update wildcard + * addresses in it. We won't pollute the shared policy. */ + stored_ts = stored_ts->clone(stored_ts); + if (host) + { + stored_ts->set_address(stored_ts, host); + } + + supplied_iter->reset(supplied_iter); + /* iterate over all supplied traffic selectors */ + while (supplied_iter->iterate(supplied_iter, (void**)&supplied_ts)) + { + DBG2(DBG_CFG, "stored %R <=> %R received", + stored_ts, supplied_ts); + + selected_ts = stored_ts->get_subset(stored_ts, supplied_ts); + if (selected_ts) + { + /* got a match, add to list */ + selected->insert_last(selected, (void*)selected_ts); + + DBG2(DBG_CFG, "found traffic selector for %s: %R", + stored == this->my_ts ? "us" : "other", selected_ts); + } + } + stored_ts->destroy(stored_ts); + } + stored_iter->destroy(stored_iter); + supplied_iter->destroy(supplied_iter); + + /* remove any redundant traffic selectors in the list */ + i1 = selected->create_iterator(selected, TRUE); + i2 = selected->create_iterator(selected, TRUE); + while (i1->iterate(i1, (void**)&ts1)) + { + while (i2->iterate(i2, (void**)&ts2)) + { + if (ts1 != ts2) + { + if (ts2->is_contained_in(ts2, ts1)) + { + i2->remove(i2); + ts2->destroy(ts2); + i1->reset(i1); + break; + } + if (ts1->is_contained_in(ts1, ts2)) + { + i1->remove(i1); + ts1->destroy(ts1); + i2->reset(i2); + break; + } + } + } + } + i1->destroy(i1); + i2->destroy(i2); + + return selected; +} + +/** + * Implementation of private_policy_t.select_my_traffic_selectors + */ +static linked_list_t *select_my_traffic_selectors(private_policy_t *this, + linked_list_t *supplied, + host_t *me) +{ + return select_traffic_selectors(this, this->my_ts, supplied, me); +} + +/** + * Implementation of private_policy_t.select_other_traffic_selectors + */ +static linked_list_t *select_other_traffic_selectors(private_policy_t *this, + linked_list_t *supplied, + host_t* other) +{ + return select_traffic_selectors(this, this->other_ts, supplied, other); +} + +/** + * Implementation of policy_t.get_proposal_iterator + */ +static linked_list_t *get_proposals(private_policy_t *this) +{ + iterator_t *iterator; + proposal_t *current; + linked_list_t *proposals = linked_list_create(); + + iterator = this->proposals->create_iterator(this->proposals, TRUE); + while (iterator->iterate(iterator, (void**)¤t)) + { + current = current->clone(current); + proposals->insert_last(proposals, (void*)current); + } + iterator->destroy(iterator); + + return proposals; +} + +/** + * Implementation of policy_t.select_proposal + */ +static proposal_t *select_proposal(private_policy_t *this, linked_list_t *proposals) +{ + iterator_t *stored_iter, *supplied_iter; + proposal_t *stored, *supplied, *selected; + + stored_iter = this->proposals->create_iterator(this->proposals, TRUE); + supplied_iter = proposals->create_iterator(proposals, TRUE); + + /* compare all stored proposals with all supplied. Stored ones are preferred. */ + while (stored_iter->iterate(stored_iter, (void**)&stored)) + { + supplied_iter->reset(supplied_iter); + while (supplied_iter->iterate(supplied_iter, (void**)&supplied)) + { + selected = stored->select(stored, supplied); + if (selected) + { + /* they match, return */ + stored_iter->destroy(stored_iter); + supplied_iter->destroy(supplied_iter); + return selected; + } + } + } + + /* no proposal match :-(, will result in a NO_PROPOSAL_CHOSEN... */ + stored_iter->destroy(stored_iter); + supplied_iter->destroy(supplied_iter); + + return NULL; +} + +/** + * Implementation of policy_t.add_authorities + */ +static void add_authorities(private_policy_t *this, identification_t *my_ca, identification_t *other_ca) +{ + this->my_ca = my_ca; + this->other_ca = other_ca; +} + +/** + * Implementation of policy_t.get_updown + */ +static char* get_updown(private_policy_t *this) +{ + return this->updown; +} + +/** + * Implementation of policy_t.get_hostaccess + */ +static bool get_hostaccess(private_policy_t *this) +{ + return this->hostaccess; +} + +/** + * Implements policy_t.get_dpd_action + */ +static dpd_action_t get_dpd_action(private_policy_t *this) +{ + return this->dpd_action; +} + +/** + * Implementation of policy_t.add_my_traffic_selector + */ +static void add_my_traffic_selector(private_policy_t *this, traffic_selector_t *traffic_selector) +{ + this->my_ts->insert_last(this->my_ts, (void*)traffic_selector); +} + +/** + * Implementation of policy_t.add_other_traffic_selector + */ +static void add_other_traffic_selector(private_policy_t *this, traffic_selector_t *traffic_selector) +{ + this->other_ts->insert_last(this->other_ts, (void*)traffic_selector); +} + +/** + * Implementation of policy_t.add_proposal + */ +static void add_proposal(private_policy_t *this, proposal_t *proposal) +{ + this->proposals->insert_last(this->proposals, (void*)proposal); +} + +/** + * Implementation of policy_t.get_soft_lifetime + */ +static u_int32_t get_soft_lifetime(private_policy_t *this) +{ + if (this->jitter == 0) + { + return this->soft_lifetime ; + } + return this->soft_lifetime - (random() % this->jitter); +} + +/** + * Implementation of policy_t.get_hard_lifetime + */ +static u_int32_t get_hard_lifetime(private_policy_t *this) +{ + return this->hard_lifetime; +} + +/** + * Implementation of policy_t.get_mode. + */ +static mode_t get_mode(private_policy_t *this) +{ + return this->mode; +} + +/** + * Implementation of policy_t.get_virtual_ip. + */ +static host_t* get_virtual_ip(private_policy_t *this, host_t *suggestion) +{ + if (suggestion == NULL) + { + if (this->my_virtual_ip) + { + return this->my_virtual_ip->clone(this->my_virtual_ip); + } + return NULL; + } + if (this->other_virtual_ip) + { + return this->other_virtual_ip->clone(this->other_virtual_ip); + } + if (suggestion->is_anyaddr(suggestion)) + { + return NULL; + } + return suggestion->clone(suggestion); +} + +/** + * Implements policy_t.get_ref. + */ +static void get_ref(private_policy_t *this) +{ + ref_get(&this->refcount); +} + +/** + * Implements policy_t.destroy. + */ +static void destroy(private_policy_t *this) +{ + if (ref_put(&this->refcount)) + { + + this->proposals->destroy_offset(this->proposals, offsetof(proposal_t, destroy)); + this->my_ts->destroy_offset(this->my_ts, offsetof(traffic_selector_t, destroy)); + this->other_ts->destroy_offset(this->other_ts, offsetof(traffic_selector_t, destroy)); + + /* delete certification authorities */ + DESTROY_IF(this->my_ca); + DESTROY_IF(this->other_ca); + + /* delete updown script */ + if (this->updown) + { + free(this->updown); + } + + /* delete ids */ + this->my_id->destroy(this->my_id); + this->other_id->destroy(this->other_id); + DESTROY_IF(this->my_virtual_ip); + DESTROY_IF(this->other_virtual_ip); + + free(this->name); + free(this); + } +} + +/* + * Described in header-file + */ +policy_t *policy_create(char *name, identification_t *my_id, identification_t *other_id, + host_t *my_virtual_ip, host_t *other_virtual_ip, + auth_method_t auth_method, eap_type_t eap_type, + u_int32_t hard_lifetime, u_int32_t soft_lifetime, + u_int32_t jitter, char *updown, bool hostaccess, + mode_t mode, dpd_action_t dpd_action) +{ + private_policy_t *this = malloc_thing(private_policy_t); + + /* public functions */ + this->public.get_name = (char* (*) (policy_t*))get_name; + this->public.get_my_id = (identification_t* (*) (policy_t*))get_my_id; + this->public.get_other_id = (identification_t* (*) (policy_t*))get_other_id; + this->public.get_my_ca = (identification_t* (*) (policy_t*))get_my_ca; + this->public.get_other_ca = (identification_t* (*) (policy_t*))get_other_ca; + this->public.get_auth_method = (auth_method_t (*) (policy_t*)) get_auth_method; + this->public.get_eap_type = (eap_type_t (*) (policy_t*)) get_eap_type; + this->public.get_my_traffic_selectors = (linked_list_t* (*) (policy_t*,host_t*))get_my_traffic_selectors; + this->public.get_other_traffic_selectors = (linked_list_t* (*) (policy_t*,host_t*))get_other_traffic_selectors; + this->public.select_my_traffic_selectors = (linked_list_t* (*) (policy_t*,linked_list_t*,host_t*))select_my_traffic_selectors; + this->public.select_other_traffic_selectors = (linked_list_t* (*) (policy_t*,linked_list_t*,host_t*))select_other_traffic_selectors; + this->public.get_proposals = (linked_list_t* (*) (policy_t*))get_proposals; + this->public.select_proposal = (proposal_t* (*) (policy_t*,linked_list_t*))select_proposal; + this->public.add_my_traffic_selector = (void (*) (policy_t*,traffic_selector_t*))add_my_traffic_selector; + this->public.add_other_traffic_selector = (void (*) (policy_t*,traffic_selector_t*))add_other_traffic_selector; + this->public.add_proposal = (void (*) (policy_t*,proposal_t*))add_proposal; + this->public.add_authorities = (void (*) (policy_t*,identification_t*,identification_t*))add_authorities; + this->public.get_updown = (char* (*) (policy_t*))get_updown; + this->public.get_hostaccess = (bool (*) (policy_t*))get_hostaccess; + this->public.get_dpd_action = (dpd_action_t (*) (policy_t*))get_dpd_action; + this->public.get_soft_lifetime = (u_int32_t (*) (policy_t *))get_soft_lifetime; + this->public.get_hard_lifetime = (u_int32_t (*) (policy_t *))get_hard_lifetime; + this->public.get_mode = (mode_t (*) (policy_t *))get_mode; + this->public.get_virtual_ip = (host_t* (*)(policy_t*,host_t*))get_virtual_ip; + this->public.get_ref = (void (*) (policy_t*))get_ref; + this->public.destroy = (void (*) (policy_t*))destroy; + + /* apply init values */ + this->name = strdup(name); + this->my_id = my_id; + this->other_id = other_id; + this->my_virtual_ip = my_virtual_ip; + this->other_virtual_ip = other_virtual_ip; + this->auth_method = auth_method; + this->eap_type = eap_type; + this->hard_lifetime = hard_lifetime; + this->soft_lifetime = soft_lifetime; + this->jitter = jitter; + this->updown = (updown == NULL) ? NULL : strdup(updown); + this->hostaccess = hostaccess; + this->dpd_action = dpd_action; + this->mode = mode; + + /* initialize private members*/ + this->refcount = 1; + this->my_ca = NULL; + this->other_ca = NULL; + this->proposals = linked_list_create(); + this->my_ts = linked_list_create(); + this->other_ts = linked_list_create(); + + return &this->public; +} diff --git a/src/charon/config/policies/policy.h b/src/charon/config/policies/policy.h new file mode 100644 index 000000000..d8916b29e --- /dev/null +++ b/src/charon/config/policies/policy.h @@ -0,0 +1,413 @@ +/** + * @file policy.h + * + * @brief Interface of policy_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * 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. + */ + +#ifndef POLICY_H_ +#define POLICY_H_ + +typedef enum dpd_action_t dpd_action_t; +typedef struct policy_t policy_t; + +#include <library.h> +#include <utils/identification.h> +#include <config/traffic_selector.h> +#include <config/proposal.h> +#include <sa/authenticators/authenticator.h> +#include <sa/authenticators/eap/eap_method.h> + + +/** + * @brief Actions to take when a peer does not respond (dead peer detected). + * + * These values are the same as in pluto/starter, so do not modify them! + * + * @ingroup config + */ +enum dpd_action_t { + /** DPD disabled */ + DPD_NONE, + /** remove CHILD_SA without replacement */ + DPD_CLEAR, + /** route the CHILD_SA to resetup when needed */ + DPD_ROUTE, + /** restart CHILD_SA in a new IKE_SA, immediately */ + DPD_RESTART, +}; + +/** + * enum names for dpd_action_t. + */ +extern enum_name_t *dpd_action_names; + +/** + * @brief Mode of an IPsec SA. + * + * These are equal to those defined in XFRM, so don't change. + * + * @ingroup config + */ +enum mode_t { + /** transport mode, no inner address */ + MODE_TRANSPORT = 0, + /** tunnel mode, inner and outer addresses */ + MODE_TUNNEL = 1, + /** BEET mode, tunnel mode but fixed, bound inner addresses */ + MODE_BEET = 4, +}; + +/** + * enum names for mode_t. + */ +extern enum_name_t *mode_names; + +/** + * @brief A policy_t defines the policies to apply to CHILD_SAs. + * + * The given two IDs identify a policy. These rules define how + * child SAs may be set up and which traffic may be IPsec'ed. + * + * @b Constructors: + * - policy_create() + * + * @ingroup config + */ +struct policy_t { + + /** + * @brief Get the name of the policy. + * + * Returned object is not getting cloned. + * + * @param this calling object + * @return policy's name + */ + char *(*get_name) (policy_t *this); + + /** + * @brief Get own id. + * + * Returned object is not getting cloned. + * + * @param this calling object + * @return own id + */ + identification_t *(*get_my_id) (policy_t *this); + + /** + * @brief Get peer id. + * + * Returned object is not getting cloned. + * + * @param this calling object + * @return other id + */ + identification_t *(*get_other_id) (policy_t *this); + + /** + * @brief Get own ca. + * + * Returned object is not getting cloned. + * + * @param this calling object + * @return own ca + */ + identification_t *(*get_my_ca) (policy_t *this); + + /** + * @brief Get peer ca. + * + * Returned object is not getting cloned. + * + * @param this calling object + * @return other ca + */ + identification_t *(*get_other_ca) (policy_t *this); + + /** + * @brief Get the authentication method to use. + * + * @param this calling object + * @return authentication method + */ + auth_method_t (*get_auth_method) (policy_t *this); + + /** + * @brief Get the EAP type to use for peer authentication. + * + * @param this calling object + * @return authentication method + */ + eap_type_t (*get_eap_type) (policy_t *this); + + /** + * @brief Get configured traffic selectors for our site. + * + * Returns a list with all traffic selectors for the local + * site. List and items must be destroyed after usage. + * + * @param this calling object + * @return list with traffic selectors + */ + linked_list_t *(*get_my_traffic_selectors) (policy_t *this, host_t *me); + + /** + * @brief Get configured traffic selectors for others site. + * + * Returns a list with all traffic selectors for the remote + * site. List and items must be destroyed after usage. + * + * @param this calling object + * @return list with traffic selectors + */ + linked_list_t *(*get_other_traffic_selectors) (policy_t *this, host_t* other); + + /** + * @brief Select traffic selectors from a supplied list for local site. + * + * Resulted list and traffic selectors must be destroyed after usage. + * As the traffic selectors may contain a wildcard address (0.0.0.0) for + * addresses we don't know in previous, an address may be supplied to + * replace these 0.0.0.0 addresses on-the-fly. + * + * @param this calling object + * @param supplied linked list with traffic selectors + * @param me host address used by us + * @return list containing the selected traffic selectors + */ + linked_list_t *(*select_my_traffic_selectors) (policy_t *this, + linked_list_t *supplied, + host_t *me); + + /** + * @brief Select traffic selectors from a supplied list for remote site. + * + * Resulted list and traffic selectors must be destroyed after usage. + * As the traffic selectors may contain a wildcard address (0.0.0.0) for + * addresses we don't know in previous, an address may be supplied to + * replace these 0.0.0.0 addresses on-the-fly. + * + * @param this calling object + * @param supplied linked list with traffic selectors + * @return list containing the selected traffic selectors + */ + linked_list_t *(*select_other_traffic_selectors) (policy_t *this, + linked_list_t *supplied, + host_t *other); + + /** + * @brief Get the list of internally stored proposals. + * + * policy_t does store proposals for AH/ESP, IKE proposals are in + * the connection_t. + * Resulting list and all of its proposals must be freed after usage. + * + * @param this calling object + * @return lists with proposals + */ + linked_list_t *(*get_proposals) (policy_t *this); + + /** + * @brief Select a proposal from a supplied list. + * + * Returned propsal is newly created and must be destroyed after usage. + * + * @param this calling object + * @param proposals list from from wich proposals are selected + * @return selected proposal, or NULL if nothing matches + */ + proposal_t *(*select_proposal) (policy_t *this, linked_list_t *proposals); + + /** + * @brief Add a traffic selector to the list for local site. + * + * After add, traffic selector is owned by policy. + * + * @param this calling object + * @param traffic_selector traffic_selector to add + */ + void (*add_my_traffic_selector) (policy_t *this, traffic_selector_t *traffic_selector); + + /** + * @brief Add a traffic selector to the list for remote site. + * + * After add, traffic selector is owned by policy. + * + * @param this calling object + * @param traffic_selector traffic_selector to add + */ + void (*add_other_traffic_selector) (policy_t *this, traffic_selector_t *traffic_selector); + + /** + * @brief Add a proposal to the list. + * + * The proposals are stored by priority, first added + * is the most prefered. + * After add, proposal is owned by policy. + * + * @param this calling object + * @param proposal proposal to add + */ + void (*add_proposal) (policy_t *this, proposal_t *proposal); + + /** + * @brief Add certification authorities. + * + * @param this calling object + * @param my_ca issuer of my certificate + * @param other_ca required issuer of the peer's certificate + */ + void (*add_authorities) (policy_t *this, identification_t *my_ca, identification_t *other_ca); + + /** + * @brief Get updown script + * + * @param this calling object + * @return path to updown script + */ + char* (*get_updown) (policy_t *this); + + /** + * @brief Get hostaccess flag + * + * @param this calling object + * @return value of hostaccess flag + */ + bool (*get_hostaccess) (policy_t *this); + + /** + * @brief What should be done with a CHILD_SA, when other peer does not respond. + * + * @param this calling object + * @return dpd action + */ + dpd_action_t (*get_dpd_action) (policy_t *this); + + /** + * @brief Get the lifetime of a policy, before rekeying starts. + * + * A call to this function automatically adds a jitter to + * avoid simultanous rekeying. + * + * @param this policy + * @return lifetime in seconds + */ + u_int32_t (*get_soft_lifetime) (policy_t *this); + + /** + * @brief Get the lifetime of a policy, before SA gets deleted. + * + * @param this policy + * @return lifetime in seconds + */ + u_int32_t (*get_hard_lifetime) (policy_t *this); + + /** + * @brief Get the mode to use for the CHILD_SA, tunnel, transport or BEET. + * + * @param this policy + * @return lifetime in seconds + */ + mode_t (*get_mode) (policy_t *this); + + /** + * @brief Get a virtual IP for the local or the remote host. + * + * By supplying NULL as IP, an IP for the local host is requested. It + * may be %any or specific. + * By supplying %any as host, an IP from the pool is selected to be + * served to the peer. + * If a specified host is supplied, it is checked if this address + * is acceptable to serve to the peer. If so, it is returned. Otherwise, + * an alternative IP is returned. + * In any mode, this call may return NULL indicating virtual IP should + * not be used. + * + * @param this policy + * @param suggestion NULL, %any or specific, see description + * @return clone of an IP to use, or NULL + */ + host_t* (*get_virtual_ip) (policy_t *this, host_t *suggestion); + + /** + * @brief Get a new reference. + * + * Get a new reference to this policy by increasing + * it's internal reference counter. + * Do not call get_ref or any other function until you + * already have a reference. Otherwise the object may get + * destroyed while calling get_ref(), + * + * @param this calling object + */ + void (*get_ref) (policy_t *this); + + /** + * @brief Destroys the policy object. + * + * Decrements the internal reference counter and + * destroys the policy when it reaches zero. + * + * @param this calling object + */ + void (*destroy) (policy_t *this); +}; + +/** + * @brief Create a configuration object for IKE_AUTH and later. + * + * name-string gets cloned, ID's not. + * Virtual IPs are used if they are != NULL. A %any host means the virtual + * IP should be obtained from the other peer. + * Lifetimes are in seconds. To prevent to peers to start rekeying at the + * same time, a jitter may be specified. Rekeying of an SA starts at + * (soft_lifetime - random(0, jitter)). After a successful rekeying, + * the hard_lifetime limit counter is reset. You should specify + * hard_lifetime > soft_lifetime > jitter. + * After a call to create, a reference is obtained (refcount = 1). + * + * @param name name of the policy + * @param my_id identification_t for ourselves + * @param other_id identification_t for the remote guy + * @param my_virtual_ip virtual IP for local host, or NULL + * @param other_virtual_ip virtual IP for remote host, or NULL + * @param auth_method Authentication method to use for our(!) auth data + * @param eap_type EAP type to use for peer authentication + * @param hard_lifetime lifetime before deleting an SA + * @param soft_lifetime lifetime before rekeying an SA + * @param jitter range of randomization time + * @param updown updown script to execute on up/down event + * @param hostaccess allow access to the host itself (used by the updown script) + * @param mode mode to propose for CHILD_SA, transport, tunnel or BEET + * @param dpd_action what to to with a CHILD_SA when other peer does not respond + * @return policy_t object + * + * @ingroup config + */ +policy_t *policy_create(char *name, + identification_t *my_id, identification_t *other_id, + host_t *my_virtual_ip, host_t *other_virtual_ip, + auth_method_t auth_method, eap_type_t eap_type, + u_int32_t hard_lifetime, u_int32_t soft_lifetime, + u_int32_t jitter, char *updown, bool hostaccess, + mode_t mode, dpd_action_t dpd_action); + +#endif /* POLICY_H_ */ diff --git a/src/charon/config/policies/policy_store.h b/src/charon/config/policies/policy_store.h new file mode 100755 index 000000000..cd8870953 --- /dev/null +++ b/src/charon/config/policies/policy_store.h @@ -0,0 +1,119 @@ +/** + * @file policy_store.h + * + * @brief Interface policy_store_t. + * + */ + +/* + * Copyright (C) 2006 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. + */ + +#ifndef POLICY_STORE_H_ +#define POLICY_STORE_H_ + +typedef struct policy_store_t policy_store_t; + +#include <library.h> +#include <config/policies/policy.h> +#include <utils/linked_list.h> + + +/** + * @brief The interface for a store of policy_t's. + * + * The store uses reference counting to manage their lifetime. Call + * destroy() for a policy which is returned from the store after usage. + * + * @b Constructors: + * - stroke_create() + * + * @ingroup config + */ +struct policy_store_t { + + /** + * @brief Returns a policy identified by two IDs and a set of traffic selectors. + * + * other_id must be fully qualified. my_id may be %any, as the + * other peer may not include an IDr Request. + * + * @param this calling object + * @param my_id own ID of the policy + * @param other_id others ID of the policy + * @param my_ts traffic selectors requested for local host + * @param other_ts traffic selectors requested for remote host + * @param my_host host to use for wilcards in TS compare + * @param other_host host to use for wildcards in TS compare + * @return + * - matching policy_t, if found + * - NULL otherwise + */ + policy_t *(*get_policy) (policy_store_t *this, + identification_t *my_id, identification_t *other_id, + linked_list_t *my_ts, linked_list_t *other_ts, + host_t *my_host, host_t* other_host); + + /** + * @brief Returns a policy identified by a connection name. + * + * @param this calling object + * @param name name of the policy + * @return + * - matching policy_t, if found + * - NULL otherwise + */ + policy_t *(*get_policy_by_name) (policy_store_t *this, char *name); + + /** + * @brief Add a policy to the list. + * + * The policy is owned by the store after the call. Do + * not modify nor free. + * + * @param this calling object + * @param policy policy to add + */ + void (*add_policy) (policy_store_t *this, policy_t *policy); + + /** + * @brief Delete a policy from the store. + * + * Remove a policy from the store identified by its name. + * + * @param this calling object + * @param policy policy to add + * @return + * - SUCCESS, or + * - NOT_FOUND + */ + status_t (*delete_policy) (policy_store_t *this, char *name); + + /** + * @brief Get an iterator for the stored policies. + * + * @param this calling object + * @return iterator over all stored policies + */ + iterator_t* (*create_iterator) (policy_store_t *this); + + /** + * @brief Destroys a policy_store_t object. + * + * @param this calling object + */ + void (*destroy) (policy_store_t *this); +}; + +#endif /*POLICY_STORE_H_*/ diff --git a/src/charon/config/proposal.c b/src/charon/config/proposal.c new file mode 100644 index 000000000..dcab8cbdd --- /dev/null +++ b/src/charon/config/proposal.c @@ -0,0 +1,641 @@ +/** + * @file proposal.c + * + * @brief Implementation of proposal_t. + * + */ + +/* + * Copyright (C) 2006 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 <string.h> + +#include "proposal.h" + +#include <daemon.h> +#include <utils/linked_list.h> +#include <utils/identification.h> +#include <utils/lexparser.h> +#include <crypto/prfs/prf.h> +#include <crypto/crypters/crypter.h> +#include <crypto/signers/signer.h> + + +ENUM(protocol_id_names, PROTO_NONE, PROTO_ESP, + "PROTO_NONE", + "IKE", + "AH", + "ESP", +); + +ENUM_BEGIN(transform_type_names, UNDEFINED_TRANSFORM_TYPE, UNDEFINED_TRANSFORM_TYPE, + "UNDEFINED_TRANSFORM_TYPE"); +ENUM_NEXT(transform_type_names, ENCRYPTION_ALGORITHM, EXTENDED_SEQUENCE_NUMBERS, UNDEFINED_TRANSFORM_TYPE, + "ENCRYPTION_ALGORITHM", + "PSEUDO_RANDOM_FUNCTION", + "INTEGRITY_ALGORITHM", + "DIFFIE_HELLMAN_GROUP", + "EXTENDED_SEQUENCE_NUMBERS"); +ENUM_END(transform_type_names, EXTENDED_SEQUENCE_NUMBERS); + +ENUM(extended_sequence_numbers_names, NO_EXT_SEQ_NUMBERS, EXT_SEQ_NUMBERS, + "NO_EXT_SEQ_NUMBERS", + "EXT_SEQ_NUMBERS", +); + +typedef struct private_proposal_t private_proposal_t; + +/** + * Private data of an proposal_t object + */ +struct private_proposal_t { + + /** + * Public part + */ + proposal_t public; + + /** + * protocol (ESP or AH) + */ + protocol_id_t protocol; + + /** + * priority ordered list of encryption algorithms + */ + linked_list_t *encryption_algos; + + /** + * priority ordered list of integrity algorithms + */ + linked_list_t *integrity_algos; + + /** + * priority ordered list of pseudo random functions + */ + linked_list_t *prf_algos; + + /** + * priority ordered list of dh groups + */ + linked_list_t *dh_groups; + + /** + * priority ordered list of extended sequence number flags + */ + linked_list_t *esns; + + /** + * senders SPI + */ + u_int64_t spi; +}; + +/** + * Add algorithm/keysize to a algorithm list + */ +static void add_algo(linked_list_t *list, u_int16_t algo, size_t key_size) +{ + algorithm_t *algo_key; + + algo_key = malloc_thing(algorithm_t); + algo_key->algorithm = algo; + algo_key->key_size = key_size; + list->insert_last(list, (void*)algo_key); +} + +/** + * Implements proposal_t.add_algorithm + */ +static void add_algorithm(private_proposal_t *this, transform_type_t type, u_int16_t algo, size_t key_size) +{ + switch (type) + { + case ENCRYPTION_ALGORITHM: + add_algo(this->encryption_algos, algo, key_size); + break; + case INTEGRITY_ALGORITHM: + add_algo(this->integrity_algos, algo, key_size); + break; + case PSEUDO_RANDOM_FUNCTION: + add_algo(this->prf_algos, algo, key_size); + break; + case DIFFIE_HELLMAN_GROUP: + add_algo(this->dh_groups, algo, 0); + break; + case EXTENDED_SEQUENCE_NUMBERS: + add_algo(this->esns, algo, 0); + break; + default: + break; + } +} + +/** + * Implements proposal_t.get_algorithm. + */ +static bool get_algorithm(private_proposal_t *this, transform_type_t type, algorithm_t** algo) +{ + linked_list_t *list; + switch (type) + { + case ENCRYPTION_ALGORITHM: + list = this->encryption_algos; + break; + case INTEGRITY_ALGORITHM: + list = this->integrity_algos; + break; + case PSEUDO_RANDOM_FUNCTION: + list = this->prf_algos; + break; + case DIFFIE_HELLMAN_GROUP: + list = this->dh_groups; + break; + case EXTENDED_SEQUENCE_NUMBERS: + list = this->esns; + break; + default: + return FALSE; + } + if (list->get_first(list, (void**)algo) != SUCCESS) + { + return FALSE; + } + return TRUE; +} + +/** + * Implements proposal_t.create_algorithm_iterator. + */ +static iterator_t *create_algorithm_iterator(private_proposal_t *this, transform_type_t type) +{ + switch (type) + { + case ENCRYPTION_ALGORITHM: + return this->encryption_algos->create_iterator(this->encryption_algos, TRUE); + case INTEGRITY_ALGORITHM: + return this->integrity_algos->create_iterator(this->integrity_algos, TRUE); + case PSEUDO_RANDOM_FUNCTION: + return this->prf_algos->create_iterator(this->prf_algos, TRUE); + case DIFFIE_HELLMAN_GROUP: + return this->dh_groups->create_iterator(this->dh_groups, TRUE); + case EXTENDED_SEQUENCE_NUMBERS: + return this->esns->create_iterator(this->esns, TRUE); + default: + break; + } + return NULL; +} + +/** + * Find a matching alg/keysize in two linked lists + */ +static bool select_algo(linked_list_t *first, linked_list_t *second, bool *add, u_int16_t *alg, size_t *key_size) +{ + iterator_t *first_iter, *second_iter; + algorithm_t *first_alg, *second_alg; + + /* if in both are zero algorithms specified, we HAVE a match */ + if (first->get_count(first) == 0 && second->get_count(second) == 0) + { + *add = FALSE; + return TRUE; + } + + first_iter = first->create_iterator(first, TRUE); + second_iter = second->create_iterator(second, TRUE); + /* compare algs, order of algs in "first" is preferred */ + while (first_iter->iterate(first_iter, (void**)&first_alg)) + { + second_iter->reset(second_iter); + while (second_iter->iterate(second_iter, (void**)&second_alg)) + { + if (first_alg->algorithm == second_alg->algorithm && + first_alg->key_size == second_alg->key_size) + { + /* ok, we have an algorithm */ + *alg = first_alg->algorithm; + *key_size = first_alg->key_size; + *add = TRUE; + first_iter->destroy(first_iter); + second_iter->destroy(second_iter); + return TRUE; + } + } + } + /* no match in all comparisons */ + first_iter->destroy(first_iter); + second_iter->destroy(second_iter); + return FALSE; +} + +/** + * Implements proposal_t.select. + */ +static proposal_t *select_proposal(private_proposal_t *this, private_proposal_t *other) +{ + proposal_t *selected; + u_int16_t algo; + size_t key_size; + bool add; + + DBG2(DBG_CFG, "selecting proposal:"); + + /* check protocol */ + if (this->protocol != other->protocol) + { + DBG2(DBG_CFG, " protocol mismatch, skipping"); + return NULL; + } + + selected = proposal_create(this->protocol); + + /* select encryption algorithm */ + if (select_algo(this->encryption_algos, other->encryption_algos, &add, &algo, &key_size)) + { + if (add) + { + selected->add_algorithm(selected, ENCRYPTION_ALGORITHM, algo, key_size); + } + } + else + { + selected->destroy(selected); + DBG2(DBG_CFG, " no acceptable ENCRYPTION_ALGORITHM found, skipping"); + return NULL; + } + /* select integrity algorithm */ + if (select_algo(this->integrity_algos, other->integrity_algos, &add, &algo, &key_size)) + { + if (add) + { + selected->add_algorithm(selected, INTEGRITY_ALGORITHM, algo, key_size); + } + } + else + { + selected->destroy(selected); + DBG2(DBG_CFG, " no acceptable INTEGRITY_ALGORITHM found, skipping"); + return NULL; + } + /* select prf algorithm */ + if (select_algo(this->prf_algos, other->prf_algos, &add, &algo, &key_size)) + { + if (add) + { + selected->add_algorithm(selected, PSEUDO_RANDOM_FUNCTION, algo, key_size); + } + } + else + { + selected->destroy(selected); + DBG2(DBG_CFG, " no acceptable PSEUDO_RANDOM_FUNCTION found, skipping"); + return NULL; + } + /* select a DH-group */ + if (select_algo(this->dh_groups, other->dh_groups, &add, &algo, &key_size)) + { + if (add) + { + selected->add_algorithm(selected, DIFFIE_HELLMAN_GROUP, algo, 0); + } + } + else + { + selected->destroy(selected); + DBG2(DBG_CFG, " no acceptable DIFFIE_HELLMAN_GROUP found, skipping"); + return NULL; + } + /* select if we use ESNs */ + if (select_algo(this->esns, other->esns, &add, &algo, &key_size)) + { + if (add) + { + selected->add_algorithm(selected, EXTENDED_SEQUENCE_NUMBERS, algo, 0); + } + } + else + { + selected->destroy(selected); + DBG2(DBG_CFG, " no acceptable EXTENDED_SEQUENCE_NUMBERS found, skipping"); + return NULL; + } + DBG2(DBG_CFG, " proposal matches"); + + /* apply SPI from "other" */ + selected->set_spi(selected, other->spi); + + /* everything matched, return new proposal */ + return selected; +} + +/** + * Implements proposal_t.get_protocols. + */ +static protocol_id_t get_protocol(private_proposal_t *this) +{ + return this->protocol; +} + +/** + * Implements proposal_t.set_spi. + */ +static void set_spi(private_proposal_t *this, u_int64_t spi) +{ + this->spi = spi; +} + +/** + * Implements proposal_t.get_spi. + */ +static u_int64_t get_spi(private_proposal_t *this) +{ + return this->spi; +} + +/** + * Clone a algorithm list + */ +static void clone_algo_list(linked_list_t *list, linked_list_t *clone_list) +{ + algorithm_t *algo, *clone_algo; + iterator_t *iterator = list->create_iterator(list, TRUE); + while (iterator->iterate(iterator, (void**)&algo)) + { + clone_algo = malloc_thing(algorithm_t); + memcpy(clone_algo, algo, sizeof(algorithm_t)); + clone_list->insert_last(clone_list, (void*)clone_algo); + } + iterator->destroy(iterator); +} + +/** + * Implements proposal_t.clone + */ +static proposal_t *clone_(private_proposal_t *this) +{ + private_proposal_t *clone = (private_proposal_t*)proposal_create(this->protocol); + + clone_algo_list(this->encryption_algos, clone->encryption_algos); + clone_algo_list(this->integrity_algos, clone->integrity_algos); + clone_algo_list(this->prf_algos, clone->prf_algos); + clone_algo_list(this->dh_groups, clone->dh_groups); + clone_algo_list(this->esns, clone->esns); + + clone->spi = this->spi; + + return &clone->public; +} + +static status_t add_string_algo(private_proposal_t *this, chunk_t alg) +{ + if (strncmp(alg.ptr, "null", alg.len) == 0) + { + add_algorithm(this, ENCRYPTION_ALGORITHM, ENCR_NULL, 0); + } + else if (strncmp(alg.ptr, "aes128", alg.len) == 0) + { + add_algorithm(this, ENCRYPTION_ALGORITHM, ENCR_AES_CBC, 128); + } + else if (strncmp(alg.ptr, "aes192", alg.len) == 0) + { + add_algorithm(this, ENCRYPTION_ALGORITHM, ENCR_AES_CBC, 192); + } + else if (strncmp(alg.ptr, "aes256", alg.len) == 0) + { + add_algorithm(this, ENCRYPTION_ALGORITHM, ENCR_AES_CBC, 256); + } + else if (strncmp(alg.ptr, "3des", alg.len) == 0) + { + add_algorithm(this, ENCRYPTION_ALGORITHM, ENCR_3DES, 0); + } + /* blowfish only uses some predefined key sizes yet */ + else if (strncmp(alg.ptr, "blowfish128", alg.len) == 0) + { + add_algorithm(this, ENCRYPTION_ALGORITHM, ENCR_BLOWFISH, 128); + } + else if (strncmp(alg.ptr, "blowfish192", alg.len) == 0) + { + add_algorithm(this, ENCRYPTION_ALGORITHM, ENCR_BLOWFISH, 192); + } + else if (strncmp(alg.ptr, "blowfish256", alg.len) == 0) + { + add_algorithm(this, ENCRYPTION_ALGORITHM, ENCR_BLOWFISH, 256); + } + else if (strncmp(alg.ptr, "sha", alg.len) == 0 || + strncmp(alg.ptr, "sha1", alg.len) == 0) + { + /* sha means we use SHA for both, PRF and AUTH */ + add_algorithm(this, INTEGRITY_ALGORITHM, AUTH_HMAC_SHA1_96, 0); + if (this->protocol == PROTO_IKE) + { + add_algorithm(this, PSEUDO_RANDOM_FUNCTION, PRF_HMAC_SHA1, 0); + } + } + else if (strncmp(alg.ptr, "sha256", alg.len) == 0) + { + add_algorithm(this, INTEGRITY_ALGORITHM, AUTH_HMAC_SHA2_256_128, 0); + if (this->protocol == PROTO_IKE) + { + add_algorithm(this, PSEUDO_RANDOM_FUNCTION, PRF_HMAC_SHA2_256, 0); + } + } + else if (strncmp(alg.ptr, "sha384", alg.len) == 0) + { + add_algorithm(this, INTEGRITY_ALGORITHM, AUTH_HMAC_SHA2_384_192, 0); + if (this->protocol == PROTO_IKE) + { + add_algorithm(this, PSEUDO_RANDOM_FUNCTION, PRF_HMAC_SHA2_384, 0); + } + } + else if (strncmp(alg.ptr, "sha512", alg.len) == 0) + { + add_algorithm(this, INTEGRITY_ALGORITHM, AUTH_HMAC_SHA2_512_256, 0); + if (this->protocol == PROTO_IKE) + { + add_algorithm(this, PSEUDO_RANDOM_FUNCTION, PRF_HMAC_SHA2_512, 0); + } + } + else if (strncmp(alg.ptr, "md5", alg.len) == 0) + { + add_algorithm(this, INTEGRITY_ALGORITHM, AUTH_HMAC_MD5_96, 0); + if (this->protocol == PROTO_IKE) + { + add_algorithm(this, PSEUDO_RANDOM_FUNCTION, PRF_HMAC_MD5, 0); + } + } + else if (strncmp(alg.ptr, "modp768", alg.len) == 0) + { + add_algorithm(this, DIFFIE_HELLMAN_GROUP, MODP_768_BIT, 0); + } + else if (strncmp(alg.ptr, "modp1024", alg.len) == 0) + { + add_algorithm(this, DIFFIE_HELLMAN_GROUP, MODP_1024_BIT, 0); + } + else if (strncmp(alg.ptr, "modp1536", alg.len) == 0) + { + add_algorithm(this, DIFFIE_HELLMAN_GROUP, MODP_1536_BIT, 0); + } + else if (strncmp(alg.ptr, "modp2048", alg.len) == 0) + { + add_algorithm(this, DIFFIE_HELLMAN_GROUP, MODP_2048_BIT, 0); + } + else if (strncmp(alg.ptr, "modp4096", alg.len) == 0) + { + add_algorithm(this, DIFFIE_HELLMAN_GROUP, MODP_4096_BIT, 0); + } + else if (strncmp(alg.ptr, "modp8192", alg.len) == 0) + { + add_algorithm(this, DIFFIE_HELLMAN_GROUP, MODP_8192_BIT, 0); + } + else + { + return FAILED; + } + return SUCCESS; +} + +/** + * Implements proposal_t.destroy. + */ +static void destroy(private_proposal_t *this) +{ + this->encryption_algos->destroy_function(this->encryption_algos, free); + this->integrity_algos->destroy_function(this->integrity_algos, free); + this->prf_algos->destroy_function(this->prf_algos, free); + this->dh_groups->destroy_function(this->dh_groups, free); + this->esns->destroy_function(this->esns, free); + free(this); +} + +/* + * Describtion in header-file + */ +proposal_t *proposal_create(protocol_id_t protocol) +{ + private_proposal_t *this = malloc_thing(private_proposal_t); + + this->public.add_algorithm = (void (*)(proposal_t*,transform_type_t,u_int16_t,size_t))add_algorithm; + this->public.create_algorithm_iterator = (iterator_t* (*)(proposal_t*,transform_type_t))create_algorithm_iterator; + this->public.get_algorithm = (bool (*)(proposal_t*,transform_type_t,algorithm_t**))get_algorithm; + this->public.select = (proposal_t* (*)(proposal_t*,proposal_t*))select_proposal; + this->public.get_protocol = (protocol_id_t(*)(proposal_t*))get_protocol; + this->public.set_spi = (void(*)(proposal_t*,u_int64_t))set_spi; + this->public.get_spi = (u_int64_t(*)(proposal_t*))get_spi; + this->public.clone = (proposal_t*(*)(proposal_t*))clone_; + this->public.destroy = (void(*)(proposal_t*))destroy; + + this->spi = 0; + this->protocol = protocol; + + this->encryption_algos = linked_list_create(); + this->integrity_algos = linked_list_create(); + this->prf_algos = linked_list_create(); + this->dh_groups = linked_list_create(); + this->esns = linked_list_create(); + + return &this->public; +} + +/* + * Describtion in header-file + */ +proposal_t *proposal_create_default(protocol_id_t protocol) +{ + private_proposal_t *this = (private_proposal_t*)proposal_create(protocol); + + switch (protocol) + { + case PROTO_IKE: + add_algorithm(this, ENCRYPTION_ALGORITHM, ENCR_AES_CBC, 128); + add_algorithm(this, ENCRYPTION_ALGORITHM, ENCR_AES_CBC, 192); + add_algorithm(this, ENCRYPTION_ALGORITHM, ENCR_AES_CBC, 256); + add_algorithm(this, ENCRYPTION_ALGORITHM, ENCR_3DES, 0); + add_algorithm(this, INTEGRITY_ALGORITHM, AUTH_HMAC_SHA2_256_128, 0); + add_algorithm(this, INTEGRITY_ALGORITHM, AUTH_HMAC_SHA1_96, 0); + add_algorithm(this, INTEGRITY_ALGORITHM, AUTH_HMAC_MD5_96, 0); + add_algorithm(this, INTEGRITY_ALGORITHM, AUTH_HMAC_SHA2_384_192, 0); + add_algorithm(this, INTEGRITY_ALGORITHM, AUTH_HMAC_SHA2_512_256, 0); + add_algorithm(this, PSEUDO_RANDOM_FUNCTION, PRF_HMAC_SHA2_256, 0); + add_algorithm(this, PSEUDO_RANDOM_FUNCTION, PRF_HMAC_SHA1, 0); + add_algorithm(this, PSEUDO_RANDOM_FUNCTION, PRF_HMAC_MD5, 0); + add_algorithm(this, PSEUDO_RANDOM_FUNCTION, PRF_HMAC_SHA2_384, 0); + add_algorithm(this, PSEUDO_RANDOM_FUNCTION, PRF_HMAC_SHA2_512, 0); + add_algorithm(this, DIFFIE_HELLMAN_GROUP, MODP_2048_BIT, 0); + add_algorithm(this, DIFFIE_HELLMAN_GROUP, MODP_1536_BIT, 0); + add_algorithm(this, DIFFIE_HELLMAN_GROUP, MODP_1024_BIT, 0); + add_algorithm(this, DIFFIE_HELLMAN_GROUP, MODP_4096_BIT, 0); + add_algorithm(this, DIFFIE_HELLMAN_GROUP, MODP_8192_BIT, 0); + break; + case PROTO_ESP: + add_algorithm(this, ENCRYPTION_ALGORITHM, ENCR_AES_CBC, 128); + add_algorithm(this, ENCRYPTION_ALGORITHM, ENCR_AES_CBC, 192); + add_algorithm(this, ENCRYPTION_ALGORITHM, ENCR_AES_CBC, 256); + add_algorithm(this, ENCRYPTION_ALGORITHM, ENCR_3DES, 0); + add_algorithm(this, ENCRYPTION_ALGORITHM, ENCR_BLOWFISH, 256); + add_algorithm(this, INTEGRITY_ALGORITHM, AUTH_HMAC_SHA1_96, 0); + add_algorithm(this, INTEGRITY_ALGORITHM, AUTH_HMAC_MD5_96, 0); + add_algorithm(this, EXTENDED_SEQUENCE_NUMBERS, NO_EXT_SEQ_NUMBERS, 0); + break; + case PROTO_AH: + add_algorithm(this, INTEGRITY_ALGORITHM, AUTH_HMAC_SHA1_96, 0); + add_algorithm(this, INTEGRITY_ALGORITHM, AUTH_HMAC_MD5_96, 0); + add_algorithm(this, EXTENDED_SEQUENCE_NUMBERS, NO_EXT_SEQ_NUMBERS, 0); + break; + default: + break; + } + + return &this->public; +} + +/* + * Describtion in header-file + */ +proposal_t *proposal_create_from_string(protocol_id_t protocol, const char *algs) +{ + private_proposal_t *this = (private_proposal_t*)proposal_create(protocol); + chunk_t string = {(void*)algs, strlen(algs)}; + chunk_t alg; + status_t status = SUCCESS; + + eat_whitespace(&string); + if (string.len < 1) + { + destroy(this); + return NULL; + } + + /* get all tokens, separated by '-' */ + while (extract_token(&alg, '-', &string)) + { + status |= add_string_algo(this, alg); + } + if (string.len) + { + status |= add_string_algo(this, string); + } + if (status != SUCCESS) + { + destroy(this); + return NULL; + } + + if (protocol == PROTO_AH || protocol == PROTO_ESP) + { + add_algorithm(this, EXTENDED_SEQUENCE_NUMBERS, NO_EXT_SEQ_NUMBERS, 0); + } + return &this->public; +} diff --git a/src/charon/config/proposal.h b/src/charon/config/proposal.h new file mode 100644 index 000000000..abcb40999 --- /dev/null +++ b/src/charon/config/proposal.h @@ -0,0 +1,266 @@ +/** + * @file proposal.h + * + * @brief Interface of proposal_t. + * + */ + +/* + * Copyright (C) 2006 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. + */ + +#ifndef PROPOSAL_H_ +#define PROPOSAL_H_ + +typedef enum protocol_id_t protocol_id_t; +typedef enum transform_type_t transform_type_t; +typedef enum extended_sequence_numbers_t extended_sequence_numbers_t; +typedef struct algorithm_t algorithm_t; +typedef struct proposal_t proposal_t; + +#include <library.h> +#include <utils/identification.h> +#include <utils/linked_list.h> +#include <utils/host.h> +#include <crypto/crypters/crypter.h> +#include <crypto/signers/signer.h> +#include <crypto/diffie_hellman.h> +#include <config/traffic_selector.h> + +/** + * Protocol ID of a proposal. + * + * @ingroup config + */ +enum protocol_id_t { + PROTO_NONE = 0, + PROTO_IKE = 1, + PROTO_AH = 2, + PROTO_ESP = 3, +}; + +/** + * enum names for protocol_id_t + * + * @ingroup config + */ +extern enum_name_t *protocol_id_names; + + +/** + * Type of a transform, as in IKEv2 RFC 3.3.2. + * + * @ingroup config + */ +enum transform_type_t { + UNDEFINED_TRANSFORM_TYPE = 241, + ENCRYPTION_ALGORITHM = 1, + PSEUDO_RANDOM_FUNCTION = 2, + INTEGRITY_ALGORITHM = 3, + DIFFIE_HELLMAN_GROUP = 4, + EXTENDED_SEQUENCE_NUMBERS = 5 +}; + +/** + * enum names for transform_type_t. + * + * @ingroup config + */ +extern enum_name_t *transform_type_names; + + +/** + * Extended sequence numbers, as in IKEv2 RFC 3.3.2. + * + * @ingroup config + */ +enum extended_sequence_numbers_t { + NO_EXT_SEQ_NUMBERS = 0, + EXT_SEQ_NUMBERS = 1 +}; + +/** + * enum strings for extended_sequence_numbers_t. + * + * @ingroup config + */ +extern enum_name_t *extended_sequence_numbers_names; + + + +/** + * Struct used to store different kinds of algorithms. The internal + * lists of algorithms contain such structures. + */ +struct algorithm_t { + /** + * Value from an encryption_algorithm_t/integrity_algorithm_t/... + */ + u_int16_t algorithm; + + /** + * the associated key size in bits, or zero if not needed + */ + u_int16_t key_size; +}; + +/** + * @brief Stores a set of algorithms used for an SA. + * + * A proposal stores algorithms for a specific + * protocol. It can store algorithms for one protocol. + * Proposals with multiple protocols are not supported, + * as it's not specified in RFC4301 anymore. + * + * @b Constructors: + * - proposal_create() + * + * @ingroup config + */ +struct proposal_t { + + /** + * @brief Add an algorithm to the proposal. + * + * The algorithms are stored by priority, first added + * is the most preferred. + * Key size is only needed for encryption algorithms + * with variable key size (such as AES). Must be set + * to zero if key size is not specified. + * The alg parameter accepts encryption_algorithm_t, + * integrity_algorithm_t, dh_group_number_t and + * extended_sequence_numbers_t. + * + * @param this calling object + * @param type kind of algorithm + * @param alg identifier for algorithm + * @param key_size key size to use + */ + void (*add_algorithm) (proposal_t *this, transform_type_t type, u_int16_t alg, size_t key_size); + + /** + * @brief Get an iterator over algorithms for a specifc algo type. + * + * @param this calling object + * @param type kind of algorithm + * @return iterator over algorithm_t's + */ + iterator_t *(*create_algorithm_iterator) (proposal_t *this, transform_type_t type); + + /** + * @brief Get the algorithm for a type to use. + * + * If there are multiple algorithms, only the first is returned. + * Result is still owned by proposal, do not modify! + * + * @param this calling object + * @param type kind of algorithm + * @param[out] algo pointer which receives algorithm and key size + * @return TRUE if algorithm of this kind available + */ + bool (*get_algorithm) (proposal_t *this, transform_type_t type, algorithm_t** algo); + + /** + * @brief Compare two proposal, and select a matching subset. + * + * If the proposals are for the same protocols (AH/ESP), they are + * compared. If they have at least one algorithm of each type + * in common, a resulting proposal of this kind is created. + * + * @param this calling object + * @param other proposal to compair agains + * @return + * - selected proposal, if possible + * - NULL, if proposals don't match + */ + proposal_t *(*select) (proposal_t *this, proposal_t *other); + + /** + * @brief Get the protocol ID of the proposal. + * + * @param this calling object + * @return protocol of the proposal + */ + protocol_id_t (*get_protocol) (proposal_t *this); + + /** + * @brief Get the SPI of the proposal. + * + * @param this calling object + * @return spi for proto + */ + u_int64_t (*get_spi) (proposal_t *this); + + /** + * @brief Set the SPI of the proposal. + * + * @param this calling object + * @param spi spi to set for proto + */ + void (*set_spi) (proposal_t *this, u_int64_t spi); + + /** + * @brief Clone a proposal. + * + * @param this proposal to clone + * @return clone of it + */ + proposal_t *(*clone) (proposal_t *this); + + /** + * @brief Destroys the proposal object. + * + * @param this calling object + */ + void (*destroy) (proposal_t *this); +}; + +/** + * @brief Create a child proposal for AH, ESP or IKE. + * + * @param protocol protocol, such as PROTO_ESP + * @return proposal_t object + * + * @ingroup config + */ +proposal_t *proposal_create(protocol_id_t protocol); + +/** + * @brief Create a default proposal if nothing further specified. + * + * @param protocol protocol, such as PROTO_ESP + * @return proposal_t object + * + * @ingroup config + */ +proposal_t *proposal_create_default(protocol_id_t protocol); + +/** + * @brief Create a proposal from a string identifying the algorithms. + * + * The string is in the same form as a in the ipsec.conf file. + * E.g.: aes128-sha2_256-modp2048 + * 3des-md5 + * An additional '!' at the end of the string forces this proposal, + * without it the peer may choose another algorithm we support. + * + * @param protocol protocol, such as PROTO_ESP + * @param algs algorithms as string + * @return proposal_t object + * + * @ingroup config + */ +proposal_t *proposal_create_from_string(protocol_id_t protocol, const char *algs); + +#endif /* PROPOSAL_H_ */ diff --git a/src/charon/config/traffic_selector.c b/src/charon/config/traffic_selector.c new file mode 100644 index 000000000..2fb012e16 --- /dev/null +++ b/src/charon/config/traffic_selector.c @@ -0,0 +1,795 @@ +/** + * @file traffic_selector.c + * + * @brief Implementation of traffic_selector_t. + * + */ + +/* + * Copyright (C) 2007 Tobias Brunner + * Copyright (C) 2005-2007 Martin Willi + * Copyright (C) 2005 Jan Hutter + * 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 <arpa/inet.h> +#include <string.h> +#include <netdb.h> +#include <stdio.h> +#include <printf.h> + +#include "traffic_selector.h" + +#include <daemon.h> +#include <utils/linked_list.h> +#include <utils/identification.h> + +ENUM(ts_type_name, TS_IPV4_ADDR_RANGE, TS_IPV6_ADDR_RANGE, + "TS_IPV4_ADDR_RANGE", + "TS_IPV6_ADDR_RANGE", +); + +typedef struct private_traffic_selector_t private_traffic_selector_t; + +/** + * Private data of an traffic_selector_t object + */ +struct private_traffic_selector_t { + + /** + * Public part + */ + traffic_selector_t public; + + /** + * Type of address + */ + ts_type_t type; + + /** + * IP protocol (UDP, TCP, ICMP, ...) + */ + u_int8_t protocol; + + /** + * narrow this traffic selector to hosts external ip + * if set, from and to have no meaning until set_address() is called + */ + bool dynamic; + + /** + * begin of address range, network order + */ + union { + /** dummy char for common address manipulation */ + char from[0]; + /** IPv4 address */ + u_int32_t from4[1]; + /** IPv6 address */ + u_int32_t from6[4]; + }; + + /** + * end of address range, network order + */ + union { + /** dummy char for common address manipulation */ + char to[0]; + /** IPv4 address */ + u_int32_t to4[1]; + /** IPv6 address */ + u_int32_t to6[4]; + }; + + /** + * begin of port range + */ + u_int16_t from_port; + + /** + * end of port range + */ + u_int16_t to_port; +}; + +/** + * calculate to "to"-address for the "from" address and a subnet size + */ +static void calc_range(private_traffic_selector_t *this, u_int8_t netbits) +{ + int byte; + size_t size = (this->type == TS_IPV4_ADDR_RANGE) ? 4 : 16; + + /* go through the from address, starting at the tail. While we + * have not processed the bits belonging to the host, set them to 1 on + * the to address. If we reach the bits for the net, copy them from "from". */ + for (byte = size - 1; byte >=0; byte--) + { + u_char mask = 0x00; + int shift; + + shift = (byte+1) * 8 - netbits; + if (shift > 0) + { + mask = 1 << shift; + if (mask != 0xFF) + { + mask--; + } + } + this->to[byte] = this->from[byte] | mask; + } +} + +/** + * calculate to subnet size from "to"- and "from"-address + */ +static u_int8_t calc_netbits(private_traffic_selector_t *this) +{ + int byte, bit; + size_t size = (this->type == TS_IPV4_ADDR_RANGE) ? 4 : 16; + + /* go trough all bits of the addresses, begging in the front. + * As longer as they equal, the subnet gets larger */ + for (byte = 0; byte < size; byte++) + { + for (bit = 7; bit >= 0; bit--) + { + if ((1<<bit & this->from[byte]) != (1<<bit & this->to[byte])) + { + return ((7 - bit) + (byte * 8)); + } + } + } + /* single host, netmask is 32/128 */ + return (size * 8); +} + +/** + * internal generic constructor + */ +static private_traffic_selector_t *traffic_selector_create(u_int8_t protocol, ts_type_t type, u_int16_t from_port, u_int16_t to_port); + +/** + * output handler in printf() + */ +static int print(FILE *stream, const struct printf_info *info, + const void *const *args) +{ + private_traffic_selector_t *this = *((private_traffic_selector_t**)(args[0])); + char addr_str[INET6_ADDRSTRLEN] = ""; + char *serv_proto = NULL; + u_int8_t mask; + bool has_proto; + bool has_ports; + size_t written = 0; + + if (this == NULL) + { + return fprintf(stream, "(null)"); + } + + if (this->type == TS_IPV4_ADDR_RANGE) + { + inet_ntop(AF_INET, &this->from4, addr_str, sizeof(addr_str)); + } + else + { + inet_ntop(AF_INET6, &this->from6, addr_str, sizeof(addr_str)); + } + mask = calc_netbits(this); + + written += fprintf(stream, "%s/%d", addr_str, mask); + + /* check if we have protocol and/or port selectors */ + has_proto = this->protocol != 0; + has_ports = !(this->from_port == 0 && this->to_port == 0xFFFF); + + if (!has_proto && !has_ports) + { + return written; + } + + written += fprintf(stream, "["); + + /* build protocol string */ + if (has_proto) + { + struct protoent *proto = getprotobynumber(this->protocol); + + if (proto) + { + written += fprintf(stream, "%s", proto->p_name); + serv_proto = proto->p_name; + } + else + { + written += fprintf(stream, "%d", this->protocol); + } + } + + if (has_proto && has_ports) + { + written += fprintf(stream, "/"); + } + + /* build port string */ + if (has_ports) + { + if (this->from_port == this->to_port) + { + struct servent *serv = getservbyport(htons(this->from_port), serv_proto); + + if (serv) + { + written += fprintf(stream, "%s", serv->s_name); + } + else + { + written += fprintf(stream, "%d", this->from_port); + } + } + else + { + written += fprintf(stream, "%d-%d", this->from_port, this->to_port); + } + } + + written += fprintf(stream, "]"); + + return written; +} + +/** + * register printf() handlers + */ +static void __attribute__ ((constructor))print_register() +{ + register_printf_function(PRINTF_TRAFFIC_SELECTOR, print, arginfo_ptr); +} + +/** + * implements traffic_selector_t.get_subset + */ +static traffic_selector_t *get_subset(private_traffic_selector_t *this, private_traffic_selector_t *other) +{ + if (this->type == other->type && (this->protocol == other->protocol || + this->protocol == 0 || other->protocol == 0)) + { + u_int16_t from_port, to_port; + u_char *from, *to; + u_int8_t protocol; + size_t size; + private_traffic_selector_t *new_ts; + + /* calculate the maximum port range allowed for both */ + from_port = max(this->from_port, other->from_port); + to_port = min(this->to_port, other->to_port); + if (from_port > to_port) + { + return NULL; + } + /* select protocol, which is not zero */ + protocol = max(this->protocol, other->protocol); + + switch (this->type) + { + case TS_IPV4_ADDR_RANGE: + size = sizeof(this->from4); + break; + case TS_IPV6_ADDR_RANGE: + size = sizeof(this->from6); + break; + default: + return NULL; + } + + /* get higher from-address */ + if (memcmp(this->from, other->from, size) > 0) + { + from = this->from; + } + else + { + from = other->from; + } + /* get lower to-address */ + if (memcmp(this->to, other->to, size) > 0) + { + to = other->to; + } + else + { + to = this->to; + } + /* if "from" > "to", we don't have a match */ + if (memcmp(from, to, size) > 0) + { + return NULL; + } + + /* we have a match in protocol, port, and address: return it... */ + new_ts = traffic_selector_create(protocol, this->type, from_port, to_port); + new_ts->type = this->type; + memcpy(new_ts->from, from, size); + memcpy(new_ts->to, to, size); + + return &new_ts->public; + } + return NULL; +} + +/** + * implements traffic_selector_t.equals + */ +static bool equals(private_traffic_selector_t *this, private_traffic_selector_t *other) +{ + if (this->type != other->type) + { + return FALSE; + } + if (!(this->from_port == other->from_port && + this->to_port == other->to_port && + this->protocol == other->protocol)) + { + return FALSE; + } + switch (this->type) + { + case TS_IPV4_ADDR_RANGE: + if (memeq(this->from4, other->from4, sizeof(this->from4))) + { + return TRUE; + } + break; + case TS_IPV6_ADDR_RANGE: + if (memeq(this->from6, other->from6, sizeof(this->from6))) + { + return TRUE; + } + break; + default: + break; + } + return FALSE; +} + +/** + * Implements traffic_selector_t.get_from_address. + */ +static chunk_t get_from_address(private_traffic_selector_t *this) +{ + chunk_t from = chunk_empty; + + switch (this->type) + { + case TS_IPV4_ADDR_RANGE: + { + from.len = sizeof(this->from4); + from.ptr = malloc(from.len); + memcpy(from.ptr, this->from4, from.len); + break; + } + case TS_IPV6_ADDR_RANGE: + { + from.len = sizeof(this->from6); + from.ptr = malloc(from.len); + memcpy(from.ptr, this->from6, from.len); + break; + } + } + return from; +} + +/** + * Implements traffic_selector_t.get_to_address. + */ +static chunk_t get_to_address(private_traffic_selector_t *this) +{ + chunk_t to = chunk_empty; + + switch (this->type) + { + case TS_IPV4_ADDR_RANGE: + { + to.len = sizeof(this->to4); + to.ptr = malloc(to.len); + memcpy(to.ptr, this->to4, to.len); + break; + } + case TS_IPV6_ADDR_RANGE: + { + to.len = sizeof(this->to6); + to.ptr = malloc(to.len); + memcpy(to.ptr, this->to6, to.len); + break; + } + } + return to; +} + +/** + * Implements traffic_selector_t.get_from_port. + */ +static u_int16_t get_from_port(private_traffic_selector_t *this) +{ + return this->from_port; +} + +/** + * Implements traffic_selector_t.get_to_port. + */ +static u_int16_t get_to_port(private_traffic_selector_t *this) +{ + return this->to_port; +} + +/** + * Implements traffic_selector_t.get_type. + */ +static ts_type_t get_type(private_traffic_selector_t *this) +{ + return this->type; +} + +/** + * Implements traffic_selector_t.get_protocol. + */ +static u_int8_t get_protocol(private_traffic_selector_t *this) +{ + return this->protocol; +} + +/** + * Implements traffic_selector_t.is_host. + */ +static bool is_host(private_traffic_selector_t *this, host_t *host) +{ + if (this->dynamic) + { + return TRUE; + } + + if (host) + { + chunk_t addr; + int family = host->get_family(host); + + if ((family == AF_INET && this->type == TS_IPV4_ADDR_RANGE) || + (family == AF_INET6 && this->type == TS_IPV6_ADDR_RANGE)) + { + addr = host->get_address(host); + if (memeq(addr.ptr, this->from, addr.len) && + memeq(addr.ptr, this->to, addr.len)) + { + return TRUE; + } + } + } + else + { + size_t length = (this->type == TS_IPV4_ADDR_RANGE) ? 4 : 16; + + if (memeq(this->from, this->to, length)) + { + return TRUE; + } + } + return FALSE; +} + +/** + * Implements traffic_selector_t.set_address. + */ +static void set_address(private_traffic_selector_t *this, host_t *host) +{ + if (this->dynamic) + { + this->type = host->get_family(host) == AF_INET ? + TS_IPV4_ADDR_RANGE : TS_IPV6_ADDR_RANGE; + + chunk_t from = host->get_address(host); + memcpy(this->from, from.ptr, from.len); + memcpy(this->to, from.ptr, from.len); + } +} + +/** + * Implements traffic_selector_t.is_contained_in. + */ +static bool is_contained_in(private_traffic_selector_t *this, + private_traffic_selector_t *other) +{ + private_traffic_selector_t *subset; + bool contained_in = FALSE; + + subset = (private_traffic_selector_t*)get_subset(this, other); + + if (subset) + { + if (equals(subset, this)) + { + contained_in = TRUE; + } + free(subset); + } + return contained_in; +} + +/** + * Implements traffic_selector_t.includes. + */ +static bool includes(private_traffic_selector_t *this, host_t *host) +{ + chunk_t addr; + int family = host->get_family(host); + + if ((family == AF_INET && this->type == TS_IPV4_ADDR_RANGE) || + (family == AF_INET6 && this->type == TS_IPV6_ADDR_RANGE)) + { + addr = host->get_address(host); + + return memcmp(this->from, addr.ptr, addr.len) <= 0 && + memcmp(this->to, addr.ptr, addr.len) >= 0; + } + + return FALSE; +} + +/** + * Implements traffic_selector_t.clone. + */ +static traffic_selector_t *clone_(private_traffic_selector_t *this) +{ + private_traffic_selector_t *clone; + + clone = traffic_selector_create(this->protocol, this->type, + this->from_port, this->to_port); + + clone->dynamic = this->dynamic; + switch (clone->type) + { + case TS_IPV4_ADDR_RANGE: + { + memcpy(clone->from4, this->from4, sizeof(this->from4)); + memcpy(clone->to4, this->to4, sizeof(this->to4)); + return &clone->public; + } + case TS_IPV6_ADDR_RANGE: + { + memcpy(clone->from6, this->from6, sizeof(this->from6)); + memcpy(clone->to6, this->to6, sizeof(this->to6)); + return &clone->public; + } + default: + { + /* unreachable */ + return &clone->public; + } + } +} + +/** + * Implements traffic_selector_t.destroy. + */ +static void destroy(private_traffic_selector_t *this) +{ + free(this); +} + +/* + * see header + */ +traffic_selector_t *traffic_selector_create_from_bytes(u_int8_t protocol, + ts_type_t type, + chunk_t from, u_int16_t from_port, + chunk_t to, u_int16_t to_port) +{ + private_traffic_selector_t *this = traffic_selector_create(protocol, type, + from_port, to_port); + + switch (type) + { + case TS_IPV4_ADDR_RANGE: + { + if (from.len != 4 || to.len != 4) + { + free(this); + return NULL; + } + memcpy(this->from4, from.ptr, from.len); + memcpy(this->to4, to.ptr, to.len); + break; + } + case TS_IPV6_ADDR_RANGE: + { + if (from.len != 16 || to.len != 16) + { + free(this); + return NULL; + } + memcpy(this->from6, from.ptr, from.len); + memcpy(this->to6, to.ptr, to.len); + break; + } + default: + { + free(this); + return NULL; + } + } + return (&this->public); +} + +/* + * see header + */ +traffic_selector_t *traffic_selector_create_from_subnet(host_t *net, + u_int8_t netbits, u_int8_t protocol, u_int16_t port) +{ + private_traffic_selector_t *this = traffic_selector_create(protocol, 0, 0, 65535); + + switch (net->get_family(net)) + { + case AF_INET: + { + chunk_t from; + + this->type = TS_IPV4_ADDR_RANGE; + from = net->get_address(net); + memcpy(this->from4, from.ptr, from.len); + if (this->from4[0] == 0) + { + /* use /0 for 0.0.0.0 */ + this->to4[0] = ~0; + } + else + { + calc_range(this, netbits); + } + break; + } + case AF_INET6: + { + chunk_t from; + + this->type = TS_IPV6_ADDR_RANGE; + from = net->get_address(net); + memcpy(this->from6, from.ptr, from.len); + if (this->from6[0] == 0 && this->from6[1] == 0 && + this->from6[2] == 0 && this->from6[3] == 0) + { + /* use /0 for ::0 */ + this->to6[0] = ~0; + this->to6[1] = ~0; + this->to6[2] = ~0; + this->to6[3] = ~0; + } + else + { + calc_range(this, netbits); + } + break; + } + default: + { + free(this); + return NULL; + } + } + if (port) + { + this->from_port = port; + this->to_port = port; + } + return (&this->public); +} + +/* + * see header + */ +traffic_selector_t *traffic_selector_create_from_string( + u_int8_t protocol, ts_type_t type, + char *from_addr, u_int16_t from_port, + char *to_addr, u_int16_t to_port) +{ + private_traffic_selector_t *this = traffic_selector_create(protocol, type, + from_port, to_port); + + this->type = type; + switch (type) + { + case TS_IPV4_ADDR_RANGE: + { + if (inet_pton(AF_INET, from_addr, (struct in_addr*)this->from4) < 0) + { + free(this); + return NULL; + } + if (inet_pton(AF_INET, to_addr, (struct in_addr*)this->to4) < 0) + { + free(this); + return NULL; + } + break; + } + case TS_IPV6_ADDR_RANGE: + { + if (inet_pton(AF_INET6, from_addr, (struct in6_addr*)this->from6) < 0) + { + free(this); + return NULL; + } + if (inet_pton(AF_INET6, to_addr, (struct in6_addr*)this->to6) < 0) + { + free(this); + return NULL; + } + break; + } + } + return (&this->public); +} + +/* + * see header + */ +traffic_selector_t *traffic_selector_create_dynamic( + u_int8_t protocol, ts_type_t type, + u_int16_t from_port, u_int16_t to_port) +{ + private_traffic_selector_t *this = traffic_selector_create(protocol, type, + from_port, to_port); + + memset(this->from6, 0, sizeof(this->from6)); + memset(this->to6, 0xFF, sizeof(this->to6)); + + this->dynamic = TRUE; + + return &this->public; +} + +/* + * see declaration + */ +static private_traffic_selector_t *traffic_selector_create(u_int8_t protocol, + ts_type_t type, u_int16_t from_port, u_int16_t to_port) +{ + private_traffic_selector_t *this = malloc_thing(private_traffic_selector_t); + + /* public functions */ + this->public.get_subset = (traffic_selector_t*(*)(traffic_selector_t*,traffic_selector_t*))get_subset; + this->public.equals = (bool(*)(traffic_selector_t*,traffic_selector_t*))equals; + this->public.get_from_address = (chunk_t(*)(traffic_selector_t*))get_from_address; + this->public.get_to_address = (chunk_t(*)(traffic_selector_t*))get_to_address; + this->public.get_from_port = (u_int16_t(*)(traffic_selector_t*))get_from_port; + this->public.get_to_port = (u_int16_t(*)(traffic_selector_t*))get_to_port; + this->public.get_type = (ts_type_t(*)(traffic_selector_t*))get_type; + this->public.get_protocol = (u_int8_t(*)(traffic_selector_t*))get_protocol; + this->public.is_host = (bool(*)(traffic_selector_t*,host_t*))is_host; + this->public.is_contained_in = (bool(*)(traffic_selector_t*,traffic_selector_t*))is_contained_in; + this->public.includes = (bool(*)(traffic_selector_t*,host_t*))includes; + this->public.set_address = (void(*)(traffic_selector_t*,host_t*))set_address; + this->public.clone = (traffic_selector_t*(*)(traffic_selector_t*))clone_; + this->public.destroy = (void(*)(traffic_selector_t*))destroy; + + this->from_port = from_port; + this->to_port = to_port; + this->protocol = protocol; + this->type = type; + this->dynamic = FALSE; + + return this; +} + +/* vim: set ts=4 sw=4 noet: */ diff --git a/src/charon/config/traffic_selector.h b/src/charon/config/traffic_selector.h new file mode 100644 index 000000000..0e798fc6a --- /dev/null +++ b/src/charon/config/traffic_selector.h @@ -0,0 +1,312 @@ +/** + * @file traffic_selector.h + * + * @brief Interface of traffic_selector_t. + * + */ + +/* + * Copyright (C) 2007 Tobias Brunner + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * 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. + */ + +#ifndef TRAFFIC_SELECTOR_H_ +#define TRAFFIC_SELECTOR_H_ + +typedef enum ts_type_t ts_type_t; +typedef struct traffic_selector_t traffic_selector_t; + +#include <library.h> +#include <utils/host.h> + +/** + * Traffic selector types. + * + * @ingroup config + */ +enum ts_type_t { + + /** + * A range of IPv4 addresses, represented by two four (4) octet + * values. The first value is the beginning IPv4 address + * (inclusive) and the second value is the ending IPv4 address + * (inclusive). All addresses falling between the two specified + * addresses are considered to be within the list. + */ + TS_IPV4_ADDR_RANGE = 7, + + /** + * A range of IPv6 addresses, represented by two sixteen (16) + * octet values. The first value is the beginning IPv6 address + * (inclusive) and the second value is the ending IPv6 address + * (inclusive). All addresses falling between the two specified + * addresses are considered to be within the list. + */ + TS_IPV6_ADDR_RANGE = 8 +}; + +/** + * enum names for ts_type_t + */ +extern enum_name_t *ts_type_name; + +/** + * @brief Object representing a traffic selector entry. + * + * A traffic selector defines an range of addresses + * and a range of ports. IPv6 is not fully supported yet. + * + * @b Constructors: + * - traffic_selector_create_from_bytes() + * - traffic_selector_create_from_string() + * + * @todo Add IPv6 support + * + * @ingroup config + */ +struct traffic_selector_t { + + /** + * @brief Compare two traffic selectors, and create a new one + * which is the largest subset of both (subnet & port). + * + * Resulting traffic_selector is newly created and must be destroyed. + * + * @param this first to compare + * @param other second to compare + * @return + * - created subset of them + * - or NULL if no match between this and other + */ + traffic_selector_t *(*get_subset) (traffic_selector_t *this, + traffic_selector_t *other); + + /** + * @brief Clone a traffic selector. + * + * @param this traffic selector to clone + * @return clone of it + */ + traffic_selector_t *(*clone) (traffic_selector_t *this); + + /** + * @brief Get starting address of this ts as a chunk. + * + * Chunk is in network order gets allocated. + * + * @param this called object + * @return chunk containing the address + */ + chunk_t (*get_from_address) (traffic_selector_t *this); + + /** + * @brief Get ending address of this ts as a chunk. + * + * Chunk is in network order gets allocated. + * + * @param this called object + * @return chunk containing the address + */ + chunk_t (*get_to_address) (traffic_selector_t *this); + + /** + * @brief Get starting port of this ts. + * + * Port is in host order, since the parser converts it. + * Size depends on protocol. + * + * @param this called object + * @return port + */ + u_int16_t (*get_from_port) (traffic_selector_t *this); + + /** + * @brief Get ending port of this ts. + * + * Port is in host order, since the parser converts it. + * Size depends on protocol. + * + * @param this called object + * @return port + */ + u_int16_t (*get_to_port) (traffic_selector_t *this); + + /** + * @brief Get the type of the traffic selector. + * + * @param this called object + * @return ts_type_t specifying the type + */ + ts_type_t (*get_type) (traffic_selector_t *this); + + /** + * @brief Get the protocol id of this ts. + * + * @param this called object + * @return protocol id + */ + u_int8_t (*get_protocol) (traffic_selector_t *this); + + /** + * @brief Check if the traffic selector is for a single host. + * + * Traffic selector may describe the end of *-to-host tunnel. In this + * case, the address range is a single address equal to the hosts + * peer address. + * If host is NULL, the traffic selector is checked if it is a single host, + * but not a specific one. + * + * @param this called object + * @param host host_t specifying the address range + */ + bool (*is_host) (traffic_selector_t *this, host_t* host); + + /** + * @brief Update the address of a traffic selector. + * + * Update the address range of a traffic selector, if it is + * constructed with the traffic_selector_create_dynamic(). + * + * @param this called object + * @param host host_t specifying the address + */ + void (*set_address) (traffic_selector_t *this, host_t* host); + + /** + * @brief Compare two traffic selectors for equality. + * + * @param this first to compare + * @param other second to compare with first + * @return pointer to a string. + */ + bool (*equals) (traffic_selector_t *this, traffic_selector_t *other); + + /** + * @brief Check if a traffic selector is contained completly in another. + * + * contains() allows to check if multiple traffic selectors are redundant. + * + * @param this ts that is contained in another + * @param other ts that contains this + * @return TRUE if other contains this completly, FALSE otherwise + */ + bool (*is_contained_in) (traffic_selector_t *this, traffic_selector_t *other); + + /** + * @brief Check if a specific host is included in the address range of + * this traffic selector. + * + * @param this called object + * @param host the host to check + */ + bool (*includes) (traffic_selector_t *this, host_t *host); + + /** + * @brief Destroys the ts object + * + * @param this called object + */ + void (*destroy) (traffic_selector_t *this); +}; + +/** + * @brief Create a new traffic selector using human readable params. + * + * @param protocol protocol for this ts, such as TCP or UDP + * @param type type of following addresses, such as TS_IPV4_ADDR_RANGE + * @param from_addr start of address range as string + * @param from_port port number in host order + * @param to_addr end of address range as string + * @param to_port port number in host order + * @return + * - traffic_selector_t object + * - NULL if invalid address strings/protocol + * + * @ingroup config + */ +traffic_selector_t *traffic_selector_create_from_string( + u_int8_t protocol, ts_type_t type, + char *from_addr, u_int16_t from_port, + char *to_addr, u_int16_t to_port); + +/** + * @brief Create a new traffic selector using data read from the net. + * + * There exists a mix of network and host order in the params. + * But the parser gives us this data in this format, so we + * don't have to convert twice. + * + * @param protocol protocol for this ts, such as TCP or UDP + * @param type type of following addresses, such as TS_IPV4_ADDR_RANGE + * @param from_address start of address range, network order + * @param from_port port number, host order + * @param to_address end of address range as string, network + * @param to_port port number, host order + * @return traffic_selector_t object + * + * @ingroup config + */ +traffic_selector_t *traffic_selector_create_from_bytes( + u_int8_t protocol, ts_type_t type, + chunk_t from_address, u_int16_t from_port, + chunk_t to_address, u_int16_t to_port); + +/** + * @brief Create a new traffic selector defining a whole subnet. + * + * In most cases, definition of a traffic selector for full subnets + * is sufficient. This constructor creates a traffic selector for + * all protocols, all ports and the address range specified by the + * subnet. + * Additionally, a protocol and a port may be specified. Port ranges + * are not supported via this constructor. + * + * @param net subnet to use + * @param netbits size of the subnet, as used in e.g. 192.168.0.0/24 notation + * @return + * - traffic_selector_t object + * - NULL if address family of net not supported + * + * @ingroup config + */ +traffic_selector_t *traffic_selector_create_from_subnet( + host_t *net, u_int8_t netbits, + u_int8_t protocol, u_int16_t port); + +/** + * @brief Create a traffic selector for host-to-host cases. + * + * For host2host or virtual IP setups, the traffic selectors gets + * created at runtime using the external/virtual IP. Using this constructor, + * a call to set_address() sets this traffic selector to the supplied host. + * + * + * @param protocol upper layer protocl to allow + * @param type family type + * @param from_port start of allowed port range + * @param to_port end of range + * @return + * - traffic_selector_t object + * - NULL if type not supported + * + * @ingroup config + */ +traffic_selector_t *traffic_selector_create_dynamic( + u_int8_t protocol, ts_type_t type, + u_int16_t from_port, u_int16_t to_port); + +#endif /* TRAFFIC_SELECTOR_H_ */ + +/* vim: set ts=4 sw=4 noet: */ |