diff options
author | Martin Willi <martin@strongswan.org> | 2007-02-28 14:04:36 +0000 |
---|---|---|
committer | Martin Willi <martin@strongswan.org> | 2007-02-28 14:04:36 +0000 |
commit | c60c7694d2d8925c5d93ff33d132f561ad89e071 (patch) | |
tree | 9c7957b0749139c5e7c9b008c927e79d69f8e500 | |
parent | a7a5e834e318d0582b6db979b63a5739c0a8244f (diff) | |
download | strongswan-c60c7694d2d8925c5d93ff33d132f561ad89e071.tar.bz2 strongswan-c60c7694d2d8925c5d93ff33d132f561ad89e071.tar.xz |
merged tasking branch into trunk
96 files changed, 8375 insertions, 8298 deletions
diff --git a/configure.in b/configure.in index 89ab54fff..aa8b624fa 100644 --- a/configure.in +++ b/configure.in @@ -45,6 +45,12 @@ AC_ARG_WITH( [AC_DEFINE_UNQUOTED(DEV_RANDOM, "$withval")], [AC_DEFINE_UNQUOTED(DEV_RANDOM, "/dev/random")] ) +AC_ARG_WITH( + [resolv-conf], + AS_HELP_STRING([--with-resolv-conf=file],[set the file to store DNS server information other than "sysconfdir/resolv.conf"]), + [AC_DEFINE_UNQUOTED(RESOLV_CONF, "$withval")], + [AC_DEFINE_UNQUOTED(RESOLV_CONF, "${sysconfdir}/resolv.conf")] +) AC_ARG_WITH( [urandom-device], diff --git a/src/charon/Makefile.am b/src/charon/Makefile.am index c4a6afa5f..e37087486 100644 --- a/src/charon/Makefile.am +++ b/src/charon/Makefile.am @@ -17,21 +17,25 @@ config/policies/local_policy_store.c config/policies/policy_store.h config/polic config/credentials/local_credential_store.c config/credentials/local_credential_store.h \ config/credentials/credential_store.h config/traffic_selector.c config/traffic_selector.h \ config/proposal.c config/proposal.h config/configuration.c config/configuration.h \ -sa/transactions/transaction.h sa/transactions/transaction.c \ -sa/transactions/ike_sa_init.h sa/transactions/ike_sa_init.c \ -sa/transactions/ike_auth.h sa/transactions/ike_auth.c \ -sa/transactions/create_child_sa.h sa/transactions/create_child_sa.c \ -sa/transactions/delete_child_sa.h sa/transactions/delete_child_sa.c \ -sa/transactions/dead_peer_detection.h sa/transactions/dead_peer_detection.c \ -sa/transactions/delete_ike_sa.h sa/transactions/delete_ike_sa.c \ -sa/transactions/rekey_ike_sa.h sa/transactions/rekey_ike_sa.c \ -sa/authenticators/authenticator.h sa/authenticators/authenticator.c \ -sa/authenticators/rsa_authenticator.h sa/authenticators/rsa_authenticator.c \ -sa/authenticators/psk_authenticator.h sa/authenticators/psk_authenticator.c \ sa/authenticators/eap_authenticator.h sa/authenticators/eap_authenticator.c \ sa/authenticators/eap/eap_method.h sa/authenticators/eap/eap_method.c \ sa/child_sa.c sa/child_sa.h sa/ike_sa.c sa/ike_sa.h sa/ike_sa_manager.c sa/ike_sa_manager.h \ -sa/ike_sa_id.c sa/ike_sa_id.h encoding/payloads/encryption_payload.c \ +sa/ike_sa_id.c sa/ike_sa_id.h sa/tasks/task.c sa/tasks/task.h \ +sa/tasks/ike_init.c sa/tasks/ike_init.h \ +sa/tasks/ike_natd.c sa/tasks/ike_natd.h \ +sa/tasks/ike_auth.c sa/tasks/ike_auth.h \ +sa/tasks/ike_config.c sa/tasks/ike_config.h \ +sa/tasks/ike_cert.c sa/tasks/ike_cert.h \ +sa/tasks/ike_rekey.c sa/tasks/ike_rekey.h \ +sa/tasks/ike_delete.c sa/tasks/ike_delete.h \ +sa/tasks/ike_dpd.c sa/tasks/ike_dpd.h \ +sa/tasks/child_create.c sa/tasks/child_create.h \ +sa/tasks/child_delete.c sa/tasks/child_delete.h \ +sa/tasks/child_rekey.c sa/tasks/child_rekey.h \ +sa/authenticators/authenticator.c sa/authenticators/authenticator.h \ +sa/authenticators/rsa_authenticator.c sa/authenticators/rsa_authenticator.h \ +sa/authenticators/psk_authenticator.c sa/authenticators/psk_authenticator.h \ +sa/task_manager.c sa/task_manager.h encoding/payloads/encryption_payload.c \ encoding/payloads/cert_payload.c encoding/payloads/payload.h encoding/payloads/traffic_selector_substructure.c \ encoding/payloads/configuration_attribute.h encoding/payloads/proposal_substructure.h \ encoding/payloads/transform_attribute.c encoding/payloads/transform_attribute.h \ @@ -51,10 +55,10 @@ encoding/payloads/vendor_id_payload.h encoding/payloads/proposal_substructure.c encoding/parser.h encoding/message.c encoding/generator.c encoding/message.h encoding/generator.h \ encoding/parser.c daemon.c daemon.h network/packet.c \ network/socket.c network/packet.h network/socket.h queues/jobs/job.h queues/jobs/job.c \ -queues/jobs/retransmit_request_job.h queues/jobs/initiate_job.h \ +queues/jobs/retransmit_job.h queues/jobs/initiate_job.h \ queues/jobs/incoming_packet_job.h queues/jobs/incoming_packet_job.c \ queues/jobs/delete_ike_sa_job.c queues/jobs/delete_ike_sa_job.h \ -queues/jobs/retransmit_request_job.c queues/jobs/initiate_job.c \ +queues/jobs/retransmit_job.c queues/jobs/initiate_job.c \ queues/jobs/send_keepalive_job.c queues/jobs/send_keepalive_job.h \ queues/jobs/rekey_child_sa_job.c queues/jobs/rekey_child_sa_job.h queues/jobs/delete_child_sa_job.c queues/jobs/delete_child_sa_job.h \ queues/jobs/send_dpd_job.c queues/jobs/send_dpd_job.h queues/jobs/route_job.c queues/jobs/route_job.h \ diff --git a/src/charon/bus/bus.c b/src/charon/bus/bus.c index afed16656..740663d5c 100644 --- a/src/charon/bus/bus.c +++ b/src/charon/bus/bus.c @@ -180,7 +180,7 @@ static int get_thread_number(private_bus_t *this) static void add_listener(private_bus_t *this, bus_listener_t *listener) { pthread_mutex_lock(&this->mutex); - this->listeners->insert_last(this->listeners, (void*)listener); + this->listeners->insert_last(this->listeners, listener); pthread_mutex_unlock(&this->mutex); } @@ -301,7 +301,12 @@ static void vsignal(private_bus_t *this, signal_t signal, level_t level, va_list args_copy; va_copy(args_copy, args); - listener->signal(listener, signal, level, thread, ike_sa, format, args_copy); + if (!listener->signal(listener, signal, level, thread, + ike_sa, format, args_copy)) + { + /* unregister listener if requested */ + iterator->remove(iterator); + } va_end(args_copy); } iterator->destroy(iterator); diff --git a/src/charon/bus/bus.h b/src/charon/bus/bus.h index 974d460b9..200525fb7 100644 --- a/src/charon/bus/bus.h +++ b/src/charon/bus/bus.h @@ -224,6 +224,8 @@ struct bus_listener_t { * an additional informational or error message with a printf() like * variable argument list. This is in the va_list form, as forwarding * a "..." parameters to functions is not (cleanly) possible. + * The implementing signal function returns TRUE to stay registered + * to the bus, or FALSE to unregister itself. * * @param this listener * @param singal kind of the signal (up, down, rekeyed, ...) @@ -232,8 +234,9 @@ struct bus_listener_t { * @param ike_sa IKE_SA associated to the event * @param format printf() style format string * @param args vprintf() style va_list argument list + " @return TRUE to stay registered, FALSE to unregister */ - void (*signal) (bus_listener_t *this, signal_t signal, level_t level, + bool (*signal) (bus_listener_t *this, signal_t signal, level_t level, int thread, ike_sa_t *ike_sa, char* format, va_list args); }; diff --git a/src/charon/bus/listeners/file_logger.c b/src/charon/bus/listeners/file_logger.c index 4c9e13b3f..14f9f72cf 100644 --- a/src/charon/bus/listeners/file_logger.c +++ b/src/charon/bus/listeners/file_logger.c @@ -53,7 +53,7 @@ struct private_file_logger_t { /** * Implementation of bus_listener_t.signal. */ -static void signal_(private_file_logger_t *this, signal_t signal, level_t level, +static bool signal_(private_file_logger_t *this, signal_t signal, level_t level, int thread, ike_sa_t* ike_sa, char *format, va_list args) { if (level <= this->levels[SIG_TYPE(signal)]) @@ -76,6 +76,8 @@ static void signal_(private_file_logger_t *this, signal_t signal, level_t level, current = next; } } + /* always stay registered */ + return TRUE; } /** @@ -114,7 +116,7 @@ file_logger_t *file_logger_create(FILE *out) private_file_logger_t *this = malloc_thing(private_file_logger_t); /* public functions */ - this->public.listener.signal = (void(*)(bus_listener_t*,signal_t,level_t,int,ike_sa_t*,char*,va_list))signal_; + this->public.listener.signal = (bool(*)(bus_listener_t*,signal_t,level_t,int,ike_sa_t*,char*,va_list))signal_; this->public.set_level = (void(*)(file_logger_t*,signal_t,level_t))set_level; this->public.destroy = (void(*)(file_logger_t*))destroy; diff --git a/src/charon/bus/listeners/sys_logger.c b/src/charon/bus/listeners/sys_logger.c index 58c59c4f7..d26d14dc0 100644 --- a/src/charon/bus/listeners/sys_logger.c +++ b/src/charon/bus/listeners/sys_logger.c @@ -54,7 +54,7 @@ struct private_sys_logger_t { /** * Implementation of bus_listener_t.signal. */ -static void signal_(private_sys_logger_t *this, signal_t signal, level_t level, +static bool signal_(private_sys_logger_t *this, signal_t signal, level_t level, int thread, ike_sa_t* ike_sa, char *format, va_list args) { if (level <= this->levels[SIG_TYPE(signal)]) @@ -78,6 +78,8 @@ static void signal_(private_sys_logger_t *this, signal_t signal, level_t level, current = next; } } + /* always stay registered */ + return TRUE; } /** @@ -117,7 +119,7 @@ sys_logger_t *sys_logger_create(int facility) private_sys_logger_t *this = malloc_thing(private_sys_logger_t); /* public functions */ - this->public.listener.signal = (void(*)(bus_listener_t*,signal_t,level_t,int,ike_sa_t*,char*,va_list))signal_; + this->public.listener.signal = (bool(*)(bus_listener_t*,signal_t,level_t,int,ike_sa_t*,char*,va_list))signal_; this->public.set_level = (void(*)(sys_logger_t*,signal_t,level_t))set_level; this->public.destroy = (void(*)(sys_logger_t*))destroy; diff --git a/src/charon/config/configuration.c b/src/charon/config/configuration.c index 39fc4d922..f43afdaa4 100755 --- a/src/charon/config/configuration.c +++ b/src/charon/config/configuration.c @@ -33,25 +33,11 @@ #define HALF_OPEN_IKE_SA_TIMEOUT 30000 /** - * The retransmission algorithm uses a multiple sequences. - * Each sequence contains multiple retransmits. Those retransmits - * are sent using a exponential backoff algorithm. The sequences - * are retried with linear timings: + * Retransmission uses a backoff algorithm. The timeout is calculated using + * TIMEOUT * (BASE ** try). + * When try reaches TRIES, retransmission is given up. * - * <------sequence---------><------sequence---------><------sequence---------> - * - * T-R---R-----R---------R--R-R---R-----R---------R--R-R---R-----R---------R--X - * - * T = first transmit - * R = retransmit - * X = giving up, peer is dead - * - * if (retransmit >= TRIES * sequences) - * => abort - * TIMEOUT * (BASE ** (try % TRIES)) - * - * Using an initial TIMEOUT of 4s, a BASE of 1.8, 5 TRIES - * per sequnce and 3 sequences, this gives us: + * Using an initial TIMEOUT of 4s, a BASE of 1.8, and 5 TRIES gives us: * * | relative | absolute * --------------------------------------------------------- @@ -61,22 +47,8 @@ * 4s * (1.8 ** (3 % 5)) = 23s 47s * 4s * (1.8 ** (4 % 5)) = 42s 89s * 4s * (1.8 ** (5 % 5)) = 76s 165s - * 4s * (1.8 ** (6 % 5)) = 4s 169s - * 4s * (1.8 ** (7 % 5)) = 7s 176s - * 4s * (1.8 ** (8 % 5)) = 13s 189s - * 4s * (1.8 ** (9 % 5)) = 23s 212s - * 4s * (1.8 ** (10 % 5)) = 42s 254s - * 4s * (1.8 ** (11 % 5)) = 76s 330s - * 4s * (1.8 ** (12 % 5)) = 4s 334 - * 4s * (1.8 ** (13 % 5)) = 7s 341s - * 4s * (1.8 ** (14 % 5)) = 13s 354s - * 4s * (1.8 ** (15 % 5)) = 23s 377s - * 4s * (1.8 ** (16 % 5)) = 42s 419s - * 4s * (1.8 ** (17 % 5)) = 76s 495s * - * If the configuration uses 1 sequence, the peer is considered dead - * after 2min 45s when no reply comes in. If it uses 3 sequences, after - * 8min 15s the DPD action is executed... + * The peer is considered dead after 2min 45s when no reply comes in. */ /** @@ -119,17 +91,15 @@ struct private_configuration_t { * Implementation of configuration_t.get_retransmit_timeout. */ static u_int32_t get_retransmit_timeout (private_configuration_t *this, - u_int32_t retransmit_count, - u_int32_t max_sequences) + u_int32_t retransmit_count) { - if (max_sequences != 0 && - retransmit_count >= RETRANSMIT_TRIES * max_sequences) + if (retransmit_count > RETRANSMIT_TRIES) { /* give up */ return 0; } - return (u_int32_t)(RETRANSMIT_TIMEOUT * - pow(RETRANSMIT_BASE, retransmit_count % RETRANSMIT_TRIES)); + return (u_int32_t) + (RETRANSMIT_TIMEOUT * pow(RETRANSMIT_BASE, retransmit_count)); } /** @@ -165,7 +135,7 @@ configuration_t *configuration_create() /* public functions */ this->public.destroy = (void(*)(configuration_t*))destroy; - this->public.get_retransmit_timeout = (u_int32_t (*) (configuration_t*,u_int32_t,u_int32_t))get_retransmit_timeout; + 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; diff --git a/src/charon/config/configuration.h b/src/charon/config/configuration.h index 2bb0b106e..77d1fd85d 100755 --- a/src/charon/config/configuration.h +++ b/src/charon/config/configuration.h @@ -40,19 +40,15 @@ struct configuration_t { /** * @brief Returns the retransmit timeout. * - * A return value of zero means the request should not be retransmitted again. - * The retransmission algorithm uses sequences of retransmits, in which - * every sequence contains exponential delayed retransmits. These - * sequences are compareable to the keyingtries mechanism used in pluto. + * 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 - * @param max_sequences maximum number of retransmission sequences to allow - * @return time in milliseconds, when to schedule next retransmit + * @return time in milliseconds, when to do next retransmit */ u_int32_t (*get_retransmit_timeout) (configuration_t *this, - u_int32_t retransmitted, - u_int32_t max_sequences); + u_int32_t retransmitted); /** * @brief Returns the timeout for an half open IKE_SA in ms. diff --git a/src/charon/config/policies/policy.c b/src/charon/config/policies/policy.c index e68a8ad2b..fa23955fb 100644 --- a/src/charon/config/policies/policy.c +++ b/src/charon/config/policies/policy.c @@ -79,6 +79,16 @@ struct private_policy_t { 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; @@ -209,7 +219,8 @@ static eap_type_t get_eap_type(private_policy_t *this) /** * 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) +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; @@ -222,7 +233,10 @@ static linked_list_t *get_traffic_selectors(private_policy_t *this, linked_list_ /* 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); - current->update_address_range(current, host); + if (host) + { + current->update_address_range(current, host); + } result->insert_last(result, (void*)current); } @@ -269,7 +283,10 @@ static linked_list_t *select_traffic_selectors(private_policy_t *this, /* 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); - stored_ts->update_address_range(stored_ts, host); + if (host) + { + stored_ts->update_address_range(stored_ts, host); + } supplied_iter->reset(supplied_iter); /* iterate over all supplied traffic selectors */ @@ -457,6 +474,30 @@ static mode_t get_mode(private_policy_t *this) } /** + * 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) @@ -477,14 +518,8 @@ static void destroy(private_policy_t *this) this->other_ts->destroy_offset(this->other_ts, offsetof(traffic_selector_t, destroy)); /* delete certification authorities */ - if (this->my_ca) - { - this->my_ca->destroy(this->my_ca); - } - if (this->other_ca) - { - this->other_ca->destroy(this->other_ca); - } + DESTROY_IF(this->my_ca); + DESTROY_IF(this->other_ca); /* delete updown script */ if (this->updown) @@ -495,6 +530,8 @@ static void destroy(private_policy_t *this) /* 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); @@ -505,6 +542,7 @@ static void destroy(private_policy_t *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, @@ -536,6 +574,7 @@ policy_t *policy_create(char *name, identification_t *my_id, identification_t *o 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; @@ -543,6 +582,8 @@ policy_t *policy_create(char *name, identification_t *my_id, identification_t *o 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; diff --git a/src/charon/config/policies/policy.h b/src/charon/config/policies/policy.h index a2d9ae8d0..d8916b29e 100644 --- a/src/charon/config/policies/policy.h +++ b/src/charon/config/policies/policy.h @@ -329,6 +329,25 @@ struct policy_t { 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 @@ -356,6 +375,8 @@ struct policy_t { * @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, @@ -366,6 +387,8 @@ struct policy_t { * @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 @@ -381,6 +404,7 @@ struct policy_t { */ 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, diff --git a/src/charon/config/traffic_selector.c b/src/charon/config/traffic_selector.c index 2afeb5e48..519c90f77 100644 --- a/src/charon/config/traffic_selector.c +++ b/src/charon/config/traffic_selector.c @@ -6,6 +6,7 @@ */ /* + * Copyright (C) 2007 Tobias Brunner * Copyright (C) 2005-2006 Martin Willi * Copyright (C) 2005 Jan Hutter * Hochschule fuer Technik Rapperswil @@ -494,6 +495,26 @@ static void update_address_range(private_traffic_selector_t *this, host_t *host) } /** + * 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) @@ -698,6 +719,7 @@ static private_traffic_selector_t *traffic_selector_create(u_int8_t protocol, ts 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.includes = (bool(*)(traffic_selector_t*,host_t*))includes; this->public.update_address_range = (void(*)(traffic_selector_t*,host_t*))update_address_range; this->public.clone = (traffic_selector_t*(*)(traffic_selector_t*))clone_; this->public.destroy = (void(*)(traffic_selector_t*))destroy; @@ -709,3 +731,6 @@ static private_traffic_selector_t *traffic_selector_create(u_int8_t protocol, ts 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 index 5d1ccaf2f..7728ba307 100644 --- a/src/charon/config/traffic_selector.h +++ b/src/charon/config/traffic_selector.h @@ -6,6 +6,7 @@ */ /* + * Copyright (C) 2007 Tobias Brunner * Copyright (C) 2005-2006 Martin Willi * Copyright (C) 2005 Jan Hutter * Hochschule fuer Technik Rapperswil @@ -105,7 +106,7 @@ struct traffic_selector_t { * * Chunk is in network order gets allocated. * - * @param this calling object + * @param this called object * @return chunk containing the address */ chunk_t (*get_from_address) (traffic_selector_t *this); @@ -115,7 +116,7 @@ struct traffic_selector_t { * * Chunk is in network order gets allocated. * - * @param this calling object + * @param this called object * @return chunk containing the address */ chunk_t (*get_to_address) (traffic_selector_t *this); @@ -126,7 +127,7 @@ struct traffic_selector_t { * Port is in host order, since the parser converts it. * Size depends on protocol. * - * @param this calling object + * @param this called object * @return port */ u_int16_t (*get_from_port) (traffic_selector_t *this); @@ -137,7 +138,7 @@ struct traffic_selector_t { * Port is in host order, since the parser converts it. * Size depends on protocol. * - * @param this calling object + * @param this called object * @return port */ u_int16_t (*get_to_port) (traffic_selector_t *this); @@ -145,7 +146,7 @@ struct traffic_selector_t { /** * @brief Get the type of the traffic selector. * - * @param this calling obect + * @param this called object * @return ts_type_t specifying the type */ ts_type_t (*get_type) (traffic_selector_t *this); @@ -153,7 +154,7 @@ struct traffic_selector_t { /** * @brief Get the protocol id of this ts. * - * @param this calling obect + * @param this called object * @return protocol id */ u_int8_t (*get_protocol) (traffic_selector_t *this); @@ -167,7 +168,7 @@ struct traffic_selector_t { * If host is NULL, the traffic selector is checked if it is a single host, * but not a specific one. * - * @param this calling obect + * @param this called object * @param host host_t specifying the address range */ bool (*is_host) (traffic_selector_t *this, host_t* host); @@ -180,7 +181,7 @@ struct traffic_selector_t { * starts from the supplied address and also ends there * (which means it is a one-host-address-range ;-). * - * @param this calling obect + * @param this called object * @param host host_t specifying the address range */ void (*update_address_range) (traffic_selector_t *this, host_t* host); @@ -193,11 +194,19 @@ struct traffic_selector_t { * @return pointer to a string. */ bool (*equals) (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 calling object + * @param this called object */ void (*destroy) (traffic_selector_t *this); }; @@ -269,3 +278,6 @@ traffic_selector_t *traffic_selector_create_from_subnet( u_int8_t protocol, u_int16_t port); #endif /* TRAFFIC_SELECTOR_H_ */ + +/* vim: set ts=4 sw=4 noet: */ + diff --git a/src/charon/encoding/message.c b/src/charon/encoding/message.c index fb37c996d..acc3abd1b 100644 --- a/src/charon/encoding/message.c +++ b/src/charon/encoding/message.c @@ -345,6 +345,7 @@ static status_t get_payload_rule(private_message_t *this, payload_type_t payload */ static void set_ike_sa_id (private_message_t *this,ike_sa_id_t *ike_sa_id) { + DESTROY_IF(this->ike_sa_id); this->ike_sa_id = ike_sa_id->clone(ike_sa_id); } @@ -490,6 +491,29 @@ static void add_payload(private_message_t *this, payload_t *payload) } /** + * Implementation of message_t.add_notify. + */ +static void add_notify(private_message_t *this, bool flush, notify_type_t type, + chunk_t data) +{ + notify_payload_t *notify; + payload_t *payload; + + if (flush) + { + while (this->payloads->remove_last(this->payloads, + (void**)&payload) == SUCCESS) + { + payload->destroy(payload); + } + } + notify = notify_payload_create(); + notify->set_notify_type(notify, type); + notify->set_notification_data(notify, data); + add_payload(this, (payload_t*)notify); +} + +/** * Implementation of message_t.set_source. */ static void set_source(private_message_t *this, host_t *host) @@ -522,7 +546,7 @@ static host_t * get_destination(private_message_t *this) } /** - * Implementation of message_t.get_destination. + * Implementation of message_t.get_payload_iterator. */ static iterator_t *get_payload_iterator(private_message_t *this) { @@ -530,6 +554,27 @@ static iterator_t *get_payload_iterator(private_message_t *this) } /** + * Implementation of message_t.get_payload. + */ +static payload_t *get_payload(private_message_t *this, payload_type_t type) +{ + payload_t *current, *found = NULL; + iterator_t *iterator; + + iterator = this->payloads->create_iterator(this->payloads, TRUE); + while (iterator->iterate(iterator, (void**)¤t)) + { + if (current->get_type(current) == type) + { + found = current; + break; + } + } + iterator->destroy(iterator); + return found; +} + +/** * output handler in printf() */ static int print(FILE *stream, const struct printf_info *info, @@ -786,6 +831,10 @@ static status_t generate(private_message_t *this, crypter_t *crypter, signer_t* */ static packet_t *get_packet (private_message_t *this) { + if (this->packet == NULL) + { + return NULL; + } return this->packet->clone(this->packet); } @@ -794,6 +843,10 @@ static packet_t *get_packet (private_message_t *this) */ static chunk_t get_packet_data (private_message_t *this) { + if (this->packet == NULL) + { + return chunk_empty; + } return chunk_clone(this->packet->get_data(this->packet)); } @@ -1147,7 +1200,6 @@ static status_t parse_body(private_message_t *this, crypter_t *crypter, signer_t status = verify(this); if (status != SUCCESS) { - DBG1(DBG_ENC, "verification of message failed"); return status; } @@ -1191,12 +1243,14 @@ message_t *message_create_from_packet(packet_t *packet) this->public.set_request = (void(*)(message_t*, bool))set_request; this->public.get_request = (bool(*)(message_t*))get_request; this->public.add_payload = (void(*)(message_t*,payload_t*))add_payload; + this->public.add_notify = (void(*)(message_t*,bool,notify_type_t,chunk_t))add_notify; this->public.generate = (status_t (*) (message_t *,crypter_t*,signer_t*,packet_t**)) generate; this->public.set_source = (void (*) (message_t*,host_t*)) set_source; this->public.get_source = (host_t * (*) (message_t*)) get_source; this->public.set_destination = (void (*) (message_t*,host_t*)) set_destination; this->public.get_destination = (host_t * (*) (message_t*)) get_destination; this->public.get_payload_iterator = (iterator_t * (*) (message_t *)) get_payload_iterator; + this->public.get_payload = (payload_t * (*) (message_t *, payload_type_t)) get_payload; this->public.parse_header = (status_t (*) (message_t *)) parse_header; this->public.parse_body = (status_t (*) (message_t *,crypter_t*,signer_t*)) parse_body; this->public.get_packet = (packet_t * (*) (message_t*)) get_packet; diff --git a/src/charon/encoding/message.h b/src/charon/encoding/message.h index dfb6d64af..73c2e05c6 100644 --- a/src/charon/encoding/message.h +++ b/src/charon/encoding/message.h @@ -184,6 +184,21 @@ struct message_t { void (*add_payload) (message_t *this, payload_t *payload); /** + * @brief Build a notify payload and add it to the message. + * + * This is a helper method to create notify messages or add + * notify payload to messages. The flush parameter specifies if existing + * payloads should get removed before appending the notify. + * + * @param this message_t object + * @param flush TRUE to remove existing payloads + * @param type type of the notify + * @param data a chunk of data to add to the notify, gets cloned + */ + void (*add_notify) (message_t *this, bool flush, notify_type_t type, + chunk_t data); + + /** * @brief Parses header of message. * * Begins parisng of a message created via message_create_from_packet(). @@ -304,6 +319,17 @@ struct message_t { iterator_t * (*get_payload_iterator) (message_t *this); /** + * @brief Find a payload of a spicific type. + * + * Returns the first occurance. + * + * @param this message_t object + * @param type type of the payload to find + * @return payload, or NULL if no such payload found + */ + payload_t* (*get_payload) (message_t *this, payload_type_t type); + + /** * @brief Returns a clone of the internal stored packet_t object. * * @param this message_t object diff --git a/src/charon/encoding/payloads/cert_payload.c b/src/charon/encoding/payloads/cert_payload.c index 2e690b45d..8f477ffd0 100644 --- a/src/charon/encoding/payloads/cert_payload.c +++ b/src/charon/encoding/payloads/cert_payload.c @@ -270,7 +270,7 @@ cert_payload_t *cert_payload_create() /* private variables */ this->critical = FALSE; this->next_payload = NO_PAYLOAD; - this->payload_length =CERT_PAYLOAD_HEADER_LENGTH; + this->payload_length = CERT_PAYLOAD_HEADER_LENGTH; this->cert_data = chunk_empty; return (&(this->public)); diff --git a/src/charon/encoding/payloads/certreq_payload.c b/src/charon/encoding/payloads/certreq_payload.c index 86f2e3524..fcddcf971 100644 --- a/src/charon/encoding/payloads/certreq_payload.c +++ b/src/charon/encoding/payloads/certreq_payload.c @@ -306,7 +306,10 @@ certreq_payload_t *certreq_payload_create_from_cacerts(void) int count = iterator->get_count(iterator); if (count == 0) + { + iterator->destroy(iterator); return NULL; + } this = certreq_payload_create(); keyids = chunk_alloc(count * HASH_SIZE_SHA1); diff --git a/src/charon/encoding/payloads/configuration_attribute.c b/src/charon/encoding/payloads/configuration_attribute.c index e7000e1b5..0aa82169f 100644 --- a/src/charon/encoding/payloads/configuration_attribute.c +++ b/src/charon/encoding/payloads/configuration_attribute.c @@ -27,6 +27,7 @@ #include <encoding/payloads/encodings.h> #include <library.h> +#include <daemon.h> typedef struct private_configuration_attribute_t private_configuration_attribute_t; @@ -50,7 +51,6 @@ struct private_configuration_attribute_t { * Length of the attribute. */ u_int16_t attribute_length; - /** * Attribute value as chunk. @@ -58,7 +58,7 @@ struct private_configuration_attribute_t { chunk_t attribute_value; }; -ENUM_BEGIN(configuration_attribute_type_name, INTERNAL_IP4_ADDRESS, INTERNAL_IP6_ADDRESS, +ENUM_BEGIN(configuration_attribute_type_names, INTERNAL_IP4_ADDRESS, INTERNAL_IP6_ADDRESS, "INTERNAL_IP4_ADDRESS", "INTERNAL_IP4_NETMASK", "INTERNAL_IP4_DNS", @@ -67,14 +67,14 @@ ENUM_BEGIN(configuration_attribute_type_name, INTERNAL_IP4_ADDRESS, INTERNAL_IP6 "INTERNAL_IP4_DHCP", "APPLICATION_VERSION", "INTERNAL_IP6_ADDRESS"); -ENUM_NEXT(configuration_attribute_type_name, INTERNAL_IP6_DNS, INTERNAL_IP6_SUBNET, INTERNAL_IP6_ADDRESS, +ENUM_NEXT(configuration_attribute_type_names, INTERNAL_IP6_DNS, INTERNAL_IP6_SUBNET, INTERNAL_IP6_ADDRESS, "INTERNAL_IP6_DNS", "INTERNAL_IP6_NBNS", "INTERNAL_IP6_DHCP", "INTERNAL_IP4_SUBNET", "SUPPORTED_ATTRIBUTES", "INTERNAL_IP6_SUBNET"); -ENUM_END(configuration_attribute_type_name, INTERNAL_IP6_SUBNET); +ENUM_END(configuration_attribute_type_names, INTERNAL_IP6_SUBNET); /** * Encoding rules to parse or generate a configuration attribute. @@ -111,6 +111,14 @@ encoding_rule_t configuration_attribute_encodings[] = { */ static status_t verify(private_configuration_attribute_t *this) { + bool failed = FALSE; + + if (this->attribute_length != this->attribute_value.len) + { + DBG1(DBG_ENC, "invalid attribute length"); + return FAILED; + } + switch (this->attribute_type) { case INTERNAL_IP4_ADDRESS: @@ -119,27 +127,54 @@ static status_t verify(private_configuration_attribute_t *this) case INTERNAL_IP4_NBNS: case INTERNAL_ADDRESS_EXPIRY: case INTERNAL_IP4_DHCP: - case APPLICATION_VERSION: + if (this->attribute_length != 0 && this->attribute_length != 4) + { + failed = TRUE; + } + break; + case INTERNAL_IP4_SUBNET: + if (this->attribute_length != 0 && this->attribute_length != 8) + { + failed = TRUE; + } + break; case INTERNAL_IP6_ADDRESS: + case INTERNAL_IP6_SUBNET: + if (this->attribute_length != 0 && this->attribute_length != 17) + { + failed = TRUE; + } + break; case INTERNAL_IP6_DNS: case INTERNAL_IP6_NBNS: case INTERNAL_IP6_DHCP: - case INTERNAL_IP4_SUBNET: + if (this->attribute_length != 0 && this->attribute_length != 16) + { + failed = TRUE; + } + break; case SUPPORTED_ATTRIBUTES: - case INTERNAL_IP6_SUBNET: - { - /* Attribute types are not checked in here */ + if (this->attribute_length % 2) + { + failed = TRUE; + } + break; + case APPLICATION_VERSION: + /* any length acceptable */ break; - } default: + DBG1(DBG_ENC, "unknown attribute type %N", + configuration_attribute_type_names, this->attribute_type); return FAILED; } - if (this->attribute_length != this->attribute_value.len) + if (failed) { + DBG1(DBG_ENC, "invalid attribute length %d for %N", + this->attribute_length, configuration_attribute_type_names, + this->attribute_type); return FAILED; } - return SUCCESS; } @@ -208,9 +243,8 @@ static chunk_t get_value (private_configuration_attribute_t *this) return this->attribute_value; } - /** - * Implementation of configuration_attribute_t.set_attribute_type. + * Implementation of configuration_attribute_t.set_type. */ static void set_attribute_type (private_configuration_attribute_t *this, u_int16_t type) { @@ -218,7 +252,7 @@ static void set_attribute_type (private_configuration_attribute_t *this, u_int16 } /** - * Implementation of configuration_attribute_t.get_attribute_type. + * Implementation of configuration_attribute_t.get_type. */ static u_int16_t get_attribute_type (private_configuration_attribute_t *this) { @@ -226,7 +260,7 @@ static u_int16_t get_attribute_type (private_configuration_attribute_t *this) } /** - * Implementation of configuration_attribute_t.get_attribute_length. + * Implementation of configuration_attribute_t.get_length. */ static u_int16_t get_attribute_length (private_configuration_attribute_t *this) { @@ -265,9 +299,9 @@ configuration_attribute_t *configuration_attribute_create() /* public functions */ this->public.set_value = (void (*) (configuration_attribute_t *,chunk_t)) set_value; this->public.get_value = (chunk_t (*) (configuration_attribute_t *)) get_value; - this->public.set_attribute_type = (void (*) (configuration_attribute_t *,u_int16_t type)) set_attribute_type; - this->public.get_attribute_type = (u_int16_t (*) (configuration_attribute_t *)) get_attribute_type; - this->public.get_attribute_length = (u_int16_t (*) (configuration_attribute_t *)) get_attribute_length; + this->public.set_type = (void (*) (configuration_attribute_t *,u_int16_t type)) set_attribute_type; + this->public.get_type = (u_int16_t (*) (configuration_attribute_t *)) get_attribute_type; + this->public.get_length = (u_int16_t (*) (configuration_attribute_t *)) get_attribute_length; this->public.destroy = (void (*) (configuration_attribute_t *)) destroy; /* set default values of the fields */ diff --git a/src/charon/encoding/payloads/configuration_attribute.h b/src/charon/encoding/payloads/configuration_attribute.h index 5a11d0a35..5c4f65b14 100644 --- a/src/charon/encoding/payloads/configuration_attribute.h +++ b/src/charon/encoding/payloads/configuration_attribute.h @@ -109,7 +109,7 @@ struct configuration_attribute_t { * @param this calling configuration_attribute_t object * @param type type to set (most significant bit is set to zero) */ - void (*set_attribute_type) (configuration_attribute_t *this, u_int16_t type); + void (*set_type) (configuration_attribute_t *this, u_int16_t type); /** * @brief get the type of the attribute. @@ -117,7 +117,7 @@ struct configuration_attribute_t { * @param this calling configuration_attribute_t object * @return type of the value */ - u_int16_t (*get_attribute_type) (configuration_attribute_t *this); + u_int16_t (*get_type) (configuration_attribute_t *this); /** * @brief get the length of an attribute. @@ -125,7 +125,7 @@ struct configuration_attribute_t { * @param this calling configuration_attribute_t object * @return type of the value */ - u_int16_t (*get_attribute_length) (configuration_attribute_t *this); + u_int16_t (*get_length) (configuration_attribute_t *this); /** * @brief Destroys an configuration_attribute_t object. diff --git a/src/charon/encoding/payloads/cp_payload.c b/src/charon/encoding/payloads/cp_payload.c index bd16abc22..380ed9681 100644 --- a/src/charon/encoding/payloads/cp_payload.c +++ b/src/charon/encoding/payloads/cp_payload.c @@ -204,9 +204,9 @@ static size_t get_length(private_cp_payload_t *this) /** * Implementation of cp_payload_t.create_configuration_attribute_iterator. */ -static iterator_t *create_configuration_attribute_iterator (private_cp_payload_t *this,bool forward) +static iterator_t *create_attribute_iterator (private_cp_payload_t *this) { - return this->attributes->create_iterator(this->attributes,forward); + return this->attributes->create_iterator(this->attributes, TRUE); } /** @@ -261,7 +261,7 @@ cp_payload_t *cp_payload_create() this->public.payload_interface.destroy = (void (*) (payload_t *))destroy; /* public functions */ - this->public.create_configuration_attribute_iterator = (iterator_t* (*) (cp_payload_t *,bool)) create_configuration_attribute_iterator; + this->public.create_attribute_iterator = (iterator_t* (*) (cp_payload_t *)) create_attribute_iterator; this->public.add_configuration_attribute = (void (*) (cp_payload_t *,configuration_attribute_t *)) add_configuration_attribute; this->public.set_config_type = (void (*) (cp_payload_t *, config_type_t)) set_config_type; this->public.get_config_type = (config_type_t (*) (cp_payload_t *)) get_config_type; diff --git a/src/charon/encoding/payloads/cp_payload.h b/src/charon/encoding/payloads/cp_payload.h index af36b48a3..27ff41005 100644 --- a/src/charon/encoding/payloads/cp_payload.h +++ b/src/charon/encoding/payloads/cp_payload.h @@ -77,23 +77,19 @@ struct cp_payload_t { /** * @brief Creates an iterator of stored configuration_attribute_t objects. * - * @warning The created iterator has to get destroyed by the caller! - * - * @warning When deleting an attribute using this iterator, - * the length of this configuration_attribute_t has to be refreshed - * by calling get_length()! + * When deleting an attribute using this iterator, the length of this + * configuration_attribute_t has to be refreshed by calling get_length()! * * @param this calling cp_payload_t object - * @param[in] forward iterator direction (TRUE: front to end) * @return created iterator_t object */ - iterator_t *(*create_configuration_attribute_iterator) (cp_payload_t *this, bool forward); + iterator_t *(*create_attribute_iterator) (cp_payload_t *this); /** * @brief Adds a configuration_attribute_t object to this object. * - * @warning The added configuration_attribute_t object is - * getting destroyed in destroy function of cp_payload_t. + * The added configuration_attribute_t object is getting destroyed in + * destroy function of cp_payload_t. * * @param this calling cp_payload_t object * @param attribute configuration_attribute_t object to add diff --git a/src/charon/encoding/payloads/notify_payload.c b/src/charon/encoding/payloads/notify_payload.c index 38ee0946d..a04901a90 100644 --- a/src/charon/encoding/payloads/notify_payload.c +++ b/src/charon/encoding/payloads/notify_payload.c @@ -209,6 +209,9 @@ static status_t verify(private_notify_payload_t *this) diffie_hellman_group_t dh_group; if (this->notification_data.len != 2) { + DBG1(DBG_ENC, "invalid notify data length for %N (%d)", + notify_type_names, this->notify_type, + this->notification_data.len); return FAILED; } dh_group = ntohs(*((u_int16_t*)this->notification_data.ptr)); @@ -403,7 +406,10 @@ static chunk_t get_notification_data(private_notify_payload_t *this) static status_t set_notification_data(private_notify_payload_t *this, chunk_t notification_data) { chunk_free(&this->notification_data); - this->notification_data = chunk_clone(notification_data); + if (notification_data.len > 0) + { + this->notification_data = chunk_clone(notification_data); + } compute_length(this); return SUCCESS; } diff --git a/src/charon/network/packet.c b/src/charon/network/packet.c index ca8b2a616..f2fa91569 100644 --- a/src/charon/network/packet.c +++ b/src/charon/network/packet.c @@ -58,10 +58,7 @@ struct private_packet_t { */ static void set_source(private_packet_t *this, host_t *source) { - if (this->source) - { - this->source->destroy(this->source); - } + DESTROY_IF(this->source); this->source = source; } @@ -70,10 +67,7 @@ static void set_source(private_packet_t *this, host_t *source) */ static void set_destination(private_packet_t *this, host_t *destination) { - if (this->destination) - { - this->destination->destroy(this->destination); - } + DESTROY_IF(this->destination); this->destination = destination; } diff --git a/src/charon/queues/jobs/acquire_job.c b/src/charon/queues/jobs/acquire_job.c index 4deadf3fe..b4ffb258d 100644 --- a/src/charon/queues/jobs/acquire_job.c +++ b/src/charon/queues/jobs/acquire_job.c @@ -57,8 +57,8 @@ static status_t execute(private_acquire_job_t *this) { ike_sa_t *ike_sa; - ike_sa = charon->ike_sa_manager->checkout_by_child(charon->ike_sa_manager, - this->reqid); + ike_sa = charon->ike_sa_manager->checkout_by_id(charon->ike_sa_manager, + this->reqid, TRUE); if (ike_sa == NULL) { DBG2(DBG_JOB, "CHILD_SA with reqid %d not found for acquiring", diff --git a/src/charon/queues/jobs/delete_child_sa_job.c b/src/charon/queues/jobs/delete_child_sa_job.c index 71ee3f00a..f694696b0 100644 --- a/src/charon/queues/jobs/delete_child_sa_job.c +++ b/src/charon/queues/jobs/delete_child_sa_job.c @@ -68,8 +68,8 @@ static status_t execute(private_delete_child_sa_job_t *this) { ike_sa_t *ike_sa; - ike_sa = charon->ike_sa_manager->checkout_by_child(charon->ike_sa_manager, - this->reqid); + ike_sa = charon->ike_sa_manager->checkout_by_id(charon->ike_sa_manager, + this->reqid, TRUE); if (ike_sa == NULL) { DBG1(DBG_JOB, "CHILD_SA with reqid %d not found for delete", diff --git a/src/charon/queues/jobs/delete_ike_sa_job.c b/src/charon/queues/jobs/delete_ike_sa_job.c index 9e8173c39..706155aa6 100644 --- a/src/charon/queues/jobs/delete_ike_sa_job.c +++ b/src/charon/queues/jobs/delete_ike_sa_job.c @@ -62,41 +62,38 @@ static status_t execute(private_delete_ike_sa_job_t *this) { ike_sa_t *ike_sa; - if (this->delete_if_established) + ike_sa = charon->ike_sa_manager->checkout(charon->ike_sa_manager, + this->ike_sa_id); + if (ike_sa) { - if (charon->ike_sa_manager->delete(charon->ike_sa_manager, - this->ike_sa_id) != SUCCESS) + if (this->delete_if_established) { - DBG2(DBG_JOB, "IKE SA didn't exist anymore"); - } - return DESTROY_ME; - } - else - { - ike_sa = charon->ike_sa_manager->checkout(charon->ike_sa_manager, this->ike_sa_id); - if (ike_sa == NULL) - { - /* hm, somebody was faster ;-) */ - return DESTROY_ME; + if (ike_sa->delete(ike_sa) == DESTROY_ME) + { + charon->ike_sa_manager->checkin_and_destroy( + charon->ike_sa_manager, ike_sa); + } + else + { + charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); + } } - - switch (ike_sa->get_state(ike_sa)) + else { - case IKE_ESTABLISHED: + /* destroy only if not ESTABLISHED */ + if (ike_sa->get_state(ike_sa) == IKE_ESTABLISHED) { - /* IKE_SA is established and so is not getting destroyed */ charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); - return DESTROY_ME; } - default: + else { - /* IKE_SA is half open and gets destroyed */ DBG1(DBG_JOB, "deleting half open IKE_SA after timeout"); - charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, ike_sa); - return DESTROY_ME; + charon->ike_sa_manager->checkin_and_destroy( + charon->ike_sa_manager, ike_sa); } } } + return DESTROY_ME; } /** diff --git a/src/charon/queues/jobs/incoming_packet_job.c b/src/charon/queues/jobs/incoming_packet_job.c index 18671610d..c4f211a04 100644 --- a/src/charon/queues/jobs/incoming_packet_job.c +++ b/src/charon/queues/jobs/incoming_packet_job.c @@ -64,6 +64,13 @@ static void send_notify_response(private_incoming_packet_job_t *this, packet_t *packet; ike_sa_id_t *ike_sa_id; + if (request->get_exchange_type(request) != IKE_SA_INIT) + { + /* TODO: Use transforms implementing the "NULL" algorithm, + we are unable to generate message otherwise */ + return; + } + ike_sa_id = request->get_ike_sa_id(request); ike_sa_id = ike_sa_id->clone(ike_sa_id); ike_sa_id->switch_initiator(ike_sa_id); @@ -80,8 +87,6 @@ static void send_notify_response(private_incoming_packet_job_t *this, ike_sa_id->destroy(ike_sa_id); notify = notify_payload_create_from_protocol_and_type(PROTO_NONE, type); response->add_payload(response, (payload_t *)notify); - /* generation may fail, as most messages need a crypter/signer. - * TODO: Use transforms implementing the "NULL" algorithm */ if (response->generate(response, NULL, NULL, &packet) != SUCCESS) { response->destroy(response); @@ -107,12 +112,12 @@ static status_t execute(private_incoming_packet_job_t *this) message = message_create_from_packet(this->packet->clone(this->packet)); src = message->get_source(message); dst = message->get_destination(message); - DBG1(DBG_NET, "received packet: from %#H to %#H", src, dst); status = message->parse_header(message); if (status != SUCCESS) { - DBG1(DBG_NET, "received message with invalid IKE header, ignored"); + DBG1(DBG_NET, "received message from %H with invalid IKE header, " + "ignored", src); message->destroy(message); return DESTROY_ME; } @@ -120,11 +125,12 @@ static status_t execute(private_incoming_packet_job_t *this) if ((message->get_major_version(message) != IKE_MAJOR_VERSION) || (message->get_minor_version(message) != IKE_MINOR_VERSION)) { - DBG1(DBG_NET, - "received a packet with IKE version %d.%d, not supported", - message->get_major_version(message), - message->get_minor_version(message)); - if ((message->get_exchange_type(message) == IKE_SA_INIT) && (message->get_request(message))) + DBG1(DBG_NET, "received message from %H with unsupported IKE " + "version %d.%d, ignored", src, message->get_major_version(message), + message->get_minor_version(message)); + + if (message->get_exchange_type(message) == IKE_SA_INIT && + message->get_request(message)) { send_notify_response(this, message, INVALID_MAJOR_VERSION); } @@ -138,19 +144,19 @@ static status_t execute(private_incoming_packet_job_t *this) ike_sa = charon->ike_sa_manager->checkout(charon->ike_sa_manager, ike_sa_id); if (ike_sa == NULL) { - DBG1(DBG_NET, "received packet for IKE_SA: %J, but no such IKE_SA", - ike_sa_id); + DBG1(DBG_NET, "received packet from %#H for IKE_SA: %J, but no such " + "IKE_SA", src, ike_sa_id); if (message->get_request(message)) { - /* TODO: send notify if we have NULL crypters, - * see todo in send_notify_response - send_notify_response(this, message, INVALID_IKE_SPI); */ + send_notify_response(this, message, INVALID_IKE_SPI); } ike_sa_id->destroy(ike_sa_id); message->destroy(message); return DESTROY_ME; } + DBG1(DBG_NET, "received packet: from %#H to %#H", src, dst); + status = ike_sa->process_message(ike_sa, message); if (status == DESTROY_ME) { diff --git a/src/charon/queues/jobs/initiate_job.c b/src/charon/queues/jobs/initiate_job.c index 8b943a3f1..af50663d6 100644 --- a/src/charon/queues/jobs/initiate_job.c +++ b/src/charon/queues/jobs/initiate_job.c @@ -45,11 +45,6 @@ struct private_initiate_job_t { connection_t *connection; /** - * host to connect to, use NULL to use connections one - */ - host_t *other; - - /** * associated policy to initiate */ policy_t *policy; @@ -70,19 +65,12 @@ static status_t execute(private_initiate_job_t *this) { ike_sa_t *ike_sa; - ike_sa = charon->ike_sa_manager->checkout_by_id(charon->ike_sa_manager, + ike_sa = charon->ike_sa_manager->checkout_by_peer(charon->ike_sa_manager, this->connection->get_my_host(this->connection), this->connection->get_other_host(this->connection), this->policy->get_my_id(this->policy), this->policy->get_other_id(this->policy)); - - if (this->other) - { - ike_sa->set_other_host(ike_sa, this->other->clone(this->other)); - } - - this->connection->get_ref(this->connection); - this->policy->get_ref(this->policy); + if (ike_sa->initiate(ike_sa, this->connection, this->policy) != SUCCESS) { DBG1(DBG_JOB, "initiation failed, going to delete IKE_SA"); @@ -101,15 +89,13 @@ static void destroy(private_initiate_job_t *this) { this->connection->destroy(this->connection); this->policy->destroy(this->policy); - DESTROY_IF(this->other); free(this); } /* * Described in header */ -initiate_job_t *initiate_job_create(connection_t *connection, host_t *other, - policy_t *policy) +initiate_job_t *initiate_job_create(connection_t *connection, policy_t *policy) { private_initiate_job_t *this = malloc_thing(private_initiate_job_t); @@ -121,7 +107,6 @@ initiate_job_t *initiate_job_create(connection_t *connection, host_t *other, /* private variables */ this->connection = connection; this->policy = policy; - this->other = other; return &this->public; } diff --git a/src/charon/queues/jobs/initiate_job.h b/src/charon/queues/jobs/initiate_job.h index 2fd0ced93..af1dd9ece 100644 --- a/src/charon/queues/jobs/initiate_job.h +++ b/src/charon/queues/jobs/initiate_job.h @@ -51,13 +51,11 @@ struct initiate_job_t { * @brief Creates a job of type INITIATE_IKE_SA. * * @param connection connection_t to initialize - * @param other another host to initiate to, NULL to use connections one * @param policy policy to set up * @return initiate_job_t object * * @ingroup jobs */ -initiate_job_t *initiate_job_create(connection_t *connection, host_t *other, - policy_t *policy); +initiate_job_t *initiate_job_create(connection_t *connection, policy_t *policy); #endif /*INITIATE_IKE_SA_JOB_H_*/ diff --git a/src/charon/queues/jobs/job.c b/src/charon/queues/jobs/job.c index d88843d7c..337558c2d 100644 --- a/src/charon/queues/jobs/job.c +++ b/src/charon/queues/jobs/job.c @@ -26,7 +26,7 @@ ENUM(job_type_names, INCOMING_PACKET, SEND_DPD, "INCOMING_PACKET", - "RETRANSMIT_REQUEST", + "RETRANSMIT", "INITIATE", "ROUTE", "ACQUIRE", diff --git a/src/charon/queues/jobs/job.h b/src/charon/queues/jobs/job.h index ae3fe7974..ae67a2bdc 100644 --- a/src/charon/queues/jobs/job.h +++ b/src/charon/queues/jobs/job.h @@ -45,9 +45,9 @@ enum job_type_t { /** * Retransmit an IKEv2-Message. * - * Job is implemented in class retransmit_request_job_t + * Job is implemented in class retransmit_job_t */ - RETRANSMIT_REQUEST, + RETRANSMIT, /** * Set up a CHILD_SA, optional with an IKE_SA. diff --git a/src/charon/queues/jobs/rekey_child_sa_job.c b/src/charon/queues/jobs/rekey_child_sa_job.c index 5944aa77f..3422b614d 100644 --- a/src/charon/queues/jobs/rekey_child_sa_job.c +++ b/src/charon/queues/jobs/rekey_child_sa_job.c @@ -67,8 +67,8 @@ static status_t execute(private_rekey_child_sa_job_t *this) { ike_sa_t *ike_sa; - ike_sa = charon->ike_sa_manager->checkout_by_child(charon->ike_sa_manager, - this->reqid); + ike_sa = charon->ike_sa_manager->checkout_by_id(charon->ike_sa_manager, + this->reqid, TRUE); if (ike_sa == NULL) { DBG2(DBG_JOB, "CHILD_SA with reqid %d not found for rekeying", diff --git a/src/charon/queues/jobs/rekey_ike_sa_job.c b/src/charon/queues/jobs/rekey_ike_sa_job.c index f0c4bef4f..2539d997e 100644 --- a/src/charon/queues/jobs/rekey_ike_sa_job.c +++ b/src/charon/queues/jobs/rekey_ike_sa_job.c @@ -61,7 +61,7 @@ static job_type_t get_type(private_rekey_ike_sa_job_t *this) static status_t execute(private_rekey_ike_sa_job_t *this) { ike_sa_t *ike_sa; - status_t status; + status_t status = SUCCESS; ike_sa = charon->ike_sa_manager->checkout(charon->ike_sa_manager, this->ike_sa_id); @@ -73,7 +73,7 @@ static status_t execute(private_rekey_ike_sa_job_t *this) if (this->reauth) { - status = ike_sa->reauth(ike_sa); + ike_sa->reestablish(ike_sa); } else { diff --git a/src/charon/queues/jobs/retransmit_request_job.c b/src/charon/queues/jobs/retransmit_job.c index 1dee6e521..5bfa20dfd 100644 --- a/src/charon/queues/jobs/retransmit_request_job.c +++ b/src/charon/queues/jobs/retransmit_job.c @@ -1,12 +1,12 @@ /** - * @file retransmit_request_job.c + * @file retransmit_job.c * - * @brief Implementation of retransmit_request_job_t. + * @brief Implementation of retransmit_job_t. * */ /* - * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005-2007 Martin Willi * Copyright (C) 2005 Jan Hutter * Hochschule fuer Technik Rapperswil * @@ -21,20 +21,20 @@ * for more details. */ -#include "retransmit_request_job.h" +#include "retransmit_job.h" #include <daemon.h> -typedef struct private_retransmit_request_job_t private_retransmit_request_job_t; +typedef struct private_retransmit_job_t private_retransmit_job_t; /** - * Private data of an retransmit_request_job_t Object. + * Private data of an retransmit_job_t Object. */ -struct private_retransmit_request_job_t { +struct private_retransmit_job_t { /** - * Public retransmit_request_job_t interface. + * Public retransmit_job_t interface. */ - retransmit_request_job_t public; + retransmit_job_t public; /** * Message ID of the request to resend. @@ -50,33 +50,32 @@ struct private_retransmit_request_job_t { /** * Implements job_t.get_type. */ -static job_type_t get_type(private_retransmit_request_job_t *this) +static job_type_t get_type(private_retransmit_job_t *this) { - return RETRANSMIT_REQUEST; + return RETRANSMIT; } /** * Implementation of job_t.execute. */ -static status_t execute(private_retransmit_request_job_t *this) +static status_t execute(private_retransmit_job_t *this) { ike_sa_t *ike_sa; - ike_sa = charon->ike_sa_manager->checkout(charon->ike_sa_manager, this->ike_sa_id); - if (ike_sa == NULL) + ike_sa = charon->ike_sa_manager->checkout(charon->ike_sa_manager, + this->ike_sa_id); + if (ike_sa) { - DBG2(DBG_JOB, "IKE SA could not be checked out. Already deleted?"); - return DESTROY_ME; - } - - if (ike_sa->retransmit_request(ike_sa, this->message_id) == DESTROY_ME) - { - /* retransmission hopeless, kill SA */ - charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, ike_sa); - } - else - { - charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); + if (ike_sa->retransmit(ike_sa, this->message_id) == DESTROY_ME) + { + /* retransmitted to many times, giving up */ + charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, + ike_sa); + } + else + { + charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); + } } return DESTROY_ME; } @@ -84,7 +83,7 @@ static status_t execute(private_retransmit_request_job_t *this) /** * Implements job_t.destroy. */ -static void destroy(private_retransmit_request_job_t *this) +static void destroy(private_retransmit_job_t *this) { this->ike_sa_id->destroy(this->ike_sa_id); free(this); @@ -93,9 +92,9 @@ static void destroy(private_retransmit_request_job_t *this) /* * Described in header. */ -retransmit_request_job_t *retransmit_request_job_create(u_int32_t message_id,ike_sa_id_t *ike_sa_id) +retransmit_job_t *retransmit_job_create(u_int32_t message_id,ike_sa_id_t *ike_sa_id) { - private_retransmit_request_job_t *this = malloc_thing(private_retransmit_request_job_t); + private_retransmit_job_t *this = malloc_thing(private_retransmit_job_t); /* interface functions */ this->public.job_interface.get_type = (job_type_t (*) (job_t *)) get_type; @@ -106,5 +105,5 @@ retransmit_request_job_t *retransmit_request_job_create(u_int32_t message_id,ike this->message_id = message_id; this->ike_sa_id = ike_sa_id->clone(ike_sa_id); - return &(this->public); + return &this->public; } diff --git a/src/charon/queues/jobs/retransmit_request_job.h b/src/charon/queues/jobs/retransmit_job.h index 1897af16d..19e29b909 100644 --- a/src/charon/queues/jobs/retransmit_request_job.h +++ b/src/charon/queues/jobs/retransmit_job.h @@ -1,12 +1,12 @@ /** - * @file retransmit_request_job.h + * @file retransmit_job.h * - * @brief Interface of retransmit_request_job_t. + * @brief Interface of retransmit_job_t. * */ /* - * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005-2007 Martin Willi * Copyright (C) 2005 Jan Hutter * Hochschule fuer Technik Rapperswil * @@ -21,28 +21,28 @@ * for more details. */ -#ifndef RESEND_MESSAGE_JOB_H_ -#define RESEND_MESSAGE_JOB_H_ +#ifndef RETRANSMIT_JOB_H_ +#define RETRANSMIT_JOB_H_ -typedef struct retransmit_request_job_t retransmit_request_job_t; +typedef struct retransmit_job_t retransmit_job_t; #include <library.h> #include <queues/jobs/job.h> #include <sa/ike_sa_id.h> /** - * @brief Class representing an RETRANSMIT_REQUEST Job. + * @brief Class representing an retransmit Job. * * This job is scheduled every time a request is sent over the * wire. If the response to the request is not received at schedule * time, the retransmission will be initiated. * * @b Constructors: - * - retransmit_request_job_create() + * - retransmit_job_create() * * @ingroup jobs */ -struct retransmit_request_job_t { +struct retransmit_job_t { /** * The job_t interface. */ @@ -50,15 +50,15 @@ struct retransmit_request_job_t { }; /** - * @brief Creates a job of type RETRANSMIT_REQUEST. + * @brief Creates a job of type retransmit. * * @param message_id message_id of the request to resend - * @param ike_sa_id identification of the ike_sa as ike_sa_id_t object (gets cloned) - * @return retransmit_request_job_t object + * @param ike_sa_id identification of the ike_sa as ike_sa_id_t + * @return retransmit_job_t object * * @ingroup jobs */ -retransmit_request_job_t *retransmit_request_job_create(u_int32_t message_id, - ike_sa_id_t *ike_sa_id); +retransmit_job_t *retransmit_job_create(u_int32_t message_id, + ike_sa_id_t *ike_sa_id); -#endif /* RESEND_MESSAGE_JOB_H_ */ +#endif /* RETRANSMIT_JOB_H_ */ diff --git a/src/charon/queues/jobs/route_job.c b/src/charon/queues/jobs/route_job.c index 5a128474b..bb6281dcc 100644 --- a/src/charon/queues/jobs/route_job.c +++ b/src/charon/queues/jobs/route_job.c @@ -69,7 +69,7 @@ static status_t execute(private_route_job_t *this) { ike_sa_t *ike_sa; - ike_sa = charon->ike_sa_manager->checkout_by_id(charon->ike_sa_manager, + ike_sa = charon->ike_sa_manager->checkout_by_peer(charon->ike_sa_manager, this->connection->get_my_host(this->connection), this->connection->get_other_host(this->connection), this->policy->get_my_id(this->policy), diff --git a/src/charon/sa/child_sa.c b/src/charon/sa/child_sa.c index 117471472..fa895eb7e 100644 --- a/src/charon/sa/child_sa.c +++ b/src/charon/sa/child_sa.c @@ -6,8 +6,8 @@ */ /* + * Copyright (C) 2005-2007 Martin Willi * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger - * Copyright (C) 2005-2006 Martin Willi * Copyright (C) 2005 Jan Hutter * Hochschule fuer Technik Rapperswil * @@ -69,11 +69,6 @@ struct private_child_sa_t { */ child_sa_t public; - /** - * Name of the policy used by this CHILD_SA - */ - char *name; - struct { /** address of peer */ host_t *addr; @@ -134,34 +129,9 @@ struct private_child_sa_t { time_t install_time; /** - * Lifetime before rekeying - */ - u_int32_t soft_lifetime; - - /** - * Lifetime before delete - */ - u_int32_t hard_lifetime; - - /** * state of the CHILD_SA */ child_sa_state_t state; - - /** - * transaction which is rekeying this CHILD_SA - */ - transaction_t *rekeying_transaction; - - /** - * Updown script - */ - char *script; - - /** - * Allow host access - */ - bool hostaccess; /** * Specifies if NAT traversal is used @@ -172,6 +142,11 @@ struct private_child_sa_t { * mode this SA uses, tunnel/transport */ mode_t mode; + + /** + * policy used to create this child + */ + policy_t *policy; }; /** @@ -179,22 +154,7 @@ struct private_child_sa_t { */ static char *get_name(private_child_sa_t *this) { - return this->name; -} - -/** - * Implementation of child_sa_t.set_name. - */ -static void set_name(private_child_sa_t *this, char* name) -{ - char buffer[64]; - - if (snprintf(buffer, sizeof(buffer), "%s[%d]", - name, this->reqid - REQID_START) > 0) - { - free(this->name); - this->name = strdup(buffer); - } + return this->policy->get_name(this->policy);; } /** @@ -234,14 +194,25 @@ static child_sa_state_t get_state(private_child_sa_t *this) } /** + * Implements child_sa_t.get_policy + */ +static policy_t* get_policy(private_child_sa_t *this) +{ + return this->policy; +} + +/** * Run the up/down script */ static void updown(private_child_sa_t *this, bool up) { sa_policy_t *policy; iterator_t *iterator; + char *script; - if (this->script == NULL) + script = this->policy->get_updown(this->policy); + + if (script == NULL) { return; } @@ -307,7 +278,7 @@ static void updown(private_child_sa_t *this, bool up) policy->my_ts->is_host(policy->my_ts, this->me.addr) ? "-host" : "-client", this->me.addr->get_family(this->me.addr) == AF_INET ? "" : "-ipv6", - this->name, + this->policy->get_name(this->policy), ifname, this->reqid, this->me.addr, @@ -322,8 +293,8 @@ static void updown(private_child_sa_t *this, bool up) other_client, other_client_mask, policy->other_ts->get_from_port(policy->other_ts), policy->other_ts->get_protocol(policy->other_ts), - this->hostaccess? "PLUTO_HOST_ACCESS='1' " : "", - this->script); + this->policy->get_hostaccess(this->policy) ? + "PLUTO_HOST_ACCESS='1' " : "", script); free(ifname); free(my_client); free(other_client); @@ -332,7 +303,7 @@ static void updown(private_child_sa_t *this, bool up) if (shell == NULL) { - DBG1(DBG_CHD, "could not execute updown script '%s'", this->script); + DBG1(DBG_CHD, "could not execute updown script '%s'", script); return; } @@ -447,7 +418,7 @@ static status_t alloc(private_child_sa_t *this, linked_list_t *proposals) static status_t install(private_child_sa_t *this, proposal_t *proposal, mode_t mode, prf_plus_t *prf_plus, bool mine) { - u_int32_t spi; + u_int32_t spi, soft, hard;; algorithm_t *enc_algo, *int_algo; algorithm_t enc_algo_none = {ENCR_UNDEFINED, 0}; algorithm_t int_algo_none = {AUTH_UNDEFINED, 0}; @@ -532,16 +503,15 @@ static status_t install(private_child_sa_t *this, proposal_t *proposal, natt = NULL; } + soft = this->policy->get_soft_lifetime(this->policy); + hard = this->policy->get_hard_lifetime(this->policy); /* send SA down to the kernel */ DBG2(DBG_CHD, " SPI 0x%.8x, src %H dst %H", ntohl(spi), src, dst); status = charon->kernel_interface->add_sa(charon->kernel_interface, - src, dst, - spi, this->protocol, - this->reqid, - mine ? this->soft_lifetime : 0, - this->hard_lifetime, - enc_algo, int_algo, + src, dst, spi, this->protocol, + this->reqid, mine ? soft : 0, + hard, enc_algo, int_algo, prf_plus, natt, mode, mine); this->encryption = *enc_algo; @@ -705,22 +675,6 @@ static linked_list_t *get_other_traffic_selectors(private_child_sa_t *this) } /** - * Implementation of child_sa_t.set_rekeying_transaction. - */ -static void set_rekeying_transaction(private_child_sa_t *this, transaction_t *transaction) -{ - this->rekeying_transaction = transaction; -} - -/** - * Implementation of child_sa_t.get_rekeying_transaction. - */ -static transaction_t* get_rekeying_transaction(private_child_sa_t *this) -{ - return this->rekeying_transaction; -} - -/** * Implementation of child_sa_t.get_use_time */ static status_t get_use_time(private_child_sa_t *this, bool inbound, time_t *use_time) @@ -769,7 +723,7 @@ static int print(FILE *stream, const struct printf_info *info, private_child_sa_t *this = *((private_child_sa_t**)(args[0])); iterator_t *iterator; sa_policy_t *policy; - u_int32_t now, rekeying; + u_int32_t now, rekeying, soft; u_int32_t use, use_in, use_fwd; status_t status; size_t written = 0; @@ -781,8 +735,9 @@ static int print(FILE *stream, const struct printf_info *info, now = (u_int32_t)time(NULL); - written += fprintf(stream, "%12s: %N, reqid: %d, %N", this->name, - child_sa_state_names, this->state, this->reqid, + written += fprintf(stream, "%12s{%d}: %N, %N", + this->policy->get_name(this->policy), this->reqid, + child_sa_state_names, this->state, mode_names, this->mode); if (this->state == CHILD_INSTALLED) @@ -793,7 +748,9 @@ static int print(FILE *stream, const struct printf_info *info, if (info->alt) { - written += fprintf(stream, "\n%12s: ", this->name); + written += fprintf(stream, "\n%12s{%d}: ", + this->policy->get_name(this->policy), + this->reqid); if (this->protocol == PROTO_ESP) { @@ -815,10 +772,11 @@ static int print(FILE *stream, const struct printf_info *info, } written += fprintf(stream, ", rekeying "); + soft = this->policy->get_soft_lifetime(this->policy); /* calculate rekey times */ - if (this->soft_lifetime) + if (soft) { - rekeying = this->soft_lifetime - (now - this->install_time); + rekeying = soft - (now - this->install_time); written += fprintf(stream, "in %ds", rekeying); } else @@ -830,8 +788,9 @@ static int print(FILE *stream, const struct printf_info *info, iterator = this->policies->create_iterator(this->policies, TRUE); while (iterator->iterate(iterator, (void**)&policy)) { - written += fprintf(stream, "\n%12s: %R===%R, last use: ", - this->name, policy->my_ts, policy->other_ts); + written += fprintf(stream, "\n%12s{%d}: %R===%R, last use: ", + this->policy->get_name(this->policy), this->reqid, + policy->my_ts, policy->other_ts); /* query time of last policy use */ @@ -1074,25 +1033,22 @@ static void destroy(private_child_sa_t *this) this->other.addr->destroy(this->other.addr); this->me.id->destroy(this->me.id); this->other.id->destroy(this->other.id); - free(this->name); - free(this->script); + this->policy->destroy(this->policy); free(this); } /* * Described in header. */ -child_sa_t * child_sa_create(u_int32_t rekey, host_t *me, host_t* other, +child_sa_t * child_sa_create(host_t *me, host_t* other, identification_t *my_id, identification_t *other_id, - u_int32_t soft_lifetime, u_int32_t hard_lifetime, - char *script, bool hostaccess, bool use_natt) + policy_t *policy, u_int32_t rekey, bool use_natt) { - static u_int32_t reqid = REQID_START; + static u_int32_t reqid = 0; private_child_sa_t *this = malloc_thing(private_child_sa_t); /* public functions */ this->public.get_name = (char*(*)(child_sa_t*))get_name; - this->public.set_name = (void(*)(child_sa_t*,char*))set_name; this->public.get_reqid = (u_int32_t(*)(child_sa_t*))get_reqid; this->public.get_spi = (u_int32_t(*)(child_sa_t*, bool))get_spi; this->public.get_protocol = (protocol_id_t(*)(child_sa_t*))get_protocol; @@ -1104,14 +1060,12 @@ child_sa_t * child_sa_create(u_int32_t rekey, host_t *me, host_t* other, this->public.get_my_traffic_selectors = (linked_list_t*(*)(child_sa_t*))get_my_traffic_selectors; this->public.get_other_traffic_selectors = (linked_list_t*(*)(child_sa_t*))get_other_traffic_selectors; this->public.get_use_time = (status_t (*)(child_sa_t*,bool,time_t*))get_use_time; - this->public.set_rekeying_transaction = (void (*)(child_sa_t*,transaction_t*))set_rekeying_transaction; - this->public.get_rekeying_transaction = (transaction_t* (*)(child_sa_t*))get_rekeying_transaction; this->public.set_state = (void(*)(child_sa_t*,child_sa_state_t))set_state; this->public.get_state = (child_sa_state_t(*)(child_sa_t*))get_state; + this->public.get_policy = (policy_t*(*)(child_sa_t*))get_policy; this->public.destroy = (void(*)(child_sa_t*))destroy; /* private data */ - this->name = strdup("(uninitialized)"); this->me.addr = me->clone(me); this->other.addr = other->clone(other); this->me.id = my_id->clone(my_id); @@ -1120,11 +1074,7 @@ child_sa_t * child_sa_create(u_int32_t rekey, host_t *me, host_t* other, this->other.spi = 0; this->alloc_ah_spi = 0; this->alloc_esp_spi = 0; - this->script = script ? strdup(script) : NULL; - this->hostaccess = hostaccess; this->use_natt = use_natt; - this->soft_lifetime = soft_lifetime; - this->hard_lifetime = hard_lifetime; this->state = CHILD_CREATED; /* reuse old reqid if we are rekeying an existing CHILD_SA */ this->reqid = rekey ? rekey : ++reqid; @@ -1137,7 +1087,8 @@ child_sa_t * child_sa_create(u_int32_t rekey, host_t *me, host_t* other, this->other_ts = linked_list_create(); this->protocol = PROTO_NONE; this->mode = MODE_TUNNEL; - this->rekeying_transaction = NULL; + this->policy = policy; + policy->get_ref(policy); return &this->public; } diff --git a/src/charon/sa/child_sa.h b/src/charon/sa/child_sa.h index 06362f35e..bd0e032da 100644 --- a/src/charon/sa/child_sa.h +++ b/src/charon/sa/child_sa.h @@ -6,8 +6,8 @@ */ /* + * Copyright (C) 2006-2007 Martin Willi * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger - * Copyright (C) 2006 Martin Willi * Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it @@ -32,7 +32,7 @@ typedef struct child_sa_t child_sa_t; #include <crypto/prf_plus.h> #include <encoding/payloads/proposal_substructure.h> #include <config/proposal.h> -#include <sa/transactions/transaction.h> +#include <config/policies/policy.h> /** * Where we should start with reqid enumeration @@ -109,18 +109,10 @@ struct child_sa_t { char* (*get_name) (child_sa_t *this); /** - * @brief Set the name of the policy this IKE_SA uses. - * - * @param this calling object - * @param name name, gets cloned - */ - void (*set_name) (child_sa_t *this, char* name); - - /** - * @brief Get the unique reqid of the CHILD SA. + * @brief Get the reqid of the CHILD SA. * - * Every CHILD_SA has a unique reqid, which is also - * stored down in the kernel. + * Every CHILD_SA has a reqid. The kernel uses this ID to + * identify it. * * @param this calling object * @return reqid of the CHILD SA @@ -259,23 +251,12 @@ struct child_sa_t { void (*set_state) (child_sa_t *this, child_sa_state_t state); /** - * @brief Set the transaction which rekeys this CHILD_SA. - * - * Since either end may initiate CHILD_SA rekeying, we must detect - * such situations to handle them cleanly. A rekeying transaction - * registers itself to the CHILD_SA, and checks later if another - * transaction is in progress of a rekey. + * @brief Get the policy used to set up this child sa. * * @param this calling object - */ - void (*set_rekeying_transaction) (child_sa_t *this, transaction_t *transaction); - - /** - * @brief Get the transaction which rekeys this CHILD_SA. - * - * @param this calling object - */ - transaction_t* (*get_rekeying_transaction) (child_sa_t *this); + * @return policy + */ + policy_t* (*get_policy) (child_sa_t *this); /** * @brief Destroys a child_sa. @@ -288,23 +269,19 @@ struct child_sa_t { /** * @brief Constructor to create a new child_sa_t. * - * @param rekey_reqid reqid of old CHILD_SA when rekeying, 0 otherwise * @param me own address * @param other remote address * @param my_id id of own peer * @param other_id id of remote peer - * @param soft_lifetime time before rekeying - * @param hard_lifteime time before delete - * @param script updown script to use when calling child_sa_t.script() - * @param hostaccess allow host access (needed by updown script) + * @param policy policy this CHILD_SA instantiates + * @param reqid reqid of old CHILD_SA when rekeying, 0 otherwise * @param use_natt TRUE if NAT traversal is used * @return child_sa_t object * * @ingroup sa */ -child_sa_t * child_sa_create(u_int32_t rekey_reqid, host_t *me, host_t *other, +child_sa_t * child_sa_create(host_t *me, host_t *other, identification_t *my_id, identification_t* other_id, - u_int32_t soft_lifetime, u_int32_t hard_lifetime, - char *script, bool hostaccess, bool use_natt); + policy_t *policy, u_int32_t reqid, bool use_natt); #endif /*CHILD_SA_H_*/ diff --git a/src/charon/sa/ike_sa.c b/src/charon/sa/ike_sa.c index e5a77edad..91ee4be0b 100644 --- a/src/charon/sa/ike_sa.c +++ b/src/charon/sa/ike_sa.c @@ -25,12 +25,14 @@ #include <sys/time.h> #include <string.h> #include <printf.h> +#include <sys/stat.h> #include "ike_sa.h" #include <library.h> #include <daemon.h> #include <utils/linked_list.h> +#include <utils/lexparser.h> #include <crypto/diffie_hellman.h> #include <crypto/prf_plus.h> #include <crypto/crypters/crypter.h> @@ -42,14 +44,19 @@ #include <encoding/payloads/transform_substructure.h> #include <encoding/payloads/transform_attribute.h> #include <encoding/payloads/ts_payload.h> -#include <sa/transactions/transaction.h> -#include <sa/transactions/ike_sa_init.h> -#include <sa/transactions/delete_ike_sa.h> -#include <sa/transactions/create_child_sa.h> -#include <sa/transactions/delete_child_sa.h> -#include <sa/transactions/dead_peer_detection.h> -#include <sa/transactions/rekey_ike_sa.h> -#include <queues/jobs/retransmit_request_job.h> +#include <sa/task_manager.h> +#include <sa/tasks/ike_init.h> +#include <sa/tasks/ike_natd.h> +#include <sa/tasks/ike_auth.h> +#include <sa/tasks/ike_config.h> +#include <sa/tasks/ike_cert.h> +#include <sa/tasks/ike_rekey.h> +#include <sa/tasks/ike_delete.h> +#include <sa/tasks/ike_dpd.h> +#include <sa/tasks/child_create.h> +#include <sa/tasks/child_delete.h> +#include <sa/tasks/child_rekey.h> +#include <queues/jobs/retransmit_job.h> #include <queues/jobs/delete_ike_sa_job.h> #include <queues/jobs/send_dpd_job.h> #include <queues/jobs/send_keepalive_job.h> @@ -57,6 +64,11 @@ #include <queues/jobs/route_job.h> #include <queues/jobs/initiate_job.h> + +#ifndef RESOLV_CONF +#define RESOLV_CONF "/etc/resolv.conf" +#endif + ENUM(ike_sa_state_names, IKE_CREATED, IKE_DELETING, "CREATED", "CONNECTING", @@ -83,14 +95,29 @@ struct private_ike_sa_t { ike_sa_id_t *ike_sa_id; /** + * unique numerical ID for this IKE_SA. + */ + u_int32_t unique_id; + + /** * Current state of the IKE_SA */ - ike_sa_state_t state; + ike_sa_state_t state; + + /** + * connection used to establish this IKE_SA. + */ + connection_t *connection; + + /** + * Peer and authentication information to establish IKE_SA. + */ + policy_t *policy; /** - * Name of the connection used by this IKE_SA + * Juggles tasks to process messages */ - char *name; + task_manager_t *task_manager; /** * Address of local host @@ -158,11 +185,6 @@ struct private_ike_sa_t { prf_t *auth_verify; /** - * NAT hasher. - */ - hasher_t *nat_hasher; - - /** * NAT status of local host. */ bool nat_here; @@ -173,14 +195,19 @@ struct private_ike_sa_t { bool nat_there; /** - * message ID for next outgoung request + * Virtual IP on local host, if any */ - u_int32_t message_id_out; - + host_t *my_virtual_ip; + /** - * will the IKE_SA be fully reauthenticated or rekeyed only? + * Virtual IP on remote host, if any */ - bool reauth; + host_t *other_virtual_ip; + + /** + * List of DNS servers installed by us + */ + linked_list_t *dns_servers; /** * Timestamps for this IKE_SA @@ -197,51 +224,12 @@ struct private_ike_sa_t { /** when IKE_SA gets deleted */ u_int32_t delete; } time; - - /** - * interval to send DPD liveness check - */ - time_t dpd_delay; - - /** - * number of retransmit sequences to go through before giving up (keyingtries) - */ - u_int32_t retrans_sequences; - - /** - * List of queued transactions to process - */ - linked_list_t *transaction_queue; - - /** - * Transaction currently initiated - * (only one supported yet, window size = 1) - */ - transaction_t *transaction_out; - - /** - * last transaction initiated by peer processed. - * (only one supported yet, window size = 1) - * Stored for retransmission. - */ - transaction_t *transaction_in; - - /** - * Next incoming transaction expected. Used to - * do multi transaction operations. - */ - transaction_t *transaction_in_next; - - /** - * Transaction which rekeys this IKE_SA, used do detect simultaneus rekeying - */ - transaction_t *rekeying_transaction; }; /** * get the time of the latest traffic processed by the kernel */ -static time_t get_kernel_time(private_ike_sa_t* this, bool inbound) +static time_t get_use_time(private_ike_sa_t* this, bool inbound) { iterator_t *iterator; child_sa_t *child_sa; @@ -257,49 +245,68 @@ static time_t get_kernel_time(private_ike_sa_t* this, bool inbound) } iterator->destroy(iterator); - return latest; + if (inbound) + { + return max(this->time.inbound, latest); + } + else + { + return max(this->time.outbound, latest); + } } /** - * get the time of the latest received traffice + * Implementation of ike_sa_t.get_unique_id */ -static time_t get_time_inbound(private_ike_sa_t *this) +static u_int32_t get_unique_id(private_ike_sa_t *this) { - return max(this->time.inbound, get_kernel_time(this, TRUE)); + return this->unique_id; } /** - * get the time of the latest sent traffic + * Implementation of ike_sa_t.get_name. */ -static time_t get_time_outbound(private_ike_sa_t *this) +static char *get_name(private_ike_sa_t *this) { - return max(this->time.outbound, get_kernel_time(this, FALSE)); + if (this->connection) + { + return this->connection->get_name(this->connection); + } + return "(unnamed)"; } /** - * Implementation of ike_sa_t.get_name. + * Implementation of ike_sa_t.get_connection */ -static char *get_name(private_ike_sa_t *this) +static connection_t* get_connection(private_ike_sa_t *this) { - return this->name; + return this->connection; } /** - * Implementation of ike_sa_t.set_name. + * Implementation of ike_sa_t.set_connection */ -static void set_name(private_ike_sa_t *this, char* name) +static void set_connection(private_ike_sa_t *this, connection_t *connection) { - free(this->name); - this->name = strdup(name); + this->connection = connection; + connection->get_ref(connection); } /** - * Implementation of ike_sa_t.apply_connection. + * Implementation of ike_sa_t.get_policy */ -static void apply_connection(private_ike_sa_t *this, connection_t *connection) +static policy_t *get_policy(private_ike_sa_t *this) { - this->dpd_delay = connection->get_dpd_delay(connection); - this->retrans_sequences = connection->get_retrans_seq(connection); + return this->policy; +} + +/** + * Implementation of ike_sa_t.set_policy + */ +static void set_policy(private_ike_sa_t *this, policy_t *policy) +{ + policy->get_ref(policy); + this->policy = policy; } /** @@ -341,35 +348,6 @@ static void set_other_host(private_ike_sa_t *this, host_t *other) */ static void update_hosts(private_ike_sa_t *this, host_t *me, host_t *other) { - /* - * Quoting RFC 4306: - * - * 2.11. Address and Port Agility - * - * IKE runs over UDP ports 500 and 4500, and implicitly sets up ESP and - * AH associations for the same IP addresses it runs over. The IP - * addresses and ports in the outer header are, however, not themselves - * cryptographically protected, and IKE is designed to work even through - * Network Address Translation (NAT) boxes. An implementation MUST - * accept incoming requests even if the source port is not 500 or 4500, - * and MUST respond to the address and port from which the request was - * received. It MUST specify the address and port at which the request - * was received as the source address and port in the response. IKE - * functions identically over IPv4 or IPv6. - * - * [...] - * - * There are cases where a NAT box decides to remove mappings that - * are still alive (for example, the keepalive interval is too long, - * or the NAT box is rebooted). To recover in these cases, hosts - * that are not behind a NAT SHOULD send all packets (including - * retransmission packets) to the IP address and port from the last - * valid authenticated packet from the other end (i.e., dynamically - * update the address). A host behind a NAT SHOULD NOT do this - * because it opens a DoS attack possibility. Any authenticated IKE - * packet or any authenticated UDP-encapsulated ESP packet can be - * used to detect that the IP address or the port has changed. - */ iterator_t *iterator = NULL; child_sa_t *child_sa = NULL; host_diff_t my_diff, other_diff; @@ -425,399 +403,149 @@ static void update_hosts(private_ike_sa_t *this, host_t *me, host_t *other) { child_sa->update_hosts(child_sa, this->my_host, this->other_host, my_diff, other_diff); - /* TODO: what to do if update fails? Delete CHILD_SA? */ } iterator->destroy(iterator); } /** - * called when the peer is not responding anymore + * Implementation of ike_sa_t.retransmit. */ -static void dpd_detected(private_ike_sa_t *this) +static status_t retransmit(private_ike_sa_t *this, u_int32_t message_id) { - connection_t *connection = NULL; - policy_t *policy; - linked_list_t *my_ts, *other_ts; - child_sa_t* child_sa; - dpd_action_t action; - job_t *job; - - DBG2(DBG_IKE, "dead peer detected, handling CHILD_SAs dpd action"); - - /* check for childrens with dpdaction = hold */ - while(this->child_sas->remove_first(this->child_sas, - (void**)&child_sa) == SUCCESS) + this->time.outbound = time(NULL); + if (this->task_manager->retransmit(this->task_manager, message_id) != SUCCESS) { - /* get the policy which belongs to this CHILD */ - my_ts = child_sa->get_my_traffic_selectors(child_sa); - other_ts = child_sa->get_other_traffic_selectors(child_sa); - policy = charon->policies->get_policy(charon->policies, - this->my_id, this->other_id, - my_ts, other_ts, - this->my_host, this->other_host); - if (policy == NULL) - { - DBG1(DBG_IKE, "no policy for CHILD to handle DPD"); - continue; - } + connection_t *connection = NULL; + policy_t *policy; + linked_list_t *my_ts, *other_ts; + child_sa_t* child_sa; + dpd_action_t action; + job_t *job; - action = policy->get_dpd_action(policy); - /* get a connection for further actions */ - if (connection == NULL && - (action == DPD_ROUTE || action == DPD_RESTART)) + DBG2(DBG_IKE, "dead peer detected, handling CHILD_SAs dpd action"); + + /* check for childrens with dpdaction = hold */ + while(this->child_sas->remove_first(this->child_sas, + (void**)&child_sa) == SUCCESS) { - connection = charon->connections->get_connection_by_hosts( - charon->connections, - this->my_host, this->other_host); - if (connection == NULL) + /* get the policy which belongs to this CHILD */ + my_ts = child_sa->get_my_traffic_selectors(child_sa); + other_ts = child_sa->get_other_traffic_selectors(child_sa); + policy = charon->policies->get_policy(charon->policies, + this->my_id, this->other_id, + my_ts, other_ts, + this->my_host, this->other_host); + if (policy == NULL) { - SIG(IKE_UP_FAILED, "no connection found to handle DPD"); - break; + DBG1(DBG_IKE, "no policy for CHILD to handle DPD"); + continue; + } + + action = policy->get_dpd_action(policy); + /* get a connection for further actions */ + if (connection == NULL && + (action == DPD_ROUTE || action == DPD_RESTART)) + { + connection = charon->connections->get_connection_by_hosts( + charon->connections, + this->my_host, this->other_host); + if (connection == NULL) + { + SIG(IKE_UP_FAILED, "no connection found to handle DPD"); + break; + } } + + DBG1(DBG_IKE, "dpd action for %s is %N", + policy->get_name(policy), dpd_action_names, action); + + switch (action) + { + case DPD_ROUTE: + connection->get_ref(connection); + job = (job_t*)route_job_create(connection, policy, TRUE); + charon->job_queue->add(charon->job_queue, job); + break; + case DPD_RESTART: + connection->get_ref(connection); + job = (job_t*)initiate_job_create(connection, policy); + charon->job_queue->add(charon->job_queue, job); + break; + default: + policy->destroy(policy); + break; + } + child_sa->destroy(child_sa); } - DBG1(DBG_IKE, "dpd action for %s is %N", - policy->get_name(policy), dpd_action_names, action); - - switch (action) + /* send a proper signal to brief interested bus listeners */ + switch (this->state) { - case DPD_ROUTE: - connection->get_ref(connection); - job = (job_t*)route_job_create(connection, policy, TRUE); - charon->job_queue->add(charon->job_queue, job); + case IKE_CONNECTING: + SIG(IKE_UP_FAILED, "establishing IKE_SA failed, peer not responding"); break; - case DPD_RESTART: - connection->get_ref(connection); - job = (job_t*)initiate_job_create(connection, NULL, policy); - charon->job_queue->add(charon->job_queue, job); + case IKE_REKEYING: + SIG(IKE_REKEY_FAILED, "rekeying IKE_SA failed, peer not responding"); + break; + case IKE_DELETING: + SIG(IKE_DOWN_FAILED, "proper IKE_SA delete failed, peer not responding"); break; default: - policy->destroy(policy); break; } - child_sa->destroy(child_sa); - } - - /* send a proper signal to brief interested bus listeners */ - switch (this->state) - { - case IKE_CONNECTING: - SIG(IKE_UP_FAILED, "establishing IKE_SA failed, peer not responding"); - break; - case IKE_REKEYING: - SIG(IKE_REKEY_FAILED, "rekeying IKE_SA failed, peer not responding"); - break; - case IKE_DELETING: - SIG(IKE_DOWN_FAILED, "proper IKE_SA delete failed, peer not responding"); - break; - default: - break; - } - - DESTROY_IF(connection); -} - -/** - * send a request and schedule retransmission - */ -static status_t transmit_request(private_ike_sa_t *this) -{ - message_t *request; - packet_t *packet; - status_t status; - retransmit_request_job_t *job; - u_int32_t transmitted; - u_int32_t timeout; - transaction_t *transaction = this->transaction_out; - u_int32_t message_id; - - transmitted = transaction->requested(transaction); - timeout = charon->configuration->get_retransmit_timeout(charon->configuration, - transmitted, - this->retrans_sequences); - if (timeout == 0) - { - DBG1(DBG_IKE, "giving up after %d retransmits, deleting IKE_SA", - transmitted - 1); - dpd_detected(this); + + DESTROY_IF(connection); return DESTROY_ME; } - - status = transaction->get_request(transaction, &request); - if (status != SUCCESS) - { - /* generating request failed */ - return status; - } - message_id = transaction->get_message_id(transaction); - /* if we retransmit, the request is already generated */ - if (transmitted == 0) - { - status = request->generate(request, this->crypter_out, this->signer_out, &packet); - if (status != SUCCESS) - { - DBG1(DBG_IKE, "request generation failed. transaction discarded"); - return FAILED; - } - } - else - { - DBG1(DBG_IKE, "sending retransmit %d for %N request with messageID %d", - transmitted, exchange_type_names, request->get_exchange_type(request), - message_id); - packet = request->get_packet(request); - } - /* finally send */ - charon->send_queue->add(charon->send_queue, packet); - this->time.outbound = time(NULL); - - /* schedule retransmission job */ - job = retransmit_request_job_create(message_id, this->ike_sa_id); - charon->event_queue->add_relative(charon->event_queue, (job_t*)job, timeout); return SUCCESS; } /** - * Implementation of ike_sa.retransmit_request. - */ -static status_t retransmit_request(private_ike_sa_t *this, u_int32_t message_id) -{ - if (this->transaction_out == NULL || - this->transaction_out->get_message_id(this->transaction_out) != message_id) - { - /* no retransmit necessary, transaction did already complete */ - return SUCCESS; - } - return transmit_request(this); -} - -/** - * Check for transactions in the queue and initiate the first transaction found. + * Implementation of ike_sa_t.generate */ -static status_t process_transaction_queue(private_ike_sa_t *this) +static status_t generate_message(private_ike_sa_t *this, message_t *message, + packet_t **packet) { - if (this->transaction_out) - { - /* already a transaction in progress */ - return SUCCESS; - } - - while (TRUE) - { - if (this->transaction_queue->remove_first(this->transaction_queue, - (void**)&this->transaction_out) != SUCCESS) - { - /* transaction queue empty */ - return SUCCESS; - } - switch (transmit_request(this)) - { - case SUCCESS: - return SUCCESS; - case DESTROY_ME: - /* critical, IKE_SA unusable, destroy immediately */ - return DESTROY_ME; - default: - /* discard transaction, process next one */ - this->transaction_out->destroy(this->transaction_out); - this->transaction_out = NULL; - /* handle next transaction */ - continue; - } - } -} - -/** - * Queue a new transaction and execute the next outstanding transaction - */ -static status_t queue_transaction(private_ike_sa_t *this, transaction_t *transaction, bool prefer) -{ - /* inject next transaction */ - if (transaction) - { - if (prefer) - { - this->transaction_queue->insert_first(this->transaction_queue, transaction); - } - else - { - this->transaction_queue->insert_last(this->transaction_queue, transaction); - } - } - /* process a transaction */ - return process_transaction_queue(this); -} - -/** - * process an incoming request. - */ -static status_t process_request(private_ike_sa_t *this, message_t *request) -{ - transaction_t *last, *current = NULL; - message_t *response; - packet_t *packet; - u_int32_t request_mid; - status_t status; - - request_mid = request->get_message_id(request); - last = this->transaction_in; - - /* check if message ID is correct */ - if (last) - { - u_int32_t last_mid = last->get_message_id(last); - - if (last_mid == request_mid) - { - /* retransmit detected */ - DBG1(DBG_IKE, "received retransmitted request for message " - "ID %d, retransmitting response", request_mid); - last->get_response(last, request, &response, &this->transaction_in_next); - packet = response->get_packet(response); - charon->send_queue->add(charon->send_queue, packet); - this->time.outbound = time(NULL); - return SUCCESS; - } - - if (last_mid > request_mid) - { - /* something seriously wrong here, message id may not decrease */ - DBG1(DBG_IKE, "received request with message ID %d, " - "excepted %d, ingored", request_mid, last_mid + 1); - return FAILED; - } - /* we allow jumps in message IDs, as long as they are incremental */ - if (last_mid + 1 < request_mid) - { - DBG1(DBG_IKE, "received request with message ID %d, excepted %d", - request_mid, last_mid + 1); - } - } - else - { - if (request_mid != 0) - { - /* warn, but allow it */ - DBG1(DBG_IKE, "first received request has message ID %d, " - "excepted 0", request_mid); - } - } - - /* check if we already have a pre-created transaction for this request */ - if (this->transaction_in_next) - { - current = this->transaction_in_next; - this->transaction_in_next = NULL; - } - else - { - current = transaction_create(&this->public, request); - if (current == NULL) - { - DBG1(DBG_IKE, "no idea how to handle received message (exchange" - " type %d), ignored", request->get_exchange_type(request)); - return FAILED; - } - } - - /* send message. get_request() always gives a valid response */ - status = current->get_response(current, request, &response, &this->transaction_in_next); - if (response->generate(response, this->crypter_out, this->signer_out, &packet) != SUCCESS) - { - DBG1(DBG_IKE, "response generation failed, discarding transaction"); - current->destroy(current); - return FAILED; - } - - charon->send_queue->add(charon->send_queue, packet); this->time.outbound = time(NULL); - /* act depending on transaction result */ - switch (status) - { - case DESTROY_ME: - /* transactions says we should destroy the IKE_SA, so do it */ - current->destroy(current); - return DESTROY_ME; - default: - /* store for retransmission, destroy old transaction */ - this->transaction_in = current; - if (last) - { - last->destroy(last); - } - return SUCCESS; - } -} - -/** - * process an incoming response - */ -static status_t process_response(private_ike_sa_t *this, message_t *response) -{ - transaction_t *current, *new = NULL; - - current = this->transaction_out; - /* check if message ID is that of our currently active transaction */ - if (current == NULL || - current->get_message_id(current) != response->get_message_id(response)) - { - DBG1(DBG_IKE, "received response with message ID %d " - "not requested, ignored", response->get_message_id(response)); - return FAILED; - } - - switch (current->conclude(current, response, &new)) - { - case DESTROY_ME: - /* state requested to destroy IKE_SA */ - return DESTROY_ME; - default: - /* discard transaction, process next one */ - break; - } - /* transaction comleted, remove */ - current->destroy(current); - this->transaction_out = NULL; - - /* queue new transaction */ - return queue_transaction(this, new, TRUE); + message->set_ike_sa_id(message, this->ike_sa_id); + message->set_destination(message, this->other_host->clone(this->other_host)); + message->set_source(message, this->my_host->clone(this->my_host)); + return message->generate(message, this->crypter_out, this->signer_out, packet); } /** * send a notify back to the sender */ -static void send_notify_response(private_ike_sa_t *this, - message_t *request, +static void send_notify_response(private_ike_sa_t *this, message_t *request, notify_type_t type) { - notify_payload_t *notify; message_t *response; - host_t *src, *dst; packet_t *packet; response = message_create(); - dst = request->get_source(request); - src = request->get_destination(request); - response->set_source(response, src->clone(src)); - response->set_destination(response, dst->clone(dst)); response->set_exchange_type(response, request->get_exchange_type(request)); response->set_request(response, FALSE); response->set_message_id(response, request->get_message_id(request)); - response->set_ike_sa_id(response, this->ike_sa_id); - notify = notify_payload_create_from_protocol_and_type(PROTO_NONE, type); - response->add_payload(response, (payload_t *)notify); - if (response->generate(response, this->crypter_out, this->signer_out, &packet) != SUCCESS) + response->add_notify(response, FALSE, type, chunk_empty); + if (this->my_host->is_anyaddr(this->my_host)) { - response->destroy(response); - return; + this->my_host->destroy(this->my_host); + this->my_host = request->get_destination(request); + this->my_host = this->my_host->clone(this->my_host); + } + if (this->other_host->is_anyaddr(this->other_host)) + { + this->other_host->destroy(this->other_host); + this->other_host = request->get_source(request); + this->other_host = this->other_host->clone(this->other_host); + } + if (generate_message(this, response, &packet) == SUCCESS) + { + charon->send_queue->add(charon->send_queue, packet); } - charon->send_queue->add(charon->send_queue, packet); - this->time.outbound = time(NULL); response->destroy(response); - return; } - /** * Implementation of ike_sa_t.process_message. */ @@ -875,27 +603,66 @@ static status_t process_message(private_ike_sa_t *this, message_t *message) exchange_type_names, message->get_exchange_type(message), message->get_request(message) ? "request" : "response", message->get_message_id(message)); + return status; } else { + host_t *me, *other; + + me = message->get_destination(message); + other = message->get_source(message); + + /* if this IKE_SA is virgin, we check for a connection */ + if (this->connection == NULL) + { + this->connection = charon->connections->get_connection_by_hosts( + charon->connections, me, other); + if (this->connection == NULL) + { + /* no connection found for these hosts, destroy */ + send_notify_response(this, message, NO_PROPOSAL_CHOSEN); + return DESTROY_ME; + } + } + /* check if message is trustworthy, and update connection information */ if (this->state == IKE_CREATED || message->get_exchange_type(message) != IKE_SA_INIT) { - update_hosts(this, message->get_destination(message), - message->get_source(message)); + update_hosts(this, me, other); this->time.inbound = time(NULL); } - if (is_request) - { - status = process_request(this, message); - } - else - { - status = process_response(this, message); - } + return this->task_manager->process_message(this->task_manager, message); + } +} + +/** + * apply the connection/policy information to this IKE_SA + */ +static void apply_config(private_ike_sa_t *this, + connection_t *connection, policy_t *policy) +{ + host_t *me, *other; + identification_t *my_id, *other_id; + + if (this->connection == NULL && this->policy == NULL) + { + this->connection = connection; + connection->get_ref(connection); + this->policy = policy; + policy->get_ref(policy); + + me = connection->get_my_host(connection); + other = connection->get_other_host(connection); + my_id = policy->get_my_id(policy); + other_id = policy->get_other_id(policy); + set_my_host(this, me->clone(me)); + set_other_host(this, other->clone(other)); + DESTROY_IF(this->my_id); + DESTROY_IF(this->other_id); + this->my_id = my_id->clone(my_id); + this->other_id = other_id->clone(other_id); } - return status; } /** @@ -904,77 +671,29 @@ static status_t process_message(private_ike_sa_t *this, message_t *message) static status_t initiate(private_ike_sa_t *this, connection_t *connection, policy_t *policy) { - switch (this->state) + task_t *task; + + if (this->state == IKE_CREATED) { - case IKE_CREATED: - { - /* in state CREATED, we must do the ike_sa_init - * and ike_auth transactions. Along with these, - * a CHILD_SA with the supplied policy is set up. - */ - ike_sa_init_t *ike_sa_init; - - DBG2(DBG_IKE, "initiating new IKE_SA for CHILD_SA"); - if (this->my_host->is_anyaddr(this->my_host)) - { - this->my_host->destroy(this->my_host); - this->my_host = connection->get_my_host(connection); - this->my_host = this->my_host->clone(this->my_host); - } - if (this->other_host->is_anyaddr(this->other_host)) - { - this->other_host->destroy(this->other_host); - this->other_host = connection->get_other_host(connection); - this->other_host = this->other_host->clone(this->other_host); - } - if (this->other_host->is_anyaddr(this->other_host)) - { - SIG(IKE_UP_START, "establishing new IKE_SA for CHILD_SA"); - SIG(IKE_UP_FAILED, "can not initiate a connection to %%any, aborting"); - policy->destroy(policy); - connection->destroy(connection); - return DESTROY_ME; - } - - this->retrans_sequences = connection->get_retrans_seq(connection); - this->dpd_delay = connection->get_dpd_delay(connection); - - this->message_id_out = 1; - ike_sa_init = ike_sa_init_create(&this->public); - ike_sa_init->set_config(ike_sa_init, connection, policy); - return queue_transaction(this, (transaction_t*)ike_sa_init, TRUE); - } - case IKE_DELETING: - case IKE_REKEYING: - { - /* if we are in DELETING/REKEYING, we deny set up of a policy. - * TODO: would it make sense to queue the transaction and adopt - * all transactions to the new IKE_SA? */ - SIG(IKE_UP_START, "creating CHILD_SA in existing IKE_SA"); - SIG(IKE_UP_FAILED, "creating CHILD_SA discarded, as IKE_SA is in state %N", - ike_sa_state_names, this->state); - policy->destroy(policy); - connection->destroy(connection); - return FAILED; - } - case IKE_CONNECTING: - case IKE_ESTABLISHED: - { - /* if we are ESTABLISHED or CONNECTING, we queue the - * transaction to create the CHILD_SA. It gets processed - * when the IKE_SA is ready to do so. We don't need the - * connection, as the IKE_SA is already established/establishing. - */ - create_child_sa_t *create_child; - - DBG1(DBG_IKE, "creating CHILD_SA in existing IKE_SA"); - connection->destroy(connection); - create_child = create_child_sa_create(&this->public); - create_child->set_policy(create_child, policy); - return queue_transaction(this, (transaction_t*)create_child, FALSE); - } - } - return FAILED; + /* if we aren't established/establishing, do so */ + apply_config(this, connection, policy); + + task = (task_t*)ike_init_create(&this->public, TRUE, NULL); + this->task_manager->queue_task(this->task_manager, task); + task = (task_t*)ike_natd_create(&this->public, TRUE); + this->task_manager->queue_task(this->task_manager, task); + task = (task_t*)ike_cert_create(&this->public, TRUE); + this->task_manager->queue_task(this->task_manager, task); + task = (task_t*)ike_config_create(&this->public, policy); + this->task_manager->queue_task(this->task_manager, task); + task = (task_t*)ike_auth_create(&this->public, TRUE); + this->task_manager->queue_task(this->task_manager, task); + } + + task = (task_t*)child_create_create(&this->public, policy); + this->task_manager->queue_task(this->task_manager, task); + + return this->task_manager->initiate(this->task_manager); } /** @@ -982,11 +701,11 @@ static status_t initiate(private_ike_sa_t *this, */ static status_t acquire(private_ike_sa_t *this, u_int32_t reqid) { - connection_t *connection; policy_t *policy; iterator_t *iterator; child_sa_t *current, *child_sa = NULL; - linked_list_t *my_ts, *other_ts; + task_t *task; + child_create_t *child_create; if (this->state == IKE_DELETING) { @@ -1014,66 +733,28 @@ static status_t acquire(private_ike_sa_t *this, u_int32_t reqid) "CHILD_SA not found", reqid); return FAILED; } - my_ts = child_sa->get_my_traffic_selectors(child_sa); - other_ts = child_sa->get_other_traffic_selectors(child_sa); - policy = charon->policies->get_policy(charon->policies, - this->my_id, this->other_id, - my_ts, other_ts, - this->my_host, this->other_host); - if (policy == NULL) - { - SIG(CHILD_UP_START, "acquiring CHILD_SA with reqid %d", reqid); - SIG(CHILD_UP_FAILED, "acquiring CHILD_SA (reqid %d) failed: " - "no policy found", reqid); - return FAILED; - } + policy = child_sa->get_policy(child_sa); - switch (this->state) + if (this->state == IKE_CREATED) { - case IKE_CREATED: - { - ike_sa_init_t *ike_sa_init; - - connection = charon->connections->get_connection_by_hosts( - charon->connections, this->my_host, this->other_host); - - if (connection == NULL) - { - SIG(CHILD_UP_START, "acquiring CHILD_SA with reqid %d", reqid); - SIG(CHILD_UP_FAILED, "acquiring CHILD_SA (reqid %d) failed: " - "no connection found to establsih IKE_SA", reqid); - policy->destroy(policy); - return FAILED; - } - - DBG1(DBG_IKE, "establishing IKE_SA to acquire CHILD_SA " - "with reqid %d", reqid); - - this->message_id_out = 1; - ike_sa_init = ike_sa_init_create(&this->public); - ike_sa_init->set_config(ike_sa_init, connection, policy); - /* reuse existing reqid */ - ike_sa_init->set_reqid(ike_sa_init, reqid); - return queue_transaction(this, (transaction_t*)ike_sa_init, TRUE); - } - case IKE_CONNECTING: - case IKE_ESTABLISHED: - { - create_child_sa_t *create_child; - - DBG1(DBG_CHD, "acquiring CHILD_SA with reqid %d", reqid); - - create_child = create_child_sa_create(&this->public); - create_child->set_policy(create_child, policy); - /* reuse existing reqid */ - create_child->set_reqid(create_child, reqid); - return queue_transaction(this, (transaction_t*)create_child, FALSE); - } - default: - break; + task = (task_t*)ike_init_create(&this->public, TRUE, NULL); + this->task_manager->queue_task(this->task_manager, task); + task = (task_t*)ike_natd_create(&this->public, TRUE); + this->task_manager->queue_task(this->task_manager, task); + task = (task_t*)ike_cert_create(&this->public, TRUE); + this->task_manager->queue_task(this->task_manager, task); + task = (task_t*)ike_config_create(&this->public, policy); + this->task_manager->queue_task(this->task_manager, task); + task = (task_t*)ike_auth_create(&this->public, TRUE); + this->task_manager->queue_task(this->task_manager, task); } - return FAILED; + + child_create = child_create_create(&this->public, policy); + child_create->use_reqid(child_create, reqid); + this->task_manager->queue_task(this->task_manager, (task_t*)child_create); + + return this->task_manager->initiate(this->task_manager); } /** @@ -1148,55 +829,25 @@ static status_t route(private_ike_sa_t *this, connection_t *connection, policy_t switch (this->state) { + case IKE_DELETING: + case IKE_REKEYING: + SIG(CHILD_ROUTE_FAILED, + "unable to route CHILD_SA, as its IKE_SA gets deleted"); + return FAILED; case IKE_CREATED: - case IKE_CONNECTING: - /* we update IKE_SA information as good as possible, - * this allows us to set up the SA later when an acquire comes in. */ - if (this->my_id->get_type(this->my_id) == ID_ANY) - { - this->my_id->destroy(this->my_id); - this->my_id = policy->get_my_id(policy); - this->my_id = this->my_id->clone(this->my_id); - } - if (this->other_id->get_type(this->other_id) == ID_ANY) - { - this->other_id->destroy(this->other_id); - this->other_id = policy->get_other_id(policy); - this->other_id = this->other_id->clone(this->other_id); - } - if (this->my_host->is_anyaddr(this->my_host)) - { - this->my_host->destroy(this->my_host); - this->my_host = connection->get_my_host(connection); - this->my_host = this->my_host->clone(this->my_host); - } - if (this->other_host->is_anyaddr(this->other_host)) - { - this->other_host->destroy(this->other_host); - this->other_host = connection->get_other_host(connection); - this->other_host = this->other_host->clone(this->other_host); - } - set_name(this, connection->get_name(connection)); - this->retrans_sequences = connection->get_retrans_seq(connection); - this->dpd_delay = connection->get_dpd_delay(connection); + /* apply connection information, we need it to acquire */ + apply_config(this, connection, policy); break; + case IKE_CONNECTING: case IKE_ESTABLISHED: - case IKE_REKEYING: - /* nothing to do. We allow it for rekeying, as it will be - * adopted by the new IKE_SA */ + default: break; - case IKE_DELETING: - /* TODO: hanlde this case, create a new IKE_SA and route CHILD_SA */ - SIG(CHILD_ROUTE_FAILED, "unable to route CHILD_SA, as its IKE_SA gets deleted"); - return FAILED; } - child_sa = child_sa_create(0, this->my_host, this->other_host, - this->my_id, this->other_id, - 0, 0, - NULL, policy->get_hostaccess(policy), - FALSE); - child_sa->set_name(child_sa, policy->get_name(policy)); + /* install kernel policies */ + child_sa = child_sa_create(this->my_host, this->other_host, + this->my_id, this->other_id, policy, FALSE, 0); + my_ts = policy->get_my_traffic_selectors(policy, this->my_host); other_ts = policy->get_other_traffic_selectors(policy, this->other_host); status = child_sa->add_policies(child_sa, my_ts, other_ts, @@ -1269,40 +920,45 @@ static status_t unroute(private_ike_sa_t *this, policy_t *policy) static status_t send_dpd(private_ike_sa_t *this) { send_dpd_job_t *job; - time_t diff; + time_t diff, delay; + + delay = this->connection->get_dpd_delay(this->connection); - if (this->dpd_delay == 0) + if (delay == 0) { /* DPD disabled */ return SUCCESS; } - if (this->transaction_out) + if (this->task_manager->busy(this->task_manager)) { - /* there is a transaction in progress. Come back later */ + /* an exchange is in the air, no need to start a DPD check */ diff = 0; } else { /* check if there was any inbound traffic */ time_t last_in, now; - last_in = get_time_inbound(this); + last_in = get_use_time(this, TRUE); now = time(NULL); diff = now - last_in; - if (diff >= this->dpd_delay) + if (diff >= delay) { /* to long ago, initiate dead peer detection */ - dead_peer_detection_t *dpd; - DBG1(DBG_IKE, "sending DPD request"); - dpd = dead_peer_detection_create(&this->public); - queue_transaction(this, (transaction_t*)dpd, FALSE); + task_t *task; + + task = (task_t*)ike_dpd_create(TRUE); diff = 0; + DBG1(DBG_IKE, "sending DPD request"); + + this->task_manager->queue_task(this->task_manager, task); + this->task_manager->initiate(this->task_manager); } } /* recheck in "interval" seconds */ job = send_dpd_job_create(this->ike_sa_id); charon->event_queue->add_relative(charon->event_queue, (job_t*)job, - (this->dpd_delay - diff) * 1000); + (delay - diff) * 1000); return SUCCESS; } @@ -1314,7 +970,7 @@ static void send_keepalive(private_ike_sa_t *this) send_keepalive_job_t *job; time_t last_out, now, diff, interval; - last_out = get_time_outbound(this); + last_out = get_use_time(this, FALSE); now = time(NULL); diff = now - last_out; @@ -1360,9 +1016,37 @@ static void set_state(private_ike_sa_t *this, ike_sa_state_t state) if (state == IKE_ESTABLISHED) { - this->time.established = time(NULL); + job_t *job; + u_int32_t now = time(NULL); + u_int32_t soft, hard; + bool reauth; + + this->time.established = now; /* start DPD checks */ send_dpd(this); + + /* schedule rekeying/reauthentication */ + soft = this->connection->get_soft_lifetime(this->connection); + hard = this->connection->get_hard_lifetime(this->connection); + reauth = this->connection->get_reauth(this->connection); + DBG1(DBG_IKE, "scheduling %s in %ds, maximum lifetime %ds", + reauth ? "reauthentication": "rekeying", soft, hard); + + if (soft) + { + this->time.rekey = now + soft; + job = (job_t*)rekey_ike_sa_job_create(this->ike_sa_id, reauth); + charon->event_queue->add_relative(charon->event_queue, job, + soft * 1000); + } + + if (hard) + { + this->time.delete = now + hard; + job = (job_t*)delete_ike_sa_job_create(this->ike_sa_id, TRUE); + charon->event_queue->add_relative(charon->event_queue, job, + hard * 1000); + } } this->state = state; @@ -1446,12 +1130,12 @@ static void set_other_id(private_ike_sa_t *this, identification_t *other) * Implementation of ike_sa_t.derive_keys. */ static status_t derive_keys(private_ike_sa_t *this, - proposal_t *proposal, diffie_hellman_t *dh, + proposal_t *proposal, chunk_t secret, chunk_t nonce_i, chunk_t nonce_r, bool initiator, prf_t *child_prf, prf_t *old_prf) { prf_plus_t *prf_plus; - chunk_t skeyseed, secret, key, nonces, prf_plus_seed; + chunk_t skeyseed, key, nonces, prf_plus_seed; algorithm_t *algo; size_t key_size; crypter_t *crypter_i, *crypter_r; @@ -1475,7 +1159,6 @@ static status_t derive_keys(private_ike_sa_t *this, return FAILED; } - dh->get_shared_secret(dh, &secret); DBG4(DBG_IKE, "shared Diffie Hellman secret %B", &secret); nonces = chunk_cat("cc", nonce_i, nonce_r); *((u_int64_t*)spi_i.ptr) = this->ike_sa_id->get_initiator_spi(this->ike_sa_id); @@ -1640,28 +1323,6 @@ static void add_child_sa(private_ike_sa_t *this, child_sa_t *child_sa) } /** - * Implementation of ike_sa_t.has_child_sa. - */ -static bool has_child_sa(private_ike_sa_t *this, u_int32_t reqid) -{ - iterator_t *iterator; - child_sa_t *current; - bool found = FALSE; - - iterator = this->child_sas->create_iterator(this->child_sas, TRUE); - while (iterator->iterate(iterator, (void**)¤t)) - { - if (current->get_reqid(current) == reqid) - { - found = TRUE; - break; - } - } - iterator->destroy(iterator); - return found; -} - -/** * Implementation of ike_sa_t.get_child_sa. */ static child_sa_t* get_child_sa(private_ike_sa_t *this, protocol_id_t protocol, @@ -1672,7 +1333,7 @@ static child_sa_t* get_child_sa(private_ike_sa_t *this, protocol_id_t protocol, iterator = this->child_sas->create_iterator(this->child_sas, TRUE); while (iterator->iterate(iterator, (void**)¤t)) - {; + { if (current->get_spi(current, inbound) == spi && current->get_protocol(current) == protocol) { @@ -1696,18 +1357,17 @@ static iterator_t* create_child_sa_iterator(private_ike_sa_t *this) */ static status_t rekey_child_sa(private_ike_sa_t *this, protocol_id_t protocol, u_int32_t spi) { - create_child_sa_t *rekey; child_sa_t *child_sa; + child_rekey_t *child_rekey; child_sa = get_child_sa(this, protocol, spi, TRUE); - if (child_sa == NULL) + if (child_sa) { - return NOT_FOUND; + child_rekey = child_rekey_create(&this->public, child_sa); + this->task_manager->queue_task(this->task_manager, &child_rekey->task); + return this->task_manager->initiate(this->task_manager); } - - rekey = create_child_sa_create(&this->public); - rekey->rekeys_child(rekey, child_sa); - return queue_transaction(this, (transaction_t*)rekey, FALSE); + return FAILED; } /** @@ -1715,24 +1375,24 @@ static status_t rekey_child_sa(private_ike_sa_t *this, protocol_id_t protocol, u */ static status_t delete_child_sa(private_ike_sa_t *this, protocol_id_t protocol, u_int32_t spi) { - delete_child_sa_t *del; child_sa_t *child_sa; + child_delete_t *child_delete; child_sa = get_child_sa(this, protocol, spi, TRUE); - if (child_sa == NULL) + if (child_sa) { - return NOT_FOUND; + child_delete = child_delete_create(&this->public, child_sa); + this->task_manager->queue_task(this->task_manager, &child_delete->task); + return this->task_manager->initiate(this->task_manager); } - - del = delete_child_sa_create(&this->public); - del->set_child_sa(del, child_sa); - return queue_transaction(this, (transaction_t*)del, FALSE); + return FAILED; } /** * Implementation of ike_sa_t.destroy_child_sa. */ -static status_t destroy_child_sa(private_ike_sa_t *this, protocol_id_t protocol, u_int32_t spi) +static status_t destroy_child_sa(private_ike_sa_t *this, protocol_id_t protocol, + u_int32_t spi) { iterator_t *iterator; child_sa_t *child_sa; @@ -1755,69 +1415,27 @@ static status_t destroy_child_sa(private_ike_sa_t *this, protocol_id_t protocol, } /** - * Implementation of ike_sa_t.set_lifetimes. - */ -static void set_lifetimes(private_ike_sa_t *this, bool reauth, - u_int32_t soft_lifetime, u_int32_t hard_lifetime) -{ - job_t *job; - u_int32_t now = time(NULL); - - this->reauth = reauth; - - if (soft_lifetime) - { - this->time.rekey = now + soft_lifetime; - job = (job_t*)rekey_ike_sa_job_create(this->ike_sa_id, reauth); - charon->event_queue->add_relative(charon->event_queue, job, - soft_lifetime * 1000); - } - - if (hard_lifetime) - { - this->time.delete = now + hard_lifetime; - job = (job_t*)delete_ike_sa_job_create(this->ike_sa_id, TRUE); - charon->event_queue->add_relative(charon->event_queue, job, - hard_lifetime * 1000); - } -} - -/** * Implementation of public_ike_sa_t.delete. */ static status_t delete_(private_ike_sa_t *this) { + ike_delete_t *ike_delete; + switch (this->state) { - case IKE_CONNECTING: - { - /* this may happen if a half open IKE_SA gets closed after a - * timeout. We signal here UP_FAILED to complete the SIG schema */ - SIG(IKE_UP_FAILED, "half open IKE_SA deleted after timeout"); - return DESTROY_ME; - } case IKE_ESTABLISHED: - { - delete_ike_sa_t *delete_ike_sa; - if (this->transaction_out) - { - /* already a transaction in progress. As this may hang - * around a while, we don't inform the other peer. */ - return DESTROY_ME; - } - delete_ike_sa = delete_ike_sa_create(&this->public); - return queue_transaction(this, (transaction_t*)delete_ike_sa, FALSE); - } - case IKE_CREATED: - case IKE_DELETING: + DBG1(DBG_IKE, "deleting IKE_SA"); + /* do not log when rekeyed */ + case IKE_REKEYING: + ike_delete = ike_delete_create(&this->public, TRUE); + this->task_manager->queue_task(this->task_manager, &ike_delete->task); + return this->task_manager->initiate(this->task_manager); default: - { - SIG(IKE_DOWN_START, "closing IKE_SA"); - SIG(IKE_DOWN_SUCCESS, "IKE_SA closed between %H[%D]...%H[%D]", - this->my_host, this->my_id, this->other_host, this->other_id); - return DESTROY_ME; - } + DBG1(DBG_IKE, "destroying IKE_SA in state %N without notification", + ike_sa_state_names, this->state); + break; } + return DESTROY_ME; } /** @@ -1825,161 +1443,373 @@ static status_t delete_(private_ike_sa_t *this) */ static status_t rekey(private_ike_sa_t *this) { - rekey_ike_sa_t *rekey_ike_sa; - - DBG1(DBG_IKE, "rekeying IKE_SA between %H[%D]..%H[%D]", - this->my_host, this->my_id, this->other_host, this->other_id); + ike_rekey_t *ike_rekey; - if (this->state != IKE_ESTABLISHED) - { - SIG(IKE_REKEY_START, "rekeying IKE_SA"); - SIG(IKE_REKEY_FAILED, "unable to rekey IKE_SA in state %N", - ike_sa_state_names, this->state); - return FAILED; - } + ike_rekey = ike_rekey_create(&this->public, TRUE); - rekey_ike_sa = rekey_ike_sa_create(&this->public); - return queue_transaction(this, (transaction_t*)rekey_ike_sa, FALSE); + this->task_manager->queue_task(this->task_manager, &ike_rekey->task); + return this->task_manager->initiate(this->task_manager); } /** - * Implementation of ike_sa_t.reauth. + * Implementation of ike_sa_t.reestablish */ -static status_t reauth(private_ike_sa_t *this) +static void reestablish(private_ike_sa_t *this) { - connection_t *connection; - child_sa_t *child_sa; + ike_sa_id_t *other_id; + private_ike_sa_t *other; iterator_t *iterator; + child_sa_t *child_sa; + policy_t *policy; + task_t *task; + job_t *job; - DBG1(DBG_IKE, "reauthenticating IKE_SA between %H[%D]..%H[%D]", - this->my_host, this->my_id, this->other_host, this->other_id); + other_id = ike_sa_id_create(0, 0, TRUE); + other = (private_ike_sa_t*)charon->ike_sa_manager->checkout( + charon->ike_sa_manager, other_id); + other_id->destroy(other_id); - /* get a connection to initiate */ - connection = charon->connections->get_connection_by_hosts(charon->connections, - this->my_host, this->other_host); - if (connection == NULL) + apply_config(other, this->connection, this->policy); + + if (this->state == IKE_ESTABLISHED) { - DBG1(DBG_IKE, "no connection found to reauthenticate"); - return FAILED; + task = (task_t*)ike_init_create(&other->public, TRUE, NULL); + other->task_manager->queue_task(other->task_manager, task); + task = (task_t*)ike_natd_create(&other->public, TRUE); + other->task_manager->queue_task(other->task_manager, task); + task = (task_t*)ike_cert_create(&other->public, TRUE); + other->task_manager->queue_task(other->task_manager, task); + task = (task_t*)ike_config_create(&other->public, other->policy); + other->task_manager->queue_task(other->task_manager, task); + task = (task_t*)ike_auth_create(&other->public, TRUE); + other->task_manager->queue_task(other->task_manager, task); } - /* queue CREATE_CHILD_SA transactions to set up all CHILD_SAs */ + other->task_manager->adopt_tasks(other->task_manager, this->task_manager); + + /* Create task for established children, adopt routed children directly */ iterator = this->child_sas->create_iterator(this->child_sas, TRUE); - while (iterator->iterate(iterator, (void**)&child_sa)) + while(iterator->iterate(iterator, (void**)&child_sa)) { - job_t *job; - policy_t *policy; - linked_list_t *my_ts, *other_ts; - host_t *other; - - my_ts = child_sa->get_my_traffic_selectors(child_sa); - other_ts = child_sa->get_other_traffic_selectors(child_sa); - policy = charon->policies->get_policy(charon->policies, - this->my_id, this->other_id, my_ts, other_ts, - this->my_host, this->other_host); - if (policy == NULL) + switch (child_sa->get_state(child_sa)) { - DBG1(DBG_IKE, "policy not found to recreate CHILD_SA, skipped"); - continue; + case CHILD_ROUTED: + { + iterator->remove(iterator); + other->child_sas->insert_first(other->child_sas, child_sa); + break; + } + default: + { + policy = child_sa->get_policy(child_sa); + task = (task_t*)child_create_create(&other->public, policy); + other->task_manager->queue_task(other->task_manager, task); + break; + } } - connection->get_ref(connection); - other = this->other_host->clone(this->other_host); - job = (job_t*)initiate_job_create(connection, other, policy); - charon->job_queue->add(charon->job_queue, job); } iterator->destroy(iterator); - connection->destroy(connection); - /* delete the old IKE_SA - * TODO: we should delay the delete to avoid connectivity gaps?! */ - return delete_(this); + other->task_manager->initiate(other->task_manager); + + charon->ike_sa_manager->checkin(charon->ike_sa_manager, &other->public); + + job = (job_t*)delete_ike_sa_job_create(this->ike_sa_id, TRUE); + charon->job_queue->add(charon->job_queue, job); } /** - * Implementation of ike_sa_t.get_rekeying_transaction. + * Implementation of ike_sa_t.inherit. */ -static transaction_t* get_rekeying_transaction(private_ike_sa_t *this) +static void inherit(private_ike_sa_t *this, private_ike_sa_t *other) { - return this->rekeying_transaction; + child_sa_t *child_sa; + host_t *ip; + + /* apply hosts and ids */ + this->my_host->destroy(this->my_host); + this->other_host->destroy(this->other_host); + this->my_id->destroy(this->my_id); + this->other_id->destroy(this->other_id); + this->my_host = other->my_host->clone(other->my_host); + this->other_host = other->other_host->clone(other->other_host); + this->my_id = other->my_id->clone(other->my_id); + this->other_id = other->other_id->clone(other->other_id); + + /* apply virtual assigned IPs... */ + if (other->my_virtual_ip) + { + this->my_virtual_ip = other->my_virtual_ip; + other->my_virtual_ip = NULL; + } + if (other->other_virtual_ip) + { + this->other_virtual_ip = other->other_virtual_ip; + other->other_virtual_ip = NULL; + } + + /* ... and DNS servers */ + while (other->dns_servers->remove_last(other->dns_servers, + (void**)&ip) == SUCCESS) + { + this->dns_servers->insert_first(this->dns_servers, ip); + } + + /* adopt all children */ + while (other->child_sas->remove_last(other->child_sas, + (void**)&child_sa) == SUCCESS) + { + this->child_sas->insert_first(this->child_sas, (void*)child_sa); + } } /** - * Implementation of ike_sa_t.set_rekeying_transaction. + * Implementation of ike_sa_t.is_natt_enabled. */ -static void set_rekeying_transaction(private_ike_sa_t *this, transaction_t *rekey) +static bool is_natt_enabled(private_ike_sa_t *this) { - this->rekeying_transaction = rekey; + return this->nat_here || this->nat_there; } /** - * Implementation of ike_sa_t.adopt_children. + * Implementation of ike_sa_t.enable_natt. */ -static void adopt_children(private_ike_sa_t *this, private_ike_sa_t *other) +static void enable_natt(private_ike_sa_t *this, bool local) { - child_sa_t *child_sa; - - while (other->child_sas->remove_last(other->child_sas, - (void**)&child_sa) == SUCCESS) + if (local) { - this->child_sas->insert_first(this->child_sas, (void*)child_sa); + DBG1(DBG_IKE, "local host is behind NAT, scheduling keep alives"); + this->nat_here = TRUE; + send_keepalive(this); + } + else + { + DBG1(DBG_IKE, "remote host is behind NAT"); + this->nat_there = TRUE; } } /** - * Implementation of ike_sa_t.get_next_message_id. + * Implementation of ike_sa_t.reset */ -static u_int32_t get_next_message_id (private_ike_sa_t *this) +static void reset(private_ike_sa_t *this) { - return this->message_id_out++; + /* the responder ID is reset, as peer may choose another one */ + if (this->ike_sa_id->is_initiator(this->ike_sa_id)) + { + this->ike_sa_id->set_responder_spi(this->ike_sa_id, 0); + } + + set_state(this, IKE_CREATED); + + this->task_manager->reset(this->task_manager); } /** - * Implementation of ike_sa_t.is_natt_enabled. + * Implementation of ike_sa_t.set_virtual_ip */ -static bool is_natt_enabled(private_ike_sa_t *this) +static void set_virtual_ip(private_ike_sa_t *this, bool local, host_t *ip) { - return this->nat_here || this->nat_there; + if (local) + { + DBG1(DBG_IKE, "installing new virtual IP %H", ip); + if (this->my_virtual_ip) + { + DBG1(DBG_IKE, "removing old virtual IP %H", this->my_virtual_ip); + charon->kernel_interface->del_ip(charon->kernel_interface, + this->my_virtual_ip, + this->other_host); + this->my_virtual_ip->destroy(this->my_virtual_ip); + } + if (charon->kernel_interface->add_ip(charon->kernel_interface, ip, + this->other_host) == SUCCESS) + { + this->my_virtual_ip = ip->clone(ip); + } + else + { + DBG1(DBG_IKE, "installing virtual IP %H failed", ip); + this->my_virtual_ip = NULL; + } + } + else + { + DESTROY_IF(this->other_virtual_ip); + this->other_virtual_ip = ip->clone(ip); + } } /** - * Implementation of ike_sa_t.enable_natt. + * Implementation of ike_sa_t.get_virtual_ip */ -static void enable_natt(private_ike_sa_t *this, bool local) +static host_t* get_virtual_ip(private_ike_sa_t *this, bool local) { if (local) { - DBG1(DBG_IKE, "local host is behind NAT, using NAT-T, " - "scheduled keep alives"); - this->nat_here = TRUE; - send_keepalive(this); + return this->my_virtual_ip; } else { - DBG1(DBG_IKE, "remote host is behind NAT, using NAT-T"); - this->nat_there = TRUE; + return this->other_virtual_ip; } } /** + * Implementation of ike_sa_t.remove_dns_server + */ +static void remove_dns_servers(private_ike_sa_t *this) +{ + FILE *file; + struct stat stats; + chunk_t contents, line, orig_line, token; + char string[INET6_ADDRSTRLEN]; + host_t *ip; + iterator_t *iterator; + + if (this->dns_servers->get_count(this->dns_servers) == 0) + { + /* don't touch anything if we have no nameservers installed */ + return; + } + + file = fopen(RESOLV_CONF, "r"); + if (file == NULL || stat(RESOLV_CONF, &stats) != 0) + { + DBG1(DBG_IKE, "unable to open DNS configuration file %s: %m", RESOLV_CONF); + return; + } + + contents = chunk_alloca((size_t)stats.st_size); + + if (fread(contents.ptr, 1, contents.len, file) != contents.len) + { + DBG1(DBG_IKE, "unable to read DNS configuration file: %m"); + fclose(file); + return; + } + + fclose(file); + file = fopen(RESOLV_CONF, "w"); + if (file == NULL) + { + DBG1(DBG_IKE, "unable to open DNS configuration file %s: %m", RESOLV_CONF); + return; + } + + iterator = this->dns_servers->create_iterator(this->dns_servers, TRUE); + while (fetchline(&contents, &line)) + { + bool found = FALSE; + orig_line = line; + if (extract_token(&token, ' ', &line) && + strncasecmp(token.ptr, "nameserver", token.len) == 0) + { + if (!extract_token(&token, ' ', &line)) + { + token = line; + } + iterator->reset(iterator); + while (iterator->iterate(iterator, (void**)&ip)) + { + snprintf(string, sizeof(string), "%H", ip); + if (strlen(string) == token.len && + strncmp(token.ptr, string, token.len) == 0) + { + iterator->remove(iterator); + ip->destroy(ip); + found = TRUE; + break; + } + } + } + + if (!found) + { + /* write line untouched back to file */ + fwrite(orig_line.ptr, orig_line.len, 1, file); + fprintf(file, "\n"); + } + } + iterator->destroy(iterator); + fclose(file); +} + +/** + * Implementation of ike_sa_t.add_dns_server + */ +static void add_dns_server(private_ike_sa_t *this, host_t *dns) +{ + FILE *file; + struct stat stats; + chunk_t contents; + + DBG1(DBG_IKE, "installing DNS server %H", dns); + + file = fopen(RESOLV_CONF, "a+"); + if (file == NULL || stat(RESOLV_CONF, &stats) != 0) + { + DBG1(DBG_IKE, "unable to open DNS configuration file %s: %m", RESOLV_CONF); + return; + } + + contents = chunk_alloca(stats.st_size); + + if (fread(contents.ptr, 1, contents.len, file) != contents.len) + { + DBG1(DBG_IKE, "unable to read DNS configuration file: %m"); + fclose(file); + return; + } + + fclose(file); + file = fopen(RESOLV_CONF, "w"); + if (file == NULL) + { + DBG1(DBG_IKE, "unable to open DNS configuration file %s: %m", RESOLV_CONF); + return; + } + + if (fprintf(file, "nameserver %H # added by strongSwan, assigned by %D\n", + dns, this->other_id) < 0) + { + DBG1(DBG_IKE, "unable to write DNS configuration: %m"); + } + else + { + this->dns_servers->insert_last(this->dns_servers, dns->clone(dns)); + } + fwrite(contents.ptr, contents.len, 1, file); + + fclose(file); +} + +/** * output handler in printf() */ static int print(FILE *stream, const struct printf_info *info, const void *const *args) { int written = 0; + bool reauth = FALSE; private_ike_sa_t *this = *((private_ike_sa_t**)(args[0])); + if (this->connection) + { + reauth = this->connection->get_reauth(this->connection); + } + if (this == NULL) { return fprintf(stream, "(null)"); } - written = fprintf(stream, "%12s: %N, %H[%D]...%H[%D]", - this->name, ike_sa_state_names, this->state, - this->my_host, this->my_id, this->other_host, this->other_id); - written += fprintf(stream, "\n%12s: IKE SPIs: %J, %s in %ds", - this->name, this->ike_sa_id, - this->reauth? "reauthentication":"rekeying", + written = fprintf(stream, "%12s[%d]: %N, %H[%D]...%H[%D]", get_name(this), + this->unique_id, ike_sa_state_names, this->state, + this->my_host, this->my_id, this->other_host, + this->other_id); + written += fprintf(stream, "\n%12s[%d]: IKE SPIs: %J, %s in %ds", + get_name(this), this->unique_id, this->ike_sa_id, + this->connection && reauth? "reauthentication":"rekeying", this->time.rekey - time(NULL)); if (info->alt) @@ -2003,11 +1833,7 @@ static void __attribute__ ((constructor))print_register() static void destroy(private_ike_sa_t *this) { this->child_sas->destroy_offset(this->child_sas, offsetof(child_sa_t, destroy)); - this->transaction_queue->destroy_offset(this->transaction_queue, offsetof(transaction_t, destroy)); - DESTROY_IF(this->transaction_in); - DESTROY_IF(this->transaction_in_next); - DESTROY_IF(this->transaction_out); DESTROY_IF(this->crypter_in); DESTROY_IF(this->crypter_out); DESTROY_IF(this->signer_in); @@ -2017,13 +1843,27 @@ static void destroy(private_ike_sa_t *this) DESTROY_IF(this->auth_verify); DESTROY_IF(this->auth_build); + if (this->my_virtual_ip) + { + charon->kernel_interface->del_ip(charon->kernel_interface, + this->my_virtual_ip, this->other_host); + this->my_virtual_ip->destroy(this->my_virtual_ip); + } + DESTROY_IF(this->other_virtual_ip); + + remove_dns_servers(this); + this->dns_servers->destroy_offset(this->dns_servers, offsetof(host_t, destroy)); + DESTROY_IF(this->my_host); DESTROY_IF(this->other_host); DESTROY_IF(this->my_id); DESTROY_IF(this->other_id); - free(this->name); + DESTROY_IF(this->connection); + DESTROY_IF(this->policy); + this->ike_sa_id->destroy(this->ike_sa_id); + this->task_manager->destroy(this->task_manager); free(this); } @@ -2033,17 +1873,21 @@ static void destroy(private_ike_sa_t *this) ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id) { private_ike_sa_t *this = malloc_thing(private_ike_sa_t); + static u_int32_t unique_id = 0; /* Public functions */ this->public.get_state = (ike_sa_state_t(*)(ike_sa_t*)) get_state; this->public.set_state = (void(*)(ike_sa_t*,ike_sa_state_t)) set_state; this->public.get_name = (char*(*)(ike_sa_t*))get_name; - this->public.set_name = (void(*)(ike_sa_t*,char*))set_name; this->public.process_message = (status_t(*)(ike_sa_t*, message_t*)) process_message; this->public.initiate = (status_t(*)(ike_sa_t*,connection_t*,policy_t*)) initiate; this->public.route = (status_t(*)(ike_sa_t*,connection_t*,policy_t*)) route; this->public.unroute = (status_t(*)(ike_sa_t*,policy_t*)) unroute; this->public.acquire = (status_t(*)(ike_sa_t*,u_int32_t)) acquire; + this->public.get_connection = (connection_t*(*)(ike_sa_t*))get_connection; + this->public.set_connection = (void(*)(ike_sa_t*,connection_t*))set_connection; + this->public.get_policy = (policy_t*(*)(ike_sa_t*))get_policy; + this->public.set_policy = (void(*)(ike_sa_t*,policy_t*))set_policy; this->public.get_id = (ike_sa_id_t*(*)(ike_sa_t*)) get_id; this->public.get_my_host = (host_t*(*)(ike_sa_t*)) get_my_host; this->public.set_my_host = (void(*)(ike_sa_t*,host_t*)) set_my_host; @@ -2053,8 +1897,7 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id) this->public.set_my_id = (void(*)(ike_sa_t*,identification_t*)) set_my_id; this->public.get_other_id = (identification_t*(*)(ike_sa_t*)) get_other_id; this->public.set_other_id = (void(*)(ike_sa_t*,identification_t*)) set_other_id; - this->public.get_next_message_id = (u_int32_t(*)(ike_sa_t*)) get_next_message_id; - this->public.retransmit_request = (status_t (*) (ike_sa_t *, u_int32_t)) retransmit_request; + this->public.retransmit = (status_t (*) (ike_sa_t *, u_int32_t)) retransmit; this->public.delete = (status_t(*)(ike_sa_t*))delete_; this->public.destroy = (void(*)(ike_sa_t*))destroy; this->public.send_dpd = (status_t (*)(ike_sa_t*)) send_dpd; @@ -2063,9 +1906,8 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id) this->public.get_child_prf = (prf_t *(*) (ike_sa_t *)) get_child_prf; this->public.get_auth_verify = (prf_t *(*) (ike_sa_t *)) get_auth_verify; this->public.get_auth_build = (prf_t *(*) (ike_sa_t *)) get_auth_build; - this->public.derive_keys = (status_t (*) (ike_sa_t *,proposal_t*,diffie_hellman_t*,chunk_t,chunk_t,bool,prf_t*,prf_t*)) derive_keys; + this->public.derive_keys = (status_t (*) (ike_sa_t *,proposal_t*,chunk_t,chunk_t,chunk_t,bool,prf_t*,prf_t*)) derive_keys; this->public.add_child_sa = (void (*) (ike_sa_t*,child_sa_t*)) add_child_sa; - this->public.has_child_sa = (bool(*)(ike_sa_t*,u_int32_t)) has_child_sa; this->public.get_child_sa = (child_sa_t* (*)(ike_sa_t*,protocol_id_t,u_int32_t,bool)) get_child_sa; this->public.create_child_sa_iterator = (iterator_t* (*)(ike_sa_t*)) create_child_sa_iterator; this->public.rekey_child_sa = (status_t(*)(ike_sa_t*,protocol_id_t,u_int32_t)) rekey_child_sa; @@ -2073,20 +1915,21 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id) this->public.destroy_child_sa = (status_t (*)(ike_sa_t*,protocol_id_t,u_int32_t))destroy_child_sa; this->public.enable_natt = (void(*)(ike_sa_t*, bool)) enable_natt; this->public.is_natt_enabled = (bool(*)(ike_sa_t*)) is_natt_enabled; - this->public.set_lifetimes = (void(*)(ike_sa_t*,bool,u_int32_t,u_int32_t))set_lifetimes; - this->public.apply_connection = (void(*)(ike_sa_t*,connection_t*))apply_connection; this->public.rekey = (status_t(*)(ike_sa_t*))rekey; - this->public.reauth = (status_t(*)(ike_sa_t*))reauth; - this->public.get_rekeying_transaction = (transaction_t*(*)(ike_sa_t*))get_rekeying_transaction; - this->public.set_rekeying_transaction = (void(*)(ike_sa_t*,transaction_t*))set_rekeying_transaction; - this->public.adopt_children = (void(*)(ike_sa_t*,ike_sa_t*))adopt_children; + this->public.reestablish = (void(*)(ike_sa_t*))reestablish; + this->public.inherit = (void(*)(ike_sa_t*,ike_sa_t*))inherit; + this->public.generate_message = (status_t(*)(ike_sa_t*,message_t*,packet_t**))generate_message; + this->public.reset = (void(*)(ike_sa_t*))reset; + this->public.get_unique_id = (u_int32_t(*)(ike_sa_t*))get_unique_id; + this->public.set_virtual_ip = (void(*)(ike_sa_t*,bool,host_t*))set_virtual_ip; + this->public.get_virtual_ip = (host_t*(*)(ike_sa_t*,bool))get_virtual_ip; + this->public.add_dns_server = (void(*)(ike_sa_t*,host_t*))add_dns_server; /* initialize private fields */ this->ike_sa_id = ike_sa_id->clone(ike_sa_id); - this->name = strdup("(uninitialized)"); this->child_sas = linked_list_create(); - this->my_host = host_create_from_string("0.0.0.0", 0); - this->other_host = host_create_from_string("0.0.0.0", 0); + this->my_host = host_create_any(AF_INET); + this->other_host = host_create_any(AF_INET); this->my_id = identification_create_from_encoding(ID_ANY, chunk_empty); this->other_id = identification_create_from_encoding(ID_ANY, chunk_empty); this->crypter_in = NULL; @@ -2099,21 +1942,18 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id) this->child_prf = NULL; this->nat_here = FALSE; this->nat_there = FALSE; - this->transaction_queue = linked_list_create(); - this->transaction_in = NULL; - this->transaction_in_next = NULL; - this->transaction_out = NULL; - this->rekeying_transaction = NULL; this->state = IKE_CREATED; - this->message_id_out = 0; - /* set to NOW, as when we rekey an existing IKE_SA no message is exchanged - * and inbound therefore uninitialized */ this->time.inbound = this->time.outbound = time(NULL); this->time.established = 0; this->time.rekey = 0; this->time.delete = 0; - this->dpd_delay = 0; - this->retrans_sequences = 0; + this->connection = NULL; + this->policy = NULL; + this->task_manager = task_manager_create(&this->public); + this->unique_id = ++unique_id; + this->my_virtual_ip = NULL; + this->other_virtual_ip = NULL; + this->dns_servers = linked_list_create(); return &this->public; } diff --git a/src/charon/sa/ike_sa.h b/src/charon/sa/ike_sa.h index 433e37292..2ba9313ab 100644 --- a/src/charon/sa/ike_sa.h +++ b/src/charon/sa/ike_sa.h @@ -33,7 +33,7 @@ typedef struct ike_sa_t ike_sa_t; #include <encoding/payloads/proposal_substructure.h> #include <sa/ike_sa_id.h> #include <sa/child_sa.h> -#include <sa/transactions/transaction.h> +#include <sa/tasks/task.h> #include <config/configuration.h> #include <utils/randomizer.h> #include <crypto/prfs/prf.h> @@ -121,7 +121,7 @@ extern enum_name_t *ike_sa_state_names; * An IKE_SA contains crypto information related to a connection * with a peer. It contains multiple IPsec CHILD_SA, for which * it is responsible. All traffic is handled by an IKE_SA, using - * transactions. + * the task manager and its tasks. * * @b Constructors: * - ike_sa_create() @@ -141,6 +141,14 @@ struct ike_sa_t { ike_sa_id_t* (*get_id) (ike_sa_t *this); /** + * @brief Get the numerical ID uniquely defining this IKE_SA. + * + * @param this calling object + * @return unique ID + */ + u_int32_t (*get_unique_id) (ike_sa_t *this); + + /** * @brief Get the state of the IKE_SA. * * @param this calling object @@ -165,14 +173,6 @@ struct ike_sa_t { char* (*get_name) (ike_sa_t *this); /** - * @brief Set the name of the connection this IKE_SA uses. - * - * @param this calling object - * @param name name, gets cloned - */ - void (*set_name) (ike_sa_t *this, char* name); - - /** * @brief Get the own host address. * * @param this calling object @@ -235,6 +235,38 @@ struct ike_sa_t { * @param other identification */ void (*set_other_id) (ike_sa_t *this, identification_t *other); + + /** + * @brief Get the connection used by this IKE_SA. + * + * @param this calling object + * @return connection + */ + connection_t* (*get_connection) (ike_sa_t *this); + + /** + * @brief Set the connection to use with this IKE_SA. + * + * @param this calling object + * @param connection connection to use + */ + void (*set_connection) (ike_sa_t *this, connection_t* connection); + + /** + * @brief Get the policy used by this IKE_SA. + * + * @param this calling object + * @return policy + */ + policy_t* (*get_policy) (ike_sa_t *this); + + /** + * @brief Set the policy to use with this IKE_SA. + * + * @param this calling object + * @param policy policy to use + */ + void (*set_policy) (ike_sa_t *this, policy_t *policy); /** * @brief Initiate a new connection. @@ -309,17 +341,6 @@ struct ike_sa_t { status_t (*delete) (ike_sa_t *this); /** - * @brief Retransmits a request. - * - * @param this calling object - * @param message_id ID of the request to retransmit - * @return - * - SUCCESS - * - NOT_FOUND if request doesn't have to be retransmited - */ - status_t (*retransmit_request) (ike_sa_t *this, u_int32_t message_id); - - /** * @brief Processes a incoming IKEv2-Message. * * Message processing may fail. If a critical failure occurs, @@ -327,7 +348,7 @@ struct ike_sa_t { * destroy the IKE_SA immediatly, as it is unusable. * * @param this calling object - * @param[in] message message to process + * @param message message to process * @return * - SUCCESS * - FAILED @@ -336,44 +357,33 @@ struct ike_sa_t { status_t (*process_message) (ike_sa_t *this, message_t *message); /** - * @brief Get the next message ID for a request. - * + * @brief Generate a IKE message to send it to the peer. + * + * This method generates all payloads in the message and encrypts/signs + * the packet. + * * @param this calling object - * @return the next message id + * @param message message to generate + * @param packet generated output packet + * @return + * - SUCCESS + * - FAILED + * - DESTROY_ME if this IKE_SA MUST be deleted */ - u_int32_t (*get_next_message_id) (ike_sa_t *this); + status_t (*generate_message) (ike_sa_t *this, message_t *message, + packet_t **packet); /** - * @brief Check if NAT traversal is enabled for this IKE_SA. - * - * @param this calling object - * @return TRUE if NAT traversal enabled - */ - bool (*is_natt_enabled) (ike_sa_t *this); - - /** - * @brief Enable NAT detection for this IKE_SA. - * - * If a Network address translation is detected with - * NAT_DETECTION notifys, a SA must switch to ports - * 4500. To enable this behavior, call enable_natt(). - * It is relevant which peer is NATted, this is specified - * with the "local" parameter. Call it twice when both - * are NATted. - * - * @param this calling object - * @param local TRUE, if we are NATted, FALSE if other - */ - void (*enable_natt) (ike_sa_t *this, bool local); - - /** - * @brief Apply connection parameters for this IKE_SA. + * @brief Retransmits a request. * - * @param this calling object - * @param connection connection definition + * @param this calling object + * @param message_id ID of the request to retransmit + * @return + * - SUCCESS + * - NOT_FOUND if request doesn't have to be retransmited */ - void (*apply_connection) (ike_sa_t *this, connection_t *connection); - + status_t (*retransmit) (ike_sa_t *this, u_int32_t message_id); + /** * @brief Sends a DPD request to the peer. * @@ -399,6 +409,29 @@ struct ike_sa_t { * @param this calling object */ void (*send_keepalive) (ike_sa_t *this); + + /** + * @brief Check if NAT traversal is enabled for this IKE_SA. + * + * @param this calling object + * @return TRUE if NAT traversal enabled + */ + bool (*is_natt_enabled) (ike_sa_t *this); + + /** + * @brief Enable NAT detection for this IKE_SA. + * + * If a Network address translation is detected with + * NAT_DETECTION notifys, a SA must switch to ports + * 4500. To enable this behavior, call enable_natt(). + * It is relevant which peer is NATted, this is specified + * with the "local" parameter. Call it twice when both + * are NATted. + * + * @param this calling object + * @param local TRUE, if we are NATted, FALSE if other + */ + void (*enable_natt) (ike_sa_t *this, bool local); /** * @brief Derive all keys and create the transforms for IKE communication. @@ -411,15 +444,14 @@ struct ike_sa_t { * * @param this calling object * @param proposal proposal which contains algorithms to use - * @param dh diffie hellman object with shared secret + * @param secret secret derived from DH exchange, gets freed * @param nonce_i initiators nonce * @param nonce_r responders nonce * @param initiator TRUE if initiator, FALSE otherwise * @param child_prf PRF with SK_d key when rekeying, NULL otherwise * @param old_prf general purpose PRF of old SA when rekeying */ - status_t (*derive_keys)(ike_sa_t *this, proposal_t* proposal, - diffie_hellman_t *dh, + status_t (*derive_keys)(ike_sa_t *this, proposal_t* proposal, chunk_t secret, chunk_t nonce_i, chunk_t nonce_r, bool initiator, prf_t *child_prf, prf_t *old_prf); @@ -464,15 +496,6 @@ struct ike_sa_t { void (*add_child_sa) (ike_sa_t *this, child_sa_t *child_sa); /** - * @brief Check if an IKE_SA has one or more CHILD_SAs with a given reqid. - * - * @param this calling object - * @param reqid reqid of the CHILD - * @return TRUE if it has such a CHILD, FALSE if not - */ - bool (*has_child_sa) (ike_sa_t *this, u_int32_t reqid); - - /** * @brief Get a CHILD_SA identified by protocol and SPI. * * @param this calling object @@ -537,22 +560,6 @@ struct ike_sa_t { status_t (*destroy_child_sa) (ike_sa_t *this, protocol_id_t protocol, u_int32_t spi); /** - * @brief Set lifetimes of an IKE_SA. - * - * Two lifetimes are specified. The soft_lifetime says, when rekeying should - * be initiated. The hard_lifetime says, when the IKE_SA has been expired - * and must be deleted. Normally, hard_lifetime > soft_lifetime, and - * hard_lifetime is only reached when rekeying at soft_lifetime fails. - * - * @param this calling object - * @param reauth use full reauthentication instead of rekeying. - * @param soft_lifetime soft_lifetime - * @param hard_lifetime hard_lifetime - */ - void (*set_lifetimes) (ike_sa_t *this, bool reauth, - u_int32_t soft_lifetime, u_int32_t hard_lifetime); - - /** * @brief Rekey the IKE_SA. * * Sets up a new IKE_SA, moves all CHILDs to it and deletes this IKE_SA. @@ -563,42 +570,60 @@ struct ike_sa_t { status_t (*rekey) (ike_sa_t *this); /** - * @brief Reauthentication the IKE_SA. + * @brief Restablish the IKE_SA. * * Create a completely new IKE_SA with authentication, recreates all children - * within the IKE_SA and shuts the old SA down. + * within the IKE_SA, but lets the old IKE_SA untouched. * * @param this calling object - * @return - SUCCESS, if IKE_SA rekeying initiated */ - status_t (*reauth) (ike_sa_t *this); - + void (*reestablish) (ike_sa_t *this); + /** - * @brief Get the transaction which rekeys this IKE_SA. + * @brief Set the virtual IP to use for this IKE_SA and its children. + * + * The virtual IP is assigned per IKE_SA, not per CHILD_SA. It has the same + * lifetime as the IKE_SA. * * @param this calling object - * @return rekey_ike_sa_t transaction or NULL */ - transaction_t* (*get_rekeying_transaction) (ike_sa_t *this); - + void (*set_virtual_ip) (ike_sa_t *this, bool local, host_t *ip); + /** - * @brief Set the transaction which rekeys this IKE_SA. + * @brief Get the virtual IP configured. * * @param this calling object - * @param rekey rekey_ike_sa_t transaction or NULL + * @param local TRUE to get local virtual IP, FALSE for remote */ - void (*set_rekeying_transaction) (ike_sa_t *this, transaction_t *rekey); - + host_t* (*get_virtual_ip) (ike_sa_t *this, bool local); + + /** + * @brief Add a DNS server to the system. + * + * An IRAS may send a DNS server. To use it, it is installed on the + * system. The DNS entry has a lifetime until the IKE_SA gets closed. + * + * @param this calling object + * @param dns DNS server to install on the system + */ + void (*add_dns_server) (ike_sa_t *this, host_t *dns); + /** - * @brief Move all children from other IKE_SA to this IKE_SA. + * @brief Inherit all attributes of other to this after rekeying. * - * After rekeying completes, all children are switched over to the - * newly created IKE_SA. + * When rekeying is completed, all CHILD_SAs, the virtual IP and all + * outstanding tasks are moved from other to this. + * + * @param this calling object + */ + void (*inherit) (ike_sa_t *this, ike_sa_t *other); + + /** + * @brief Reset the IKE_SA, useable when initiating fails * - * @param this stepfather - * @param other deceased (rekeyed) IKE_SA + * @param this calling object */ - void (*adopt_children) (ike_sa_t *this, ike_sa_t *other); + void (*reset) (ike_sa_t *this); /** * @brief Destroys a ike_sa_t object. @@ -611,8 +636,6 @@ struct ike_sa_t { /** * @brief Creates an ike_sa_t object with a specific ID. * - * The ID gets cloned internally. - * * @param ike_sa_id ike_sa_id_t object to associate with new IKE_SA * @return ike_sa_t object * diff --git a/src/charon/sa/ike_sa_manager.c b/src/charon/sa/ike_sa_manager.c index 31972e0bc..6e9d867fd 100644 --- a/src/charon/sa/ike_sa_manager.c +++ b/src/charon/sa/ike_sa_manager.c @@ -297,13 +297,134 @@ static u_int64_t get_next_spi(private_ike_sa_manager_t *this) } /** + * Implementation of of ike_sa_manager.checkout. + */ +static ike_sa_t* checkout(private_ike_sa_manager_t *this, ike_sa_id_t *ike_sa_id) +{ + bool responder_spi_set; + bool initiator_spi_set; + bool original_initiator; + ike_sa_t *ike_sa = NULL; + + DBG2(DBG_MGR, "checkout IKE_SA: %J", ike_sa_id); + + DBG2(DBG_MGR, "%d IKE_SAs in manager", + this->ike_sa_list->get_count(this->ike_sa_list)); + + /* each access is locked */ + pthread_mutex_lock(&(this->mutex)); + + responder_spi_set = ike_sa_id->get_responder_spi(ike_sa_id); + initiator_spi_set = ike_sa_id->get_initiator_spi(ike_sa_id); + original_initiator = ike_sa_id->is_initiator(ike_sa_id); + + if ((initiator_spi_set && responder_spi_set) || + ((initiator_spi_set && !responder_spi_set) && (original_initiator))) + { + /* we SHOULD have an IKE_SA for these SPIs in the list, + * if not, we can't handle the request... + */ + entry_t *entry; + /* look for the entry */ + if (get_entry_by_id(this, ike_sa_id, &entry) == SUCCESS) + { + if (wait_for_entry(this, entry)) + { + DBG2(DBG_MGR, "IKE_SA successfully checked out"); + /* ok, this IKE_SA is finally ours */ + entry->checked_out = TRUE; + ike_sa = entry->ike_sa; + /* update responder SPI when it's not set */ + if (entry->ike_sa_id->get_responder_spi(entry->ike_sa_id) == 0) + { + ike_sa_id_t *ike_sa_ike_sa_id = ike_sa->get_id(ike_sa); + u_int64_t spi = ike_sa_id->get_responder_spi(ike_sa_id); + + ike_sa_ike_sa_id->set_responder_spi(ike_sa_ike_sa_id, spi); + entry->ike_sa_id->set_responder_spi(entry->ike_sa_id, spi); + } + } + else + { + DBG2(DBG_MGR, "IKE_SA found, but not allowed to check it out"); + } + } + else + { + DBG2(DBG_MGR, "IKE_SA not stored in list"); + /* looks like there is no such IKE_SA, better luck next time... */ + } + } + else if ((initiator_spi_set && !responder_spi_set) && (!original_initiator)) + { + /* an IKE_SA_INIT from an another endpoint, + * he is the initiator. + * For simplicity, we do NOT check for retransmitted + * IKE_SA_INIT-Requests here, so EVERY single IKE_SA_INIT- + * Request (even a retransmitted one) will result in a + * IKE_SA. This could be improved... + */ + u_int64_t responder_spi; + entry_t *new_entry; + + /* set SPIs, we are the responder */ + responder_spi = get_next_spi(this); + + /* we also set arguments spi, so its still valid */ + ike_sa_id->set_responder_spi(ike_sa_id, responder_spi); + + /* create entry */ + new_entry = entry_create(ike_sa_id); + + this->ike_sa_list->insert_last(this->ike_sa_list, new_entry); + + /* check ike_sa out */ + DBG2(DBG_MGR, "IKE_SA added to list of known IKE_SAs"); + new_entry->checked_out = TRUE; + ike_sa = new_entry->ike_sa; + } + else if (!initiator_spi_set && !responder_spi_set) + { + /* checkout of a new and unused IKE_SA, used for rekeying */ + entry_t *new_entry; + + if (original_initiator) + { + ike_sa_id->set_initiator_spi(ike_sa_id, get_next_spi(this)); + } + else + { + ike_sa_id->set_responder_spi(ike_sa_id, get_next_spi(this)); + } + /* create entry */ + new_entry = entry_create(ike_sa_id); + DBG2(DBG_MGR, "created IKE_SA: %J", ike_sa_id); + + this->ike_sa_list->insert_last(this->ike_sa_list, new_entry); + + /* check ike_sa out */ + new_entry->checked_out = TRUE; + ike_sa = new_entry->ike_sa; + } + else + { + /* responder set, initiator not: here is something seriously wrong! */ + DBG2(DBG_MGR, "invalid IKE_SA SPIs"); + } + + pthread_mutex_unlock(&(this->mutex)); + + charon->bus->set_sa(charon->bus, ike_sa); + return ike_sa; +} + +/** * Implementation of of ike_sa_manager.checkout_by_id. */ -static ike_sa_t* checkout_by_id(private_ike_sa_manager_t *this, - host_t *my_host, - host_t *other_host, - identification_t *my_id, - identification_t *other_id) +static ike_sa_t* checkout_by_peer(private_ike_sa_manager_t *this, + host_t *my_host, host_t *other_host, + identification_t *my_id, + identification_t *other_id) { iterator_t *iterator; entry_t *entry; @@ -387,105 +508,53 @@ static ike_sa_t* checkout_by_id(private_ike_sa_manager_t *this, } /** - * Implementation of of ike_sa_manager.checkout. + * Implementation of of ike_sa_manager.checkout_by_id. */ -static ike_sa_t* checkout(private_ike_sa_manager_t *this, ike_sa_id_t *ike_sa_id) +static ike_sa_t* checkout_by_id(private_ike_sa_manager_t *this, u_int32_t id, + bool child) { - bool responder_spi_set; - bool initiator_spi_set; - bool original_initiator; + iterator_t *iterator, *children; + entry_t *entry; ike_sa_t *ike_sa = NULL; + child_sa_t *child_sa; - DBG2(DBG_MGR, "checkout IKE_SA: %J", ike_sa_id); - - DBG2(DBG_MGR, "%d IKE_SAs in manager", - this->ike_sa_list->get_count(this->ike_sa_list)); - - /* each access is locked */ pthread_mutex_lock(&(this->mutex)); - responder_spi_set = ike_sa_id->get_responder_spi(ike_sa_id); - initiator_spi_set = ike_sa_id->get_initiator_spi(ike_sa_id); - original_initiator = ike_sa_id->is_initiator(ike_sa_id); - - if ((initiator_spi_set && responder_spi_set) || - ((initiator_spi_set && !responder_spi_set) && (original_initiator))) + iterator = this->ike_sa_list->create_iterator(this->ike_sa_list, TRUE); + while (iterator->iterate(iterator, (void**)&entry)) { - /* we SHOULD have an IKE_SA for these SPIs in the list, - * if not, we can't handle the request... - */ - entry_t *entry; - /* look for the entry */ - if (get_entry_by_id(this, ike_sa_id, &entry) == SUCCESS) + if (wait_for_entry(this, entry)) { - if (wait_for_entry(this, entry)) + /* look for a child with such a reqid ... */ + if (child) { - DBG2(DBG_MGR, "IKE_SA successfully checked out"); - /* ok, this IKE_SA is finally ours */ - entry->checked_out = TRUE; - ike_sa = entry->ike_sa; + children = entry->ike_sa->create_child_sa_iterator(entry->ike_sa); + while (children->iterate(children, (void**)&child_sa)) + { + if (child_sa->get_reqid(child_sa) == id) + { + ike_sa = entry->ike_sa; + break; + } + } + children->destroy(children); } - else + else /* ... or for a IKE_SA with such a unique id */ { - DBG2(DBG_MGR, "IKE_SA found, but not allowed to check it out"); + if (entry->ike_sa->get_unique_id(entry->ike_sa) == id) + { + ike_sa = entry->ike_sa; + } + } + /* got one, return */ + if (ike_sa) + { + entry->checked_out = TRUE; + break; } - } - else - { - DBG2(DBG_MGR, "IKE_SA not stored in list"); - /* looks like there is no such IKE_SA, better luck next time... */ } } - else if ((initiator_spi_set && !responder_spi_set) && (!original_initiator)) - { - /* an IKE_SA_INIT from an another endpoint, - * he is the initiator. - * For simplicity, we do NOT check for retransmitted - * IKE_SA_INIT-Requests here, so EVERY single IKE_SA_INIT- - * Request (even a retransmitted one) will result in a - * IKE_SA. This could be improved... - */ - u_int64_t responder_spi; - entry_t *new_entry; - - /* set SPIs, we are the responder */ - responder_spi = get_next_spi(this); - - /* we also set arguments spi, so its still valid */ - ike_sa_id->set_responder_spi(ike_sa_id, responder_spi); - - /* create entry */ - new_entry = entry_create(ike_sa_id); - - this->ike_sa_list->insert_last(this->ike_sa_list, new_entry); - - /* check ike_sa out */ - DBG2(DBG_MGR, "IKE_SA added to list of known IKE_SAs"); - new_entry->checked_out = TRUE; - ike_sa = new_entry->ike_sa; - } - else if (!initiator_spi_set && !responder_spi_set && original_initiator) - { - /* checkout of a new and unused IKE_SA, used for rekeying */ - entry_t *new_entry; - - ike_sa_id->set_initiator_spi(ike_sa_id, get_next_spi(this)); - /* create entry */ - new_entry = entry_create(ike_sa_id); - DBG2(DBG_MGR, "created IKE_SA: %J", ike_sa_id); - - this->ike_sa_list->insert_last(this->ike_sa_list, new_entry); - - /* check ike_sa out */ - new_entry->checked_out = TRUE; - ike_sa = new_entry->ike_sa; - } - else - { - /* responder set, initiator not: here is something seriously wrong! */ - DBG2(DBG_MGR, "invalid IKE_SA SPIs"); - } - + iterator->destroy(iterator); pthread_mutex_unlock(&(this->mutex)); charon->bus->set_sa(charon->bus, ike_sa); @@ -493,14 +562,15 @@ static ike_sa_t* checkout(private_ike_sa_manager_t *this, ike_sa_id_t *ike_sa_id } /** - * Implementation of of ike_sa_manager.checkout_by_child. + * Implementation of of ike_sa_manager.checkout_by_name. */ -static ike_sa_t* checkout_by_child(private_ike_sa_manager_t *this, - u_int32_t reqid) +static ike_sa_t* checkout_by_name(private_ike_sa_manager_t *this, char *name, + bool child) { - iterator_t *iterator; + iterator_t *iterator, *children; entry_t *entry; ike_sa_t *ike_sa = NULL; + child_sa_t *child_sa; pthread_mutex_lock(&(this->mutex)); @@ -509,12 +579,31 @@ static ike_sa_t* checkout_by_child(private_ike_sa_manager_t *this, { if (wait_for_entry(this, entry)) { - /* ok, access is exclusive for us, check for child */ - if (entry->ike_sa->has_child_sa(entry->ike_sa, reqid)) + /* look for a child with such a policy name ... */ + if (child) + { + children = entry->ike_sa->create_child_sa_iterator(entry->ike_sa); + while (children->iterate(children, (void**)&child_sa)) + { + if (streq(child_sa->get_name(child_sa), name)) + { + ike_sa = entry->ike_sa; + break; + } + } + children->destroy(children); + } + else /* ... or for a IKE_SA with such a connection name */ + { + if (streq(entry->ike_sa->get_name(entry->ike_sa), name)) + { + ike_sa = entry->ike_sa; + } + } + /* got one, return */ + if (ike_sa) { - /* match */ entry->checked_out = TRUE; - ike_sa = entry->ike_sa; break; } } @@ -529,9 +618,16 @@ static ike_sa_t* checkout_by_child(private_ike_sa_manager_t *this, /** * Iterator hook for iterate, gets ike_sas instead of entries */ -static void* iterator_hook(void *value) +static bool iterator_hook(private_ike_sa_manager_t* this, entry_t *in, + ike_sa_t **out) { - return ((entry_t*)value)->ike_sa; + /* check out entry */ + if (wait_for_entry(this, in)) + { + *out = in->ike_sa; + return TRUE; + } + return FALSE; } /** @@ -540,9 +636,9 @@ static void* iterator_hook(void *value) static iterator_t *create_iterator(private_ike_sa_manager_t* this) { iterator_t *iterator = this->ike_sa_list->create_iterator_locked( - this->ike_sa_list, &this->mutex); + this->ike_sa_list, &this->mutex); /* register hook to iterator over ike_sas, not entries */ - iterator->set_iterator_hook(iterator, iterator_hook); + iterator->set_iterator_hook(iterator, (iterator_hook_t*)iterator_hook, this); return iterator; } @@ -634,170 +730,6 @@ static status_t checkin_and_destroy(private_ike_sa_manager_t *this, ike_sa_t *ik } /** - * Implementation of ike_sa_manager_t.delete. - */ -static status_t delete_(private_ike_sa_manager_t *this, ike_sa_id_t *ike_sa_id) -{ - /* deletion is a bit complex, we must garant that no thread is waiting for - * this SA. - * We take this SA from the list, and start signaling while threads - * are in the condvar. - */ - entry_t *entry; - status_t retval; - - DBG2(DBG_MGR, "delete IKE_SA: %J", ike_sa_id); - - pthread_mutex_lock(&(this->mutex)); - - if (get_entry_by_id(this, ike_sa_id, &entry) == SUCCESS) - { - /* we try a delete. If it succeeds, our job is done here. The - * other peer will reply, and the IKE SA gets the finally deleted... - */ - if (entry->ike_sa->delete(entry->ike_sa) == SUCCESS) - { - DBG2(DBG_MGR, "initiated delete for IKE_SA"); - } - /* but if the IKE SA is not in a state where the deletion is - * negotiated with the other peer, we can destroy the IKE SA on our own. - */ - else - { - - } - retval = SUCCESS; - } - else - { - DBG2(DBG_MGR, "tried to delete nonexisting IKE_SA"); - retval = NOT_FOUND; - } - - pthread_mutex_unlock(&(this->mutex)); - return retval; -} - -/** - * Implementation of ike_sa_manager_t.delete_by_name. - */ -static status_t delete_by_name(private_ike_sa_manager_t *this, char *name) -{ - iterator_t *iterator; - iterator_t *child_iter; - entry_t *entry; - size_t name_len = strlen(name); - - pthread_mutex_lock(&(this->mutex)); - - iterator = this->ike_sa_list->create_iterator(this->ike_sa_list, TRUE); - while (iterator->iterate(iterator, (void**)&entry)) - { - if (wait_for_entry(this, entry)) - { - /* delete ike_sa if: - * name{x} matches completely - * name{} matches by name - * name matches by name - */ - bool del = FALSE; - char *ike_name; - char *child_name; - child_sa_t *child_sa; - - ike_name = entry->ike_sa->get_name(entry->ike_sa); - /* check if "name{x}" matches completely */ - if (strcmp(name, ike_name) == 0) - { - del = TRUE; - } - /* check if name is in form of "name{}" and matches to ike_name */ - else if (name_len > 1 && - name[name_len - 2] == '{' && name[name_len - 1] == '}' && - strlen(ike_name) > name_len && - ike_name[name_len - 2] == '{' && - strncmp(name, ike_name, name_len - 2) == 0) - { - del = TRUE; - } - /* finally, check if name is "name" and matches ike_name */ - else if (name_len == strchr(ike_name, '{') - ike_name && - strncmp(name, ike_name, name_len) == 0) - { - del = TRUE; - } - - if (del) - { - if (entry->ike_sa->delete(entry->ike_sa) == DESTROY_ME) - { - delete_entry(this, entry); - iterator->reset(iterator); - } - /* no need to check children, as we delete all */ - continue; - } - - /* and now the same game for all children. delete child_sa if: - * name[x] matches completely - * name[] matches by name - * name matches by name - */ - child_iter = entry->ike_sa->create_child_sa_iterator(entry->ike_sa); - while (child_iter->iterate(child_iter, (void**)&child_sa)) - { - /* skip ROUTED children, they have their "unroute" command */ - if (child_sa->get_state(child_sa) == CHILD_ROUTED) - { - continue; - } - - child_name = child_sa->get_name(child_sa); - del = FALSE; - /* check if "name[x]" matches completely */ - if (strcmp(name, child_name) == 0) - { - del = TRUE; - } - /* check if name is in form of "name[]" and matches to child_name */ - else if (name_len > 1 && - name[name_len - 2] == '[' && name[name_len - 1] == ']' && - strlen(child_name) > name_len && - child_name[name_len - 2] == '[' && - strncmp(name, child_name, name_len - 2) == 0) - { - del = TRUE; - } - /* finally, check if name is "name" and matches child_name */ - else if (name_len == strchr(child_name, '[') - child_name && - strncmp(name, child_name, name_len) == 0) - { - del = TRUE; - } - if (del) - { - if (entry->ike_sa->delete_child_sa(entry->ike_sa, - child_sa->get_protocol(child_sa), - child_sa->get_spi(child_sa, TRUE)) == DESTROY_ME) - { - /* when a fatal error occurs, we are responsible to - * remove the IKE_SA */ - delete_entry(this, entry); - iterator->reset(iterator); - break; - } - } - } - child_iter->destroy(child_iter); - } - } - iterator->destroy(iterator); - pthread_mutex_unlock(&(this->mutex)); - - return SUCCESS; -} - -/** * Implementation of ike_sa_manager_t.destroy. */ static void destroy(private_ike_sa_manager_t *this) @@ -859,19 +791,18 @@ ike_sa_manager_t *ike_sa_manager_create() /* assign public functions */ this->public.destroy = (void(*)(ike_sa_manager_t*))destroy; - this->public.checkout_by_id = (ike_sa_t*(*)(ike_sa_manager_t*,host_t*,host_t*,identification_t*,identification_t*))checkout_by_id; this->public.checkout = (ike_sa_t*(*)(ike_sa_manager_t*, ike_sa_id_t*))checkout; - this->public.checkout_by_child = (ike_sa_t*(*)(ike_sa_manager_t*,u_int32_t))checkout_by_child; + this->public.checkout_by_peer = (ike_sa_t*(*)(ike_sa_manager_t*,host_t*,host_t*,identification_t*,identification_t*))checkout_by_peer; + this->public.checkout_by_id = (ike_sa_t*(*)(ike_sa_manager_t*,u_int32_t,bool))checkout_by_id; + this->public.checkout_by_name = (ike_sa_t*(*)(ike_sa_manager_t*,char*,bool))checkout_by_name; this->public.create_iterator = (iterator_t*(*)(ike_sa_manager_t*))create_iterator; this->public.checkin = (status_t(*)(ike_sa_manager_t*,ike_sa_t*))checkin; - this->public.delete = (status_t(*)(ike_sa_manager_t*,ike_sa_id_t*))delete_; - this->public.delete_by_name = (status_t(*)(ike_sa_manager_t*,char*))delete_by_name; this->public.checkin_and_destroy = (status_t(*)(ike_sa_manager_t*,ike_sa_t*))checkin_and_destroy; /* initialize private variables */ this->ike_sa_list = linked_list_create(); - pthread_mutex_init(&(this->mutex), NULL); + pthread_mutex_init(&this->mutex, NULL); this->randomizer = randomizer_create(); - return (ike_sa_manager_t*)this; + return &this->public; } diff --git a/src/charon/sa/ike_sa_manager.h b/src/charon/sa/ike_sa_manager.h index 671062c4e..ecd3db156 100644 --- a/src/charon/sa/ike_sa_manager.h +++ b/src/charon/sa/ike_sa_manager.h @@ -59,7 +59,7 @@ struct ike_sa_manager_t { * result in a deadlock! * * @param this the manager object - * @param[in/out] ike_sa_id the SA identifier, will be updated + * @param ike_sa_id the SA identifier, will be updated * @returns * - checked out IKE_SA if found * - NULL, if no such IKE_SA available @@ -82,25 +82,44 @@ struct ike_sa_manager_t { * @param other_id ID used by remote * @return checked out/created IKE_SA */ - ike_sa_t* (*checkout_by_id) (ike_sa_manager_t* this, - host_t *my_host, host_t* other_host, - identification_t *my_id, - identification_t *other_id); + ike_sa_t* (*checkout_by_peer) (ike_sa_manager_t* this, + host_t *my_host, host_t* other_host, + identification_t *my_id, + identification_t *other_id); /** - * @brief Check out an IKE_SA by protocol and SPI of one of its CHILD_SA. + * @brief Check out an IKE_SA a unique ID. * - * The kernel sends us expire messages for IPsec SAs. To fullfill - * this request, we must check out the IKE SA which contains the - * CHILD_SA the kernel wants to modify. + * Every IKE_SA and every CHILD_SA is uniquely identified by an ID. + * These checkout function uses, depending + * on the child parameter, the unique ID of the IKE_SA or the reqid + * of one of a IKE_SAs CHILD_SA. * * @param this the manager object - * @param reqid reqid of the CHILD_SA + * @param id unique ID of the object + * @param child TRUE to use CHILD, FALSE to use IKE_SA * @return * - checked out IKE_SA, if found * - NULL, if not found */ - ike_sa_t* (*checkout_by_child) (ike_sa_manager_t* this, u_int32_t reqid); + ike_sa_t* (*checkout_by_id) (ike_sa_manager_t* this, u_int32_t id, + bool child); + + /** + * @brief Check out an IKE_SA by the policy/connection name. + * + * Check out the IKE_SA by the connections name or by a CHILD_SAs policy + * name. + * + * @param this the manager object + * @param name name of the connection/policy + * @param child TRUE to use policy name, FALSE to use conn name + * @return + * - checked out IKE_SA, if found + * - NULL, if not found + */ + ike_sa_t* (*checkout_by_name) (ike_sa_manager_t* this, char *name, + bool child); /** * @brief Create an iterator over all stored IKE_SAs. @@ -121,8 +140,8 @@ struct ike_sa_manager_t { * The SA must be checked out again! * * @param this the manager object - * @param[in/out] ike_sa_id the SA identifier, will be updated - * @param[out] ike_sa checked out SA + * @param ike_sa_id the SA identifier, will be updated + * @param ike_sa checked out SA * @returns * - SUCCESS if checked in * - NOT_FOUND when not found (shouldn't happen!) @@ -130,47 +149,6 @@ struct ike_sa_manager_t { status_t (*checkin) (ike_sa_manager_t* this, ike_sa_t *ike_sa); /** - * @brief Delete a SA, which was not checked out. - * - * If the state allows it, the IKE SA is destroyed immediately. If it is - * in the state ESTABLSIHED, a delete message - * is sent to the remote peer, which has to be acknowledged. - * - * @warning do not use this when the SA is already checked out, this will - * deadlock! - * - * @param this the manager object - * @param[in/out] ike_sa_id the SA identifier - * @returns - * - SUCCESS if found - * - NOT_FOUND when no such SA is available - */ - status_t (*delete) (ike_sa_manager_t* this, ike_sa_id_t *ike_sa_id); - - /** - * @brief Delete a SA identified by its name, which was not checked out. - * - * Using delete_by_name allows the delete of IKE_SAs and CHILD_SAs. - * The supplied name may have one of the following format: - * - * name{x} => delete IKE_SA with "name" and unique id "x" - * name{} => delete all IKE_SAs with "name" - * name[x] => delete CHILD_SA with "name" and unique id "x" - * name[] => delete all CHILD_SAs with "name" - * name => delete all CHILD_SAs or IKE_SAs with "name" - * - * @warning do not use this when the SA is already checked out, this will - * deadlock! - * - * @param this the manager object - * @param name name in one of the format described above - * @returns - * - SUCCESS if found - * - NOT_FOUND when no such SA is available - */ - status_t (*delete_by_name) (ike_sa_manager_t* this, char *name); - - /** * @brief Destroy a checked out SA. * * The IKE SA is destroyed without notification of the remote peer. diff --git a/src/charon/sa/task_manager.c b/src/charon/sa/task_manager.c new file mode 100644 index 000000000..061b0239a --- /dev/null +++ b/src/charon/sa/task_manager.c @@ -0,0 +1,780 @@ +/** + * @file task_manager.c + * + * @brief Implementation of task_manager_t. + * + */ + +/* + * Copyright (C) 2007 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "task_manager.h" + +#include <daemon.h> +#include <sa/tasks/ike_init.h> +#include <sa/tasks/ike_natd.h> +#include <sa/tasks/ike_auth.h> +#include <sa/tasks/ike_cert.h> +#include <sa/tasks/ike_rekey.h> +#include <sa/tasks/ike_delete.h> +#include <sa/tasks/ike_config.h> +#include <sa/tasks/ike_dpd.h> +#include <sa/tasks/child_create.h> +#include <sa/tasks/child_rekey.h> +#include <sa/tasks/child_delete.h> +#include <encoding/payloads/delete_payload.h> +#include <queues/jobs/retransmit_job.h> + +typedef struct exchange_t exchange_t; + +/** + * An exchange in the air, used do detect and handle retransmission + */ +struct exchange_t { + + /** + * Message ID used for this transaction + */ + u_int32_t mid; + + /** + * generated packet for retransmission + */ + packet_t *packet; +}; + +typedef struct private_task_manager_t private_task_manager_t; + +/** + * private data of the task manager + */ +struct private_task_manager_t { + + /** + * public functions + */ + task_manager_t public; + + /** + * associated IKE_SA we are serving + */ + ike_sa_t *ike_sa; + + /** + * Exchange we are currently handling as responder + */ + struct { + /** + * Message ID of the exchange + */ + u_int32_t mid; + + /** + * packet for retransmission + */ + packet_t *packet; + + } responding; + + /** + * Exchange we are currently handling as initiator + */ + struct { + /** + * Message ID of the exchange + */ + u_int32_t mid; + + /** + * how many times we have retransmitted so far + */ + u_int retransmitted; + + /** + * packet for retransmission + */ + packet_t *packet; + + /** + * type of the initated exchange + */ + exchange_type_t type; + + } initiating; + + /** + * List of queued tasks not yet in action + */ + linked_list_t *queued_tasks; + + /** + * List of active tasks, initiated by ourselve + */ + linked_list_t *active_tasks; + + /** + * List of tasks initiated by peer + */ + linked_list_t *passive_tasks; + + /** + * ike_sa_init message we sent, stored here for later authentication + */ + packet_t *ike_sa_init; +}; + +/** + * move a task of a specific type from the queue to the active list + */ +static bool activate_task(private_task_manager_t *this, task_type_t type) +{ + iterator_t *iterator; + task_t *task; + bool found = FALSE; + + iterator = this->queued_tasks->create_iterator(this->queued_tasks, TRUE); + while (iterator->iterate(iterator, (void**)&task)) + { + if (task->get_type(task) == type) + { + DBG2(DBG_IKE, " activating %N task", task_type_names, type); + iterator->remove(iterator); + this->active_tasks->insert_last(this->active_tasks, task); + found = TRUE; + break; + } + } + iterator->destroy(iterator); + return found; +} + +/** + * Implementation of task_manager_t.retransmit + */ +static status_t retransmit(private_task_manager_t *this, u_int32_t message_id) +{ + if (message_id == this->initiating.mid) + { + u_int32_t timeout; + job_t *job; + + timeout = charon->configuration->get_retransmit_timeout( + charon->configuration, this->initiating.retransmitted); + if (timeout == 0) + { + DBG1(DBG_IKE, "giving up after %d retransmits", + this->initiating.retransmitted - 1); + return DESTROY_ME; + } + + if (this->initiating.retransmitted) + { + DBG1(DBG_IKE, "retransmit %d of request with message ID %d", + this->initiating.retransmitted, message_id); + } + this->initiating.retransmitted++; + + charon->send_queue->add(charon->send_queue, + this->initiating.packet->clone(this->initiating.packet)); + job = (job_t*)retransmit_job_create(this->initiating.mid, + this->ike_sa->get_id(this->ike_sa)); + charon->event_queue->add_relative(charon->event_queue, job, timeout); + } + return SUCCESS; +} + +/** + * build a request using the active task list + * Implementation of task_manager_t.initiate + */ +static status_t build_request(private_task_manager_t *this) +{ + iterator_t *iterator; + task_t *task; + message_t *message; + status_t status; + exchange_type_t exchange = 0; + + if (this->active_tasks->get_count(this->active_tasks) == 0) + { + DBG2(DBG_IKE, "activating new tasks"); + switch (this->ike_sa->get_state(this->ike_sa)) + { + case IKE_CREATED: + if (activate_task(this, IKE_INIT)) + { + exchange = IKE_SA_INIT; + activate_task(this, IKE_NATD); + activate_task(this, IKE_CERT); + activate_task(this, IKE_AUTHENTICATE); + activate_task(this, IKE_CONFIG); + activate_task(this, CHILD_CREATE); + } + break; + case IKE_ESTABLISHED: + if (activate_task(this, CHILD_CREATE)) + { + exchange = CREATE_CHILD_SA; + activate_task(this, IKE_CONFIG); + break; + } + if (activate_task(this, CHILD_DELETE)) + { + exchange = INFORMATIONAL; + break; + } + if (activate_task(this, CHILD_REKEY)) + { + exchange = CREATE_CHILD_SA; + break; + } + if (activate_task(this, IKE_DELETE)) + { + exchange = INFORMATIONAL; + break; + } + if (activate_task(this, IKE_REKEY)) + { + exchange = CREATE_CHILD_SA; + break; + } + if (activate_task(this, IKE_DEADPEER)) + { + exchange = INFORMATIONAL; + break; + } + case IKE_REKEYING: + if (activate_task(this, IKE_DELETE)) + { + exchange = INFORMATIONAL; + break; + } + case IKE_DELETING: + default: + break; + } + } + else + { + DBG2(DBG_IKE, "reinitiating already active tasks"); + iterator = this->active_tasks->create_iterator(this->active_tasks, TRUE); + while (iterator->iterate(iterator, (void**)&task)) + { + DBG2(DBG_IKE, " %N task", task_type_names, task->get_type(task)); + switch (task->get_type(task)) + { + case IKE_INIT: + exchange = IKE_SA_INIT; + break; + case IKE_AUTHENTICATE: + exchange = IKE_AUTH; + break; + default: + continue; + } + break; + } + iterator->destroy(iterator); + } + + if (exchange == 0) + { + DBG2(DBG_IKE, "nothing to initiate"); + /* nothing to do yet... */ + return SUCCESS; + } + + message = message_create(); + message->set_message_id(message, this->initiating.mid); + message->set_exchange_type(message, exchange); + this->initiating.type = exchange; + this->initiating.retransmitted = 0; + + iterator = this->active_tasks->create_iterator(this->active_tasks, TRUE); + while (iterator->iterate(iterator, (void*)&task)) + { + switch (task->build(task, message)) + { + case SUCCESS: + /* task completed, remove it */ + iterator->remove(iterator); + task->destroy(task); + break; + case NEED_MORE: + /* processed, but task needs another exchange */ + break; + case FAILED: + default: + /* critical failure, destroy IKE_SA */ + iterator->destroy(iterator); + message->destroy(message); + return DESTROY_ME; + } + } + iterator->destroy(iterator); + + DESTROY_IF(this->initiating.packet); + status = this->ike_sa->generate_message(this->ike_sa, message, + &this->initiating.packet); + message->destroy(message); + if (status != SUCCESS) + { + /* message generation failed. There is nothing more to do than to + * close the SA */ + return DESTROY_ME; + } + + return retransmit(this, this->initiating.mid); +} + +/** + * handle an incoming response message + */ +static status_t process_response(private_task_manager_t *this, + message_t *message) +{ + iterator_t *iterator; + task_t *task; + + if (message->get_exchange_type(message) != this->initiating.type) + { + DBG1(DBG_IKE, "received %N response, but expected %N", + exchange_type_names, message->get_exchange_type(message), + exchange_type_names, this->initiating.type); + return DESTROY_ME; + } + + iterator = this->active_tasks->create_iterator(this->active_tasks, TRUE); + while (iterator->iterate(iterator, (void*)&task)) + { + switch (task->process(task, message)) + { + case SUCCESS: + /* task completed, remove it */ + iterator->remove(iterator); + task->destroy(task); + break; + case NEED_MORE: + /* processed, but task needs another exchange */ + break; + case FAILED: + default: + /* critical failure, destroy IKE_SA */ + iterator->destroy(iterator); + return DESTROY_ME; + } + } + iterator->destroy(iterator); + + this->initiating.mid++; + + return build_request(this); +} + +/** + * build a response depending on the "passive" task list + */ +static status_t build_response(private_task_manager_t *this, + exchange_type_t exchange) +{ + iterator_t *iterator; + task_t *task; + message_t *message; + bool delete = FALSE; + status_t status; + + message = message_create(); + message->set_exchange_type(message, exchange); + message->set_message_id(message, this->responding.mid); + message->set_request(message, FALSE); + + iterator = this->passive_tasks->create_iterator(this->passive_tasks, TRUE); + while (iterator->iterate(iterator, (void*)&task)) + { + switch (task->build(task, message)) + { + case SUCCESS: + /* task completed, remove it */ + iterator->remove(iterator); + task->destroy(task); + break; + case NEED_MORE: + /* processed, but task needs another exchange */ + break; + case FAILED: + default: + /* destroy IKE_SA, but SEND response first */ + delete = TRUE; + break; + } + if (delete) + { + break; + } + } + iterator->destroy(iterator); + + /* message complete, send it */ + DESTROY_IF(this->responding.packet); + status = this->ike_sa->generate_message(this->ike_sa, message, + &this->responding.packet); + message->destroy(message); + if (status != SUCCESS) + { + return DESTROY_ME; + } + + charon->send_queue->add(charon->send_queue, + this->responding.packet->clone(this->responding.packet)); + if (delete) + { + return DESTROY_ME; + } + return SUCCESS; +} + +/** + * handle an incoming request message + */ +static status_t process_request(private_task_manager_t *this, + message_t *message) +{ + iterator_t *iterator; + task_t *task = NULL; + exchange_type_t exchange; + payload_t *payload; + notify_payload_t *notify; + + exchange = message->get_exchange_type(message); + + /* create tasks depending on request type */ + switch (exchange) + { + case IKE_SA_INIT: + { + task = (task_t*)ike_init_create(this->ike_sa, FALSE, NULL); + this->passive_tasks->insert_last(this->passive_tasks, task); + task = (task_t*)ike_natd_create(this->ike_sa, FALSE); + this->passive_tasks->insert_last(this->passive_tasks, task); + task = (task_t*)ike_cert_create(this->ike_sa, FALSE); + this->passive_tasks->insert_last(this->passive_tasks, task); + task = (task_t*)ike_config_create(this->ike_sa, NULL); + this->passive_tasks->insert_last(this->passive_tasks, task); + task = (task_t*)ike_auth_create(this->ike_sa, FALSE); + this->passive_tasks->insert_last(this->passive_tasks, task); + task = (task_t*)child_create_create(this->ike_sa, NULL); + this->passive_tasks->insert_last(this->passive_tasks, task); + break; + } + case CREATE_CHILD_SA: + { + bool notify_found = FALSE, ts_found = FALSE; + iterator = message->get_payload_iterator(message); + while (iterator->iterate(iterator, (void**)&payload)) + { + switch (payload->get_type(payload)) + { + case NOTIFY: + { + /* if we find a rekey notify, its CHILD_SA rekeying */ + notify = (notify_payload_t*)payload; + if (notify->get_notify_type(notify) == REKEY_SA && + (notify->get_protocol_id(notify) == PROTO_AH || + notify->get_protocol_id(notify) == PROTO_ESP)) + { + notify_found = TRUE; + } + break; + } + case TRAFFIC_SELECTOR_INITIATOR: + case TRAFFIC_SELECTOR_RESPONDER: + { + /* if we don't find a TS, its IKE rekeying */ + ts_found = TRUE; + break; + } + default: + break; + } + } + iterator->destroy(iterator); + + if (ts_found) + { + if (notify_found) + { + task = (task_t*)child_rekey_create(this->ike_sa, NULL); + } + else + { + task = (task_t*)child_create_create(this->ike_sa, NULL); + } + } + else + { + task = (task_t*)ike_rekey_create(this->ike_sa, FALSE); + } + this->passive_tasks->insert_last(this->passive_tasks, task); + + break; + } + case INFORMATIONAL: + { + delete_payload_t *delete; + + delete = (delete_payload_t*)message->get_payload(message, DELETE); + if (delete) + { + if (delete->get_protocol_id(delete) == PROTO_IKE) + { + task = (task_t*)ike_delete_create(this->ike_sa, FALSE); + this->passive_tasks->insert_last(this->passive_tasks, task); + } + else + { + task = (task_t*)child_delete_create(this->ike_sa, NULL); + this->passive_tasks->insert_last(this->passive_tasks, task); + } + } + else + { + task = (task_t*)ike_dpd_create(FALSE); + this->passive_tasks->insert_last(this->passive_tasks, task); + } + break; + } + default: + break; + } + + /* let the tasks process the message */ + iterator = this->passive_tasks->create_iterator(this->passive_tasks, TRUE); + while (iterator->iterate(iterator, (void*)&task)) + { + switch (task->process(task, message)) + { + case SUCCESS: + /* task completed, remove it */ + iterator->remove(iterator); + task->destroy(task); + break; + case NEED_MORE: + /* processed, but task needs at least another call to build() */ + break; + case FAILED: + default: + /* critical failure, destroy IKE_SA */ + iterator->destroy(iterator); + return DESTROY_ME; + } + } + iterator->destroy(iterator); + + return build_response(this, exchange); +} + +/** + * Implementation of task_manager_t.process_message + */ +static status_t process_message(private_task_manager_t *this, message_t *msg) +{ + u_int32_t mid = msg->get_message_id(msg); + + if (msg->get_request(msg)) + { + if (mid == this->responding.mid) + { + if (process_request(this, msg) != SUCCESS) + { + return DESTROY_ME; + } + this->responding.mid++; + } + else if ((mid == this->responding.mid - 1) && this->responding.packet) + { + DBG1(DBG_IKE, "received retransmit of request with ID %d, " + "retransmitting response", mid); + charon->send_queue->add(charon->send_queue, + this->responding.packet->clone( + this->responding.packet)); + } + else + { + DBG1(DBG_IKE, "received message ID %d, excepted %d. Ignored", + mid, this->responding.mid); + } + } + else + { + if (mid == this->initiating.mid) + { + if (process_response(this, msg) != SUCCESS) + { + return DESTROY_ME; + } + } + else + { + DBG1(DBG_IKE, "received message ID %d, excepted %d. Ignored", + mid, this->initiating.mid); + return SUCCESS; + } + } + return SUCCESS; +} + +/** + * Implementation of task_manager_t.queue_task + */ +static void queue_task(private_task_manager_t *this, task_t *task) +{ + DBG2(DBG_IKE, "queueing %N task", task_type_names, task->get_type(task)); + this->queued_tasks->insert_last(this->queued_tasks, task); +} + +/** + * Implementation of task_manager_t.adopt_tasks + */ +static void adopt_tasks(private_task_manager_t *this, private_task_manager_t *other) +{ + task_t *task; + + /* move queued tasks from other to this */ + while (other->queued_tasks->remove_last(other->queued_tasks, + (void**)&task) == SUCCESS) + { + task->migrate(task, this->ike_sa); + this->queued_tasks->insert_first(this->queued_tasks, task); + } + + /* reset active tasks and move them to others queued tasks */ + while (other->active_tasks->remove_last(other->active_tasks, + (void**)&task) == SUCCESS) + { + task->migrate(task, this->ike_sa); + this->queued_tasks->insert_first(this->queued_tasks, task); + } +} + +/** + * Implementation of task_manager_t.busy + */ +static bool busy(private_task_manager_t *this) +{ + return (this->active_tasks->get_count(this->active_tasks) > 0); +} + +/** + * Implementation of task_manager_t.reset + */ +static void reset(private_task_manager_t *this) +{ + task_t *task; + + /* reset message counters and retransmit packets */ + DESTROY_IF(this->responding.packet); + DESTROY_IF(this->initiating.packet); + DESTROY_IF(this->ike_sa_init); + this->responding.packet = NULL; + this->initiating.packet = NULL; + this->ike_sa_init = NULL; + this->responding.mid = 0; + this->initiating.mid = -1; + + /* reset active tasks */ + while (this->active_tasks->remove_last(this->active_tasks, + (void**)&task) == SUCCESS) + { + task->migrate(task, this->ike_sa); + this->queued_tasks->insert_first(this->queued_tasks, task); + } +} + +/** + * Implementation of task_manager_t.destroy + */ +static void destroy(private_task_manager_t *this) +{ + task_t *task; + + this->queued_tasks->destroy_offset(this->queued_tasks, + offsetof(task_t, destroy)); + this->passive_tasks->destroy_offset(this->passive_tasks, + offsetof(task_t, destroy)); + + /* emmit outstanding signals for tasks */ + while (this->active_tasks->remove_last(this->active_tasks, + (void**)&task) == SUCCESS) + { + switch (task->get_type(task)) + { + case IKE_AUTH: + SIG(IKE_UP_FAILED, "establishing IKE_SA failed"); + break; + case IKE_DELETE: + SIG(IKE_DOWN_FAILED, "deleteing IKE_SA properly failed"); + break; + case IKE_REKEY: + SIG(IKE_REKEY_FAILED, "rekeying IKE_SA failed"); + break; + case CHILD_CREATE: + SIG(CHILD_UP_FAILED, "establishing CHILD_SA failed"); + break; + case CHILD_DELETE: + SIG(CHILD_DOWN_FAILED, "deleting CHILD_SA failed"); + break; + case CHILD_REKEY: + SIG(IKE_REKEY_FAILED, "rekeying CHILD_SA failed"); + break; + default: + break; + } + task->destroy(task); + } + this->active_tasks->destroy(this->active_tasks); + DESTROY_IF(this->responding.packet); + DESTROY_IF(this->initiating.packet); + DESTROY_IF(this->ike_sa_init); + free(this); +} + +/* + * see header file + */ +task_manager_t *task_manager_create(ike_sa_t *ike_sa) +{ + private_task_manager_t *this = malloc_thing(private_task_manager_t); + + this->public.process_message = (status_t(*)(task_manager_t*,message_t*))process_message; + this->public.queue_task = (void(*)(task_manager_t*,task_t*))queue_task; + this->public.initiate = (status_t(*)(task_manager_t*))build_request; + this->public.retransmit = (status_t(*)(task_manager_t*,u_int32_t))retransmit; + this->public.reset = (void(*)(task_manager_t*))reset; + this->public.adopt_tasks = (void(*)(task_manager_t*,task_manager_t*))adopt_tasks; + this->public.busy = (bool(*)(task_manager_t*))busy; + this->public.destroy = (void(*)(task_manager_t*))destroy; + + this->ike_sa = ike_sa; + this->responding.packet = NULL; + this->initiating.packet = NULL; + this->responding.mid = 0; + this->initiating.mid = 0; + this->queued_tasks = linked_list_create(); + this->active_tasks = linked_list_create(); + this->passive_tasks = linked_list_create(); + this->ike_sa_init = NULL; + + return &this->public; +} diff --git a/src/charon/sa/task_manager.h b/src/charon/sa/task_manager.h new file mode 100644 index 000000000..c766d4a65 --- /dev/null +++ b/src/charon/sa/task_manager.h @@ -0,0 +1,144 @@ +/** + * @file task_manager.h + * + * @brief Interface of task_manager_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 TASK_MANAGER_H_ +#define TASK_MANAGER_H_ + +typedef struct task_manager_t task_manager_t; + +#include <library.h> +#include <encoding/message.h> +#include <sa/ike_sa.h> +#include <sa/tasks/task.h> + +/** + * @brief The task manager, juggles task and handles message exchanges. + * + * On incoming requests, the task manager creates new tasks on demand and + * juggles the request through all available tasks. Each task inspects the + * request and adds payloads as necessary to the response. + * On outgoing requests, the task manager delivers the request through the tasks + * to build it, the response gets processed by each task to complete. + * The task manager has an internal Queue to store task which should get + * completed. + * For the initial IKE_SA setup, several tasks are queued: One for the + * unauthenticated IKE_SA setup, one for authentication, one for CHILD_SA setup + * and maybe one for virtual IP assignement. + * + * @b Constructors: + * - task_manager_create() + * + * @ingroup sa + */ +struct task_manager_t { + + /** + * @brief Process an incoming message. + * + * @param this calling object + * @param message message to add payloads to + * @return + * - DESTROY_ME if IKE_SA must be closed + * - SUCCESS otherwise + */ + status_t (*process_message) (task_manager_t *this, message_t *message); + + /** + * @brief Initiate an exchange with the currently queued tasks. + * + * @param this calling object + */ + status_t (*initiate) (task_manager_t *this); + + /** + * @brief Queue a task in the manager. + * + * @param this calling object + * @param task task to queue + */ + void (*queue_task) (task_manager_t *this, task_t *task); + + /** + * @brief Retransmit a request if it hasn't been acknowledged yet. + * + * A return value of INVALID_STATE means that the message was already + * acknowledged and has not to be retransmitted. A return value of SUCCESS + * means retransmission was required and the message has been resent. + * + * @param this calling object + * @param message_id ID of the message to retransmit + * @return + * - INVALID_STATE if retransmission not required + * - SUCCESS if retransmission sent + */ + status_t (*retransmit) (task_manager_t *this, u_int32_t message_id); + + /** + * @brief Migrate all tasks from other to this. + * + * To rekey or reestablish an IKE_SA completely, all queued or active + * tasks should get migrated to the new IKE_SA. + * + * @param this manager which gets all tasks + * @param other manager which gives away its tasks + */ + void (*adopt_tasks) (task_manager_t *this, task_manager_t *other); + + /** + * @brief Reset message ID counters of the task manager. + * + * The IKEv2 protocol requires to restart exchanges with message IDs + * reset to zero (INVALID_KE_PAYLOAD, COOKIES, ...). The reset() method + * resets the message IDs and resets all active tasks using the migrate() + * method. + * + * @param this calling object + * @param other manager which gives away its tasks + */ + void (*reset) (task_manager_t *this); + + /** + * @brief Check if we are currently waiting for a reply. + * + * @param this calling object + * @return TRUE if we are waiting, FALSE otherwise + */ + bool (*busy) (task_manager_t *this); + + /** + * @brief Destroy the task_manager_t. + * + * @param this calling object + */ + void (*destroy) (task_manager_t *this); +}; + +/** + * @brief Create an instance of the task manager. + * + * @param ike_sa IKE_SA to manage. + * + * @ingroup sa + */ +task_manager_t *task_manager_create(ike_sa_t *ike_sa); + +#endif /* TASK_MANAGER_H_ */ diff --git a/src/charon/sa/tasks/child_create.c b/src/charon/sa/tasks/child_create.c new file mode 100644 index 000000000..165baa4be --- /dev/null +++ b/src/charon/sa/tasks/child_create.c @@ -0,0 +1,718 @@ +/** + * @file child_create.c + * + * @brief Implementation of the child_create task. + * + */ + +/* + * 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 "child_create.h" + +#include <daemon.h> +#include <crypto/diffie_hellman.h> +#include <encoding/payloads/sa_payload.h> +#include <encoding/payloads/ts_payload.h> +#include <encoding/payloads/nonce_payload.h> +#include <encoding/payloads/notify_payload.h> + + +typedef struct private_child_create_t private_child_create_t; + +/** + * Private members of a child_create_t task. + */ +struct private_child_create_t { + + /** + * Public methods and task_t interface. + */ + child_create_t public; + + /** + * Assigned IKE_SA. + */ + ike_sa_t *ike_sa; + + /** + * Are we the initiator? + */ + bool initiator; + + /** + * nonce chosen by us + */ + chunk_t my_nonce; + + /** + * nonce chosen by peer + */ + chunk_t other_nonce; + + /** + * policy to create the CHILD_SA from + */ + policy_t *policy; + + /** + * list of proposal candidates + */ + linked_list_t *proposals; + + /** + * selected proposal to use for CHILD_SA + */ + proposal_t *proposal; + + /** + * traffic selectors for initiators side + */ + linked_list_t *tsi; + + /** + * traffic selectors for responders side + */ + linked_list_t *tsr; + + /** + * mode the new CHILD_SA uses (transport/tunnel/beet) + */ + mode_t mode; + + /** + * reqid to use if we are rekeying + */ + u_int32_t reqid; + + /** + * CHILD_SA which gets established + */ + child_sa_t *child_sa; +}; + +/** + * get the nonce from a message + */ +static status_t get_nonce(message_t *message, chunk_t *nonce) +{ + nonce_payload_t *payload; + + payload = (nonce_payload_t*)message->get_payload(message, NONCE); + if (payload == NULL) + { + return FAILED; + } + *nonce = payload->get_nonce(payload); + return NEED_MORE; +} + +/** + * generate a new nonce to include in a CREATE_CHILD_SA message + */ +static status_t generate_nonce(chunk_t *nonce) +{ + status_t status; + randomizer_t *randomizer = randomizer_create(); + + status = randomizer->allocate_pseudo_random_bytes(randomizer, NONCE_SIZE, + nonce); + randomizer->destroy(randomizer); + if (status != SUCCESS) + { + DBG1(DBG_IKE, "error generating random nonce value"); + return FAILED; + } + return SUCCESS; +} + +/** + * Check a list of traffic selectors if any selector belongs to host + */ +static bool ts_list_is_host(linked_list_t *list, host_t *host) +{ + traffic_selector_t *ts; + bool is_host = TRUE; + iterator_t *iterator = list->create_iterator(list, TRUE); + + while (is_host && iterator->iterate(iterator, (void**)&ts)) + { + is_host = is_host && ts->is_host(ts, host); + } + iterator->destroy(iterator); + return is_host; +} + +/** + * Install a CHILD_SA for usage + */ +static status_t select_and_install(private_child_create_t *this) +{ + prf_plus_t *prf_plus; + status_t status; + chunk_t nonce_i, nonce_r, seed; + linked_list_t *my_ts, *other_ts; + host_t *me, *other, *other_vip, *my_vip; + + if (this->proposals == NULL || this->tsi == NULL || this->tsr == NULL) + { + SIG(CHILD_UP_FAILED, "SA/TS payloads missing in message"); + return FAILED; + } + + if (this->initiator) + { + nonce_i = this->my_nonce; + nonce_r = this->other_nonce; + my_ts = this->tsi; + other_ts = this->tsr; + } + else + { + nonce_r = this->my_nonce; + nonce_i = this->other_nonce; + my_ts = this->tsr; + other_ts = this->tsi; + } + + me = this->ike_sa->get_my_host(this->ike_sa); + other = this->ike_sa->get_other_host(this->ike_sa); + my_vip = this->ike_sa->get_virtual_ip(this->ike_sa, TRUE); + other_vip = this->ike_sa->get_virtual_ip(this->ike_sa, FALSE); + + this->proposal = this->policy->select_proposal(this->policy, this->proposals); + + if (this->initiator && my_vip) + { /* if we have a virtual IP, shorten our TS to the minimum */ + my_ts = this->policy->select_my_traffic_selectors(this->policy, my_ts, + my_vip); + } + else + { /* shorten in the host2host case only */ + my_ts = this->policy->select_my_traffic_selectors(this->policy, + my_ts, me); + } + if (other_vip) + { /* if other has a virtual IP, shorten it's traffic selectors to it */ + other_ts = this->policy->select_other_traffic_selectors(this->policy, + other_ts, other_vip); + } + else + { /* use his host for the host2host case */ + other_ts = this->policy->select_other_traffic_selectors(this->policy, + other_ts, other); + } + this->tsr->destroy_offset(this->tsr, offsetof(traffic_selector_t, destroy)); + this->tsi->destroy_offset(this->tsi, offsetof(traffic_selector_t, destroy)); + if (this->initiator) + { + this->tsi = my_ts; + this->tsr = other_ts; + } + else + { + this->tsr = my_ts; + this->tsi = other_ts; + } + + if (this->proposal == NULL || + this->tsi->get_count(this->tsi) == 0 || + this->tsr->get_count(this->tsr) == 0) + { + SIG(CHILD_UP_FAILED, "no acceptable proposal found"); + return FAILED; + } + + if (!this->initiator) + { + /* check if requested mode is acceptable, downgrade if required */ + switch (this->mode) + { + case MODE_TRANSPORT: + if (!ts_list_is_host(this->tsi, other) || + !ts_list_is_host(this->tsr, me)) + { + this->mode = MODE_TUNNEL; + DBG1(DBG_IKE, "not using tranport mode, not host-to-host"); + } + else if (this->ike_sa->is_natt_enabled(this->ike_sa)) + { + this->mode = MODE_TUNNEL; + DBG1(DBG_IKE, "not using tranport mode, connection NATed"); + } + break; + case MODE_BEET: + if (!ts_list_is_host(this->tsi, NULL) || + !ts_list_is_host(this->tsr, NULL)) + { + this->mode = MODE_TUNNEL; + DBG1(DBG_IKE, "not using BEET mode, not host-to-host"); + } + break; + default: + break; + } + } + + seed = chunk_cata("cc", nonce_i, nonce_r); + prf_plus = prf_plus_create(this->ike_sa->get_child_prf(this->ike_sa), seed); + + if (this->initiator) + { + status = this->child_sa->update(this->child_sa, this->proposal, + this->mode, prf_plus); + } + else + { + status = this->child_sa->add(this->child_sa, this->proposal, + this->mode, prf_plus); + } + prf_plus->destroy(prf_plus); + + if (status != SUCCESS) + { + SIG(CHILD_UP_FAILED, "unable to install IPsec SA (SAD) in kernel"); + return status; + } + + status = this->child_sa->add_policies(this->child_sa, my_ts, other_ts, + this->mode); + + if (status != SUCCESS) + { + SIG(CHILD_UP_FAILED, "unable to install IPsec policies (SPD) in kernel"); + return status; + } + /* add to IKE_SA, and remove from task */ + this->child_sa->set_state(this->child_sa, CHILD_INSTALLED); + this->ike_sa->add_child_sa(this->ike_sa, this->child_sa); + this->child_sa = NULL; + return SUCCESS; +} + +/** + * build the payloads for the message + */ +static void build_payloads(private_child_create_t *this, message_t *message) +{ + sa_payload_t *sa_payload; + ts_payload_t *ts_payload; + nonce_payload_t *nonce_payload; + + /* add SA payload */ + if (this->initiator) + { + sa_payload = sa_payload_create_from_proposal_list(this->proposals); + } + else + { + sa_payload = sa_payload_create_from_proposal(this->proposal); + } + message->add_payload(message, (payload_t*)sa_payload); + + /* add TSi/TSr payloads */ + ts_payload = ts_payload_create_from_traffic_selectors(TRUE, this->tsi); + message->add_payload(message, (payload_t*)ts_payload); + ts_payload = ts_payload_create_from_traffic_selectors(FALSE, this->tsr); + message->add_payload(message, (payload_t*)ts_payload); + + /* add nonce payload if not in IKE_AUTH */ + if (message->get_exchange_type(message) == CREATE_CHILD_SA) + { + nonce_payload = nonce_payload_create(); + nonce_payload->set_nonce(nonce_payload, this->my_nonce); + message->add_payload(message, (payload_t*)nonce_payload); + } + + /* add a notify if we are not in tunnel mode */ + switch (this->mode) + { + case MODE_TRANSPORT: + message->add_notify(message, FALSE, USE_TRANSPORT_MODE, chunk_empty); + break; + case MODE_BEET: + message->add_notify(message, FALSE, USE_BEET_MODE, chunk_empty); + break; + default: + break; + } +} + +/** + * Read payloads from message + */ +static void process_payloads(private_child_create_t *this, message_t *message) +{ + iterator_t *iterator; + payload_t *payload; + sa_payload_t *sa_payload; + ts_payload_t *ts_payload; + notify_payload_t *notify_payload; + + /* defaults to TUNNEL mode */ + this->mode = MODE_TUNNEL; + + iterator = message->get_payload_iterator(message); + while (iterator->iterate(iterator, (void**)&payload)) + { + switch (payload->get_type(payload)) + { + case SECURITY_ASSOCIATION: + sa_payload = (sa_payload_t*)payload; + this->proposals = sa_payload->get_proposals(sa_payload); + break; + case TRAFFIC_SELECTOR_INITIATOR: + ts_payload = (ts_payload_t*)payload; + this->tsi = ts_payload->get_traffic_selectors(ts_payload); + break; + case TRAFFIC_SELECTOR_RESPONDER: + ts_payload = (ts_payload_t*)payload; + this->tsr = ts_payload->get_traffic_selectors(ts_payload); + break; + case NOTIFY: + notify_payload = (notify_payload_t*)payload; + switch (notify_payload ->get_notify_type(notify_payload )) + { + case USE_TRANSPORT_MODE: + this->mode = MODE_TRANSPORT; + break; + case USE_BEET_MODE: + this->mode = MODE_BEET; + break; + default: + break; + } + break; + default: + break; + } + } + iterator->destroy(iterator); +} + +/** + * Implementation of task_t.build for initiator + */ +static status_t build_i(private_child_create_t *this, message_t *message) +{ + host_t *me, *other, *vip; + + switch (message->get_exchange_type(message)) + { + case IKE_SA_INIT: + return get_nonce(message, &this->my_nonce); + case CREATE_CHILD_SA: + if (generate_nonce(&this->my_nonce) != SUCCESS) + { + message->add_notify(message, FALSE, NO_PROPOSAL_CHOSEN, chunk_empty); + return SUCCESS; + } + break; + default: + break; + } + + SIG(CHILD_UP_START, "establishing CHILD_SA"); + + me = this->ike_sa->get_my_host(this->ike_sa); + other = this->ike_sa->get_other_host(this->ike_sa); + vip = this->policy->get_virtual_ip(this->policy, NULL); + + if (vip) + { /* propose a 0.0.0.0/0 subnet when we use virtual ip */ + this->tsi = this->policy->get_my_traffic_selectors(this->policy, NULL); + vip->destroy(vip); + } + else + { /* but shorten a 0.0.0.0/0 subnet to the actual address if host2host */ + this->tsi = this->policy->get_my_traffic_selectors(this->policy, me); + } + this->tsr = this->policy->get_other_traffic_selectors(this->policy, other); + this->proposals = this->policy->get_proposals(this->policy); + this->mode = this->policy->get_mode(this->policy); + + this->child_sa = child_sa_create(me, other, + this->ike_sa->get_my_id(this->ike_sa), + this->ike_sa->get_other_id(this->ike_sa), + this->policy, this->reqid, + this->ike_sa->is_natt_enabled(this->ike_sa)); + + if (this->child_sa->alloc(this->child_sa, this->proposals) != SUCCESS) + { + SIG(CHILD_UP_FAILED, "unable to allocate SPIs from kernel"); + return FAILED; + } + + build_payloads(this, message); + + this->tsi->destroy_offset(this->tsi, offsetof(traffic_selector_t, destroy)); + this->tsr->destroy_offset(this->tsr, offsetof(traffic_selector_t, destroy)); + this->proposals->destroy_offset(this->proposals, offsetof(proposal_t, destroy)); + this->tsi = NULL; + this->tsr = NULL; + this->proposals = NULL; + + return NEED_MORE; +} + +/** + * Implementation of task_t.process for initiator + */ +static status_t process_r(private_child_create_t *this, message_t *message) +{ + switch (message->get_exchange_type(message)) + { + case IKE_SA_INIT: + return get_nonce(message, &this->other_nonce); + case CREATE_CHILD_SA: + get_nonce(message, &this->other_nonce); + break; + default: + break; + } + + process_payloads(this, message); + + if (this->tsi == NULL || this->tsr == NULL) + { + DBG1(DBG_IKE, "TS payload missing in message"); + return NEED_MORE; + } + + this->policy = charon->policies->get_policy(charon->policies, + this->ike_sa->get_my_id(this->ike_sa), + this->ike_sa->get_other_id(this->ike_sa), + this->tsr, this->tsi, + this->ike_sa->get_my_host(this->ike_sa), + this->ike_sa->get_other_host(this->ike_sa)); + + if (this->policy && this->ike_sa->get_policy(this->ike_sa) == NULL) + { + this->ike_sa->set_policy(this->ike_sa, this->policy); + } + + return NEED_MORE; +} + +/** + * Implementation of task_t.build for responder + */ +static status_t build_r(private_child_create_t *this, message_t *message) +{ + switch (message->get_exchange_type(message)) + { + case IKE_SA_INIT: + return get_nonce(message, &this->my_nonce); + case CREATE_CHILD_SA: + if (generate_nonce(&this->my_nonce) != SUCCESS) + { + message->add_notify(message, FALSE, NO_PROPOSAL_CHOSEN, chunk_empty); + return SUCCESS; + } + break; + default: + break; + } + + if (this->policy == NULL) + { + SIG(CHILD_UP_FAILED, "received traffic selectors inacceptable"); + message->add_notify(message, FALSE, TS_UNACCEPTABLE, chunk_empty); + return SUCCESS; + } + + this->child_sa = child_sa_create(this->ike_sa->get_my_host(this->ike_sa), + this->ike_sa->get_other_host(this->ike_sa), + this->ike_sa->get_my_id(this->ike_sa), + this->ike_sa->get_other_id(this->ike_sa), + this->policy, this->reqid, + this->ike_sa->is_natt_enabled(this->ike_sa)); + + if (select_and_install(this) != SUCCESS) + { + message->add_notify(message, FALSE, NO_PROPOSAL_CHOSEN, chunk_empty); + return SUCCESS; + } + + build_payloads(this, message); + + SIG(CHILD_UP_SUCCESS, "established CHILD_SA successfully"); + + return SUCCESS; +} + +/** + * Implementation of task_t.process for initiator + */ +static status_t process_i(private_child_create_t *this, message_t *message) +{ + iterator_t *iterator; + payload_t *payload; + status_t status; + + switch (message->get_exchange_type(message)) + { + case IKE_SA_INIT: + return get_nonce(message, &this->other_nonce); + case CREATE_CHILD_SA: + get_nonce(message, &this->other_nonce); + break; + default: + break; + } + + /* check for erronous notifies */ + iterator = message->get_payload_iterator(message); + while (iterator->iterate(iterator, (void**)&payload)) + { + if (payload->get_type(payload) == NOTIFY) + { + notify_payload_t *notify = (notify_payload_t*)payload; + notify_type_t type = notify->get_notify_type(notify); + + if (type < 16383) + { + SIG(CHILD_UP_FAILED, "received %N notify error", + notify_type_names, type); + iterator->destroy(iterator); + /* an error in CHILD_SA creation is not critical */ + return SUCCESS; + } + } + } + iterator->destroy(iterator); + + process_payloads(this, message); + + status = select_and_install(this); + + SIG(CHILD_UP_SUCCESS, "established CHILD_SA successfully"); + + return SUCCESS; +} + +/** + * Implementation of task_t.get_type + */ +static task_type_t get_type(private_child_create_t *this) +{ + return CHILD_CREATE; +} + +/** + * Implementation of child_create_t.use_reqid + */ +static void use_reqid(private_child_create_t *this, u_int32_t reqid) +{ + this->reqid = reqid; +} + +/** + * Implementation of task_t.migrate + */ +static void migrate(private_child_create_t *this, ike_sa_t *ike_sa) +{ + chunk_free(&this->my_nonce); + chunk_free(&this->other_nonce); + if (this->tsi) + { + this->tsr->destroy_offset(this->tsr, offsetof(traffic_selector_t, destroy)); + } + if (this->tsr) + { + this->tsi->destroy_offset(this->tsi, offsetof(traffic_selector_t, destroy)); + } + DESTROY_IF(this->child_sa); + DESTROY_IF(this->proposal); + if (this->proposals) + { + this->proposals->destroy_offset(this->proposals, offsetof(proposal_t, destroy)); + } + + this->ike_sa = ike_sa; + this->proposals = NULL; + this->tsi = NULL; + this->tsr = NULL; + this->child_sa = NULL; + this->mode = MODE_TUNNEL; + this->reqid = 0; +} + +/** + * Implementation of task_t.destroy + */ +static void destroy(private_child_create_t *this) +{ + chunk_free(&this->my_nonce); + chunk_free(&this->other_nonce); + if (this->tsi) + { + this->tsr->destroy_offset(this->tsr, offsetof(traffic_selector_t, destroy)); + } + if (this->tsr) + { + this->tsi->destroy_offset(this->tsi, offsetof(traffic_selector_t, destroy)); + } + DESTROY_IF(this->child_sa); + DESTROY_IF(this->proposal); + if (this->proposals) + { + this->proposals->destroy_offset(this->proposals, offsetof(proposal_t, destroy)); + } + + DESTROY_IF(this->policy); + free(this); +} + +/* + * Described in header. + */ +child_create_t *child_create_create(ike_sa_t *ike_sa, policy_t *policy) +{ + private_child_create_t *this = malloc_thing(private_child_create_t); + + this->public.use_reqid = (void(*)(child_create_t*,u_int32_t))use_reqid; + this->public.task.get_type = (task_type_t(*)(task_t*))get_type; + this->public.task.migrate = (void(*)(task_t*,ike_sa_t*))migrate; + this->public.task.destroy = (void(*)(task_t*))destroy; + if (policy) + { + this->public.task.build = (status_t(*)(task_t*,message_t*))build_i; + this->public.task.process = (status_t(*)(task_t*,message_t*))process_i; + this->initiator = TRUE; + policy->get_ref(policy); + } + else + { + this->public.task.build = (status_t(*)(task_t*,message_t*))build_r; + this->public.task.process = (status_t(*)(task_t*,message_t*))process_r; + this->initiator = FALSE; + } + + this->ike_sa = ike_sa; + this->policy = policy; + this->my_nonce = chunk_empty; + this->other_nonce = chunk_empty; + this->proposals = NULL; + this->proposal = NULL; + this->tsi = NULL; + this->tsr = NULL; + this->child_sa = NULL; + this->mode = MODE_TUNNEL; + this->reqid = 0; + + return &this->public; +} diff --git a/src/charon/sa/tasks/child_create.h b/src/charon/sa/tasks/child_create.h new file mode 100644 index 000000000..1644865b9 --- /dev/null +++ b/src/charon/sa/tasks/child_create.h @@ -0,0 +1,72 @@ +/** + * @file child_create.h + * + * @brief Interface child_create_t. + * + */ + +/* + * Copyright (C) 2007 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#ifndef CHILD_CREATE_H_ +#define CHILD_CREATE_H_ + +typedef struct child_create_t child_create_t; + +#include <library.h> +#include <sa/ike_sa.h> +#include <sa/tasks/task.h> +#include <config/policies/policy.h> + +/** + * @brief Task of type CHILD_CREATE, established a new CHILD_SA. + * + * This task may be included in the IKE_AUTH message or in a separate + * CREATE_CHILD_SA exchange. + * + * @b Constructors: + * - child_create_create() + * + * @ingroup tasks + */ +struct child_create_t { + + /** + * Implements the task_t interface + */ + task_t task; + + /** + * @brief Use a specific reqid for the CHILD_SA. + * + * When this task is used for rekeying, the same reqid is used + * for the new CHILD_SA. + * + * @param this calling object + * @param reqid reqid to use + */ + void (*use_reqid) (child_create_t *this, u_int32_t reqid); +}; + +/** + * @brief Create a new child_create task. + * + * @param ike_sa IKE_SA this task works for + * @param policy policy if task initiator, NULL if responder + * @return child_create task to handle by the task_manager + */ +child_create_t *child_create_create(ike_sa_t *ike_sa, policy_t *policy); + +#endif /* CHILD_CREATE_H_ */ diff --git a/src/charon/sa/tasks/child_delete.c b/src/charon/sa/tasks/child_delete.c new file mode 100644 index 000000000..875f14acc --- /dev/null +++ b/src/charon/sa/tasks/child_delete.c @@ -0,0 +1,264 @@ +/** + * @file child_delete.c + * + * @brief Implementation of the child_delete task. + * + */ + +/* + * Copyright (C) 2006-2007 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "child_delete.h" + +#include <daemon.h> +#include <encoding/payloads/delete_payload.h> + + +typedef struct private_child_delete_t private_child_delete_t; + +/** + * Private members of a child_delete_t task. + */ +struct private_child_delete_t { + + /** + * Public methods and task_t interface. + */ + child_delete_t public; + + /** + * Assigned IKE_SA. + */ + ike_sa_t *ike_sa; + + /** + * Are we the initiator? + */ + bool initiator; + + /** + * CHILD_SAs which get deleted + */ + linked_list_t *child_sas; +}; + +/** + * build the delete payloads from the listed child_sas + */ +static void build_payloads(private_child_delete_t *this, message_t *message) +{ + iterator_t *iterator; + delete_payload_t *ah = NULL, *esp = NULL; + u_int32_t spi; + child_sa_t *child_sa; + + iterator = this->child_sas->create_iterator(this->child_sas, TRUE); + while (iterator->iterate(iterator, (void**)&child_sa)) + { + spi = child_sa->get_spi(child_sa, TRUE); + switch (child_sa->get_protocol(child_sa)) + { + case PROTO_ESP: + if (esp == NULL) + { + esp = delete_payload_create(PROTO_ESP); + message->add_payload(message, (payload_t*)esp); + } + esp->add_spi(esp, spi); + break; + case PROTO_AH: + if (ah == NULL) + { + ah = delete_payload_create(PROTO_AH); + message->add_payload(message, (payload_t*)ah); + } + ah->add_spi(ah, spi); + break; + default: + break; + } + child_sa->set_state(child_sa, CHILD_DELETING); + } + iterator->destroy(iterator); +} + +/** + * read in payloads and find the children to delete + */ +static void process_payloads(private_child_delete_t *this, message_t *message) +{ + iterator_t *payloads, *spis; + payload_t *payload; + delete_payload_t *delete_payload; + u_int32_t spi; + protocol_id_t protocol; + child_sa_t *child_sa; + + payloads = message->get_payload_iterator(message); + while (payloads->iterate(payloads, (void**)&payload)) + { + if (payload->get_type(payload) == DELETE) + { + delete_payload = (delete_payload_t*)payload; + protocol = delete_payload->get_protocol_id(delete_payload); + if (protocol != PROTO_ESP && protocol != PROTO_AH) + { + continue; + } + spis = delete_payload->create_spi_iterator(delete_payload); + while (spis->iterate(spis, (void**)&spi)) + { + child_sa = this->ike_sa->get_child_sa(this->ike_sa, protocol, + spi, FALSE); + if (child_sa == NULL) + { + DBG1(DBG_IKE, "received DELETE for %N CHILD_SA with SPI 0x%x, " + "but no such SA", protocol_id_names, protocol, ntohl(spi)); + break; + } + + if (child_sa->get_state(child_sa) == CHILD_REKEYING) + { + /* TODO: handle rekeying */ + } + + this->child_sas->insert_last(this->child_sas, child_sa); + } + spis->destroy(spis); + } + } + payloads->destroy(payloads); +} + +/** + * destroy the children listed in this->child_sas + */ +static void destroy_children(private_child_delete_t *this) +{ + iterator_t *iterator; + child_sa_t *child_sa; + protocol_id_t protocol; + u_int32_t spi; + + iterator = this->child_sas->create_iterator(this->child_sas, TRUE); + while (iterator->iterate(iterator, (void**)&child_sa)) + { + /* TODO: can we do this more cleanly? */ + spi = child_sa->get_spi(child_sa, TRUE); + protocol = child_sa->get_protocol(child_sa); + this->ike_sa->destroy_child_sa(this->ike_sa, protocol, spi); + } + iterator->destroy(iterator); +} + +/** + * Implementation of task_t.build for initiator + */ +static status_t build_i(private_child_delete_t *this, message_t *message) +{ + build_payloads(this, message); + return NEED_MORE; +} + +/** + * Implementation of task_t.process for initiator + */ +static status_t process_i(private_child_delete_t *this, message_t *message) +{ + /* flush the list before adding new SAs */ + this->child_sas->destroy(this->child_sas); + this->child_sas = linked_list_create(); + + process_payloads(this, message); + destroy_children(this); + return SUCCESS; +} + +/** + * Implementation of task_t.process for initiator + */ +static status_t process_r(private_child_delete_t *this, message_t *message) +{ + process_payloads(this, message); + return NEED_MORE; +} + +/** + * Implementation of task_t.build for responder + */ +static status_t build_r(private_child_delete_t *this, message_t *message) +{ + build_payloads(this, message); + destroy_children(this); + return SUCCESS; +} + +/** + * Implementation of task_t.get_type + */ +static task_type_t get_type(private_child_delete_t *this) +{ + return CHILD_DELETE; +} + +/** + * Implementation of task_t.migrate + */ +static void migrate(private_child_delete_t *this, ike_sa_t *ike_sa) +{ + this->ike_sa = ike_sa; + + this->child_sas->destroy(this->child_sas); + this->child_sas = linked_list_create(); +} + +/** + * Implementation of task_t.destroy + */ +static void destroy(private_child_delete_t *this) +{ + this->child_sas->destroy(this->child_sas); + free(this); +} + +/* + * Described in header. + */ +child_delete_t *child_delete_create(ike_sa_t *ike_sa, child_sa_t *child_sa) +{ + private_child_delete_t *this = malloc_thing(private_child_delete_t); + + this->public.task.get_type = (task_type_t(*)(task_t*))get_type; + this->public.task.migrate = (void(*)(task_t*,ike_sa_t*))migrate; + this->public.task.destroy = (void(*)(task_t*))destroy; + + this->ike_sa = ike_sa; + this->child_sas = linked_list_create(); + + if (child_sa != NULL) + { + this->public.task.build = (status_t(*)(task_t*,message_t*))build_i; + this->public.task.process = (status_t(*)(task_t*,message_t*))process_i; + this->initiator = TRUE; + this->child_sas->insert_last(this->child_sas, child_sa); + } + else + { + this->public.task.build = (status_t(*)(task_t*,message_t*))build_r; + this->public.task.process = (status_t(*)(task_t*,message_t*))process_r; + this->initiator = FALSE; + } + return &this->public; +} diff --git a/src/charon/sa/tasks/child_delete.h b/src/charon/sa/tasks/child_delete.h new file mode 100644 index 000000000..327a2ec15 --- /dev/null +++ b/src/charon/sa/tasks/child_delete.h @@ -0,0 +1,58 @@ +/** + * @file child_delete.h + * + * @brief Interface child_delete_t. + * + */ + +/* + * Copyright (C) 2007 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#ifndef CHILD_DELETE_H_ +#define CHILD_DELETE_H_ + +typedef struct child_delete_t child_delete_t; + +#include <library.h> +#include <sa/ike_sa.h> +#include <sa/tasks/task.h> +#include <sa/child_sa.h> + +/** + * @brief Task of type child_delete, delete a CHILD_SA. + * + * @b Constructors: + * - child_delete_create() + * + * @ingroup tasks + */ +struct child_delete_t { + + /** + * Implements the task_t interface + */ + task_t task; +}; + +/** + * @brief Create a new child_delete task. + * + * @param ike_sa IKE_SA this task works for + * @param child_sa CHILD_SA to delete, or NULL as responder + * @return child_delete task to handle by the task_manager + */ +child_delete_t *child_delete_create(ike_sa_t *ike_sa, child_sa_t *child_sa); + +#endif /* CHILD_DELETE_H_ */ diff --git a/src/charon/sa/tasks/child_rekey.c b/src/charon/sa/tasks/child_rekey.c new file mode 100644 index 000000000..41533b68c --- /dev/null +++ b/src/charon/sa/tasks/child_rekey.c @@ -0,0 +1,353 @@ +/** + * @file child_rekey.c + * + * @brief Implementation of the child_rekey task. + * + */ + +/* + * 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 "child_rekey.h" + +#include <daemon.h> +#include <crypto/diffie_hellman.h> +#include <encoding/payloads/notify_payload.h> +#include <encoding/payloads/nonce_payload.h> +#include <sa/tasks/child_create.h> + + +typedef struct private_child_rekey_t private_child_rekey_t; + +/** + * Private members of a child_rekey_t task. + */ +struct private_child_rekey_t { + + /** + * Public methods and task_t interface. + */ + child_rekey_t public; + + /** + * Assigned IKE_SA. + */ + ike_sa_t *ike_sa; + + /** + * Are we the initiator? + */ + bool initiator; + + /** + * the CHILD_CREATE task which is reused to simplify rekeying + */ + child_create_t *child_create; + + /** + * CHILD_SA which gets rekeyed + */ + child_sa_t *child_sa; + + /** + * redundandt CHILD_SA created simultaneously + */ + child_sa_t *simultaneous; + + /** + * the lowest nonce compared so far + */ + chunk_t nonce; + + /** + * TRUE if we have the lower nonce + */ + bool winner; +}; + +/** + * get the nonce from a message, return TRUE if it was lower than this->nonce + */ +static bool get_nonce(private_child_rekey_t *this, message_t *message) +{ + nonce_payload_t *payload; + chunk_t nonce; + + payload = (nonce_payload_t*)message->get_payload(message, NONCE); + if (payload == NULL) + { + return FALSE; + } + nonce = payload->get_nonce(payload); + + if (this->nonce.ptr && memcmp(nonce.ptr, this->nonce.ptr, + min(nonce.len, this->nonce.len)) > 0) + { + chunk_free(&nonce); + return FALSE; + } + + chunk_free(&this->nonce); + this->nonce = nonce; + return TRUE; +} + +/** + * find a child using the REKEY_SA notify + */ +static void find_child(private_child_rekey_t *this, message_t *message) +{ + iterator_t *iterator; + payload_t *payload; + + iterator = message->get_payload_iterator(message); + while (iterator->iterate(iterator, (void**)&payload)) + { + notify_payload_t *notify; + u_int32_t spi; + protocol_id_t protocol; + + if (payload->get_type(payload) != NOTIFY) + { + continue; + } + + notify = (notify_payload_t*)payload; + protocol = notify->get_protocol_id(notify); + spi = notify->get_spi(notify); + + if (protocol != PROTO_ESP && protocol != PROTO_AH) + { + continue; + } + this->child_sa = this->ike_sa->get_child_sa(this->ike_sa, protocol, + spi, FALSE); + break; + + } + iterator->destroy(iterator); +} + +#if 0 +/** + * handle a detected simultaneous rekeying situation as responder + */ +static void simultaneous_r(private_child_rekey_t *this, message_t *message) +{ + private_child_rekey_t *other = NULL; + task_t *task; + iterator_t *iterator; + + this->ike_sa->create_task_iterator(this->ike_sa); + while (iterator->iterate(iterator, (void**)&task)) + { + if (task->get_type(task) == CHILD_REKEY) + { + other = (private_child_rekey_t*)task; + break; + } + } + iterator->destroy(iterator); + + if (other) + { + other->simultaneous = this->child_create->get_child(this->child_create); + + if (!get_nonce(other, message)) + { + /* this wins the race, other lost */ + other->winner = FALSE; + } + } +} + +/** + * was there a simultaneous rekeying, did we win the nonce compare? + */ +static bool simultaneous_i(private_child_rekey_t *this, message_t *message) +{ + if (this->winner || get_nonce(this, message)) + { + /* we have the lower nonce and win */ + return TRUE; + } + return FALSE; +} +#endif + +/** + * Implementation of task_t.build for initiator + */ +static status_t build_i(private_child_rekey_t *this, message_t *message) +{ + notify_payload_t *notify; + protocol_id_t protocol; + u_int32_t spi, reqid; + + /* our CHILD_CREATE task does the hard work for us... */ + reqid = this->child_sa->get_reqid(this->child_sa); + this->child_create->use_reqid(this->child_create, reqid); + this->child_create->task.build(&this->child_create->task, message); + get_nonce(this, message); + + /* ... we just need the rekey notify */ + protocol = this->child_sa->get_protocol(this->child_sa); + spi = this->child_sa->get_spi(this->child_sa, TRUE); + notify = notify_payload_create_from_protocol_and_type(protocol, REKEY_SA); + notify->set_spi(notify, spi); + message->add_payload(message, (payload_t*)notify); + + this->child_sa->set_state(this->child_sa, CHILD_REKEYING); + + return NEED_MORE; +} + +/** + * Implementation of task_t.process for initiator + */ +static status_t process_r(private_child_rekey_t *this, message_t *message) +{ + /* let the CHILD_CREATE task process the message */ + this->child_create->task.process(&this->child_create->task, message); + get_nonce(this, message); + + find_child(this, message); + + return NEED_MORE; +} + +/** + * Implementation of task_t.build for responder + */ +static status_t build_r(private_child_rekey_t *this, message_t *message) +{ + u_int32_t reqid; + + if (this->child_sa == NULL || + this->child_sa->get_state(this->child_sa) == CHILD_DELETING) + { + message->add_notify(message, TRUE, NO_PROPOSAL_CHOSEN, chunk_empty); + return SUCCESS; + } + + /* let the CHILD_CREATE task build the response */ + reqid = this->child_sa->get_reqid(this->child_sa); + this->child_create->use_reqid(this->child_create, reqid); + this->child_create->task.build(&this->child_create->task, message); + get_nonce(this, message); + + if (this->child_sa->get_state(this->child_sa) == CHILD_REKEYING) + { + /* simultaneous_detected(this); */ + } + + this->child_sa->set_state(this->child_sa, CHILD_REKEYING); + + return SUCCESS; +} + +/** + * Implementation of task_t.process for initiator + */ +static status_t process_i(private_child_rekey_t *this, message_t *message) +{ + protocol_id_t protocol; + u_int32_t spi; + + this->child_create->task.process(&this->child_create->task, message); + + /*if (!simultaneous_won(this, message)) + { + * delete the redundant CHILD_SA, instead of the rekeyed * + this->child_sa = this->create_child->get_child(this->create_child); + }*/ + spi = this->child_sa->get_spi(this->child_sa, TRUE); + protocol = this->child_sa->get_protocol(this->child_sa); + + /* TODO: don't delete when rekeying failed */ + if (this->ike_sa->delete_child_sa(this->ike_sa, protocol, spi) != SUCCESS) + { + return FAILED; + } + return SUCCESS; +} + +/** + * Implementation of task_t.get_type + */ +static task_type_t get_type(private_child_rekey_t *this) +{ + return CHILD_REKEY; +} + +/** + * Implementation of task_t.migrate + */ +static void migrate(private_child_rekey_t *this, ike_sa_t *ike_sa) +{ + this->child_create->task.migrate(&this->child_create->task, ike_sa); + chunk_free(&this->nonce); + + this->ike_sa = ike_sa; + this->winner = TRUE; + this->simultaneous = NULL; +} + +/** + * Implementation of task_t.destroy + */ +static void destroy(private_child_rekey_t *this) +{ + this->child_create->task.destroy(&this->child_create->task); + chunk_free(&this->nonce); + free(this); +} + +/* + * Described in header. + */ +child_rekey_t *child_rekey_create(ike_sa_t *ike_sa, child_sa_t *child_sa) +{ + private_child_rekey_t *this = malloc_thing(private_child_rekey_t); + policy_t *policy; + + this->public.task.get_type = (task_type_t(*)(task_t*))get_type; + this->public.task.migrate = (void(*)(task_t*,ike_sa_t*))migrate; + this->public.task.destroy = (void(*)(task_t*))destroy; + if (child_sa != NULL) + { + this->public.task.build = (status_t(*)(task_t*,message_t*))build_i; + this->public.task.process = (status_t(*)(task_t*,message_t*))process_i; + this->initiator = TRUE; + policy = child_sa->get_policy(child_sa); + this->child_create = child_create_create(ike_sa, policy); + } + else + { + this->public.task.build = (status_t(*)(task_t*,message_t*))build_r; + this->public.task.process = (status_t(*)(task_t*,message_t*))process_r; + this->initiator = FALSE; + this->child_create = child_create_create(ike_sa, NULL); + } + + this->ike_sa = ike_sa; + this->child_sa = child_sa; + this->nonce = chunk_empty; + this->winner = TRUE; + this->simultaneous = NULL; + + return &this->public; +} diff --git a/src/charon/sa/tasks/child_rekey.h b/src/charon/sa/tasks/child_rekey.h new file mode 100644 index 000000000..77297b13d --- /dev/null +++ b/src/charon/sa/tasks/child_rekey.h @@ -0,0 +1,58 @@ +/** + * @file child_rekey.h + * + * @brief Interface child_rekey_t. + * + */ + +/* + * Copyright (C) 2007 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#ifndef CHILD_REKEY_H_ +#define CHILD_REKEY_H_ + +typedef struct child_rekey_t child_rekey_t; + +#include <library.h> +#include <sa/ike_sa.h> +#include <sa/child_sa.h> +#include <sa/tasks/task.h> + +/** + * @brief Task of type CHILD_REKEY, rekey an established CHILD_SA. + * + * @b Constructors: + * - child_rekey_create() + * + * @ingroup tasks + */ +struct child_rekey_t { + + /** + * Implements the task_t interface + */ + task_t task; +}; + +/** + * @brief Create a new CHILD_REKEY task. + * + * @param ike_sa IKE_SA this task works for + * @param child_sa child_sa to rekey, NULL if responder + * @return child_rekey task to handle by the task_manager + */ +child_rekey_t *child_rekey_create(ike_sa_t *ike_sa, child_sa_t *child_sa); + +#endif /* CHILD_REKEY_H_ */ diff --git a/src/charon/sa/tasks/ike_auth.c b/src/charon/sa/tasks/ike_auth.c new file mode 100644 index 000000000..2e54b7797 --- /dev/null +++ b/src/charon/sa/tasks/ike_auth.c @@ -0,0 +1,513 @@ +/** + * @file ike_auth.c + * + * @brief Implementation of the ike_auth task. + * + */ + +/* + * 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 "ike_auth.h" + +#include <string.h> + +#include <daemon.h> +#include <crypto/diffie_hellman.h> +#include <encoding/payloads/id_payload.h> +#include <encoding/payloads/auth_payload.h> +#include <encoding/payloads/nonce_payload.h> + + +typedef struct private_ike_auth_t private_ike_auth_t; + +/** + * Private members of a ike_auth_t task. + */ +struct private_ike_auth_t { + + /** + * Public methods and task_t interface. + */ + ike_auth_t public; + + /** + * Assigned IKE_SA. + */ + ike_sa_t *ike_sa; + + /** + * Are we the initiator? + */ + bool initiator; + + /** + * Nonce chosen by us in ike_init + */ + chunk_t my_nonce; + + /** + * Nonce chosen by peer in ike_init + */ + chunk_t other_nonce; + + /** + * IKE_SA_INIT message sent by us + */ + packet_t *my_packet; + + /** + * IKE_SA_INIT message sent by peer + */ + packet_t *other_packet; + + /** + * authenticator to authenticate us + */ + authenticator_t *my_auth; + + /** + * authenticator to authenticate peer + */ + authenticator_t *other_auth; + + /** + * has the peer been authenticated successfully? + */ + bool peer_authenticated; +}; + +/** + * build the payloads for the message + */ +static status_t build_payloads(private_ike_auth_t *this, message_t *message) +{ + authenticator_t *auth; + auth_payload_t *auth_payload; + id_payload_t *id_payload; + chunk_t ike_sa_init; + identification_t *me, *other; + policy_t *policy; + auth_method_t method = AUTH_RSA; + status_t status; + + /* add own ID payload */ + me = this->ike_sa->get_my_id(this->ike_sa); + other = this->ike_sa->get_other_id(this->ike_sa); + + id_payload = id_payload_create_from_identification(this->initiator, me); + message->add_payload(message, (payload_t*)id_payload); + + /* as initiator, include other ID if it does not contain wildcards */ + if (this->initiator && !other->contains_wildcards(other)) + { + id_payload = id_payload_create_from_identification(FALSE, other); + message->add_payload(message, (payload_t*)id_payload); + } + + /* create own authenticator and add auth payload */ + policy = this->ike_sa->get_policy(this->ike_sa); + if (policy) + { + method = policy->get_auth_method(policy); + } + auth = authenticator_create(this->ike_sa, method); + if (auth == NULL) + { + SIG(IKE_UP_FAILED, "configured authentication method %N not supported", + auth_method_names, method); + return FAILED; + } + + ike_sa_init = this->my_packet->get_data(this->my_packet); + status = auth->build(auth, ike_sa_init, this->other_nonce, &auth_payload); + auth->destroy(auth); + if (status != SUCCESS) + { + SIG(IKE_UP_FAILED, "generating authentication data failed"); + return FAILED; + } + message->add_payload(message, (payload_t*)auth_payload); + + return SUCCESS; +} + +/** + * process payloads from message + */ +static void process_payloads(private_ike_auth_t *this, message_t *message) +{ + iterator_t *iterator; + payload_t *payload; + payload_type_t type; + identification_t *idi = NULL, *idr = NULL; + auth_payload_t *auth_payload = NULL; + authenticator_t *auth; + auth_method_t auth_method; + status_t status; + + iterator = message->get_payload_iterator(message); + while (iterator->iterate(iterator, (void**)&payload)) + { + type = payload->get_type(payload); + switch (type) + { + case ID_INITIATOR: + { + id_payload_t *id_payload = (id_payload_t*)payload; + idi = id_payload->get_identification(id_payload); + break; + } + case ID_RESPONDER: + { + id_payload_t *id_payload = (id_payload_t*)payload; + idr = id_payload->get_identification(id_payload); + break; + } + case AUTHENTICATION: + { + auth_payload = (auth_payload_t*)payload; + break; + } + default: + break; + } + } + iterator->destroy(iterator); + + /* apply IDs */ + if ((this->initiator && idr == NULL) || (!this->initiator && idi == NULL)) + { + SIG(IKE_UP_FAILED, "ID payload missing in message"); + DESTROY_IF(idr); DESTROY_IF(idi); + return; + } + + if (this->initiator) + { + identification_t *other_id = this->ike_sa->get_other_id(this->ike_sa); + if (!idr->matches(idr, other_id, NULL)) + { + SIG(IKE_UP_FAILED, "received inacceptable id %D, %D required", idr, + this->ike_sa->get_other_id(this->ike_sa)); + DESTROY_IF(idi); DESTROY_IF(idr); + return; + } + this->ike_sa->set_other_id(this->ike_sa, idr); + } + else + { + identification_t *my_id = this->ike_sa->get_other_id(this->ike_sa); + if (idr) + { + if (!idr->matches(idr, my_id, NULL)) + { + SIG(IKE_UP_FAILED, "received inacceptable id %D, %D required", + idr, this->ike_sa->get_other_id(this->ike_sa)); + DESTROY_IF(idi); DESTROY_IF(idr); + return; + } + this->ike_sa->set_my_id(this->ike_sa, idr); + } + else + { + if (my_id->contains_wildcards(my_id)) + { + SIG(IKE_UP_FAILED, "own ID (%D) not defined after exchange", + my_id); + DESTROY_IF(idi); + return; + } + } + this->ike_sa->set_other_id(this->ike_sa, idi); + } + + /* verify auth payload */ + if (auth_payload == NULL) + { + SIG(IKE_UP_FAILED, "AUTH payload missing in message"); + return; + } + + auth_method = auth_payload->get_auth_method(auth_payload); + auth = authenticator_create(this->ike_sa, auth_method); + if (auth == NULL) + { + SIG(IKE_UP_FAILED, "authentication method %N used by %D not " + "supported", auth_method_names, auth_method, + this->ike_sa->get_other_id(this->ike_sa)); + return; + } + status = auth->verify(auth, this->other_packet->get_data(this->other_packet), + this->my_nonce, auth_payload); + auth->destroy(auth); + if (status != SUCCESS) + { + SIG(IKE_UP_FAILED, "authentication of %D using %N failed", + this->ike_sa->get_other_id(this->ike_sa), + auth_method_names, auth_method); + return; + } + this->peer_authenticated = TRUE; +} + +/** + * collect the needed information in the IKE_SA_INIT exchange from our message + */ +static status_t collect_my_init_data(private_ike_auth_t *this, message_t *message) +{ + nonce_payload_t *nonce; + + /* get the nonce that was generated in ike_init */ + nonce = (nonce_payload_t*)message->get_payload(message, NONCE); + if (nonce == NULL) + { + return FAILED; + } + this->my_nonce = nonce->get_nonce(nonce); + + /* pre-generate the message, so we can store it for us */ + if (this->ike_sa->generate_message(this->ike_sa, message, + &this->my_packet) != SUCCESS) + { + return FAILED; + } + return NEED_MORE; +} + +/** + * collect the needed information in the IKE_SA_INIT exchange from others message + */ +static status_t collect_other_init_data(private_ike_auth_t *this, message_t *message) +{ + /* we collect the needed information in the IKE_SA_INIT exchange */ + nonce_payload_t *nonce; + + /* get the nonce that was generated in ike_init */ + nonce = (nonce_payload_t*)message->get_payload(message, NONCE); + if (nonce == NULL) + { + return FAILED; + } + this->other_nonce = nonce->get_nonce(nonce); + + /* pre-generate the message, so we can store it for us */ + this->other_packet = message->get_packet(message); + return NEED_MORE; +} + +/** + * Implementation of task_t.build for initiator + */ +static status_t build_i(private_ike_auth_t *this, message_t *message) +{ + if (message->get_exchange_type(message) == IKE_SA_INIT) + { + return collect_my_init_data(this, message); + } + + if (build_payloads(this, message) == SUCCESS) + { + return NEED_MORE; + } + return FAILED; +} + +/** + * Implementation of task_t.process for initiator + */ +static status_t process_r(private_ike_auth_t *this, message_t *message) +{ + if (message->get_exchange_type(message) == IKE_SA_INIT) + { + return collect_other_init_data(this, message); + } + + process_payloads(this, message); + + return NEED_MORE; +} + +/** + * Implementation of task_t.build for responder + */ +static status_t build_r(private_ike_auth_t *this, message_t *message) +{ + if (message->get_exchange_type(message) == IKE_SA_INIT) + { + return collect_my_init_data(this, message); + } + + if (!this->peer_authenticated) + { + message->add_notify(message, TRUE, AUTHENTICATION_FAILED, chunk_empty); + return FAILED; + } + + if (build_payloads(this, message) == SUCCESS) + { + this->ike_sa->set_state(this->ike_sa, IKE_ESTABLISHED); + SIG(IKE_UP_SUCCESS, "IKE_SA established between %D[%H]...[%H]%D", + this->ike_sa->get_my_id(this->ike_sa), + this->ike_sa->get_my_host(this->ike_sa), + this->ike_sa->get_other_host(this->ike_sa), + this->ike_sa->get_other_id(this->ike_sa)); + return SUCCESS; + } + return FAILED; +} + +/** + * Implementation of task_t.process for initiator + */ +static status_t process_i(private_ike_auth_t *this, message_t *message) +{ + iterator_t *iterator; + payload_t *payload; + + if (message->get_exchange_type(message) == IKE_SA_INIT) + { + return collect_other_init_data(this, message); + } + + iterator = message->get_payload_iterator(message); + while (iterator->iterate(iterator, (void**)&payload)) + { + if (payload->get_type(payload) == NOTIFY) + { + notify_payload_t *notify = (notify_payload_t*)payload; + notify_type_t type = notify->get_notify_type(notify); + + switch (type) + { + case NO_PROPOSAL_CHOSEN: + case SINGLE_PAIR_REQUIRED: + case NO_ADDITIONAL_SAS: + case INTERNAL_ADDRESS_FAILURE: + case FAILED_CP_REQUIRED: + case TS_UNACCEPTABLE: + case INVALID_SELECTORS: + /* these are errors, but are not critical as only the + * CHILD_SA won't get build, but IKE_SA establishes anyway */ + DBG1(DBG_IKE, "received %N notify, no CHILD_SA built", + notify_type_names, type); + iterator->destroy(iterator); + return SUCCESS; + default: + { + if (type < 16383) + { + DBG1(DBG_IKE, "received %N notify error", + notify_type_names, type); + iterator->destroy(iterator); + return FAILED; + } + } + } + } + } + iterator->destroy(iterator); + + process_payloads(this, message); + + if (this->peer_authenticated) + { + this->ike_sa->set_state(this->ike_sa, IKE_ESTABLISHED); + SIG(IKE_UP_SUCCESS, "IKE_SA established between %D[%H]...[%H]%D", + this->ike_sa->get_my_id(this->ike_sa), + this->ike_sa->get_my_host(this->ike_sa), + this->ike_sa->get_other_host(this->ike_sa), + this->ike_sa->get_other_id(this->ike_sa)); + return SUCCESS; + } + return FAILED; +} + +/** + * Implementation of task_t.get_type + */ +static task_type_t get_type(private_ike_auth_t *this) +{ + return IKE_AUTHENTICATE; +} + +/** + * Implementation of task_t.migrate + */ +static void migrate(private_ike_auth_t *this, ike_sa_t *ike_sa) +{ + chunk_free(&this->my_nonce); + chunk_free(&this->other_nonce); + DESTROY_IF(this->my_packet); + DESTROY_IF(this->other_packet); + DESTROY_IF(this->my_auth); + DESTROY_IF(this->other_auth); + this->my_packet = NULL; + this->other_packet = NULL; + this->my_auth = NULL; + this->other_auth = NULL; + this->peer_authenticated = FALSE; + this->ike_sa = ike_sa; +} + +/** + * Implementation of task_t.destroy + */ +static void destroy(private_ike_auth_t *this) +{ + chunk_free(&this->my_nonce); + chunk_free(&this->other_nonce); + DESTROY_IF(this->my_packet); + DESTROY_IF(this->other_packet); + DESTROY_IF(this->my_auth); + DESTROY_IF(this->other_auth); + free(this); +} + +/* + * Described in header. + */ +ike_auth_t *ike_auth_create(ike_sa_t *ike_sa, bool initiator) +{ + private_ike_auth_t *this = malloc_thing(private_ike_auth_t); + + this->public.task.get_type = (task_type_t(*)(task_t*))get_type; + this->public.task.migrate = (void(*)(task_t*,ike_sa_t*))migrate; + this->public.task.destroy = (void(*)(task_t*))destroy; + + if (initiator) + { + this->public.task.build = (status_t(*)(task_t*,message_t*))build_i; + this->public.task.process = (status_t(*)(task_t*,message_t*))process_i; + } + else + { + this->public.task.build = (status_t(*)(task_t*,message_t*))build_r; + this->public.task.process = (status_t(*)(task_t*,message_t*))process_r; + } + + this->ike_sa = ike_sa; + this->initiator = initiator; + this->my_nonce = chunk_empty; + this->other_nonce = chunk_empty; + this->my_packet = NULL; + this->other_packet = NULL; + this->my_auth = NULL; + this->other_auth = NULL; + this->peer_authenticated = FALSE; + + return &this->public; +} diff --git a/src/charon/sa/tasks/ike_auth.h b/src/charon/sa/tasks/ike_auth.h new file mode 100644 index 000000000..e59e6811a --- /dev/null +++ b/src/charon/sa/tasks/ike_auth.h @@ -0,0 +1,60 @@ +/** + * @file ike_auth.h + * + * @brief Interface ike_auth_t. + * + */ + +/* + * Copyright (C) 2007 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#ifndef IKE_AUTH_H_ +#define IKE_AUTH_H_ + +typedef struct ike_auth_t ike_auth_t; + +#include <library.h> +#include <sa/ike_sa.h> +#include <sa/tasks/task.h> + +/** + * @brief Task of type ike_auth, authenticates an IKE_SA authenticators. + * + * The ike_auth task authenticates the IKE_SA using the IKE_AUTH + * exchange. + * + * @b Constructors: + * - ike_auth_create() + * + * @ingroup tasks + */ +struct ike_auth_t { + + /** + * Implements the task_t interface + */ + task_t task; +}; + +/** + * @brief Create a new task of type IKE_AUTHENTICATE. + * + * @param ike_sa IKE_SA this task works for + * @param initiator TRUE if thask is the initator of an exchange + * @return ike_auth task to handle by the task_manager + */ +ike_auth_t *ike_auth_create(ike_sa_t *ike_sa, bool initiator); + +#endif /* IKE_AUTH_H_ */ diff --git a/src/charon/sa/tasks/ike_cert.c b/src/charon/sa/tasks/ike_cert.c new file mode 100644 index 000000000..3cddc0073 --- /dev/null +++ b/src/charon/sa/tasks/ike_cert.c @@ -0,0 +1,361 @@ +/** + * @file ike_cert.c + * + * @brief Implementation of the ike_cert task. + * + */ + +/* + * Copyright (C) 2006-2007 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "ike_cert.h" + +#include <daemon.h> +#include <sa/ike_sa.h> +#include <crypto/hashers/hasher.h> +#include <encoding/payloads/cert_payload.h> +#include <encoding/payloads/certreq_payload.h> + + +typedef struct private_ike_cert_t private_ike_cert_t; + +/** + * Private members of a ike_cert_t task. + */ +struct private_ike_cert_t { + + /** + * Public methods and task_t interface. + */ + ike_cert_t public; + + /** + * Assigned IKE_SA. + */ + ike_sa_t *ike_sa; + + /** + * Are we the initiator? + */ + bool initiator; + + /** + * list of CA cert hashes requested, items point to 20 byte chunk + */ + linked_list_t *cas; +}; + +/** + * read certificate requests + */ +static void process_certreqs(private_ike_cert_t *this, message_t *message) +{ + iterator_t *iterator; + payload_t *payload; + + iterator = message->get_payload_iterator(message); + while (iterator->iterate(iterator, (void**)&payload)) + { + if (payload->get_type(payload) == CERTIFICATE_REQUEST) + { + certreq_payload_t *certreq = (certreq_payload_t*)payload; + cert_encoding_t encoding; + chunk_t keyids, keyid; + + encoding = certreq->get_cert_encoding(certreq); + if (encoding != CERT_X509_SIGNATURE) + { + DBG1(DBG_IKE, "certreq payload %N not supported, ignored", + cert_encoding_names, encoding); + continue; + } + + keyids = certreq->get_data(certreq); + + while (keyids.len >= HASH_SIZE_SHA1) + { + keyid = chunk_create(keyids.ptr, HASH_SIZE_SHA1); + keyid = chunk_clone(keyid); + this->cas->insert_last(this->cas, keyid.ptr); + keyids = chunk_skip(keyids, HASH_SIZE_SHA1); + } + } + } + iterator->destroy(iterator); +} + +/** + * import certificates + */ +static void process_certs(private_ike_cert_t *this, message_t *message) +{ + iterator_t *iterator; + payload_t *payload; + + iterator = message->get_payload_iterator(message); + while (iterator->iterate(iterator, (void**)&payload)) + { + if (payload->get_type(payload) == CERTIFICATE) + { + cert_encoding_t encoding; + x509_t *cert; + chunk_t cert_data; + bool found; + cert_payload_t *cert_payload = (cert_payload_t*)payload; + + encoding = cert_payload->get_cert_encoding(cert_payload); + if (encoding != CERT_X509_SIGNATURE) + { + DBG1(DBG_IKE, "certificate payload %N not supported, ignored", + cert_encoding_names, encoding); + continue; + } + + cert_data = cert_payload->get_data_clone(cert_payload); + cert = x509_create_from_chunk(cert_data); + if (cert) + { + if (charon->credentials->verify(charon->credentials, + cert, &found)) + { + DBG2(DBG_IKE, "received end entity certificate is trusted, " + "added to store"); + if (!found) + { + charon->credentials->add_end_certificate( + charon->credentials, cert); + } + else + { + cert->destroy(cert); + } + } + else + { + DBG1(DBG_IKE, "received end entity certificate is not " + "trusted, discarded"); + cert->destroy(cert); + } + } + else + { + DBG1(DBG_IKE, "parsing of received certificate failed, discarded"); + chunk_free(&cert_data); + } + } + } + iterator->destroy(iterator); +} + +/** + * build certificate requests + */ +static void build_certreqs(private_ike_cert_t *this, message_t *message) +{ + connection_t *connection; + policy_t *policy; + identification_t *ca; + certreq_payload_t *certreq; + + connection = this->ike_sa->get_connection(this->ike_sa); + + if (connection->get_certreq_policy(connection) != CERT_NEVER_SEND) + { + policy = this->ike_sa->get_policy(this->ike_sa); + + if (policy) + { + ca = policy->get_other_ca(policy); + + if (ca && ca->get_type(ca) != ID_ANY) + { + certreq = certreq_payload_create_from_cacert(ca); + } + else + { + certreq = certreq_payload_create_from_cacerts(); + } + } + else + { + certreq = certreq_payload_create_from_cacerts(); + } + + if (certreq) + { + message->add_payload(message, (payload_t*)certreq); + } + } +} + +/** + * add certificates to message + */ +static void build_certs(private_ike_cert_t *this, message_t *message) +{ + policy_t *policy; + connection_t *connection; + x509_t *cert; + cert_payload_t *payload; + + policy = this->ike_sa->get_policy(this->ike_sa); + connection = this->ike_sa->get_connection(this->ike_sa); + + if (policy && policy->get_auth_method(policy) == AUTH_RSA) + { + switch (connection->get_cert_policy(connection)) + { + case CERT_NEVER_SEND: + break; + case CERT_SEND_IF_ASKED: + if (this->cas->get_count(this->cas) == 0) + { + break; + } + /* FALL */ + case CERT_ALWAYS_SEND: + { + /* TODO: respect CA cert request */ + cert = charon->credentials->get_certificate(charon->credentials, + policy->get_my_id(policy)); + if (cert) + { + payload = cert_payload_create_from_x509(cert); + message->add_payload(message, (payload_t*)payload); + } + } + } + } +} + +/** + * Implementation of task_t.process for initiator + */ +static status_t build_i(private_ike_cert_t *this, message_t *message) +{ + if (message->get_exchange_type(message) == IKE_SA_INIT) + { + return NEED_MORE; + } + + build_certreqs(this, message); + build_certs(this, message); + + return NEED_MORE; +} + +/** + * Implementation of task_t.process for responder + */ +static status_t process_r(private_ike_cert_t *this, message_t *message) +{ + if (message->get_exchange_type(message) == IKE_SA_INIT) + { + return NEED_MORE; + } + + process_certreqs(this, message); + process_certs(this, message); + + return NEED_MORE; +} + +/** + * Implementation of task_t.build for responder + */ +static status_t build_r(private_ike_cert_t *this, message_t *message) +{ + if (message->get_exchange_type(message) == IKE_SA_INIT) + { + build_certreqs(this, message); + return NEED_MORE; + } + + build_certs(this, message); + + return SUCCESS; +} + +/** + * Implementation of task_t.process for initiator + */ +static status_t process_i(private_ike_cert_t *this, message_t *message) +{ + if (message->get_exchange_type(message) == IKE_SA_INIT) + { + process_certreqs(this, message); + return NEED_MORE; + } + + process_certs(this, message); + return SUCCESS; +} + +/** + * Implementation of task_t.get_type + */ +static task_type_t get_type(private_ike_cert_t *this) +{ + return IKE_CERT; +} + +/** + * Implementation of task_t.migrate + */ +static void migrate(private_ike_cert_t *this, ike_sa_t *ike_sa) +{ + this->ike_sa = ike_sa; + + this->cas->destroy_function(this->cas, free); + this->cas = linked_list_create(); +} + +/** + * Implementation of task_t.destroy + */ +static void destroy(private_ike_cert_t *this) +{ + this->cas->destroy_function(this->cas, free); + free(this); +} + +/* + * Described in header. + */ +ike_cert_t *ike_cert_create(ike_sa_t *ike_sa, bool initiator) +{ + private_ike_cert_t *this = malloc_thing(private_ike_cert_t); + + this->public.task.get_type = (task_type_t(*)(task_t*))get_type; + this->public.task.migrate = (void(*)(task_t*,ike_sa_t*))migrate; + this->public.task.destroy = (void(*)(task_t*))destroy; + + if (initiator) + { + this->public.task.build = (status_t(*)(task_t*,message_t*))build_i; + this->public.task.process = (status_t(*)(task_t*,message_t*))process_i; + } + else + { + this->public.task.build = (status_t(*)(task_t*,message_t*))build_r; + this->public.task.process = (status_t(*)(task_t*,message_t*))process_r; + } + + this->ike_sa = ike_sa; + this->initiator = initiator; + this->cas = linked_list_create(); + + return &this->public; +} diff --git a/src/charon/sa/tasks/ike_cert.h b/src/charon/sa/tasks/ike_cert.h new file mode 100644 index 000000000..ba0283953 --- /dev/null +++ b/src/charon/sa/tasks/ike_cert.h @@ -0,0 +1,61 @@ +/** + * @file ike_cert.h + * + * @brief Interface ike_cert_t. + * + */ + +/* + * Copyright (C) 2007 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#ifndef IKE_CERT_H_ +#define IKE_CERT_H_ + +typedef struct ike_cert_t ike_cert_t; + +#include <library.h> +#include <sa/ike_sa.h> +#include <sa/tasks/task.h> + +/** + * @brief Task of type ike_cert, exchanges certificates and + * certificate requests. + * + * @b Constructors: + * - ike_cert_create() + * + * @ingroup tasks + */ +struct ike_cert_t { + + /** + * Implements the task_t interface + */ + task_t task; +}; + +/** + * @brief Create a new ike_cert task. + * + * The initiator parameter means the original initiator, not the initiator + * of the certificate request. + * + * @param ike_sa IKE_SA this task works for + * @param initiator TRUE if thask is the original initator + * @return ike_cert task to handle by the task_manager + */ +ike_cert_t *ike_cert_create(ike_sa_t *ike_sa, bool initiator); + +#endif /* IKE_CERT_H_ */ diff --git a/src/charon/sa/tasks/ike_config.c b/src/charon/sa/tasks/ike_config.c new file mode 100644 index 000000000..b6e883b48 --- /dev/null +++ b/src/charon/sa/tasks/ike_config.c @@ -0,0 +1,421 @@ +/** + * @file ike_config.c + * + * @brief Implementation of the ike_config task. + * + */ + +/* + * Copyright (C) 2007 Martin Willi + * Copyright (C) 2006-2007 Fabian Hartmann, Noah Heusser + * 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 "ike_config.h" + +#include <daemon.h> +#include <encoding/payloads/cp_payload.h> + +typedef struct private_ike_config_t private_ike_config_t; + +/** + * Private members of a ike_config_t task. + */ +struct private_ike_config_t { + + /** + * Public methods and task_t interface. + */ + ike_config_t public; + + /** + * Assigned IKE_SA. + */ + ike_sa_t *ike_sa; + + /** + * Are we the initiator? + */ + bool initiator; + + /** + * associated policy with virtual IP configuration + */ + policy_t *policy; + + /** + * virtual ip + */ + host_t *virtual_ip; + + /** + * list of DNS servers + */ + linked_list_t *dns; +}; + +/** + * build configuration payloads and attributes + */ +static void build_payloads(private_ike_config_t *this, message_t *message, + config_type_t type) +{ + cp_payload_t *cp; + configuration_attribute_t *ca; + chunk_t chunk, prefix; + + if (!this->virtual_ip) + { + return; + } + + cp = cp_payload_create(); + cp->set_config_type(cp, type); + + ca = configuration_attribute_create(); + + if (this->virtual_ip->get_family(this->virtual_ip) == AF_INET) + { + ca->set_type(ca, INTERNAL_IP4_ADDRESS); + if (this->virtual_ip->is_anyaddr(this->virtual_ip)) + { + chunk = chunk_empty; + } + else + { + chunk = this->virtual_ip->get_address(this->virtual_ip); + } + } + else + { + ca->set_type(ca, INTERNAL_IP6_ADDRESS); + if (this->virtual_ip->is_anyaddr(this->virtual_ip)) + { + chunk = chunk_empty; + } + else + { + prefix = chunk_alloca(1); + *prefix.ptr = 64; + chunk = this->virtual_ip->get_address(this->virtual_ip); + chunk = chunk_cata("cc", chunk, prefix); + } + } + ca->set_value(ca, chunk); + cp->add_configuration_attribute(cp, ca); + + /* we currently always add a DNS request if we request an IP */ + if (this->initiator) + { + ca = configuration_attribute_create(); + if (this->virtual_ip->get_family(this->virtual_ip) == AF_INET) + { + ca->set_type(ca, INTERNAL_IP4_DNS); + } + else + { + ca->set_type(ca, INTERNAL_IP6_DNS); + } + cp->add_configuration_attribute(cp, ca); + } + else + { + host_t *ip; + iterator_t *iterator = this->dns->create_iterator(this->dns, TRUE); + while (iterator->iterate(iterator, (void**)&ip)) + { + ca = configuration_attribute_create(); + if (ip->get_family(ip) == AF_INET) + { + ca->set_type(ca, INTERNAL_IP4_DNS); + } + else + { + ca->set_type(ca, INTERNAL_IP6_DNS); + } + chunk = ip->get_address(ip); + ca->set_value(ca, chunk); + cp->add_configuration_attribute(cp, ca); + } + iterator->destroy(iterator); + } + message->add_payload(message, (payload_t*)cp); +} + +/** + * process a single configuration attribute + */ +static void process_attribute(private_ike_config_t *this, + configuration_attribute_t *ca) +{ + host_t *ip; + chunk_t addr; + int family = AF_INET6; + + switch (ca->get_type(ca)) + { + case INTERNAL_IP4_ADDRESS: + family = AF_INET; + /* fall */ + case INTERNAL_IP6_ADDRESS: + { + addr = ca->get_value(ca); + if (addr.len == 0) + { + ip = host_create_any(family); + } + else + { + /* skip prefix byte in IPv6 payload*/ + if (family == AF_INET6) + { + addr.len--; + } + ip = host_create_from_chunk(family, addr, 0); + } + if (ip && !this->virtual_ip) + { + this->virtual_ip = ip; + } + break; + } + case INTERNAL_IP4_DNS: + family = AF_INET; + /* fall */ + case INTERNAL_IP6_DNS: + { + addr = ca->get_value(ca); + if (addr.len == 0) + { + ip = host_create_any(family); + } + else + { + ip = host_create_from_chunk(family, addr, 0); + } + if (ip) + { + this->dns->insert_last(this->dns, ip); + } + break; + } + case INTERNAL_IP4_NBNS: + case INTERNAL_IP6_NBNS: + /* TODO */ + default: + DBG1(DBG_IKE, "ignoring %N config attribute", + configuration_attribute_type_names, + ca->get_type(ca)); + break; + } +} + +/** + * Scan for configuration payloads and attributes + */ +static void process_payloads(private_ike_config_t *this, message_t *message) +{ + iterator_t *iterator, *attributes; + payload_t *payload; + + iterator = message->get_payload_iterator(message); + while (iterator->iterate(iterator, (void**)&payload)) + { + if (payload->get_type(payload) == CONFIGURATION) + { + cp_payload_t *cp = (cp_payload_t*)payload; + configuration_attribute_t *ca; + switch (cp->get_config_type(cp)) + { + case CFG_REQUEST: + case CFG_REPLY: + { + attributes = cp->create_attribute_iterator(cp); + while (attributes->iterate(attributes, (void**)&ca)) + { + process_attribute(this, ca); + } + attributes->destroy(attributes); + break; + } + default: + DBG1(DBG_IKE, "ignoring %N config payload", + config_type_names, cp->get_config_type(cp)); + break; + } + } + } + iterator->destroy(iterator); +} + +/** + * Implementation of task_t.process for initiator + */ +static status_t build_i(private_ike_config_t *this, message_t *message) +{ + if (message->get_exchange_type(message) != IKE_SA_INIT) + { + this->virtual_ip = this->policy->get_virtual_ip(this->policy, NULL); + + build_payloads(this, message, CFG_REQUEST); + } + + return NEED_MORE; +} + +/** + * Implementation of task_t.process for responder + */ +static status_t process_r(private_ike_config_t *this, message_t *message) +{ + if (message->get_exchange_type(message) != IKE_SA_INIT) + { + process_payloads(this, message); + } + return NEED_MORE; +} + +/** + * Implementation of task_t.build for responder + */ +static status_t build_r(private_ike_config_t *this, message_t *message) +{ + if (message->get_exchange_type(message) != IKE_SA_INIT) + { + this->policy = this->ike_sa->get_policy(this->ike_sa); + + if (this->policy && this->virtual_ip) + { + host_t *ip; + + DBG1(DBG_IKE, "peer requested virtual IP %H", this->virtual_ip); + ip = this->policy->get_virtual_ip(this->policy, this->virtual_ip); + if (ip == NULL) + { + DBG1(DBG_IKE, "not assigning a virtual IP to peer"); + return SUCCESS; + } + DBG1(DBG_IKE, "assigning virtual IP %H to peer", ip); + this->ike_sa->set_virtual_ip(this->ike_sa, FALSE, ip); + + this->virtual_ip->destroy(this->virtual_ip); + this->virtual_ip = ip; + + /* DNS is for testing only */ + if (this->dns->remove_last(this->dns, (void**)&ip) == SUCCESS) + { + ip->destroy(ip); + ip = host_create_from_string("10.3.0.1", 0); + this->dns->insert_last(this->dns, ip); + ip = host_create_from_string("10.3.0.2", 0); + this->dns->insert_last(this->dns, ip); + } + + build_payloads(this, message, CFG_REPLY); + } + return SUCCESS; + } + return NEED_MORE; +} + +/** + * Implementation of task_t.process for initiator + */ +static status_t process_i(private_ike_config_t *this, message_t *message) +{ + if (message->get_exchange_type(message) != IKE_SA_INIT) + { + host_t *ip; + + DESTROY_IF(this->virtual_ip); + this->virtual_ip = NULL; + + process_payloads(this, message); + + if (this->virtual_ip) + { + this->ike_sa->set_virtual_ip(this->ike_sa, TRUE, this->virtual_ip); + + while (this->dns->remove_last(this->dns, (void**)&ip) == SUCCESS) + { + this->ike_sa->add_dns_server(this->ike_sa, ip); + ip->destroy(ip); + } + } + return SUCCESS; + } + return NEED_MORE; +} + +/** + * Implementation of task_t.get_type + */ +static task_type_t get_type(private_ike_config_t *this) +{ + return IKE_CONFIG; +} + +/** + * Implementation of task_t.migrate + */ +static void migrate(private_ike_config_t *this, ike_sa_t *ike_sa) +{ + DESTROY_IF(this->virtual_ip); + this->dns->destroy_offset(this->dns, offsetof(host_t, destroy)); + + this->ike_sa = ike_sa; + this->virtual_ip = NULL; + this->dns = linked_list_create(); +} + +/** + * Implementation of task_t.destroy + */ +static void destroy(private_ike_config_t *this) +{ + DESTROY_IF(this->virtual_ip); + this->dns->destroy_offset(this->dns, offsetof(host_t, destroy)); + free(this); +} + +/* + * Described in header. + */ +ike_config_t *ike_config_create(ike_sa_t *ike_sa, policy_t *policy) +{ + private_ike_config_t *this = malloc_thing(private_ike_config_t); + + this->public.task.get_type = (task_type_t(*)(task_t*))get_type; + this->public.task.migrate = (void(*)(task_t*,ike_sa_t*))migrate; + this->public.task.destroy = (void(*)(task_t*))destroy; + + if (policy) + { + this->public.task.build = (status_t(*)(task_t*,message_t*))build_i; + this->public.task.process = (status_t(*)(task_t*,message_t*))process_i; + this->initiator = TRUE; + } + else + { + this->public.task.build = (status_t(*)(task_t*,message_t*))build_r; + this->public.task.process = (status_t(*)(task_t*,message_t*))process_r; + this->initiator = FALSE; + } + + this->ike_sa = ike_sa; + this->policy = policy; + this->virtual_ip = NULL; + this->dns = linked_list_create(); + + return &this->public; +} diff --git a/src/charon/sa/tasks/ike_config.h b/src/charon/sa/tasks/ike_config.h new file mode 100644 index 000000000..0c9b961b4 --- /dev/null +++ b/src/charon/sa/tasks/ike_config.h @@ -0,0 +1,59 @@ +/** + * @file ike_config.h + * + * @brief Interface ike_config_t. + * + */ + +/* + * Copyright (C) 2007 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#ifndef IKE_CONFIG_H_ +#define IKE_CONFIG_H_ + +typedef struct ike_config_t ike_config_t; + +#include <library.h> +#include <sa/ike_sa.h> +#include <sa/tasks/task.h> +#include <config/policies/policy.h> + +/** + * @brief Task of type IKE_CONFIG, sets up a virtual IP and other + * configurations for an IKE_SA. + * + * @b Constructors: + * - ike_config_create() + * + * @ingroup tasks + */ +struct ike_config_t { + + /** + * Implements the task_t interface + */ + task_t task; +}; + +/** + * @brief Create a new ike_config task. + * + * @param ike_sa IKE_SA this task works for + * @param policy policy for the initiator, NULL for the responder + * @return ike_config task to handle by the task_manager + */ +ike_config_t *ike_config_create(ike_sa_t *ike_sa, policy_t *policy); + +#endif /* IKE_CONFIG_H_ */ diff --git a/src/charon/sa/tasks/ike_delete.c b/src/charon/sa/tasks/ike_delete.c new file mode 100644 index 000000000..b9efc4ea9 --- /dev/null +++ b/src/charon/sa/tasks/ike_delete.c @@ -0,0 +1,169 @@ +/** + * @file ike_delete.c + * + * @brief Implementation of the ike_delete task. + * + */ + +/* + * Copyright (C) 2006-2007 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "ike_delete.h" + +#include <daemon.h> +#include <encoding/payloads/delete_payload.h> + + +typedef struct private_ike_delete_t private_ike_delete_t; + +/** + * Private members of a ike_delete_t task. + */ +struct private_ike_delete_t { + + /** + * Public methods and task_t interface. + */ + ike_delete_t public; + + /** + * Assigned IKE_SA. + */ + ike_sa_t *ike_sa; + + /** + * Are we the initiator? + */ + bool initiator; + + /** + * are we responding to a delete, but have initated our own? + */ + bool simultaneous; +}; + +/** + * Implementation of task_t.build for initiator + */ +static status_t build_i(private_ike_delete_t *this, message_t *message) +{ + delete_payload_t *delete_payload; + + delete_payload = delete_payload_create(PROTO_IKE); + message->add_payload(message, (payload_t*)delete_payload); + + this->ike_sa->set_state(this->ike_sa, IKE_DELETING); + + return NEED_MORE; +} + +/** + * Implementation of task_t.process for initiator + */ +static status_t process_i(private_ike_delete_t *this, message_t *message) +{ + /* completed, delete IKE_SA by returning FAILED */ + return FAILED; +} + +/** + * Implementation of task_t.process for initiator + */ +static status_t process_r(private_ike_delete_t *this, message_t *message) +{ + /* we don't even scan the payloads, as the message wouldn't have + * come so far without being correct */ + switch (this->ike_sa->get_state(this->ike_sa)) + { + case IKE_DELETING: + this->simultaneous = TRUE; + break; + case IKE_ESTABLISHED: + DBG1(DBG_IKE, "deleting IKE_SA on request"); + /* warn only if we are established */ + default: + this->ike_sa->set_state(this->ike_sa, IKE_DELETING); + break; + } + return NEED_MORE; +} + +/** + * Implementation of task_t.build for responder + */ +static status_t build_r(private_ike_delete_t *this, message_t *message) +{ + if (this->simultaneous) + { + /* wait for peers response for our delete request */ + return SUCCESS; + } + /* completed, delete IKE_SA by returning FAILED */ + return FAILED; +} + +/** + * Implementation of task_t.get_type + */ +static task_type_t get_type(private_ike_delete_t *this) +{ + return IKE_DELETE; +} + +/** + * Implementation of task_t.migrate + */ +static void migrate(private_ike_delete_t *this, ike_sa_t *ike_sa) +{ + this->ike_sa = ike_sa; + this->simultaneous = FALSE; +} + +/** + * Implementation of task_t.destroy + */ +static void destroy(private_ike_delete_t *this) +{ + free(this); +} + +/* + * Described in header. + */ +ike_delete_t *ike_delete_create(ike_sa_t *ike_sa, bool initiator) +{ + private_ike_delete_t *this = malloc_thing(private_ike_delete_t); + + this->public.task.get_type = (task_type_t(*)(task_t*))get_type; + this->public.task.migrate = (void(*)(task_t*,ike_sa_t*))migrate; + this->public.task.destroy = (void(*)(task_t*))destroy; + + if (initiator) + { + this->public.task.build = (status_t(*)(task_t*,message_t*))build_i; + this->public.task.process = (status_t(*)(task_t*,message_t*))process_i; + } + else + { + this->public.task.build = (status_t(*)(task_t*,message_t*))build_r; + this->public.task.process = (status_t(*)(task_t*,message_t*))process_r; + } + + this->ike_sa = ike_sa; + this->initiator = initiator; + this->simultaneous = FALSE; + + return &this->public; +} diff --git a/src/charon/sa/tasks/ike_delete.h b/src/charon/sa/tasks/ike_delete.h new file mode 100644 index 000000000..e8ec5ebbe --- /dev/null +++ b/src/charon/sa/tasks/ike_delete.h @@ -0,0 +1,57 @@ +/** + * @file ike_delete.h + * + * @brief Interface ike_delete_t. + * + */ + +/* + * Copyright (C) 2007 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#ifndef IKE_DELETE_H_ +#define IKE_DELETE_H_ + +typedef struct ike_delete_t ike_delete_t; + +#include <library.h> +#include <sa/ike_sa.h> +#include <sa/tasks/task.h> + +/** + * @brief Task of type ike_delete, delete an IKE_SA. + * + * @b Constructors: + * - ike_delete_create() + * + * @ingroup tasks + */ +struct ike_delete_t { + + /** + * Implements the task_t interface + */ + task_t task; +}; + +/** + * @brief Create a new ike_delete task. + * + * @param ike_sa IKE_SA this task works for + * @param initiator TRUE if we initiate the delete + * @return ike_delete task to handle by the task_manager + */ +ike_delete_t *ike_delete_create(ike_sa_t *ike_sa, bool initiator); + +#endif /* IKE_DELETE_H_ */ diff --git a/src/charon/sa/tasks/ike_dpd.c b/src/charon/sa/tasks/ike_dpd.c new file mode 100644 index 000000000..1cb05c45c --- /dev/null +++ b/src/charon/sa/tasks/ike_dpd.c @@ -0,0 +1,106 @@ +/** + * @file ike_dpd.c + * + * @brief Implementation of the ike_dpd task. + * + */ + +/* + * Copyright (C) 2007 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "ike_dpd.h" + +#include <daemon.h> + + +typedef struct private_ike_dpd_t private_ike_dpd_t; + +/** + * Private members of a ike_dpd_t task. + */ +struct private_ike_dpd_t { + + /** + * Public methods and task_t interface. + */ + ike_dpd_t public; +}; + +/** + * Implementation of task_t.build for initiator + * Implementation of task_t.process for responder + */ +static status_t return_need_more(private_ike_dpd_t *this, message_t *message) +{ + return NEED_MORE; +} + +/** + * Implementation of task_t.process for initiator + * Implementation of task_t.build for responder + */ +static status_t return_success(private_ike_dpd_t *this, message_t *message) +{ + return SUCCESS; +} + +/** + * Implementation of task_t.get_type + */ +static task_type_t get_type(private_ike_dpd_t *this) +{ + return IKE_DEADPEER; +} + +/** + * Implementation of task_t.migrate + */ +static void migrate(private_ike_dpd_t *this, ike_sa_t *ike_sa) +{ + +} + +/** + * Implementation of task_t.destroy + */ +static void destroy(private_ike_dpd_t *this) +{ + free(this); +} + +/* + * Described in header. + */ +ike_dpd_t *ike_dpd_create(bool initiator) +{ + private_ike_dpd_t *this = malloc_thing(private_ike_dpd_t); + + this->public.task.get_type = (task_type_t(*)(task_t*))get_type; + this->public.task.migrate = (void(*)(task_t*,ike_sa_t*))migrate; + this->public.task.destroy = (void(*)(task_t*))destroy; + + if (initiator) + { + this->public.task.build = (status_t(*)(task_t*,message_t*))return_need_more; + this->public.task.process = (status_t(*)(task_t*,message_t*))return_success; + } + else + { + this->public.task.build = (status_t(*)(task_t*,message_t*))return_success; + this->public.task.process = (status_t(*)(task_t*,message_t*))return_need_more; + } + + return &this->public; +} diff --git a/src/charon/sa/tasks/ike_dpd.h b/src/charon/sa/tasks/ike_dpd.h new file mode 100644 index 000000000..531b0502d --- /dev/null +++ b/src/charon/sa/tasks/ike_dpd.h @@ -0,0 +1,58 @@ +/** + * @file ike_dpd.h + * + * @brief Interface ike_dpd_t. + * + */ + +/* + * Copyright (C) 2007 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#ifndef IKE_DPD_H_ +#define IKE_DPD_H_ + +typedef struct ike_dpd_t ike_dpd_t; + +#include <library.h> +#include <sa/ike_sa.h> +#include <sa/tasks/task.h> + +/** + * @brief Task of type ike_dpd, detects dead peers. + * + * The DPD task actually does nothing, as a DPD has no associated payloads. + * + * @b Constructors: + * - ike_dpd_create() + * + * @ingroup tasks + */ +struct ike_dpd_t { + + /** + * Implements the task_t interface + */ + task_t task; +}; + +/** + * @brief Create a new ike_dpd task. + * + * @param initiator TRUE if thask is the original initator + * @return ike_dpd task to handle by the task_manager + */ +ike_dpd_t *ike_dpd_create(bool initiator); + +#endif /* IKE_DPD_H_ */ diff --git a/src/charon/sa/tasks/ike_init.c b/src/charon/sa/tasks/ike_init.c new file mode 100644 index 000000000..9149aab91 --- /dev/null +++ b/src/charon/sa/tasks/ike_init.c @@ -0,0 +1,536 @@ +/** + * @file ike_init.c + * + * @brief Implementation of the ike_init task. + * + */ + +/* + * 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 "ike_init.h" + +#include <string.h> + +#include <daemon.h> +#include <crypto/diffie_hellman.h> +#include <encoding/payloads/sa_payload.h> +#include <encoding/payloads/ke_payload.h> +#include <encoding/payloads/nonce_payload.h> + + +typedef struct private_ike_init_t private_ike_init_t; + +/** + * Private members of a ike_init_t task. + */ +struct private_ike_init_t { + + /** + * Public methods and task_t interface. + */ + ike_init_t public; + + /** + * Assigned IKE_SA. + */ + ike_sa_t *ike_sa; + + /** + * Are we the initiator? + */ + bool initiator; + + /** + * Connection established by this IKE_SA + */ + connection_t *connection; + + /** + * diffie hellman group to use + */ + diffie_hellman_group_t dh_group; + + /** + * Diffie hellman object used to generate public DH value. + */ + diffie_hellman_t *diffie_hellman; + + /** + * nonce chosen by us + */ + chunk_t my_nonce; + + /** + * nonce chosen by peer + */ + chunk_t other_nonce; + + /** + * Negotiated proposal used for IKE_SA + */ + proposal_t *proposal; + + /** + * Old IKE_SA which gets rekeyed + */ + ike_sa_t *old_sa; +}; + +/** + * build the payloads for the message + */ +static void build_payloads(private_ike_init_t *this, message_t *message) +{ + sa_payload_t *sa_payload; + ke_payload_t *ke_payload; + nonce_payload_t *nonce_payload; + linked_list_t *proposal_list; + ike_sa_id_t *id; + proposal_t *proposal; + iterator_t *iterator; + + id = this->ike_sa->get_id(this->ike_sa); + + this->connection = this->ike_sa->get_connection(this->ike_sa); + + if (this->initiator) + { + proposal_list = this->connection->get_proposals(this->connection); + if (this->old_sa) + { + /* include SPI of new IKE_SA when we are rekeying */ + iterator = proposal_list->create_iterator(proposal_list, TRUE); + while (iterator->iterate(iterator, (void**)&proposal)) + { + proposal->set_spi(proposal, id->get_initiator_spi(id)); + } + iterator->destroy(iterator); + } + + sa_payload = sa_payload_create_from_proposal_list(proposal_list); + proposal_list->destroy_offset(proposal_list, offsetof(proposal_t, destroy)); + } + else + { + if (this->old_sa) + { + /* include SPI of new IKE_SA when we are rekeying */ + this->proposal->set_spi(this->proposal, id->get_responder_spi(id)); + } + sa_payload = sa_payload_create_from_proposal(this->proposal); + } + message->add_payload(message, (payload_t*)sa_payload); + + ke_payload = ke_payload_create_from_diffie_hellman(this->diffie_hellman); + message->add_payload(message, (payload_t*)ke_payload); + + nonce_payload = nonce_payload_create(); + nonce_payload->set_nonce(nonce_payload, this->my_nonce); + + message->add_payload(message, (payload_t*)nonce_payload); +} + +/** + * Read payloads from message + */ +static void process_payloads(private_ike_init_t *this, message_t *message) +{ + iterator_t *iterator; + payload_t *payload; + + iterator = message->get_payload_iterator(message); + while (iterator->iterate(iterator, (void**)&payload)) + { + switch (payload->get_type(payload)) + { + case SECURITY_ASSOCIATION: + { + sa_payload_t *sa_payload = (sa_payload_t*)payload; + linked_list_t *proposal_list; + + proposal_list = sa_payload->get_proposals(sa_payload); + this->proposal = this->connection->select_proposal( + this->connection, proposal_list); + proposal_list->destroy_offset(proposal_list, + offsetof(proposal_t, destroy)); + break; + } + case KEY_EXCHANGE: + { + ke_payload_t *ke_payload = (ke_payload_t*)payload; + diffie_hellman_group_t dh_group; + chunk_t key_data; + + dh_group = ke_payload->get_dh_group_number(ke_payload); + + if (this->initiator) + { + if (dh_group != this->dh_group) + { + DBG1(DBG_IKE, "received a DH group not requested (%N)", + diffie_hellman_group_names, dh_group); + break; + } + } + else + { + this->dh_group = dh_group; + if (!this->connection->check_dh_group(this->connection, + dh_group)) + { + break; + } + this->diffie_hellman = diffie_hellman_create(dh_group); + } + if (this->diffie_hellman) + { + key_data = ke_payload->get_key_exchange_data(ke_payload); + this->diffie_hellman->set_other_public_value(this->diffie_hellman, key_data); + } + break; + } + case NONCE: + { + nonce_payload_t *nonce_payload = (nonce_payload_t*)payload; + this->other_nonce = nonce_payload->get_nonce(nonce_payload); + break; + } + default: + break; + } + } + iterator->destroy(iterator); +} + +/** + * Implementation of task_t.process for initiator + */ +static status_t build_i(private_ike_init_t *this, message_t *message) +{ + randomizer_t *randomizer; + status_t status; + + this->connection = this->ike_sa->get_connection(this->ike_sa); + SIG(IKE_UP_START, "initiating IKE_SA to %H", + this->connection->get_other_host(this->connection)); + this->ike_sa->set_state(this->ike_sa, IKE_CONNECTING); + + /* if the DH group is set via use_dh_group(), we already have a DH object */ + if (!this->diffie_hellman) + { + this->dh_group = this->connection->get_dh_group(this->connection); + this->diffie_hellman = diffie_hellman_create(this->dh_group); + if (this->diffie_hellman == NULL) + { + SIG(IKE_UP_FAILED, "configured DH group %N not supported", + diffie_hellman_group_names, this->dh_group); + return FAILED; + } + } + + randomizer = randomizer_create(); + status = randomizer->allocate_pseudo_random_bytes(randomizer, NONCE_SIZE, + &this->my_nonce); + randomizer->destroy(randomizer); + if (status != SUCCESS) + { + SIG(IKE_UP_FAILED, "error generating random nonce value"); + return FAILED; + } + + build_payloads(this, message); + + return NEED_MORE; +} + +/** + * Implementation of task_t.process for initiator + */ +static status_t process_r(private_ike_init_t *this, message_t *message) +{ + randomizer_t *randomizer; + + this->connection = this->ike_sa->get_connection(this->ike_sa); + SIG(IKE_UP_FAILED, "%H is initiating an IKE_SA", + message->get_source(message)); + this->ike_sa->set_state(this->ike_sa, IKE_CONNECTING); + + randomizer = randomizer_create(); + if (randomizer->allocate_pseudo_random_bytes(randomizer, NONCE_SIZE, + &this->my_nonce) != SUCCESS) + { + DBG1(DBG_IKE, "error generating random nonce value"); + } + randomizer->destroy(randomizer); + + process_payloads(this, message); + + return NEED_MORE; +} + +/** + * Implementation of task_t.build for responder + */ +static status_t build_r(private_ike_init_t *this, message_t *message) +{ + chunk_t secret; + status_t status; + + /* check if we have everything we need */ + if (this->proposal == NULL || + this->other_nonce.len == 0 || this->my_nonce.len == 0) + { + SIG(IKE_UP_FAILED, "received proposals inacceptable"); + message->add_notify(message, TRUE, NO_PROPOSAL_CHOSEN, chunk_empty); + return FAILED; + } + + if (this->diffie_hellman == NULL || + this->diffie_hellman->get_shared_secret(this->diffie_hellman, + &secret) != SUCCESS) + { + chunk_t chunk; + u_int16_t dh_enc; + + SIG(IKE_UP_FAILED, "received inacceptable DH group (%N)", + diffie_hellman_group_names, this->dh_group); + this->dh_group = this->connection->get_dh_group(this->connection); + dh_enc = htons(this->dh_group); + chunk.ptr = (u_int8_t*)&dh_enc; + chunk.len = sizeof(dh_enc); + message->add_notify(message, TRUE, INVALID_KE_PAYLOAD, chunk); + DBG1(DBG_IKE, "requesting DH group %N", + diffie_hellman_group_names, this->dh_group); + return FAILED; + } + + + if (this->old_sa) + { + ike_sa_id_t *id; + prf_t *prf, *child_prf; + + /* Apply SPI if we are rekeying */ + id = this->ike_sa->get_id(this->ike_sa); + id->set_initiator_spi(id, this->proposal->get_spi(this->proposal)); + + /* setup crypto keys for the rekeyed SA */ + prf = this->old_sa->get_prf(this->old_sa); + child_prf = this->old_sa->get_child_prf(this->old_sa); + status = this->ike_sa->derive_keys(this->ike_sa, this->proposal, secret, + this->other_nonce, this->my_nonce, + FALSE, child_prf, prf); + } + else + { + /* setup crypto keys */ + status = this->ike_sa->derive_keys(this->ike_sa, this->proposal, secret, + this->other_nonce, this->my_nonce, + FALSE, NULL, NULL); + } + if (status != SUCCESS) + { + SIG(IKE_UP_FAILED, "key derivation failed"); + message->add_notify(message, TRUE, NO_PROPOSAL_CHOSEN, chunk_empty); + return FAILED; + } + + build_payloads(this, message); + + return SUCCESS; +} + +/** + * Implementation of task_t.process for initiator + */ +static status_t process_i(private_ike_init_t *this, message_t *message) +{ + chunk_t secret; + status_t status; + iterator_t *iterator; + payload_t *payload; + + /* check for erronous notifies */ + iterator = message->get_payload_iterator(message); + while (iterator->iterate(iterator, (void**)&payload)) + { + if (payload->get_type(payload) == NOTIFY) + { + notify_payload_t *notify = (notify_payload_t*)payload; + notify_type_t type = notify->get_notify_type(notify); + + switch (type) + { + case INVALID_KE_PAYLOAD: + { + chunk_t data; + diffie_hellman_group_t old_dh_group; + + old_dh_group = this->dh_group; + data = notify->get_notification_data(notify); + this->dh_group = ntohs(*((u_int16_t*)data.ptr)); + + DBG1(DBG_IKE, "peer didn't accept DH group %N, it requested" + " %N", diffie_hellman_group_names, old_dh_group, + diffie_hellman_group_names, this->dh_group); + if (!this->connection->check_dh_group(this->connection, + this->dh_group)) + { + SIG(IKE_UP_FAILED, "requested DH group %N not " + "acceptable, giving up", diffie_hellman_group_names, + this->dh_group); + iterator->destroy(iterator); + return FAILED; + } + + this->ike_sa->reset(this->ike_sa); + + iterator->destroy(iterator); + return NEED_MORE; + } + default: + { + if (type < 16383) + { + SIG(IKE_UP_FAILED, "received %N notify error", + notify_type_names, type); + iterator->destroy(iterator); + return FAILED; + } + } + } + } + } + iterator->destroy(iterator); + + process_payloads(this, message); + + /* check if we have everything */ + if (this->proposal == NULL || + this->other_nonce.len == 0 || this->my_nonce.len == 0) + { + SIG(IKE_UP_FAILED, "peers proposal selection invalid"); + return FAILED; + } + + if (this->diffie_hellman == NULL || + this->diffie_hellman->get_shared_secret(this->diffie_hellman, + &secret) != SUCCESS) + { + SIG(IKE_UP_FAILED, "peers DH group selection invalid"); + return FAILED; + } + + /* Apply SPI if we are rekeying */ + if (this->old_sa) + { + ike_sa_id_t *id; + prf_t *prf, *child_prf; + + id = this->ike_sa->get_id(this->ike_sa); + id->set_responder_spi(id, this->proposal->get_spi(this->proposal)); + + /* setup crypto keys for the rekeyed SA */ + prf = this->old_sa->get_prf(this->old_sa); + child_prf = this->old_sa->get_child_prf(this->old_sa); + status = this->ike_sa->derive_keys(this->ike_sa, this->proposal, secret, + this->my_nonce, this->other_nonce, + TRUE, child_prf, prf); + } + else + { + /* setup crypto keys for a new SA */ + status = this->ike_sa->derive_keys(this->ike_sa, this->proposal, secret, + this->my_nonce, this->other_nonce, + TRUE, NULL, NULL); + } + if (status != SUCCESS) + { + SIG(IKE_UP_FAILED, "key derivation failed"); + return FAILED; + } + return SUCCESS; +} + +/** + * Implementation of task_t.get_type + */ +static task_type_t get_type(private_ike_init_t *this) +{ + return IKE_INIT; +} + +/** + * Implementation of task_t.migrate + */ +static void migrate(private_ike_init_t *this, ike_sa_t *ike_sa) +{ + DESTROY_IF(this->proposal); + DESTROY_IF(this->diffie_hellman); + chunk_free(&this->my_nonce); + chunk_free(&this->other_nonce); + + this->ike_sa = ike_sa; + this->proposal = NULL; + this->diffie_hellman = diffie_hellman_create(this->dh_group); +} + +/** + * Implementation of task_t.destroy + */ +static void destroy(private_ike_init_t *this) +{ + DESTROY_IF(this->proposal); + DESTROY_IF(this->diffie_hellman); + chunk_free(&this->my_nonce); + chunk_free(&this->other_nonce); + free(this); +} + +/* + * Described in header. + */ +ike_init_t *ike_init_create(ike_sa_t *ike_sa, bool initiator, ike_sa_t *old_sa) +{ + private_ike_init_t *this = malloc_thing(private_ike_init_t); + + this->public.task.get_type = (task_type_t(*)(task_t*))get_type; + this->public.task.migrate = (void(*)(task_t*,ike_sa_t*))migrate; + this->public.task.destroy = (void(*)(task_t*))destroy; + if (initiator) + { + this->public.task.build = (status_t(*)(task_t*,message_t*))build_i; + this->public.task.process = (status_t(*)(task_t*,message_t*))process_i; + } + else + { + this->public.task.build = (status_t(*)(task_t*,message_t*))build_r; + this->public.task.process = (status_t(*)(task_t*,message_t*))process_r; + } + + this->ike_sa = ike_sa; + this->initiator = initiator; + this->dh_group = MODP_NONE; + this->diffie_hellman = NULL; + this->my_nonce = chunk_empty; + this->other_nonce = chunk_empty; + this->proposal = NULL; + this->connection = NULL; + this->old_sa = old_sa; + + return &this->public; +} diff --git a/src/charon/sa/tasks/ike_init.h b/src/charon/sa/tasks/ike_init.h new file mode 100644 index 000000000..290cc46e8 --- /dev/null +++ b/src/charon/sa/tasks/ike_init.h @@ -0,0 +1,60 @@ +/** + * @file ike_init.h + * + * @brief Interface ike_init_t. + * + */ + +/* + * Copyright (C) 2007 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#ifndef IKE_INIT_H_ +#define IKE_INIT_H_ + +typedef struct ike_init_t ike_init_t; + +#include <library.h> +#include <sa/ike_sa.h> +#include <sa/tasks/task.h> + +/** + * @brief Task of type IKE_INIT, creates an IKE_SA without authentication. + * + * The authentication of is handle in the ike_auth and/or ike_auth_eap task. + * + * @b Constructors: + * - ike_init_create() + * + * @ingroup tasks + */ +struct ike_init_t { + + /** + * Implements the task_t interface + */ + task_t task; +}; + +/** + * @brief Create a new IKE_INIT task. + * + * @param ike_sa IKE_SA this task works for (new one when rekeying) + * @param initiator TRUE if thask is the original initator + * @param old_sa old IKE_SA when we are rekeying + * @return ike_init task to handle by the task_manager + */ +ike_init_t *ike_init_create(ike_sa_t *ike_sa, bool initiator, ike_sa_t *old_sa); + +#endif /* IKE_INIT_H_ */ diff --git a/src/charon/sa/tasks/ike_natd.c b/src/charon/sa/tasks/ike_natd.c new file mode 100644 index 000000000..a25b6e0d7 --- /dev/null +++ b/src/charon/sa/tasks/ike_natd.c @@ -0,0 +1,378 @@ +/** + * @file ike_natd.c + * + * @brief Implementation of the ike_natd task. + * + */ + +/* + * Copyright (C) 2006-2007 Martin Willi + * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger + * 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 "ike_natd.h" + +#include <string.h> + +#include <daemon.h> +#include <crypto/hashers/hasher.h> +#include <encoding/payloads/notify_payload.h> + + +typedef struct private_ike_natd_t private_ike_natd_t; + +/** + * Private members of a ike_natd_t task. + */ +struct private_ike_natd_t { + + /** + * Public methods and task_t interface. + */ + ike_natd_t public; + + /** + * Assigned IKE_SA. + */ + ike_sa_t *ike_sa; + + /** + * Are we the initiator? + */ + bool initiator; + + /** + * Hasher used to build NAT detection hashes + */ + hasher_t *hasher; + + /** + * Did we process any NAT detection notifys for a source address? + */ + bool src_seen; + + /** + * Did we process any NAT detection notifys for a destination address? + */ + bool dst_seen; + + /** + * Have we found a matching source address NAT hash? + */ + bool src_matched; + + /** + * Have we found a matching destination address NAT hash? + */ + bool dst_matched; +}; + + +/** + * Build NAT detection hash for a host + */ +static chunk_t generate_natd_hash(private_ike_natd_t *this, + ike_sa_id_t *ike_sa_id, host_t *host) +{ + chunk_t natd_chunk, spi_i_chunk, spi_r_chunk, addr_chunk, port_chunk; + chunk_t natd_hash; + u_int64_t spi_i, spi_r; + u_int16_t port; + + /* prepare all requred chunks */ + spi_i = ike_sa_id->get_initiator_spi(ike_sa_id); + spi_r = ike_sa_id->get_responder_spi(ike_sa_id); + spi_i_chunk.ptr = (void*)&spi_i; + spi_i_chunk.len = sizeof(spi_i); + spi_r_chunk.ptr = (void*)&spi_r; + spi_r_chunk.len = sizeof(spi_r); + port = htons(host->get_port(host)); + port_chunk.ptr = (void*)&port; + port_chunk.len = sizeof(port); + addr_chunk = host->get_address(host); + DBG2(DBG_IKE, "using SPI %J", ike_sa_id); + + /* natd_hash = SHA1( spi_i | spi_r | address | port ) */ + natd_chunk = chunk_cat("cccc", spi_i_chunk, spi_r_chunk, addr_chunk, port_chunk); + this->hasher->allocate_hash(this->hasher, natd_chunk, &natd_hash); + DBG3(DBG_IKE, "natd_chunk %B", &natd_chunk); + DBG3(DBG_IKE, "natd_hash %B", &natd_hash); + + chunk_free(&natd_chunk); + return natd_hash; +} + +/** + * Build a NAT detection notify payload. + */ +static notify_payload_t *build_natd_payload(private_ike_natd_t *this, + notify_type_t type, host_t *host) +{ + chunk_t hash; + notify_payload_t *notify; + ike_sa_id_t *ike_sa_id; + + ike_sa_id = this->ike_sa->get_id(this->ike_sa); + notify = notify_payload_create(); + notify->set_notify_type(notify, type); + hash = generate_natd_hash(this, ike_sa_id, host); + notify->set_notification_data(notify, hash); + chunk_free(&hash); + + return notify; +} + +/** + * read notifys from message and evaluate them + */ +static void process_payloads(private_ike_natd_t *this, message_t *message) +{ + iterator_t *iterator; + payload_t *payload; + notify_payload_t *notify; + chunk_t hash, src_hash, dst_hash; + ike_sa_id_t *ike_sa_id; + host_t *me, *other; + + /* Precompute NAT-D hashes for incoming NAT notify comparison */ + ike_sa_id = message->get_ike_sa_id(message); + me = this->ike_sa->get_my_host(this->ike_sa); + other = this->ike_sa->get_other_host(this->ike_sa); + dst_hash = generate_natd_hash(this, ike_sa_id, me); + src_hash = generate_natd_hash(this, ike_sa_id, other); + + DBG2(DBG_IKE, "precalculated src_hash %B", &src_hash); + DBG2(DBG_IKE, "precalculated dst_hash %B", &dst_hash); + + iterator = message->get_payload_iterator(message); + while (iterator->iterate(iterator, (void**)&payload)) + { + if (payload->get_type(payload) != NOTIFY) + { + continue; + } + notify = (notify_payload_t*)payload; + switch (notify->get_notify_type(notify)) + { + case NAT_DETECTION_DESTINATION_IP: + { + this->dst_seen = TRUE; + if (!this->dst_matched) + { + hash = notify->get_notification_data(notify); + DBG2(DBG_IKE, "received dst_hash %B", &hash); + if (chunk_equals(hash, dst_hash)) + { + this->dst_matched = TRUE; + } + } + break; + } + case NAT_DETECTION_SOURCE_IP: + { + this->src_seen = TRUE; + if (!this->src_matched) + { + hash = notify->get_notification_data(notify); + DBG2(DBG_IKE, "received src_hash %B", &hash); + if (chunk_equals(hash, src_hash)) + { + this->src_matched = TRUE; + } + } + break; + } + default: + break; + } + } + iterator->destroy(iterator); + + chunk_free(&src_hash); + chunk_free(&dst_hash); + + if (this->src_seen && this->dst_seen) + { + if (!this->dst_matched) + { + this->ike_sa->enable_natt(this->ike_sa, TRUE); + } + if (!this->src_matched) + { + this->ike_sa->enable_natt(this->ike_sa, FALSE); + } + } +} + +/** + * Implementation of task_t.process for initiator + */ +static status_t process_i(private_ike_natd_t *this, message_t *message) +{ + process_payloads(this, message); + + if (this->ike_sa->is_natt_enabled(this->ike_sa)) + { + host_t *me, *other; + + me = this->ike_sa->get_my_host(this->ike_sa); + me->set_port(me, IKEV2_NATT_PORT); + other = this->ike_sa->get_other_host(this->ike_sa); + other->set_port(other, IKEV2_NATT_PORT); + } + + return SUCCESS; +} + +/** + * Implementation of task_t.process for initiator + */ +static status_t build_i(private_ike_natd_t *this, message_t *message) +{ + notify_payload_t *notify; + linked_list_t *list; + host_t *host; + + /* include one notify if our address is defined, all addresses otherwise */ + host = this->ike_sa->get_my_host(this->ike_sa); + if (host->is_anyaddr(host)) + { + /* TODO: we could get the src address from netlink!? */ + list = charon->socket->create_local_address_list(charon->socket); + while (list->remove_first(list, (void**)&host) == SUCCESS) + { + notify = build_natd_payload(this, NAT_DETECTION_SOURCE_IP, host); + host->destroy(host); + message->add_payload(message, (payload_t*)notify); + } + list->destroy(list); + } + else + { + notify = build_natd_payload(this, NAT_DETECTION_SOURCE_IP, host); + message->add_payload(message, (payload_t*)notify); + } + + host = this->ike_sa->get_other_host(this->ike_sa); + notify = build_natd_payload(this, NAT_DETECTION_DESTINATION_IP, host); + message->add_payload(message, (payload_t*)notify); + + return NEED_MORE; +} + +/** + * Implementation of task_t.build for responder + */ +static status_t build_r(private_ike_natd_t *this, message_t *message) +{ + notify_payload_t *notify; + host_t *me, *other; + iterator_t *iterator; + u_int count; + + /* when only one payload is in the message, an error occured. + * TODO: find a better hack */ + iterator = message->get_payload_iterator(message); + count = iterator->get_count(iterator); + iterator->destroy(iterator); + if (count < 3) + { + return NEED_MORE; + } + + if (this->src_seen && this->dst_seen) + { + /* initiator seems to support NAT detection, add response */ + me = this->ike_sa->get_my_host(this->ike_sa); + notify = build_natd_payload(this, NAT_DETECTION_SOURCE_IP, me); + message->add_payload(message, (payload_t*)notify); + + other = this->ike_sa->get_other_host(this->ike_sa); + notify = build_natd_payload(this, NAT_DETECTION_DESTINATION_IP, other); + message->add_payload(message, (payload_t*)notify); + } + return SUCCESS; +} + +/** + * Implementation of task_t.process for responder + */ +static status_t process_r(private_ike_natd_t *this, message_t *message) +{ + process_payloads(this, message); + + return NEED_MORE; +} + +/** + * Implementation of task_t.get_type + */ +static task_type_t get_type(private_ike_natd_t *this) +{ + return IKE_NATD; +} + +/** + * Implementation of task_t.migrate + */ +static void migrate(private_ike_natd_t *this, ike_sa_t *ike_sa) +{ + this->ike_sa = ike_sa; + this->src_seen = FALSE; + this->dst_seen = FALSE; + this->src_matched = FALSE; + this->dst_matched = FALSE; +} + +/** + * Implementation of task_t.destroy + */ +static void destroy(private_ike_natd_t *this) +{ + this->hasher->destroy(this->hasher); + free(this); +} + +/* + * Described in header. + */ +ike_natd_t *ike_natd_create(ike_sa_t *ike_sa, bool initiator) +{ + private_ike_natd_t *this = malloc_thing(private_ike_natd_t); + + this->public.task.get_type = (task_type_t(*)(task_t*))get_type; + this->public.task.migrate = (void(*)(task_t*,ike_sa_t*))migrate; + this->public.task.destroy = (void(*)(task_t*))destroy; + + if (initiator) + { + this->public.task.build = (status_t(*)(task_t*,message_t*))build_i; + this->public.task.process = (status_t(*)(task_t*,message_t*))process_i; + } + else + { + this->public.task.build = (status_t(*)(task_t*,message_t*))build_r; + this->public.task.process = (status_t(*)(task_t*,message_t*))process_r; + } + + this->ike_sa = ike_sa; + this->initiator = initiator; + this->hasher = hasher_create(HASH_SHA1); + this->src_seen = FALSE; + this->dst_seen = FALSE; + this->src_matched = FALSE; + this->dst_matched = FALSE; + + return &this->public; +} diff --git a/src/charon/sa/tasks/ike_natd.h b/src/charon/sa/tasks/ike_natd.h new file mode 100644 index 000000000..8d0cb58b4 --- /dev/null +++ b/src/charon/sa/tasks/ike_natd.h @@ -0,0 +1,57 @@ +/** + * @file ike_natd.h + * + * @brief Interface ike_natd_t. + * + */ + +/* + * Copyright (C) 2007 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#ifndef IKE_NATD_H_ +#define IKE_NATD_H_ + +typedef struct ike_natd_t ike_natd_t; + +#include <library.h> +#include <sa/ike_sa.h> +#include <sa/tasks/task.h> + +/** + * @brief Task of type ike_natd, detects NAT situation in IKE_SA_INIT exchange. + * + * @b Constructors: + * - ike_natd_create() + * + * @ingroup tasks + */ +struct ike_natd_t { + + /** + * Implements the task_t interface + */ + task_t task; +}; + +/** + * @brief Create a new ike_natd task. + * + * @param ike_sa IKE_SA this task works for + * @param initiator TRUE if thask is the original initator + * @return ike_natd task to handle by the task_manager + */ +ike_natd_t *ike_natd_create(ike_sa_t *ike_sa, bool initiator); + +#endif /* IKE_NATD_H_ */ diff --git a/src/charon/sa/tasks/ike_rekey.c b/src/charon/sa/tasks/ike_rekey.c new file mode 100644 index 000000000..bbbd310bc --- /dev/null +++ b/src/charon/sa/tasks/ike_rekey.c @@ -0,0 +1,232 @@ +/** + * @file ike_rekey.c + * + * @brief Implementation of the ike_rekey task. + * + */ + +/* + * 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 "ike_rekey.h" + +#include <daemon.h> +#include <crypto/diffie_hellman.h> +#include <encoding/payloads/notify_payload.h> +#include <encoding/payloads/nonce_payload.h> +#include <sa/tasks/ike_init.h> +#include <queues/jobs/delete_ike_sa_job.h> + + +typedef struct private_ike_rekey_t private_ike_rekey_t; + +/** + * Private members of a ike_rekey_t task. + */ +struct private_ike_rekey_t { + + /** + * Public methods and task_t interface. + */ + ike_rekey_t public; + + /** + * Assigned IKE_SA. + */ + ike_sa_t *ike_sa; + + /** + * New IKE_SA which replaces the current one + */ + ike_sa_t *new_sa; + + /** + * Are we the initiator? + */ + bool initiator; + + /** + * the IKE_INIT task which is reused to simplify rekeying + */ + ike_init_t *ike_init; +}; + +/** + * Implementation of task_t.build for initiator + */ +static status_t build_i(private_ike_rekey_t *this, message_t *message) +{ + connection_t *connection; + policy_t *policy; + ike_sa_id_t *id; + + id = ike_sa_id_create(0, 0, TRUE); + this->new_sa = charon->ike_sa_manager->checkout(charon->ike_sa_manager, id); + id->destroy(id); + + connection = this->ike_sa->get_connection(this->ike_sa); + policy = this->ike_sa->get_policy(this->ike_sa); + this->new_sa->set_connection(this->new_sa, connection); + this->new_sa->set_policy(this->new_sa, policy); + + this->ike_init = ike_init_create(this->new_sa, TRUE, this->ike_sa); + this->ike_init->task.build(&this->ike_init->task, message); + + this->ike_sa->set_state(this->ike_sa, IKE_REKEYING); + + return NEED_MORE; +} + +/** + * Implementation of task_t.process for initiator + */ +static status_t process_r(private_ike_rekey_t *this, message_t *message) +{ + connection_t *connection; + policy_t *policy; + ike_sa_id_t *id; + + id = ike_sa_id_create(0, 0, FALSE); + this->new_sa = charon->ike_sa_manager->checkout(charon->ike_sa_manager, id); + id->destroy(id); + + connection = this->ike_sa->get_connection(this->ike_sa); + policy = this->ike_sa->get_policy(this->ike_sa); + this->new_sa->set_connection(this->new_sa, connection); + this->new_sa->set_policy(this->new_sa, policy); + + this->ike_init = ike_init_create(this->new_sa, FALSE, this->ike_sa); + this->ike_init->task.process(&this->ike_init->task, message); + + return NEED_MORE; +} + +/** + * Implementation of task_t.build for responder + */ +static status_t build_r(private_ike_rekey_t *this, message_t *message) +{ + if (this->ike_init->task.build(&this->ike_init->task, message) == FAILED) + { + return SUCCESS; + } + + this->ike_sa->set_state(this->ike_sa, IKE_REKEYING); + this->new_sa->inherit(this->new_sa, this->ike_sa); + this->new_sa->set_state(this->new_sa, IKE_ESTABLISHED); + charon->ike_sa_manager->checkin(charon->ike_sa_manager, this->new_sa); + this->new_sa = NULL; + + return SUCCESS; +} + +/** + * Implementation of task_t.process for initiator + */ +static status_t process_i(private_ike_rekey_t *this, message_t *message) +{ + job_t *job; + + if (this->ike_init->task.process(&this->ike_init->task, message) == FAILED) + { + return SUCCESS; + } + + this->new_sa->set_state(this->new_sa, IKE_ESTABLISHED); + this->new_sa->inherit(this->new_sa, this->ike_sa); + charon->ike_sa_manager->checkin(charon->ike_sa_manager, this->new_sa); + this->new_sa = NULL; + + job = (job_t*)delete_ike_sa_job_create(this->ike_sa->get_id(this->ike_sa), + TRUE); + + charon->job_queue->add(charon->job_queue, job); + return SUCCESS; +} + +/** + * Implementation of task_t.get_type + */ +static task_type_t get_type(private_ike_rekey_t *this) +{ + return IKE_REKEY; +} + +/** + * Implementation of task_t.migrate + */ +static void migrate(private_ike_rekey_t *this, ike_sa_t *ike_sa) +{ + if (this->ike_init) + { + this->ike_init->task.destroy(&this->ike_init->task); + } + if (this->new_sa) + { + charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, + this->new_sa); + } + + this->ike_sa = ike_sa; + this->new_sa = NULL; + this->ike_init = NULL; +} + +/** + * Implementation of task_t.destroy + */ +static void destroy(private_ike_rekey_t *this) +{ + if (this->ike_init) + { + this->ike_init->task.destroy(&this->ike_init->task); + } + if (this->new_sa) + { + charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, + this->new_sa); + } + free(this); +} + +/* + * Described in header. + */ +ike_rekey_t *ike_rekey_create(ike_sa_t *ike_sa, bool initiator) +{ + private_ike_rekey_t *this = malloc_thing(private_ike_rekey_t); + + this->public.task.get_type = (task_type_t(*)(task_t*))get_type; + this->public.task.migrate = (void(*)(task_t*,ike_sa_t*))migrate; + this->public.task.destroy = (void(*)(task_t*))destroy; + if (initiator) + { + this->public.task.build = (status_t(*)(task_t*,message_t*))build_i; + this->public.task.process = (status_t(*)(task_t*,message_t*))process_i; + } + else + { + this->public.task.build = (status_t(*)(task_t*,message_t*))build_r; + this->public.task.process = (status_t(*)(task_t*,message_t*))process_r; + } + + this->ike_sa = ike_sa; + this->new_sa = NULL; + this->ike_init = NULL; + this->initiator = initiator; + + return &this->public; +} diff --git a/src/charon/sa/tasks/ike_rekey.h b/src/charon/sa/tasks/ike_rekey.h new file mode 100644 index 000000000..f1caf5758 --- /dev/null +++ b/src/charon/sa/tasks/ike_rekey.h @@ -0,0 +1,57 @@ +/** + * @file ike_rekey.h + * + * @brief Interface ike_rekey_t. + * + */ + +/* + * Copyright (C) 2007 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#ifndef IKE_REKEY_H_ +#define IKE_REKEY_H_ + +typedef struct ike_rekey_t ike_rekey_t; + +#include <library.h> +#include <sa/ike_sa.h> +#include <sa/tasks/task.h> + +/** + * @brief Task of type IKE_REKEY, rekey an established IKE_SA. + * + * @b Constructors: + * - ike_rekey_create() + * + * @ingroup tasks + */ +struct ike_rekey_t { + + /** + * Implements the task_t interface + */ + task_t task; +}; + +/** + * @brief Create a new IKE_REKEY task. + * + * @param ike_sa IKE_SA this task works for + * @param initiator TRUE for initiator, FALSE for responder + * @return IKE_REKEY task to handle by the task_manager + */ +ike_rekey_t *ike_rekey_create(ike_sa_t *ike_sa, bool initiator); + +#endif /* IKE_REKEY_H_ */ diff --git a/src/charon/sa/tasks/task.c b/src/charon/sa/tasks/task.c new file mode 100644 index 000000000..68d8ebf0c --- /dev/null +++ b/src/charon/sa/tasks/task.c @@ -0,0 +1,38 @@ +/** + * @file task.c + * + * @brief Enum values for task types + * + */ + +/* + * Copyright (C) 2007 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "task.h" + +ENUM(task_type_names, IKE_INIT, CHILD_REKEY, + "IKE_INIT", + "IKE_NATD", + "IKE_AUTHENTICATE", + "IKE_CERT", + "IKE_CONFIG", + "IKE_DPD", + "IKE_REKEY", + "IKE_DELETE", + "IKE_DEADPEER", + "CHILD_CREATE", + "CHILD_DELETE", + "CHILD_REKEY", +); diff --git a/src/charon/sa/tasks/task.h b/src/charon/sa/tasks/task.h new file mode 100644 index 000000000..128d7db4a --- /dev/null +++ b/src/charon/sa/tasks/task.h @@ -0,0 +1,151 @@ +/** + * @file task.h + * + * @brief Interface task_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 TASK_H_ +#define TASK_H_ + +typedef enum task_type_t task_type_t; +typedef struct task_t task_t; + +#include <library.h> +#include <sa/ike_sa.h> +#include <encoding/message.h> + +/** + * @brief Different kinds of tasks. + * + * @ingroup tasks + */ +enum task_type_t { + /** establish an unauthenticated IKE_SA */ + IKE_INIT, + /** detect NAT situation */ + IKE_NATD, + /** authenticate the initiated IKE_SA */ + IKE_AUTHENTICATE, + /** exchange certificates and requests */ + IKE_CERT, + /** Configuration payloads, virtual IP and such */ + IKE_CONFIG, + /** DPD detection */ + IKE_DEADPEER, + /** rekey an IKE_SA */ + IKE_REKEY, + /** delete an IKE_SA */ + IKE_DELETE, + /** liveness check */ + IKE_DPD, + /** establish a CHILD_SA within an IKE_SA */ + CHILD_CREATE, + /** delete an established CHILD_SA */ + CHILD_DELETE, + /** rekey an CHILD_SA */ + CHILD_REKEY, +}; + +/** + * enum names for task_type_t. + */ +extern enum_name_t *task_type_names; + +/** + * @brief Interface for a task, an operation handled within exchanges. + * + * A task is an elemantary operation. It may be handled by a single or by + * multiple exchanges. An exchange may even complete multiple tasks. + * A task has a build() and an process() operation. The build() operation + * creates payloads and adds it to the message. The process() operation + * inspects a message and handles its payloads. An initiator of an exchange + * first calls build() to build the request, and processes the response message + * with the process() method. + * A responder does the opposite; it calls process() first to handle an incoming + * request and secondly calls build() to build an appropriate response. + * Both methods return either SUCCESS, NEED_MORE or FAILED. A SUCCESS indicates + * that the task completed, even when the task completed unsuccesfully. The + * manager then removes the task from the list. A NEED_MORE is returned when + * the task needs further build()/process() calls to complete, the manager + * leaves the taks in the queue. A returned FAILED indicates a critical failure. + * The manager closes the IKE_SA whenever a task returns FAILED. + * + * @b Constructors: + * - None, use implementations specific constructors + * + * @ingroup tasks + */ +struct task_t { + + /** + * @brief Build a request or response message for this task. + * + * @param this calling object + * @param message message to add payloads to + * @return + * - FAILED if a critical error occured + * - NEED_MORE if another call to build/process needed + * - SUCCESS if task completed + */ + status_t (*build) (task_t *this, message_t *message); + + /** + * @brief Process a request or response message for this task. + * + * @param this calling object + * @param message message to read payloads from + * @return + * - FAILED if a critical error occured + * - NEED_MORE if another call to build/process needed + * - SUCCESS if task completed + */ + status_t (*process) (task_t *this, message_t *message); + + /** + * @brief Get the type of the task implementation. + * + * @param this calling object + */ + task_type_t (*get_type) (task_t *this); + + /** + * @brief Migrate a task to a new IKE_SA. + * + * After migrating a task, it goes back to a state where it can be + * used again to initate an exchange. This is useful when a task + * has to get migrated to a new IKE_SA. + * A special usage is when a INVALID_KE_PAYLOAD is received. A call + * to reset resets the task, but uses another DH group for the next + * try. + * The ike_sa is the new IKE_SA this task belongs to and operates on. + * + * @param this calling object + * @param ike_sa new IKE_SA this task works for + */ + void (*migrate) (task_t *this, ike_sa_t *ike_sa); + + /** + * @brief Destroys a task_t object. + * + * @param this calling object + */ + void (*destroy) (task_t *this); +}; + +#endif /* TASK_H_ */ diff --git a/src/charon/sa/transactions/create_child_sa.c b/src/charon/sa/transactions/create_child_sa.c deleted file mode 100644 index 67e4d782b..000000000 --- a/src/charon/sa/transactions/create_child_sa.c +++ /dev/null @@ -1,1121 +0,0 @@ -/** - * @file create_child_sa.c - * - * @brief Implementation of create_child_sa_t transaction. - * - */ - -/* - * 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 "create_child_sa.h" - -#include <string.h> - -#include <daemon.h> -#include <encoding/payloads/sa_payload.h> -#include <encoding/payloads/nonce_payload.h> -#include <encoding/payloads/ts_payload.h> -#include <sa/transactions/delete_child_sa.h> -#include <utils/randomizer.h> - - -typedef struct private_create_child_sa_t private_create_child_sa_t; - -/** - * Private members of a create_child_sa_t object.. - */ -struct private_create_child_sa_t { - - /** - * Public methods and transaction_t interface. - */ - create_child_sa_t public; - - /** - * Assigned IKE_SA. - */ - ike_sa_t *ike_sa; - - /** - * Message sent by our peer, if already generated - */ - message_t *message; - - /** - * Message ID this transaction uses - */ - u_int32_t message_id; - - /** - * Times we did send the request - */ - u_int32_t requested; - - /** - * initiators inbound SPI of the CHILD_SA which gets rekeyed - */ - u_int32_t rekey_spi; - - /** - * reqid to use for new CHILD_SA - */ - u_int32_t reqid; - - /** - * policy definition used - */ - policy_t *policy; - - /** - * Negotiated proposal used for CHILD_SA - */ - proposal_t *proposal; - - /** - * initiator chosen nonce - */ - chunk_t nonce_i; - - /** - * responder chosen nonce - */ - chunk_t nonce_r; - - /** - * lower of the nonces of a simultaneus rekeying request - */ - chunk_t nonce_s; - - /** - * Negotiated traffic selectors for initiator - */ - linked_list_t *tsi; - - /** - * Negotiated traffic selectors for responder - */ - linked_list_t *tsr; - - /** - * CHILD_SA created by this transaction - */ - child_sa_t *child_sa; - - /** - * CHILD_SA rekeyed if we are rekeying - */ - child_sa_t *rekeyed_sa; - - /** - * mode of the CHILD_SA to create: transport/tunnel - */ - mode_t mode; - - /** - * Have we lost the simultaneous rekeying nonce compare? - */ - bool lost; - - /** - * source of randomness - */ - randomizer_t *randomizer; - - /** - * signal to emit when transaction fails. As this transaction is used - * for CHILD_SA creation AND rekeying, we must emit different signals. - */ - signal_t failsig; -}; - -/** - * Implementation of transaction_t.get_message_id. - */ -static u_int32_t get_message_id(private_create_child_sa_t *this) -{ - return this->message_id; -} - -/** - * Implementation of transaction_t.requested. - */ -static u_int32_t requested(private_create_child_sa_t *this) -{ - return this->requested++; -} - -/** - * Implementation of create_child_sa_t.set_policy. - */ -static void set_policy(private_create_child_sa_t *this, policy_t *policy) -{ - this->policy = policy; -} - -/** - * Implementation of create_child_sa_t.set_reqid. - */ -static void set_reqid(private_create_child_sa_t *this, u_int32_t reqid) -{ - this->reqid = reqid; -} - -/** - * Implementation of create_child_sa_t.rekeys_child. - */ -static void rekeys_child(private_create_child_sa_t *this, child_sa_t *child_sa) -{ - this->rekeyed_sa = child_sa; - this->failsig = CHILD_REKEY_FAILED; -} - -/** - * Implementation of create_child_sa_t.cancel. - */ -static void cancel(private_create_child_sa_t *this) -{ - this->rekeyed_sa = NULL; - this->lost = TRUE; -} - -/** - * Build a notify message. - */ -static void build_notify(notify_type_t type, chunk_t data, message_t *message, bool flush_message) -{ - notify_payload_t *notify; - - if (flush_message) - { - payload_t *payload; - iterator_t *iterator = message->get_payload_iterator(message); - while (iterator->iterate(iterator, (void**)&payload)) - { - payload->destroy(payload); - iterator->remove(iterator); - } - iterator->destroy(iterator); - } - - notify = notify_payload_create(); - notify->set_notify_type(notify, type); - notify->set_notification_data(notify, data); - message->add_payload(message, (payload_t*)notify); -} - -/** - * Implementation of transaction_t.get_request. - */ -static status_t get_request(private_create_child_sa_t *this, message_t **result) -{ - message_t *request; - host_t *me, *other; - identification_t *my_id, *other_id; - - /* check if we already have built a message (retransmission) */ - if (this->message) - { - *result = this->message; - return SUCCESS; - } - - /* check if we are not already rekeying */ - if (this->rekeyed_sa) - { - SIG(CHILD_REKEY_START, "rekeying CHILD_SA"); - - switch (this->rekeyed_sa->get_state(this->rekeyed_sa)) - { - case CHILD_REKEYING: - SIG(CHILD_REKEY_FAILED, - "rekeying a CHILD_SA which is already rekeying, aborted"); - return FAILED; - case CHILD_DELETING: - SIG(CHILD_REKEY_FAILED, - "rekeying a CHILD_SA which is deleting, aborted"); - return FAILED; - default: - break; - } - this->rekeyed_sa->set_state(this->rekeyed_sa, CHILD_REKEYING); - } - else - { - SIG(CHILD_UP_START, "creating CHILD_SA"); - } - - me = this->ike_sa->get_my_host(this->ike_sa); - other = this->ike_sa->get_other_host(this->ike_sa); - my_id = this->ike_sa->get_my_id(this->ike_sa); - other_id = this->ike_sa->get_other_id(this->ike_sa); - - /* build the request */ - request = message_create(); - request->set_source(request, me->clone(me)); - request->set_destination(request, other->clone(other)); - request->set_exchange_type(request, CREATE_CHILD_SA); - request->set_request(request, TRUE); - request->set_ike_sa_id(request, this->ike_sa->get_id(this->ike_sa)); - *result = request; - this->message = request; - - { /* build SA payload */ - sa_payload_t *sa_payload; - linked_list_t *proposals; - bool use_natt; - - /* get a policy, if we are rekeying */ - if (this->rekeyed_sa) - { - linked_list_t *my_ts, *other_ts; - identification_t *my_id, *other_id; - - my_ts = this->rekeyed_sa->get_my_traffic_selectors(this->rekeyed_sa); - other_ts = this->rekeyed_sa->get_other_traffic_selectors(this->rekeyed_sa); - my_id = this->ike_sa->get_my_id(this->ike_sa); - other_id = this->ike_sa->get_other_id(this->ike_sa); - - this->policy = charon->policies->get_policy(charon->policies, - my_id, other_id, - my_ts, other_ts, - me, other); - - this->reqid = this->rekeyed_sa->get_reqid(this->rekeyed_sa); - - if (this->policy == NULL) - { - SIG(IKE_REKEY_FAILED, "no policy found to rekey " - "CHILD_SA with reqid %d", this->reqid); - return FAILED; - } - } - - proposals = this->policy->get_proposals(this->policy); - use_natt = this->ike_sa->is_natt_enabled(this->ike_sa); - this->child_sa = child_sa_create(this->reqid, me, other, my_id, other_id, - this->policy->get_soft_lifetime(this->policy), - this->policy->get_hard_lifetime(this->policy), - this->policy->get_updown(this->policy), - this->policy->get_hostaccess(this->policy), - use_natt); - this->child_sa->set_name(this->child_sa, this->policy->get_name(this->policy)); - if (this->child_sa->alloc(this->child_sa, proposals) != SUCCESS) - { - SIG(this->failsig, "could not install CHILD_SA, CHILD_SA creation failed"); - return FAILED; - } - sa_payload = sa_payload_create_from_proposal_list(proposals); - proposals->destroy_offset(proposals, offsetof(proposal_t, destroy)); - request->add_payload(request, (payload_t*)sa_payload); - } - - /* notify for transport/BEET mode, we propose it - * independent of the traffic selectors */ - switch (this->policy->get_mode(this->policy)) - { - case MODE_TUNNEL: - /* is the default */ - break; - case MODE_TRANSPORT: - if (this->ike_sa->is_natt_enabled(this->ike_sa)) - { - DBG1(DBG_IKE, "not using tranport mode, as connection NATed"); - } - else - { - build_notify(USE_TRANSPORT_MODE, chunk_empty, request, FALSE); - } - break; - case MODE_BEET: - build_notify(USE_BEET_MODE, chunk_empty, request, FALSE); - break; - } - - { /* build the NONCE payload for us (initiator) */ - nonce_payload_t *nonce_payload; - - if (this->randomizer->allocate_pseudo_random_bytes(this->randomizer, - NONCE_SIZE, &this->nonce_i) != SUCCESS) - { - SIG(this->failsig, "could not create nonce, CHILD_SA creation failed"); - return FAILED; - } - nonce_payload = nonce_payload_create(); - nonce_payload->set_nonce(nonce_payload, this->nonce_i); - request->add_payload(request, (payload_t*)nonce_payload); - } - - { /* build TSi payload */ - linked_list_t *ts_list; - ts_payload_t *ts_payload; - - ts_list = this->policy->get_my_traffic_selectors(this->policy, me); - ts_payload = ts_payload_create_from_traffic_selectors(TRUE, ts_list); - ts_list->destroy_offset(ts_list, offsetof(traffic_selector_t, destroy)); - request->add_payload(request, (payload_t*)ts_payload); - } - - { /* build TSr payload */ - linked_list_t *ts_list; - ts_payload_t *ts_payload; - - ts_list = this->policy->get_other_traffic_selectors(this->policy, other); - ts_payload = ts_payload_create_from_traffic_selectors(FALSE, ts_list); - ts_list->destroy_offset(ts_list, offsetof(traffic_selector_t, destroy)); - request->add_payload(request, (payload_t*)ts_payload); - } - - if (this->rekeyed_sa) - { /* add REKEY_SA notify if we are rekeying */ - notify_payload_t *notify; - protocol_id_t protocol; - - protocol = this->rekeyed_sa->get_protocol(this->rekeyed_sa); - notify = notify_payload_create_from_protocol_and_type(protocol, REKEY_SA); - notify->set_spi(notify, this->rekeyed_sa->get_spi(this->rekeyed_sa, TRUE)); - request->add_payload(request, (payload_t*)notify); - - /* register us as rekeying to detect multiple rekeying */ - this->rekeyed_sa->set_rekeying_transaction(this->rekeyed_sa, - &this->public.transaction); - } - - this->message_id = this->ike_sa->get_next_message_id(this->ike_sa); - request->set_message_id(request, this->message_id); - - return SUCCESS; -} - -/** - * Handle all kind of notifys - */ -static status_t process_notifys(private_create_child_sa_t *this, notify_payload_t *notify_payload) -{ - notify_type_t notify_type = notify_payload->get_notify_type(notify_payload); - - DBG2(DBG_IKE, "process notify type %N", notify_type_names, notify_type); - - switch (notify_type) - { - case SINGLE_PAIR_REQUIRED: - { - SIG(this->failsig, "received a SINGLE_PAIR_REQUIRED notify"); - return FAILED; - } - case TS_UNACCEPTABLE: - { - SIG(this->failsig, "received TS_UNACCEPTABLE notify"); - return FAILED; - } - case NO_PROPOSAL_CHOSEN: - { - SIG(this->failsig, "received NO_PROPOSAL_CHOSEN notify"); - return FAILED; - } - case USE_TRANSPORT_MODE: - { - this->mode = MODE_TRANSPORT; - return SUCCESS; - } - case USE_BEET_MODE: - { - this->mode = MODE_BEET; - return SUCCESS; - } - case REKEY_SA: - { - u_int32_t spi; - protocol_id_t protocol; - - protocol = notify_payload->get_protocol_id(notify_payload); - switch (protocol) - { - case PROTO_AH: - case PROTO_ESP: - spi = notify_payload->get_spi(notify_payload); - this->rekeyed_sa = this->ike_sa->get_child_sa(this->ike_sa, - protocol, spi, - FALSE); - this->failsig = CHILD_REKEY_FAILED; - break; - default: - break; - } - return SUCCESS; - } - default: - { - if (notify_type < 16383) - { - SIG(this->failsig, "received %N notify error, CHILD_SA " - "creation failed", notify_type_names, notify_type); - return FAILED; - } - else - { - DBG1(DBG_IKE, "received %N notify, ignored", - notify_type_names, notify_type); - return SUCCESS; - } - } - } -} - -/** - * Check a list of traffic selectors if any selector belongs to host - */ -static bool ts_list_is_host(linked_list_t *list, host_t *host) -{ - traffic_selector_t *ts; - bool is_host = TRUE; - iterator_t *iterator = list->create_iterator(list, TRUE); - - while (is_host && iterator->iterate(iterator, (void**)&ts)) - { - is_host = is_host && ts->is_host(ts, host); - } - iterator->destroy(iterator); - return is_host; -} - -/** - * Install a CHILD_SA for usage - */ -static status_t install_child_sa(private_create_child_sa_t *this, bool initiator) -{ - prf_plus_t *prf_plus; - chunk_t seed; - status_t status; - - seed = chunk_alloc(this->nonce_i.len + this->nonce_r.len); - memcpy(seed.ptr, this->nonce_i.ptr, this->nonce_i.len); - memcpy(seed.ptr + this->nonce_i.len, this->nonce_r.ptr, this->nonce_r.len); - prf_plus = prf_plus_create(this->ike_sa->get_child_prf(this->ike_sa), seed); - chunk_free(&seed); - - if (initiator) - { - status = this->child_sa->update(this->child_sa, this->proposal, - this->mode, prf_plus); - } - else - { - status = this->child_sa->add(this->child_sa, this->proposal, - this->mode, prf_plus); - } - prf_plus->destroy(prf_plus); - if (status != SUCCESS) - { - return DESTROY_ME; - } - if (initiator) - { - status = this->child_sa->add_policies(this->child_sa, this->tsi, - this->tsr, this->mode); - } - else - { - status = this->child_sa->add_policies(this->child_sa, this->tsr, - this->tsi, this->mode); - } - if (status != SUCCESS) - { - return DESTROY_ME; - } - - /* add to IKE_SA, and remove from transaction */ - this->child_sa->set_state(this->child_sa, CHILD_INSTALLED); - this->ike_sa->add_child_sa(this->ike_sa, this->child_sa); - this->child_sa = NULL; - return SUCCESS; -} - -/** - * Implementation of transaction_t.get_response. - */ -static status_t get_response(private_create_child_sa_t *this, message_t *request, - message_t **result, transaction_t **next) -{ - host_t *me, *other; - identification_t *my_id, *other_id; - message_t *response; - status_t status; - iterator_t *payloads; - payload_t *payload; - sa_payload_t *sa_request = NULL; - nonce_payload_t *nonce_request = NULL; - ts_payload_t *tsi_request = NULL; - ts_payload_t *tsr_request = NULL; - nonce_payload_t *nonce_response; - - /* check if we already have built a response (retransmission) */ - if (this->message) - { - *result = this->message; - return SUCCESS; - } - - me = this->ike_sa->get_my_host(this->ike_sa); - other = this->ike_sa->get_other_host(this->ike_sa); - my_id = this->ike_sa->get_my_id(this->ike_sa); - other_id = this->ike_sa->get_other_id(this->ike_sa); - this->message_id = request->get_message_id(request); - - /* set up response */ - response = message_create(); - response->set_source(response, me->clone(me)); - response->set_destination(response, other->clone(other)); - response->set_exchange_type(response, CREATE_CHILD_SA); - response->set_request(response, FALSE); - response->set_message_id(response, this->message_id); - response->set_ike_sa_id(response, this->ike_sa->get_id(this->ike_sa)); - this->message = response; - *result = response; - - /* check message type */ - if (request->get_exchange_type(request) != CREATE_CHILD_SA) - { - DBG1(DBG_IKE, "CREATE_CHILD_SA response of invalid type, aborted"); - return FAILED; - } - - /* we do not allow the creation of new CHILDren/rekeying when IKE_SA is - * rekeying */ - if (this->ike_sa->get_state(this->ike_sa) == IKE_REKEYING || - this->ike_sa->get_state(this->ike_sa) == IKE_DELETING) - { - build_notify(NO_ADDITIONAL_SAS, chunk_empty, response, TRUE); - DBG1(DBG_IKE, "unable to create new CHILD_SAs, as rekeying in progress"); - return FAILED; - } - - /* Iterate over all payloads. */ - payloads = request->get_payload_iterator(request); - while (payloads->iterate(payloads, (void**)&payload)) - { - switch (payload->get_type(payload)) - { - case SECURITY_ASSOCIATION: - sa_request = (sa_payload_t*)payload; - break; - case NONCE: - nonce_request = (nonce_payload_t*)payload; - break; - case TRAFFIC_SELECTOR_INITIATOR: - tsi_request = (ts_payload_t*)payload; - break; - case TRAFFIC_SELECTOR_RESPONDER: - tsr_request = (ts_payload_t*)payload; - break; - case KEY_EXCHANGE: - { - u_int8_t dh_buffer[] = {0x00, 0x00}; /* MODP_NONE */ - chunk_t group = chunk_from_buf(dh_buffer); - build_notify(INVALID_KE_PAYLOAD, group, response, TRUE); - DBG1(DBG_IKE, "CREATE_CHILD_SA used PFS, sending INVALID_KE_PAYLOAD"); - return FAILED; - } - case NOTIFY: - { - status = process_notifys(this, (notify_payload_t*)payload); - if (status != SUCCESS) - { - payloads->destroy(payloads); - return status; - } - break; - } - default: - { - DBG1(DBG_IKE, "ignoring %N payload", - payload_type_names, payload->get_type(payload)); - break; - } - } - } - payloads->destroy(payloads); - - /* after processing the notify payloads, we know if this transaction is - * for rekeying or for a new CHILD_SA. We can emit the signals now. */ - if (this->rekeyed_sa) - { - SIG(CHILD_REKEY_START, "rekeying CHILD_SA"); - } - else - { - SIG(CHILD_UP_START, "creating CHILD_SA"); - } - - /* check if we have all payloads */ - if (!(sa_request && nonce_request && tsi_request && tsr_request)) - { - build_notify(INVALID_SYNTAX, chunk_empty, response, TRUE); - SIG(this->failsig, "request message incomplete, no CHILD_SA created"); - return FAILED; - } - - { /* process nonce payload */ - this->nonce_i = nonce_request->get_nonce(nonce_request); - if (this->randomizer->allocate_pseudo_random_bytes(this->randomizer, - NONCE_SIZE, &this->nonce_r) != SUCCESS) - { - build_notify(NO_PROPOSAL_CHOSEN, chunk_empty, response, TRUE); - SIG(this->failsig, "nonce generation failed, no CHILD_SA created"); - return FAILED; - } - nonce_response = nonce_payload_create(); - nonce_response->set_nonce(nonce_response, this->nonce_r); - } - - { /* get a policy and process traffic selectors */ - identification_t *my_id, *other_id; - linked_list_t *my_ts, *other_ts; - - my_id = this->ike_sa->get_my_id(this->ike_sa); - other_id = this->ike_sa->get_other_id(this->ike_sa); - - my_ts = tsr_request->get_traffic_selectors(tsr_request); - other_ts = tsi_request->get_traffic_selectors(tsi_request); - - this->policy = charon->policies->get_policy(charon->policies, - my_id, other_id, - my_ts, other_ts, - me, other); - if (this->policy) - { - this->tsr = this->policy->select_my_traffic_selectors(this->policy, my_ts, me); - this->tsi = this->policy->select_other_traffic_selectors(this->policy, other_ts, other); - } - my_ts->destroy_offset(my_ts, offsetof(traffic_selector_t, destroy)); - other_ts->destroy_offset(other_ts, offsetof(traffic_selector_t, destroy)); - - if (this->policy == NULL) - { - SIG(this->failsig, "no acceptable policy found, sending TS_UNACCEPTABLE notify"); - build_notify(TS_UNACCEPTABLE, chunk_empty, response, TRUE); - return FAILED; - } - } - - { /* process SA payload */ - linked_list_t *proposal_list; - sa_payload_t *sa_response; - ts_payload_t *ts_response; - bool use_natt; - u_int32_t soft_lifetime, hard_lifetime; - - sa_response = sa_payload_create(); - /* get proposals from request, and select one with ours */ - proposal_list = sa_request->get_proposals(sa_request); - DBG2(DBG_IKE, "selecting proposals:"); - this->proposal = this->policy->select_proposal(this->policy, proposal_list); - proposal_list->destroy_offset(proposal_list, offsetof(proposal_t, destroy)); - - /* do we have a proposal? */ - if (this->proposal == NULL) - { - SIG(this->failsig, "CHILD_SA proposals unacceptable, sending NO_PROPOSAL_CHOSEN notify"); - build_notify(NO_PROPOSAL_CHOSEN, chunk_empty, response, TRUE); - return FAILED; - } - /* do we have traffic selectors? */ - else if (this->tsi->get_count(this->tsi) == 0 || this->tsr->get_count(this->tsr) == 0) - { - SIG(this->failsig, "CHILD_SA traffic selectors unacceptable, sending TS_UNACCEPTABLE notify"); - build_notify(TS_UNACCEPTABLE, chunk_empty, response, TRUE); - return FAILED; - } - else - { /* create child sa */ - if (this->rekeyed_sa) - { - this->reqid = this->rekeyed_sa->get_reqid(this->rekeyed_sa); - } - soft_lifetime = this->policy->get_soft_lifetime(this->policy); - hard_lifetime = this->policy->get_hard_lifetime(this->policy); - use_natt = this->ike_sa->is_natt_enabled(this->ike_sa); - this->child_sa = child_sa_create(this->reqid, me, other, my_id, other_id, - soft_lifetime, hard_lifetime, - this->policy->get_updown(this->policy), - this->policy->get_hostaccess(this->policy), - use_natt); - this->child_sa->set_name(this->child_sa, this->policy->get_name(this->policy)); - - /* check mode, and include notify into reply */ - switch (this->mode) - { - case MODE_TUNNEL: - /* is the default */ - break; - case MODE_TRANSPORT: - if (!ts_list_is_host(this->tsi, other) || - !ts_list_is_host(this->tsr, me)) - { - this->mode = MODE_TUNNEL; - DBG1(DBG_IKE, "not using tranport mode, not host-to-host"); - } - else if (this->ike_sa->is_natt_enabled(this->ike_sa)) - { - this->mode = MODE_TUNNEL; - DBG1(DBG_IKE, "not using tranport mode, as connection NATed"); - } - else - { - build_notify(USE_TRANSPORT_MODE, chunk_empty, response, FALSE); - } - break; - case MODE_BEET: - if (!ts_list_is_host(this->tsi, NULL) || - !ts_list_is_host(this->tsr, NULL)) - { - this->mode = MODE_TUNNEL; - DBG1(DBG_IKE, "not using BEET mode, not host-to-host"); - } - else - { - build_notify(USE_BEET_MODE, chunk_empty, response, FALSE); - } - break; - } - - if (install_child_sa(this, FALSE) != SUCCESS) - { - SIG(this->failsig, "installing CHILD_SA failed, sending NO_PROPOSAL_CHOSEN notify"); - build_notify(NO_PROPOSAL_CHOSEN, chunk_empty, response, TRUE); - return FAILED; - } - /* add proposal to sa payload */ - sa_response->add_proposal(sa_response, this->proposal); - } - response->add_payload(response, (payload_t*)sa_response); - - /* add nonce/ts payload after sa payload */ - response->add_payload(response, (payload_t *)nonce_response); - ts_response = ts_payload_create_from_traffic_selectors(TRUE, this->tsi); - response->add_payload(response, (payload_t*)ts_response); - ts_response = ts_payload_create_from_traffic_selectors(FALSE, this->tsr); - response->add_payload(response, (payload_t*)ts_response); - } - /* CHILD_SA successfully created. If another transaction is already rekeying - * this SA, our lower nonce must be registered for a later nonce compare. */ - if (this->rekeyed_sa) - { - private_create_child_sa_t *other; - - other = (private_create_child_sa_t*) - this->rekeyed_sa->get_rekeying_transaction(this->rekeyed_sa); - if (other) - { - /* store our lower nonce in the simultaneus transaction, it - * will later compare it against its nonces when it calls conclude(). - */ - if (memcmp(this->nonce_i.ptr, this->nonce_r.ptr, - min(this->nonce_i.len, this->nonce_r.len)) < 0) - { - other->nonce_s = chunk_clone(this->nonce_i); - } - else - { - other->nonce_s = chunk_clone(this->nonce_r); - } - } - else - { - /* we only signal when no other transaction is rekeying */ - SIG(CHILD_REKEY_SUCCESS, "CHILD_SA rekeyed"); - } - this->rekeyed_sa->set_state(this->rekeyed_sa, CHILD_REKEYING); - } - else - { - SIG(CHILD_UP_SUCCESS, "CHILD_SA '%s' created", - this->policy->get_name(this->policy)); - } - return SUCCESS; -} - -/** - * Implementation of transaction_t.conclude - */ -static status_t conclude(private_create_child_sa_t *this, message_t *response, - transaction_t **next) -{ - iterator_t *payloads; - payload_t *payload; - host_t *me, *other; - sa_payload_t *sa_payload = NULL; - nonce_payload_t *nonce_payload = NULL; - ts_payload_t *tsi_payload = NULL; - ts_payload_t *tsr_payload = NULL; - status_t status; - child_sa_t *new_child = NULL; - delete_child_sa_t *delete_child_sa; - - /* check message type */ - if (response->get_exchange_type(response) != CREATE_CHILD_SA) - { - SIG(this->failsig, "CREATE_CHILD_SA response of invalid type, aborting"); - return FAILED; - } - - me = this->ike_sa->get_my_host(this->ike_sa); - other = this->ike_sa->get_other_host(this->ike_sa); - - /* Iterate over all payloads to collect them */ - payloads = response->get_payload_iterator(response); - while (payloads->iterate(payloads, (void**)&payload)) - { - switch (payload->get_type(payload)) - { - case SECURITY_ASSOCIATION: - sa_payload = (sa_payload_t*)payload; - break; - case NONCE: - nonce_payload = (nonce_payload_t*)payload; - break; - case TRAFFIC_SELECTOR_INITIATOR: - tsi_payload = (ts_payload_t*)payload; - break; - case TRAFFIC_SELECTOR_RESPONDER: - tsr_payload = (ts_payload_t*)payload; - break; - case NOTIFY: - { - status = process_notifys(this, (notify_payload_t*)payload); - if (status != SUCCESS) - { - payloads->destroy(payloads); - return status; - } - break; - } - default: - { - DBG1(DBG_IKE, "ignoring %N payload", - payload_type_names, payload->get_type(payload)); - break; - } - } - } - payloads->destroy(payloads); - - if (!(sa_payload && nonce_payload && tsi_payload && tsr_payload)) - { - SIG(this->failsig, "response message incomplete, no CHILD_SA built"); - return FAILED; - } - - { /* process NONCE payload */ - this->nonce_r = nonce_payload->get_nonce(nonce_payload); - } - - { /* process traffic selectors for us */ - linked_list_t *ts_received = tsi_payload->get_traffic_selectors(tsi_payload); - this->tsi = this->policy->select_my_traffic_selectors(this->policy, ts_received, me); - ts_received->destroy_offset(ts_received, offsetof(traffic_selector_t, destroy)); - } - - { /* process traffic selectors for other */ - linked_list_t *ts_received = tsr_payload->get_traffic_selectors(tsr_payload); - this->tsr = this->policy->select_other_traffic_selectors(this->policy, ts_received, other); - ts_received->destroy_offset(ts_received, offsetof(traffic_selector_t, destroy)); - } - - { /* process sa payload */ - linked_list_t *proposal_list; - - proposal_list = sa_payload->get_proposals(sa_payload); - /* we have to re-check here if other's selection is valid */ - this->proposal = this->policy->select_proposal(this->policy, proposal_list); - proposal_list->destroy_offset(proposal_list, offsetof(proposal_t, destroy)); - - /* everything fine to create CHILD? */ - if (this->proposal == NULL || - this->tsi->get_count(this->tsi) == 0 || - this->tsr->get_count(this->tsr) == 0) - { - SIG(this->failsig, "CHILD_SA negotiation failed, no CHILD_SA built"); - return FAILED; - } - - /* check mode if it is acceptable */ - switch (this->mode) - { - case MODE_TUNNEL: - /* is the default */ - break; - case MODE_TRANSPORT: - /* TODO: we should close the CHILD_SA if negotiated - * mode is not acceptable for us */ - if (!ts_list_is_host(this->tsi, me) || - !ts_list_is_host(this->tsr, other)) - { - this->mode = MODE_TUNNEL; - DBG1(DBG_IKE, "not using tranport mode, not host-to-host"); - } - else if (this->ike_sa->is_natt_enabled(this->ike_sa)) - { - this->mode = MODE_TUNNEL; - DBG1(DBG_IKE, "not using tranport mode, as connection NATed"); - } - break; - case MODE_BEET: - if (!ts_list_is_host(this->tsi, NULL) || - !ts_list_is_host(this->tsr, NULL)) - { - this->mode = MODE_TUNNEL; - DBG1(DBG_IKE, "not using BEET mode, not host-to-host"); - } - break; - } - - new_child = this->child_sa; - if (install_child_sa(this, TRUE) != SUCCESS) - { - SIG(this->failsig, "installing CHILD_SA failed, no CHILD_SA built"); - return FAILED; - } - } - /* CHILD_SA successfully created. If the other peer initiated rekeying - * in the meantime, we detect this by comparing the rekeying_transaction - * of the SA. If it changed, we are not alone. Then we must compare the nonces. - * If no simultaneous rekeying is going on, we just initiate the delete of - * the superseded SA. */ - if (this->rekeyed_sa) - { - /* rekeying finished, update SA status */ - this->rekeyed_sa->set_rekeying_transaction(this->rekeyed_sa, NULL); - - if (this->nonce_s.ptr) - { /* simlutaneous rekeying is going on, not so good */ - chunk_t this_lowest; - - /* first get our lowest nonce */ - if (memcmp(this->nonce_i.ptr, this->nonce_r.ptr, - min(this->nonce_i.len, this->nonce_r.len)) < 0) - { - this_lowest = this->nonce_i; - } - else - { - this_lowest = this->nonce_r; - } - /* then compare against other lowest nonce */ - if (memcmp(this_lowest.ptr, this->nonce_s.ptr, - min(this_lowest.len, this->nonce_s.len)) < 0) - { - DBG1(DBG_IKE, "detected simultaneous CHILD_SA rekeying, deleting ours"); - this->lost = TRUE; - } - else - { - DBG1(DBG_IKE, "detected simultaneous CHILD_SA rekeying, but ours is preferred"); - } - } - /* delete the old SA if we have won the rekeying nonce compare*/ - if (!this->lost) - { - delete_child_sa = delete_child_sa_create(this->ike_sa); - delete_child_sa->set_child_sa(delete_child_sa, this->rekeyed_sa); - *next = (transaction_t*)delete_child_sa; - } - /* we send a rekey SUCCESS signal in any case. If the other transaction - * detected our transaction, it did not send a signal. We do it for it. */ - SIG(CHILD_REKEY_SUCCESS, "CHILD_SA rekeyed"); - } - else - { - SIG(CHILD_UP_SUCCESS, "CHILD_SA '%s' created", - this->policy->get_name(this->policy)); - } - if (this->lost) - { - /* we have lost simlutaneous rekeying, delete the CHILD_SA we just have created */ - delete_child_sa = delete_child_sa_create(this->ike_sa); - delete_child_sa->set_child_sa(delete_child_sa, new_child); - *next = (transaction_t*)delete_child_sa; - } - return SUCCESS; -} - -/** - * implements transaction_t.destroy - */ -static void destroy(private_create_child_sa_t *this) -{ - DESTROY_IF(this->message); - DESTROY_IF(this->proposal); - DESTROY_IF(this->child_sa); - DESTROY_IF(this->policy); - if (this->tsi) - { - this->tsi->destroy_offset(this->tsi, offsetof(traffic_selector_t, destroy)); - } - if (this->tsr) - { - this->tsr->destroy_offset(this->tsr, offsetof(traffic_selector_t, destroy)); - } - chunk_free(&this->nonce_i); - chunk_free(&this->nonce_r); - chunk_free(&this->nonce_s); - this->randomizer->destroy(this->randomizer); - free(this); -} - -/* - * Described in header. - */ -create_child_sa_t *create_child_sa_create(ike_sa_t *ike_sa) -{ - private_create_child_sa_t *this = malloc_thing(private_create_child_sa_t); - - /* transaction interface functions */ - this->public.transaction.get_request = (status_t(*)(transaction_t*,message_t**))get_request; - this->public.transaction.get_response = (status_t(*)(transaction_t*,message_t*,message_t**,transaction_t**))get_response; - this->public.transaction.conclude = (status_t(*)(transaction_t*,message_t*,transaction_t**))conclude; - this->public.transaction.get_message_id = (u_int32_t(*)(transaction_t*))get_message_id; - this->public.transaction.requested = (u_int32_t(*)(transaction_t*))requested; - this->public.transaction.destroy = (void(*)(transaction_t*))destroy; - - /* public functions */ - this->public.set_policy = (void(*)(create_child_sa_t*,policy_t*))set_policy; - this->public.set_reqid = (void(*)(create_child_sa_t*,u_int32_t))set_reqid; - this->public.rekeys_child = (void(*)(create_child_sa_t*,child_sa_t*))rekeys_child; - this->public.cancel = (void(*)(create_child_sa_t*))cancel; - - /* private data */ - this->ike_sa = ike_sa; - this->message_id = 0; - this->message = NULL; - this->requested = 0; - this->rekey_spi = 0; - this->reqid = 0; - this->nonce_i = chunk_empty; - this->nonce_r = chunk_empty; - this->nonce_s = chunk_empty; - this->child_sa = NULL; - this->rekeyed_sa = NULL; - this->lost = FALSE; - this->proposal = NULL; - this->policy = NULL; - this->tsi = NULL; - this->tsr = NULL; - this->mode = MODE_TUNNEL; - this->randomizer = randomizer_create(); - this->failsig = CHILD_UP_FAILED; - - return &this->public; -} diff --git a/src/charon/sa/transactions/create_child_sa.h b/src/charon/sa/transactions/create_child_sa.h deleted file mode 100644 index 8ce72e11d..000000000 --- a/src/charon/sa/transactions/create_child_sa.h +++ /dev/null @@ -1,123 +0,0 @@ -/** - * @file create_child_sa.h - * - * @brief Interface of transaction create_child_sa. - * - */ - -/* - * 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 CREATE_CHILD_SA_H_ -#define CREATE_CHILD_SA_H_ - -typedef struct create_child_sa_t create_child_sa_t; - -#include <sa/transactions/transaction.h> -#include <sa/ike_sa.h> -#include <sa/child_sa.h> - -/** - * @brief A transaction to create a new or rekey an existing CHILD_SA. - * - * If the CHILD_SA is intended to create a new CHILD_SA, set the policy - * with set_policy(). If it is intended to rekey an existing CHILD_SA, - * set the appropriate CHILD_SA with rekeys_child(). - * - * Rekeying of an CHILD_SA works the same way as creating a new one, - * but includes an additional REKEY_SA notify and deletes the old - * one (in a separate transaction). - * - * __________ _________ - * Cyq \/ Czq - * __________/\_________ - * detect __________ _________ detect - * Czp \/ Czp - * compare nonces, won __________/\_________ compare nonces, lost - * - * delete old __________ - * Dxq \__________ - * __________ - * __________/ Dxp - * __________ delete created - * __________/ Dzq - * __________ - * Dzp \__________ - * - * - * @b Constructors: - * - create_child_sa_create() - * - transaction_create() with the appropriate message - * - * @ingroup transactions - */ -struct create_child_sa_t { - - /** - * The transaction_t interface. - */ - transaction_t transaction; - - /** - * @brief Set the policy to use for creating a new CHILD_SA. - * - * @param this calling object - * @param policy policy for CHILD_SA - */ - void (*set_policy) (create_child_sa_t* this, policy_t *policy); - - /** - * @brief Set the reqid used for CHILD_SA setup. - * - * If we acquire, we must use the same reqid as the - * installed policy. - * - * @param this calling object - * @param reqid reqid to use for the CHILD_SA - */ - void (*set_reqid) (create_child_sa_t* this, u_int32_t reqid); - - /** - * @brief Set the CHILD_SA which gets rekeyed by the new one. - * - * @param this calling object - * @param child_sa CHILD_SA to rekey - */ - void (*rekeys_child) (create_child_sa_t* this, child_sa_t *child_sa); - - /** - * @brief Cancel the request. - * - * Cancelling the request will set a flag in the transaction. When - * the response for the transaction is received, the created CHILD_SA - * gets deleted afterwards. - * - * @param this calling object - * @param child_sa CHILD_SA to rekey - */ - void (*cancel) (create_child_sa_t* this); -}; - -/** - * @brief Create a new transaction which creates/rekeys CHILD_SAs. - * - * @param ike_sa assigned IKE_SA - * @return created create_child_sa transaction - * - * @ingroup transactions - */ -create_child_sa_t *create_child_sa_create(ike_sa_t *ike_sa); - -#endif /* CREATE_CHILD_SA_H_ */ diff --git a/src/charon/sa/transactions/dead_peer_detection.c b/src/charon/sa/transactions/dead_peer_detection.c deleted file mode 100644 index 390ce3401..000000000 --- a/src/charon/sa/transactions/dead_peer_detection.c +++ /dev/null @@ -1,187 +0,0 @@ -/** - * @file dead_peer_detection.c - * - * @brief Implementation of the dead_peer_detection transaction. - * - */ - -/* - * 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 "dead_peer_detection.h" - -#include <daemon.h> - - -typedef struct private_dead_peer_detection_t private_dead_peer_detection_t; - -/** - * Private members of a dead_peer_detection_t object.. - */ -struct private_dead_peer_detection_t { - - /** - * Public methods and transaction_t interface. - */ - dead_peer_detection_t public; - - /** - * Assigned IKE_SA. - */ - ike_sa_t *ike_sa; - - /** - * Message sent by our peer, if already generated - */ - message_t *message; - - /** - * Message ID this transaction uses - */ - u_int32_t message_id; - - /** - * Times we did send the request - */ - u_int32_t requested; -}; - -/** - * Implementation of transaction_t.get_message_id. - */ -static u_int32_t get_message_id(private_dead_peer_detection_t *this) -{ - return this->message_id; -} - -/** - * Implementation of transaction_t.requested. - */ -static u_int32_t requested(private_dead_peer_detection_t *this) -{ - return this->requested++; -} - -/** - * Implementation of transaction_t.get_request. - */ -static status_t get_request(private_dead_peer_detection_t *this, message_t **result) -{ - message_t *request; - host_t *me, *other; - - /* check if we already have built a message (retransmission) */ - if (this->message) - { - *result = this->message; - return SUCCESS; - } - - me = this->ike_sa->get_my_host(this->ike_sa); - other = this->ike_sa->get_other_host(this->ike_sa); - - /* build the request */ - request = message_create(); - request->set_source(request, me->clone(me)); - request->set_destination(request, other->clone(other)); - request->set_exchange_type(request, INFORMATIONAL); - request->set_request(request, TRUE); - this->message_id = this->ike_sa->get_next_message_id(this->ike_sa); - request->set_message_id(request, this->message_id); - request->set_ike_sa_id(request, this->ike_sa->get_id(this->ike_sa)); - /* apply for caller */ - *result = request; - /* store for retransmission */ - this->message = request; - - return SUCCESS; -} - -/** - * Implementation of transaction_t.get_response. - */ -static status_t get_response(private_dead_peer_detection_t *this, message_t *request, - message_t **result, transaction_t **next) -{ - host_t *me, *other; - message_t *response; - - /* check if we already have built a response (retransmission) */ - if (this->message) - { - *result = this->message; - return SUCCESS; - } - - me = this->ike_sa->get_my_host(this->ike_sa); - other = this->ike_sa->get_other_host(this->ike_sa); - this->message_id = request->get_message_id(request); - - /* set up response */ - response = message_create(); - response->set_source(response, me->clone(me)); - response->set_destination(response, other->clone(other)); - response->set_exchange_type(response, INFORMATIONAL); - response->set_request(response, FALSE); - response->set_message_id(response, this->message_id); - response->set_ike_sa_id(response, this->ike_sa->get_id(this->ike_sa)); - this->message = response; - *result = response; - - return SUCCESS; -} - - -/** - * Implementation of transaction_t.conclude - */ -static status_t conclude(private_dead_peer_detection_t *this, message_t *response, - transaction_t **transaction) -{ - return SUCCESS; -} - -/** - * implements transaction_t.destroy - */ -static void destroy(private_dead_peer_detection_t *this) -{ - DESTROY_IF(this->message); - free(this); -} - -/* - * Described in header. - */ -dead_peer_detection_t *dead_peer_detection_create(ike_sa_t *ike_sa) -{ - private_dead_peer_detection_t *this = malloc_thing(private_dead_peer_detection_t); - - /* transaction interface functions */ - this->public.transaction.get_request = (status_t(*)(transaction_t*,message_t**))get_request; - this->public.transaction.get_response = (status_t(*)(transaction_t*,message_t*,message_t**,transaction_t**))get_response; - this->public.transaction.conclude = (status_t(*)(transaction_t*,message_t*,transaction_t**))conclude; - this->public.transaction.get_message_id = (u_int32_t(*)(transaction_t*))get_message_id; - this->public.transaction.requested = (u_int32_t(*)(transaction_t*))requested; - this->public.transaction.destroy = (void(*)(transaction_t*))destroy; - - /* private data */ - this->ike_sa = ike_sa; - this->message_id = 0; - this->message = NULL; - this->requested = 0; - - return &this->public; -} diff --git a/src/charon/sa/transactions/dead_peer_detection.h b/src/charon/sa/transactions/dead_peer_detection.h deleted file mode 100644 index 78d7b9b4f..000000000 --- a/src/charon/sa/transactions/dead_peer_detection.h +++ /dev/null @@ -1,58 +0,0 @@ -/** - * @file dead_peer_detection.h - * - * @brief Interface of transaction dead_peer_detection. - * - */ - -/* - * 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 DEAD_PEER_DETECTION_H_ -#define DEAD_PEER_DETECTION_H_ - -typedef struct dead_peer_detection_t dead_peer_detection_t; - -#include <sa/ike_sa.h> -#include <sa/transactions/transaction.h> - -/** - * @brief A transaction used to detect dead peers. - * - * In IKEv2, dead peer detection is done using empty - * informational messages. These must be acknowledged. - * - * @ingroup transactions - */ -struct dead_peer_detection_t { - - /** - * The transaction_t interface. - */ - transaction_t transaction; -}; - -/** - * @brief Create a new transaction which detects dead peers. - * - * @param ike_sa assigned IKE_SA - * @return created dead_peer_detection transaction - * - * @ingroup transactions - */ -dead_peer_detection_t *dead_peer_detection_create(ike_sa_t *ike_sa); - -#endif /* DEAD_PEER_DETECTION_H_ */ diff --git a/src/charon/sa/transactions/delete_child_sa.c b/src/charon/sa/transactions/delete_child_sa.c deleted file mode 100644 index 7ec332004..000000000 --- a/src/charon/sa/transactions/delete_child_sa.c +++ /dev/null @@ -1,356 +0,0 @@ -/** - * @file delete_child_sa.c - * - * @brief Implementation of the delete_child_sa transaction. - * - */ - -/* - * 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 "delete_child_sa.h" - -#include <daemon.h> -#include <encoding/payloads/delete_payload.h> -#include <sa/transactions/create_child_sa.h> - - -typedef struct private_delete_child_sa_t private_delete_child_sa_t; - -/** - * Private members of a delete_child_sa_t object.. - */ -struct private_delete_child_sa_t { - - /** - * Public methods and transaction_t interface. - */ - delete_child_sa_t public; - - /** - * Assigned IKE_SA. - */ - ike_sa_t *ike_sa; - - /** - * Message sent by our peer, if already generated - */ - message_t *message; - - /** - * Message ID this transaction uses - */ - u_int32_t message_id; - - /** - * Times we did send the request - */ - u_int32_t requested; - - /** - * CHILD SA to delete - */ - child_sa_t *child_sa; -}; - -/** - * Implementation of transaction_t.get_message_id. - */ -static u_int32_t get_message_id(private_delete_child_sa_t *this) -{ - return this->message_id; -} - -/** - * Implementation of transaction_t.requested. - */ -static u_int32_t requested(private_delete_child_sa_t *this) -{ - return this->requested++; -} - -/** - * Implementation of delete_child_sa_t.set_child_sa. - */ -static void set_child_sa(private_delete_child_sa_t *this, child_sa_t *child_sa) -{ - this->child_sa = child_sa; -} - -/** - * Implementation of transaction_t.get_request. - */ -static status_t get_request(private_delete_child_sa_t *this, message_t **result) -{ - message_t *request; - host_t *me, *other; - - /* check if we already have built a message (retransmission) */ - if (this->message) - { - *result = this->message; - return SUCCESS; - } - - me = this->ike_sa->get_my_host(this->ike_sa); - other = this->ike_sa->get_other_host(this->ike_sa); - - /* build the request */ - request = message_create(); - request->set_source(request, me->clone(me)); - request->set_destination(request, other->clone(other)); - request->set_exchange_type(request, INFORMATIONAL); - request->set_request(request, TRUE); - this->message_id = this->ike_sa->get_next_message_id(this->ike_sa); - request->set_message_id(request, this->message_id); - request->set_ike_sa_id(request, this->ike_sa->get_id(this->ike_sa)); - *result = request; - this->message = request; - - { /* add delete payload */ - delete_payload_t *delete_payload; - protocol_id_t protocol; - u_int32_t spi; - - protocol = this->child_sa->get_protocol(this->child_sa); - spi = this->child_sa->get_spi(this->child_sa, TRUE); - delete_payload = delete_payload_create(protocol); - - DBG1(DBG_IKE, "created DELETE payload for %N CHILD_SA with SPI 0x%x", - protocol_id_names, protocol, htonl(spi)); - delete_payload->add_spi(delete_payload, spi); - request->add_payload(request, (payload_t*)delete_payload); - } - - this->child_sa->set_state(this->child_sa, CHILD_DELETING); - - return SUCCESS; -} - -/** - * process a delete payload - */ -static status_t process_delete(private_delete_child_sa_t *this, delete_payload_t *delete_request, message_t *response) -{ - protocol_id_t protocol; - u_int32_t spi; - iterator_t *iterator; - delete_payload_t *delete_response = NULL; - - /* get requested CHILD */ - protocol = delete_request->get_protocol_id(delete_request); - if (protocol != PROTO_ESP && protocol != PROTO_AH) - { - DBG1(DBG_IKE, "CHILD_SA delete response contained unexpected protocol"); - return FAILED; - } - - /* prepare response payload */ - if (response) - { - delete_response = delete_payload_create(protocol); - response->add_payload(response, (payload_t*)delete_response); - } - - iterator = delete_request->create_spi_iterator(delete_request); - while (iterator->iterate(iterator, (void**)&spi)) - { - child_sa_t *child_sa; - - child_sa = this->ike_sa->get_child_sa(this->ike_sa, protocol, spi, FALSE); - - if (child_sa != NULL) - { - create_child_sa_t *rekey; - - child_sa->set_state(child_sa, CHILD_DELETING); - - DBG1(DBG_IKE, "received DELETE for %N CHILD_SA with SPI 0x%x, deleting", - protocol_id_names, protocol, ntohl(spi)); - - rekey = (create_child_sa_t*)child_sa->get_rekeying_transaction(child_sa); - if (rekey) - { - /* we have received a delete for an SA which we are still rekeying. - * this means we have lost the nonce comparison, and the rekeying - * will fail. We set a flag in the transaction for this special case. - */ - rekey->cancel(rekey); - } - /* delete it, with inbound spi */ - spi = child_sa->get_spi(child_sa, TRUE); - this->ike_sa->destroy_child_sa(this->ike_sa, protocol, spi); - /* add delete response to message, if we are responding */ - if (response) - { - delete_response->add_spi(delete_response, spi); - } - } - else - { - DBG1(DBG_IKE, "received DELETE for %N CHILD_SA with SPI 0x%x, but no such SA", - protocol_id_names, protocol, ntohl(spi)); - } - } - iterator->destroy(iterator); - return SUCCESS; -} - -/** - * Implementation of transaction_t.get_response. - */ -static status_t get_response(private_delete_child_sa_t *this, message_t *request, - message_t **result, transaction_t **next) -{ - host_t *me, *other; - message_t *response; - iterator_t *payloads; - payload_t *payload; - - /* check if we already have built a response (retransmission) */ - if (this->message) - { - *result = this->message; - return SUCCESS; - } - - me = this->ike_sa->get_my_host(this->ike_sa); - other = this->ike_sa->get_other_host(this->ike_sa); - this->message_id = request->get_message_id(request); - - /* set up response */ - response = message_create(); - response->set_source(response, me->clone(me)); - response->set_destination(response, other->clone(other)); - response->set_exchange_type(response, INFORMATIONAL); - response->set_request(response, FALSE); - response->set_message_id(response, this->message_id); - response->set_ike_sa_id(response, this->ike_sa->get_id(this->ike_sa)); - this->message = response; - *result = response; - - if (request->get_exchange_type(request) != INFORMATIONAL) - { - DBG1(DBG_IKE, "INFORMATIONAL response of invalid type, aborting"); - return FAILED; - } - - /* we can't handle a delete for a CHILD when we are rekeying. There - * is no proper solution for this. We send a empty informational response, - * as described in ikev2-clarifications draft */ - if (this->ike_sa->get_state(this->ike_sa) == IKE_REKEYING || - this->ike_sa->get_state(this->ike_sa) == IKE_DELETING) - { - DBG1(DBG_IKE, "unable to delete CHILD_SA, as rekeying in progress"); - return FAILED; - } - - /* iterate over all payloads */ - payloads = request->get_payload_iterator(request); - while (payloads->iterate(payloads, (void**)&payload)) - { - switch (payload->get_type(payload)) - { - case DELETE: - { - process_delete(this, (delete_payload_t*)payload, response); - break; - } - default: - { - DBG2(DBG_IKE, "ignoring payload %N", - payload_type_names, payload->get_type(payload)); - break; - } - } - } - payloads->destroy(payloads); - return SUCCESS; -} - -/** - * Implementation of transaction_t.conclude - */ -static status_t conclude(private_delete_child_sa_t *this, message_t *response, - transaction_t **transaction) -{ - iterator_t *payloads; - payload_t *payload; - - /* check message type */ - if (response->get_exchange_type(response) != INFORMATIONAL) - { - DBG1(DBG_IKE, "INFORMATIONAL response of invalid type, aborting"); - return FAILED; - } - - /* iterate over all payloads */ - payloads = response->get_payload_iterator(response); - while (payloads->iterate(payloads, (void**)&payload)) - { - switch (payload->get_type(payload)) - { - case DELETE: - { - process_delete(this, (delete_payload_t*)payload, NULL); - break; - } - default: - { - DBG1(DBG_IKE, "ignoring payload %N", - payload_type_names, payload->get_type(payload)); - break; - } - } - } - payloads->destroy(payloads); - return SUCCESS; -} - -/** - * implements transaction_t.destroy - */ -static void destroy(private_delete_child_sa_t *this) -{ - DESTROY_IF(this->message); - free(this); -} - -/* - * Described in header. - */ -delete_child_sa_t *delete_child_sa_create(ike_sa_t *ike_sa) -{ - private_delete_child_sa_t *this = malloc_thing(private_delete_child_sa_t); - - /* transaction interface functions */ - this->public.transaction.get_request = (status_t(*)(transaction_t*,message_t**))get_request; - this->public.transaction.get_response = (status_t(*)(transaction_t*,message_t*,message_t**,transaction_t**))get_response; - this->public.transaction.conclude = (status_t(*)(transaction_t*,message_t*,transaction_t**))conclude; - this->public.transaction.get_message_id = (u_int32_t(*)(transaction_t*))get_message_id; - this->public.transaction.requested = (u_int32_t(*)(transaction_t*))requested; - this->public.transaction.destroy = (void(*)(transaction_t*))destroy; - - /* publics */ - this->public.set_child_sa = (void(*)(delete_child_sa_t*,child_sa_t*))set_child_sa; - - /* private data */ - this->ike_sa = ike_sa; - this->message_id = 0; - this->message = NULL; - this->requested = 0; - - return &this->public; -} diff --git a/src/charon/sa/transactions/delete_child_sa.h b/src/charon/sa/transactions/delete_child_sa.h deleted file mode 100644 index b4cd8ea7a..000000000 --- a/src/charon/sa/transactions/delete_child_sa.h +++ /dev/null @@ -1,68 +0,0 @@ -/** - * @file delete_child_sa.h - * - * @brief Interface of transaction delete_child_sa. - * - */ - -/* - * 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 DELETE_CHILD_SA_H_ -#define DELETE_CHILD_SA_H_ - -typedef struct delete_child_sa_t delete_child_sa_t; - -#include <sa/ike_sa.h> -#include <sa/transactions/transaction.h> - - -/** - * @brief A transaction used to delete a CHILD_SA. - * - * @b Constructors: - * - delete_child_sa_create() - * - transaction_create() with the appropriate message - * - * @ingroup transactions - */ -struct delete_child_sa_t { - - /** - * The transaction_t interface. - */ - transaction_t transaction; - - /** - * @brief Set the CHILD_SA to delete. - * - * @param this calling object - * @param child_sa CHILD_SA to rekey - */ - void (*set_child_sa) (delete_child_sa_t* this, child_sa_t *child_sa); -}; - -/** - * @brief Create a new transaction which deletes a CHILD_SA. - * - * @param ike_sa assigned IKE_SA - * @return created delete_child_sa transaction - * - * @ingroup transactions - */ -delete_child_sa_t *delete_child_sa_create(ike_sa_t *ike_sa); - -#endif /* DELETE_CHILD_SA_H_ */ diff --git a/src/charon/sa/transactions/delete_ike_sa.c b/src/charon/sa/transactions/delete_ike_sa.c deleted file mode 100644 index 26817bb6a..000000000 --- a/src/charon/sa/transactions/delete_ike_sa.c +++ /dev/null @@ -1,284 +0,0 @@ -/** - * @file delete_ike_sa.c - * - * @brief Implementation of the delete_ike_sa transaction. - * - */ - -/* - * 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 "delete_ike_sa.h" - -#include <daemon.h> -#include <encoding/payloads/delete_payload.h> - -typedef struct private_delete_ike_sa_t private_delete_ike_sa_t; - -/** - * Private members of a delete_ike_sa_t object.. - */ -struct private_delete_ike_sa_t { - - /** - * Public methods and transaction_t interface. - */ - delete_ike_sa_t public; - - /** - * Assigned IKE_SA. - */ - ike_sa_t *ike_sa; - - /** - * Message sent by our peer, if already generated - */ - message_t *message; - - /** - * Message ID this transaction uses - */ - u_int32_t message_id; - - /** - * Times we did send the request - */ - u_int32_t requested; - - /** - * is the IKE_SA redundant and gets deleted without further notification? - */ - bool redundant; -}; - -/** - * Implementation of transaction_t.get_message_id. - */ -static u_int32_t get_message_id(private_delete_ike_sa_t *this) -{ - return this->message_id; -} - -/** - * Implementation of transaction_t.requested. - */ -static u_int32_t requested(private_delete_ike_sa_t *this) -{ - return this->requested++; -} - -/** - * Implementation of transaction_t.get_request. - */ -static status_t get_request(private_delete_ike_sa_t *this, message_t **result) -{ - message_t *request; - host_t *me, *other; - delete_payload_t *delete_payload; - - /* check if we already have built a message (retransmission) */ - if (this->message) - { - *result = this->message; - return SUCCESS; - } - - me = this->ike_sa->get_my_host(this->ike_sa); - other = this->ike_sa->get_other_host(this->ike_sa); - this->redundant = this->ike_sa->get_state(this->ike_sa) == IKE_REKEYING; - - if (!this->redundant) - { - SIG(IKE_DOWN_START, "deleting IKE_SA"); - } - - /* build the request */ - request = message_create(); - request->set_source(request, me->clone(me)); - request->set_destination(request, other->clone(other)); - request->set_exchange_type(request, INFORMATIONAL); - request->set_request(request, TRUE); - this->message_id = this->ike_sa->get_next_message_id(this->ike_sa); - request->set_message_id(request, this->message_id); - request->set_ike_sa_id(request, this->ike_sa->get_id(this->ike_sa)); - /* apply for caller */ - *result = request; - /* store for retransmission */ - this->message = request; - - delete_payload = delete_payload_create(PROTO_IKE); - request->add_payload(request, (payload_t*)delete_payload); - - /* transit to state SA_DELETING */ - this->ike_sa->set_state(this->ike_sa, IKE_DELETING); - - return SUCCESS; -} - -/** - * Implementation of transaction_t.get_response. - */ -static status_t get_response(private_delete_ike_sa_t *this, message_t *request, - message_t **result, transaction_t **next) -{ - host_t *me, *other; - message_t *response; - iterator_t *payloads; - payload_t *payload; - delete_payload_t *delete_request = NULL; - - /* check if we already have built a response (retransmission) - * this only happens in special simultanous transaction cases, - * as we delete the IKE_SA after the response is sent. */ - if (this->message) - { - *result = this->message; - return SUCCESS; - } - - me = this->ike_sa->get_my_host(this->ike_sa); - other = this->ike_sa->get_other_host(this->ike_sa); - this->message_id = request->get_message_id(request); - this->redundant = this->ike_sa->get_state(this->ike_sa) == IKE_REKEYING; - - if (!this->redundant) - { - SIG(IKE_DOWN_START, "deleting IKE_SA"); - } - - /* set up response */ - response = message_create(); - response->set_source(response, me->clone(me)); - response->set_destination(response, other->clone(other)); - response->set_exchange_type(response, INFORMATIONAL); - response->set_request(response, FALSE); - response->set_message_id(response, this->message_id); - response->set_ike_sa_id(response, this->ike_sa->get_id(this->ike_sa)); - this->message = response; - *result = response; - - /* check message type */ - if (request->get_exchange_type(request) != INFORMATIONAL) - { - if (!this->redundant) - { - SIG(IKE_DOWN_FAILED, "INFORMATIONAL response of invalid type, deleting IKE_SA"); - } - return DESTROY_ME; - } - - /* iterate over all payloads */ - payloads = request->get_payload_iterator(request); - while (payloads->iterate(payloads, (void**)&payload)) - { - switch (payload->get_type(payload)) - { - case DELETE: - { - delete_request = (delete_payload_t *)payload; - break; - } - default: - { - DBG1(DBG_IKE, "ignoring payload %N", - payload_type_names, payload->get_type(payload)); - break; - } - } - } - payloads->destroy(payloads); - - if (delete_request && - delete_request->get_protocol_id(delete_request) == PROTO_IKE) - { - DBG1(DBG_IKE, "DELETE request for IKE_SA received, deleting IKE_SA"); - } - else - { - /* should not happen, as we preparsed this at transaction construction */ - DBG1(DBG_IKE, "received a weird DELETE request for IKE_SA, deleting anyway"); - } - if (this->ike_sa->get_state(this->ike_sa) == IKE_DELETING) - { - /* if we are already deleting an IKE_SA, we do not destroy. We wait - * until we get the response for our initiated delete. */ - return SUCCESS; - } - this->ike_sa->set_state(this->ike_sa, IKE_DELETING); - if (!this->redundant) - { - SIG(IKE_DOWN_SUCCESS, "IKE_SA deleted on request"); - } - return DESTROY_ME; -} - - -/** - * Implementation of transaction_t.conclude - */ -static status_t conclude(private_delete_ike_sa_t *this, message_t *response, - transaction_t **transaction) -{ - /* check message type */ - if (response->get_exchange_type(response) != INFORMATIONAL) - { - if (!this->redundant) - { - SIG(IKE_DOWN_FAILED, "INFORMATIONAL response of invalid type, deleting IKE_SA"); - } - return DESTROY_ME; - } - /* this is only an acknowledge. We can't do anything here, but delete - * the IKE_SA. */ - if (!this->redundant) - { - SIG(IKE_DOWN_SUCCESS, "IKE_SA deleted"); - } - return DESTROY_ME; -} - -/** - * implements transaction_t.destroy - */ -static void destroy(private_delete_ike_sa_t *this) -{ - DESTROY_IF(this->message); - free(this); -} - -/* - * Described in header. - */ -delete_ike_sa_t *delete_ike_sa_create(ike_sa_t *ike_sa) -{ - private_delete_ike_sa_t *this = malloc_thing(private_delete_ike_sa_t); - - /* transaction interface functions */ - this->public.transaction.get_request = (status_t(*)(transaction_t*,message_t**))get_request; - this->public.transaction.get_response = (status_t(*)(transaction_t*,message_t*,message_t**,transaction_t**))get_response; - this->public.transaction.conclude = (status_t(*)(transaction_t*,message_t*,transaction_t**))conclude; - this->public.transaction.get_message_id = (u_int32_t(*)(transaction_t*))get_message_id; - this->public.transaction.requested = (u_int32_t(*)(transaction_t*))requested; - this->public.transaction.destroy = (void(*)(transaction_t*))destroy; - - /* private data */ - this->ike_sa = ike_sa; - this->message_id = 0; - this->message = NULL; - this->requested = 0; - this->redundant = FALSE; - - return &this->public; -} diff --git a/src/charon/sa/transactions/delete_ike_sa.h b/src/charon/sa/transactions/delete_ike_sa.h deleted file mode 100644 index 139e65ebb..000000000 --- a/src/charon/sa/transactions/delete_ike_sa.h +++ /dev/null @@ -1,81 +0,0 @@ -/** - * @file delete_ike_sa.h - * - * @brief Interface of transaction delete_ike_sa. - * - */ - -/* - * 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 DELETE_IKE_SA_H_ -#define DELETE_IKE_SA_H_ - -typedef struct delete_ike_sa_t delete_ike_sa_t; - -#include <sa/ike_sa.h> -#include <sa/transactions/transaction.h> - -/** - * @brief A transaction used to delete the IKE_SA. - * - * Notation as follows: - * Mx{D} means: Message, with message ID "x", containing a Delete payload - * - * The clarifcation Document says in 5.8, that a IKE_SA delete should not - * be acknowledged with the same delete. This only makes sense for CHILD_SAs, - * as they are paired. IKE_SAs are not, there is only one for both ends. - * - * Normal case: - * ---------------- - * Mx{D} --> - * <-- Mx{} - * Delete request is sent, and we wait for the acknowledge. - * - * Special case 1: - * --------------- - * Mx{D} --> - * <-- My{D} - * My{} --> - * <-- Mx{} - * Both initate a delete at the same time. We ack the delete, but wait for - * our delete to be acknowledged. - * - * @b Constructors: - * - delete_ike_sa_create() - * - transaction_create() with the appropriate message - * - * @ingroup transactions - */ -struct delete_ike_sa_t { - - /** - * The transaction_t interface. - */ - transaction_t transaction; -}; - -/** - * @brief Create a new transaction which deletes the IKE_SA. - * - * @param ike_sa assigned IKE_SA - * @return created delete_ike_sa transaction - * - * @ingroup transactions - */ -delete_ike_sa_t *delete_ike_sa_create(ike_sa_t *ike_sa); - -#endif /* DELETE_IKE_SA_H_ */ diff --git a/src/charon/sa/transactions/ike_auth.c b/src/charon/sa/transactions/ike_auth.c deleted file mode 100644 index bf7fd6d12..000000000 --- a/src/charon/sa/transactions/ike_auth.c +++ /dev/null @@ -1,1562 +0,0 @@ -/** - * @file ike_auth.c - * - * @brief Implementation of ike_auth_t transaction. - * - */ - -/* - * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger - * 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 "ike_auth.h" - -#include <string.h> - -#include <daemon.h> -#include <encoding/payloads/sa_payload.h> -#include <encoding/payloads/id_payload.h> -#include <encoding/payloads/cert_payload.h> -#include <encoding/payloads/certreq_payload.h> -#include <encoding/payloads/auth_payload.h> -#include <encoding/payloads/ts_payload.h> -#include <sa/authenticators/authenticator.h> -#include <sa/authenticators/eap_authenticator.h> -#include <sa/child_sa.h> - - -typedef struct private_ike_auth_t private_ike_auth_t; - -/** - * Private members of a ike_auth_t object.. - */ -struct private_ike_auth_t { - - /** - * Public methods and transaction_t interface. - */ - ike_auth_t public; - - /** - * Assigned IKE_SA. - */ - ike_sa_t *ike_sa; - - /** - * Message sent by our peer, if already generated - */ - message_t *message; - - /** - * Message ID this transaction uses - */ - u_int32_t message_id; - - /** - * Times we did send the request - */ - u_int32_t requested; - - /** - * initiator chosen nonce - */ - chunk_t nonce_i; - - /** - * responder chosen nonce - */ - chunk_t nonce_r; - - /** - * encoded request message of ike_sa_init transaction - */ - chunk_t init_request; - - /** - * encoded response message of ike_sa_init transaction - */ - chunk_t init_response; - - /** - * connection definition used for IKE_SA setup - */ - connection_t *connection; - - /** - * policy definition used CHILD_SA creation - */ - policy_t *policy; - - /** - * Negotiated proposal used for CHILD_SA - */ - proposal_t *proposal; - - /** - * Negotiated traffic selectors for initiator - */ - linked_list_t *tsi; - - /** - * Negotiated traffic selectors for responder - */ - linked_list_t *tsr; - - /** - * CHILD_SA created along with IKE_AUTH - */ - child_sa_t *child_sa; - - /** - * did other peer create a CHILD_SA? - */ - bool build_child; - - /** - * reqid to use for CHILD_SA setup - */ - u_int32_t reqid; - - /** - * List of CA certificates the other peer trusts - */ - linked_list_t *cacerts; - - /** - * EAP uses this authentication, which is passed along multiple ike_auths - */ - eap_authenticator_t *eap_auth; - - /** - * if the client receives a EAP request, it is stored here for later use - */ - eap_payload_t *eap_next; - - /** - * set to TRUE if authentication should be done with EAP only - */ - bool eap_only; - - /** - * has the other peer been authenticated yet? - */ - bool peer_authenticated; - - /** - * mode the CHILD_SA uses: tranport, tunnel, BEET - */ - mode_t mode; -}; - -/** - * Implementation of transaction_t.get_message_id. - */ -static u_int32_t get_message_id(private_ike_auth_t *this) -{ - return this->message_id; -} - -/** - * Implementation of transaction_t.requested. - */ -static u_int32_t requested(private_ike_auth_t *this) -{ - return this->requested++; -} - -/** - * Implementation of transaction_t.set_config. - */ -static void set_config(private_ike_auth_t *this, - connection_t *connection, policy_t *policy) -{ - this->connection = connection; - this->policy = policy; -} - -/** - * Implementation of transaction_t.set_reqid. - */ -static void set_reqid(private_ike_auth_t *this, u_int32_t reqid) -{ - this->reqid = reqid; -} - -/** - * Implementation of transaction_t.set_nonces. - */ -static void set_nonces(private_ike_auth_t *this, chunk_t nonce_i, chunk_t nonce_r) -{ - this->nonce_i = nonce_i; - this->nonce_r = nonce_r; -} - -/** - * Implementation of transaction_t.set_init_messages. - */ -static void set_init_messages(private_ike_auth_t *this, chunk_t init_request, chunk_t init_response) -{ - this->init_request = init_request; - this->init_response = init_response; -} - -/** - * Build a notify message. - */ -static void build_notify(notify_type_t type, message_t *message, bool flush_message) -{ - notify_payload_t *notify; - - if (flush_message) - { - payload_t *payload; - iterator_t *iterator = message->get_payload_iterator(message); - while (iterator->iterate(iterator, (void**)&payload)) - { - payload->destroy(payload); - iterator->remove(iterator); - } - iterator->destroy(iterator); - } - - notify = notify_payload_create(); - notify->set_notify_type(notify, type); - message->add_payload(message, (payload_t*)notify); -} - -/** - * Implementation of transaction_t.get_request. - */ -static status_t get_request(private_ike_auth_t *this, message_t **result) -{ - message_t *request; - host_t *me, *other; - identification_t *my_id, *other_id; - id_payload_t *my_id_payload; - - /* check if we already have built a message (retransmission) */ - if (this->message) - { - *result = this->message; - return SUCCESS; - } - - me = this->ike_sa->get_my_host(this->ike_sa); - other = this->ike_sa->get_other_host(this->ike_sa); - my_id = this->policy->get_my_id(this->policy); - other_id = this->policy->get_other_id(this->policy); - - /* build the request */ - request = message_create(); - request->set_source(request, me->clone(me)); - request->set_destination(request, other->clone(other)); - request->set_exchange_type(request, IKE_AUTH); - request->set_request(request, TRUE); - request->set_ike_sa_id(request, this->ike_sa->get_id(this->ike_sa)); - /* apply for caller */ - *result = request; - /* store for retransmission */ - this->message = request; - - this->message_id = this->ike_sa->get_next_message_id(this->ike_sa); - request->set_message_id(request, this->message_id); - - if (this->eap_auth) - { - /* we already sent ID, SA, TS in an earlier ike_auth, we now - * continiue EAP processing */ - if (this->eap_next) - { - /* if we have another outstanding EAP response, send it */ - request->add_payload(request, (payload_t*)this->eap_next); - this->eap_next = NULL; - return SUCCESS; - } - else - { - /* if not, we have received an EAP_SUCCESS, send AUTH payload. - * we only send our data if: - * a) The peer has been authenticated using RSA/PSK, or - * b) The EAP method is mutual and gives us enough security - */ - if (this->eap_auth->is_mutual(this->eap_auth) || - this->peer_authenticated) - { - auth_payload_t *auth_payload; - authenticator_t *authenticator = (authenticator_t*)this->eap_auth; - - if (authenticator->build(authenticator, this->init_request, - this->nonce_r, &auth_payload) != SUCCESS) - { - SIG(IKE_UP_FAILED, - "EAP authentication data generation failed, deleting IKE_SA"); - SIG(CHILD_UP_FAILED, - "initiating CHILD_SA failed, unable to create IKE_SA"); - return DESTROY_ME; - } - request->add_payload(request, (payload_t*)auth_payload); - return SUCCESS; - } - else - { - SIG(IKE_UP_FAILED, - "peer didn't send authentication data, deleting IKE_SA"); - SIG(CHILD_UP_FAILED, - "initiating CHILD_SA failed, unable to create IKE_SA"); - return DESTROY_ME; - } - } - } - /* otherwise we do a normal ike_auth request... */ - - { /* build ID payload */ - my_id_payload = id_payload_create_from_identification(TRUE, my_id); - request->add_payload(request, (payload_t*)my_id_payload); - } - - /* build certificate request payload */ - if (this->connection->get_certreq_policy(this->connection) != CERT_NEVER_SEND) - { - certreq_payload_t *certreq_payload; - identification_t *other_ca = this->policy->get_other_ca(this->policy); - - if (other_ca) - { - if (other_ca->get_type(other_ca) == ID_ANY) - { - certreq_payload = certreq_payload_create_from_cacerts(); - } - else - { - certreq_payload = certreq_payload_create_from_cacert(other_ca); - } - if (certreq_payload != NULL) - { - request->add_payload(request, (payload_t*)certreq_payload); - } - } - } - - /* build certificate payload. TODO: Handle certreq from init_ike_sa. */ - if (this->policy->get_auth_method(this->policy) == AUTH_RSA && - this->connection->get_cert_policy(this->connection) != CERT_NEVER_SEND) - { - cert_payload_t *cert_payload; - - x509_t *cert = charon->credentials->get_certificate(charon->credentials, my_id); - - if (cert) - { - cert_payload = cert_payload_create_from_x509(cert); - request->add_payload(request, (payload_t*)cert_payload); - } - else - { - DBG1(DBG_IKE, "could not find my certificate, certificate payload omitted"); - } - } - - { /* build IDr payload, if other_id defined */ - id_payload_t *id_payload; - if (!other_id->contains_wildcards(other_id)) - { - id_payload = id_payload_create_from_identification(FALSE, other_id); - request->add_payload(request, (payload_t*)id_payload); - } - } - - if (this->policy->get_auth_method(this->policy) != AUTH_EAP) - { /* build auth payload */ - authenticator_t *authenticator; - auth_payload_t *auth_payload; - auth_method_t auth_method; - status_t status; - - auth_method = this->policy->get_auth_method(this->policy); - authenticator = authenticator_create(this->ike_sa, auth_method); - if (authenticator == NULL) - { - SIG(IKE_UP_FAILED, "auth method %N not supported, deleting IKE_SA", - auth_method_names, auth_method); - SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA"); - return DESTROY_ME; - } - status = authenticator->build(authenticator, this->init_request, - this->nonce_r, &auth_payload); - authenticator->destroy(authenticator); - if (status != SUCCESS) - { - SIG(IKE_UP_FAILED, "could not generate AUTH data, deleting IKE_SA"); - SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA"); - return DESTROY_ME; - } - request->add_payload(request, (payload_t*)auth_payload); - } - else - { - this->eap_auth = eap_authenticator_create(this->ike_sa); - /* include notify that we support EAP only authentication */ - build_notify(EAP_ONLY_AUTHENTICATION, request, FALSE); - } - - { /* build SA payload for CHILD_SA */ - linked_list_t *proposal_list; - sa_payload_t *sa_payload; - u_int32_t soft_lifetime, hard_lifetime; - bool enable_natt; - - proposal_list = this->policy->get_proposals(this->policy); - soft_lifetime = this->policy->get_soft_lifetime(this->policy); - hard_lifetime = this->policy->get_hard_lifetime(this->policy); - enable_natt = this->ike_sa->is_natt_enabled(this->ike_sa); - this->child_sa = child_sa_create(this->reqid, me, other, my_id, other_id, - soft_lifetime, hard_lifetime, - this->policy->get_updown(this->policy), - this->policy->get_hostaccess(this->policy), - enable_natt); - this->child_sa->set_name(this->child_sa, this->policy->get_name(this->policy)); - if (this->child_sa->alloc(this->child_sa, proposal_list) != SUCCESS) - { - SIG(IKE_UP_FAILED, "could not install CHILD_SA, deleting IKE_SA"); - SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA"); - return DESTROY_ME; - } - sa_payload = sa_payload_create_from_proposal_list(proposal_list); - proposal_list->destroy_offset(proposal_list, offsetof(proposal_t, destroy)); - request->add_payload(request, (payload_t*)sa_payload); - } - - /* notify for transport/BEET mode, we propose it - * independent of the traffic selectors */ - switch (this->policy->get_mode(this->policy)) - { - case MODE_TUNNEL: - /* is the default */ - break; - case MODE_TRANSPORT: - if (this->ike_sa->is_natt_enabled(this->ike_sa)) - { - DBG1(DBG_IKE, "not using tranport mode, as connection NATed"); - } - else - { - build_notify(USE_TRANSPORT_MODE, request, FALSE); - } - break; - case MODE_BEET: - build_notify(USE_BEET_MODE, request, FALSE); - break; - } - - { /* build TSi payload */ - linked_list_t *ts_list; - ts_payload_t *ts_payload; - - ts_list = this->policy->get_my_traffic_selectors(this->policy, me); - ts_payload = ts_payload_create_from_traffic_selectors(TRUE, ts_list); - ts_list->destroy_offset(ts_list, offsetof(traffic_selector_t, destroy)); - - request->add_payload(request, (payload_t*)ts_payload); - } - - { /* build TSr payload */ - linked_list_t *ts_list; - ts_payload_t *ts_payload; - - ts_list = this->policy->get_other_traffic_selectors(this->policy, other); - ts_payload = ts_payload_create_from_traffic_selectors(FALSE, ts_list); - ts_list->destroy_offset(ts_list, offsetof(traffic_selector_t, destroy)); - - request->add_payload(request, (payload_t*)ts_payload); - } - - return SUCCESS; -} - -/** - * Handle all kind of notifies - */ -static status_t process_notifies(private_ike_auth_t *this, notify_payload_t *notify_payload) -{ - notify_type_t notify_type = notify_payload->get_notify_type(notify_payload); - - DBG2(DBG_IKE, "process notify type %N", notify_type_names, notify_type); - - switch (notify_type) - { - /* these notifies are not critical. no child_sa is built, but IKE stays alive */ - case SINGLE_PAIR_REQUIRED: - { - SIG(CHILD_UP_FAILED, "received a SINGLE_PAIR_REQUIRED notify"); - this->build_child = FALSE; - return SUCCESS; - } - case TS_UNACCEPTABLE: - { - SIG(CHILD_UP_FAILED, "received TS_UNACCEPTABLE notify"); - this->build_child = FALSE; - return SUCCESS; - } - case NO_PROPOSAL_CHOSEN: - { - SIG(CHILD_UP_FAILED, "received NO_PROPOSAL_CHOSEN notify"); - this->build_child = FALSE; - return SUCCESS; - } - case EAP_ONLY_AUTHENTICATION: - { - DBG1(DBG_IKE, "peer requested EAP_ONLY_AUTHENTICATION"); - this->eap_only = TRUE; - return SUCCESS; - } - case USE_TRANSPORT_MODE: - { - this->mode = MODE_TRANSPORT; - return SUCCESS; - } - case USE_BEET_MODE: - { - this->mode = MODE_BEET; - return SUCCESS; - } - default: - { - if (notify_type < 16383) - { - SIG(IKE_UP_FAILED, "received %N notify error, deleting IKE_SA", - notify_type_names, notify_type); - return DESTROY_ME; - } - else - { - DBG1(DBG_IKE, "received %N notify, ignored", - notify_type_names, notify_type); - return SUCCESS; - } - } - } -} - -/** - * Import certificate requests from a certreq payload - */ -static void process_certificate_request(private_ike_auth_t *this, - certreq_payload_t *certreq_payload) -{ - chunk_t keyids; - cert_encoding_t encoding = certreq_payload->get_cert_encoding(certreq_payload); - - if (encoding != CERT_X509_SIGNATURE) - { - DBG1(DBG_IKE, "certreq payload %N not supported, ignored", - cert_encoding_names, encoding); - return; - } - - keyids = certreq_payload->get_data(certreq_payload); - while (keyids.len >= HASH_SIZE_SHA1) - { - chunk_t keyid = { keyids.ptr, HASH_SIZE_SHA1}; - x509_t *cacert = charon->credentials->get_ca_certificate_by_keyid( - charon->credentials, keyid); - if (cacert) - { - DBG2(DBG_IKE, "request for certificate issued by ca '%D'", - cacert->get_subject(cacert)); - this->cacerts->insert_last(this->cacerts, cacert); - } - else - { - DBG2(DBG_IKE, "request for certificate issued by unknown ca"); - } - DBG2(DBG_IKE, " with keyid %#B", &keyid); - - keyids.ptr += HASH_SIZE_SHA1; - keyids.len -= HASH_SIZE_SHA1; - } -} - -/** - * Import a certificate from a cert payload - */ -static void import_certificate(cert_payload_t *cert_payload) -{ - bool found; - x509_t *cert; - cert_encoding_t encoding; - - encoding = cert_payload->get_cert_encoding(cert_payload); - if (encoding != CERT_X509_SIGNATURE) - { - DBG1(DBG_IKE, "certificate payload %N not supported, ignored", - cert_encoding_names, encoding); - return; - } - cert = x509_create_from_chunk(cert_payload->get_data_clone(cert_payload)); - if (cert) - { - if (charon->credentials->verify(charon->credentials, cert, &found)) - { - DBG2(DBG_IKE, "received end entity certificate is trusted, added to store"); - if (!found) - { - charon->credentials->add_end_certificate(charon->credentials, cert); - } - else - { - cert->destroy(cert); - } - } - else - { - DBG1(DBG_IKE, "received end entity certificate is not trusted, discarded"); - cert->destroy(cert); - } - } - else - { - DBG1(DBG_IKE, "parsing of received certificate failed, discarded"); - } -} - -/** - * Check a list of traffic selectors if any selector belongs to host - */ -static bool ts_list_is_host(linked_list_t *list, host_t *host) -{ - traffic_selector_t *ts; - bool is_host = TRUE; - iterator_t *iterator = list->create_iterator(list, TRUE); - - while (is_host && iterator->iterate(iterator, (void**)&ts)) - { - is_host = is_host && ts->is_host(ts, host); - } - iterator->destroy(iterator); - return is_host; -} - -/** - * Install a CHILD_SA for usage - */ -static status_t install_child_sa(private_ike_auth_t *this, bool initiator) -{ - prf_plus_t *prf_plus; - chunk_t seed; - status_t status; - - seed = chunk_alloc(this->nonce_i.len + this->nonce_r.len); - memcpy(seed.ptr, this->nonce_i.ptr, this->nonce_i.len); - memcpy(seed.ptr + this->nonce_i.len, this->nonce_r.ptr, this->nonce_r.len); - prf_plus = prf_plus_create(this->ike_sa->get_child_prf(this->ike_sa), seed); - chunk_free(&seed); - - if (initiator) - { - status = this->child_sa->update(this->child_sa, this->proposal, - this->mode, prf_plus); - } - else - { - status = this->child_sa->add(this->child_sa, this->proposal, - this->mode, prf_plus); - } - prf_plus->destroy(prf_plus); - if (status != SUCCESS) - { - return DESTROY_ME; - } - if (initiator) - { - status = this->child_sa->add_policies(this->child_sa, this->tsi, - this->tsr, this->mode); - } - else - { - status = this->child_sa->add_policies(this->child_sa, this->tsr, - this->tsi, this->mode); - } - if (status != SUCCESS) - { - return DESTROY_ME; - } - - /* add to IKE_SA, and remove from transaction */ - this->child_sa->set_state(this->child_sa, CHILD_INSTALLED); - this->ike_sa->add_child_sa(this->ike_sa, this->child_sa); - this->child_sa = NULL; - return SUCCESS; -} - -/** - * create a CHILD SA, install it, and build response message - */ -static void setup_child_sa(private_ike_auth_t *this, message_t *response) -{ - bool use_natt; - u_int32_t soft_lifetime, hard_lifetime; - host_t *me, *other; - identification_t *my_id, *other_id; - - me = this->ike_sa->get_my_host(this->ike_sa); - other = this->ike_sa->get_other_host(this->ike_sa); - my_id = this->ike_sa->get_my_id(this->ike_sa); - other_id = this->ike_sa->get_other_id(this->ike_sa); - - soft_lifetime = this->policy->get_soft_lifetime(this->policy); - hard_lifetime = this->policy->get_hard_lifetime(this->policy); - use_natt = this->ike_sa->is_natt_enabled(this->ike_sa); - this->child_sa = child_sa_create(this->reqid, me, other, my_id, other_id, - soft_lifetime, hard_lifetime, - this->policy->get_updown(this->policy), - this->policy->get_hostaccess(this->policy), - use_natt); - this->child_sa->set_name(this->child_sa, this->policy->get_name(this->policy)); - /* check mode, and include notify into reply */ - switch (this->mode) - { - case MODE_TUNNEL: - /* is the default */ - break; - case MODE_TRANSPORT: - if (!ts_list_is_host(this->tsi, other) || - !ts_list_is_host(this->tsr, me)) - { - this->mode = MODE_TUNNEL; - DBG1(DBG_IKE, "not using tranport mode, not host-to-host"); - } - else if (this->ike_sa->is_natt_enabled(this->ike_sa)) - { - this->mode = MODE_TUNNEL; - DBG1(DBG_IKE, "not using tranport mode, as connection NATed"); - } - else - { - build_notify(USE_TRANSPORT_MODE, response, FALSE); - } - break; - case MODE_BEET: - if (!ts_list_is_host(this->tsi, NULL) || - !ts_list_is_host(this->tsr, NULL)) - { - this->mode = MODE_TUNNEL; - DBG1(DBG_IKE, "not using BEET mode, not host-to-host"); - } - else - { - build_notify(USE_BEET_MODE, response, FALSE); - } - break; - } - - if (install_child_sa(this, FALSE) != SUCCESS) - { - SIG(CHILD_UP_FAILED, "installing CHILD_SA %s failed, no CHILD_SA created", - this->policy->get_name(this->policy)); - DBG1(DBG_IKE, "adding NO_PROPOSAL_CHOSEN notify to response"); - build_notify(NO_PROPOSAL_CHOSEN, response, FALSE); - } - else - { - /* build SA and TS payloads */ - SIG(CHILD_UP_SUCCESS, "CHILD_SA created"); - ts_payload_t *ts_response; - sa_payload_t *sa_response = sa_payload_create(); - sa_response->add_proposal(sa_response, this->proposal); - response->add_payload(response, (payload_t*)sa_response); - ts_response = ts_payload_create_from_traffic_selectors(TRUE, this->tsi); - response->add_payload(response, (payload_t*)ts_response); - ts_response = ts_payload_create_from_traffic_selectors(FALSE, this->tsr); - response->add_payload(response, (payload_t*)ts_response); - } -} - -/** - * clone the transaction to requeue it for EAP handling - */ -static private_ike_auth_t *clone_for_eap(private_ike_auth_t *this) -{ - private_ike_auth_t *clone; - clone = (private_ike_auth_t*)ike_auth_create(this->ike_sa); - - clone->tsi = this->tsi; this->tsi = NULL; - clone->tsr = this->tsr; this->tsr = NULL; - clone->eap_auth = this->eap_auth; this->eap_auth = NULL; - clone->eap_next = this->eap_next; this->eap_next = NULL; - clone->build_child = this->build_child; - clone->nonce_i = this->nonce_i; this->nonce_i = chunk_empty; - clone->nonce_r = this->nonce_r; this->nonce_r = chunk_empty; - clone->child_sa = this->child_sa; this->child_sa = NULL; - clone->proposal = this->proposal; this->proposal = NULL; - clone->peer_authenticated = this->peer_authenticated; - clone->connection = this->connection; this->connection = NULL; - clone->policy = this->policy; this->policy = NULL; - - return clone; -} - -/** - * Implementation of transaction_t.get_response. - */ -static status_t get_response(private_ike_auth_t *this, message_t *request, - message_t **result, transaction_t **next) -{ - host_t *me, *other; - identification_t *my_id, *other_id; - message_t *response; - status_t status; - iterator_t *payloads; - payload_t *payload; - id_payload_t *idi_request = NULL; - id_payload_t *idr_request = NULL; - auth_payload_t *auth_request = NULL; - certreq_payload_t *certreq_request = NULL; - cert_payload_t *cert_request = NULL; - sa_payload_t *sa_request = NULL; - ts_payload_t *tsi_request = NULL; - ts_payload_t *tsr_request = NULL; - eap_payload_t *eap_request = NULL; - id_payload_t *idr_response; - - /* check if we already have built a response (retransmission) */ - if (this->message) - { - *result = this->message; - return SUCCESS; - } - - SIG(CHILD_UP_START, "setting up CHILD_SA along with IKE_AUTH"); - - me = this->ike_sa->get_my_host(this->ike_sa); - other = this->ike_sa->get_other_host(this->ike_sa); - my_id = this->ike_sa->get_my_id(this->ike_sa); - other_id = this->ike_sa->get_other_id(this->ike_sa); - this->message_id = request->get_message_id(request); - - /* set up response */ - response = message_create(); - response->set_source(response, me->clone(me)); - response->set_destination(response, other->clone(other)); - response->set_exchange_type(response, IKE_AUTH); - response->set_request(response, FALSE); - response->set_message_id(response, this->message_id); - response->set_ike_sa_id(response, this->ike_sa->get_id(this->ike_sa)); - this->message = response; - *result = response; - - /* check message type */ - if (request->get_exchange_type(request) != IKE_AUTH) - { - SIG(IKE_UP_FAILED, "IKE_AUTH response of invalid type, deleting IKE_SA"); - SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA"); - return DESTROY_ME; - } - - /* Iterate over all payloads. */ - payloads = request->get_payload_iterator(request); - while (payloads->iterate(payloads, (void**)&payload)) - { - switch (payload->get_type(payload)) - { - case ID_INITIATOR: - idi_request = (id_payload_t*)payload; - break; - case ID_RESPONDER: - idr_request = (id_payload_t*)payload; - break; - case AUTHENTICATION: - auth_request = (auth_payload_t*)payload; - break; - case CERTIFICATE_REQUEST: - certreq_request = (certreq_payload_t*)payload; - process_certificate_request(this, certreq_request); - break; - case CERTIFICATE: - cert_request = (cert_payload_t*)payload; - break; - case SECURITY_ASSOCIATION: - sa_request = (sa_payload_t*)payload; - break; - case TRAFFIC_SELECTOR_INITIATOR: - tsi_request = (ts_payload_t*)payload; - break; - case TRAFFIC_SELECTOR_RESPONDER: - tsr_request = (ts_payload_t*)payload; - break; - case EXTENSIBLE_AUTHENTICATION: - eap_request = (eap_payload_t*)payload; - break; - case NOTIFY: - { - status = process_notifies(this, (notify_payload_t*)payload); - if (status == FAILED) - { - payloads->destroy(payloads); - /* we return SUCCESS, returned FAILED means do next transaction */ - return SUCCESS; - } - if (status == DESTROY_ME) - { - payloads->destroy(payloads); - SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA"); - return DESTROY_ME; - } - break; - } - default: - { - DBG1(DBG_IKE, "ignoring %N payload", - payload_type_names, payload->get_type(payload)); - break; - } - } - } - payloads->destroy(payloads); - - /* if message contains an EAP payload, we process it */ - if (eap_request && this->eap_auth) - { - eap_payload_t *eap_response; - private_ike_auth_t *next_auth; - - status = this->eap_auth->process(this->eap_auth, eap_request, &eap_response); - response->add_payload(response, (payload_t*)eap_response); - - if (status == FAILED) - { - /* shut down if EAP message is EAP_FAILURE */ - return DESTROY_ME; - } - - next_auth = clone_for_eap(this); - next_auth->message_id = this->message_id + 1; - *next = (transaction_t*)next_auth; - return SUCCESS; - } - - /* if we do EAP authentication and a AUTH payload comes in, verify it */ - if (auth_request && this->eap_auth) - { - auth_payload_t *auth_response; - authenticator_t *authenticator = (authenticator_t*)this->eap_auth; - - if (authenticator->verify(authenticator, this->init_request, - this->nonce_r, auth_request) != SUCCESS) - { - SIG(IKE_UP_FAILED, "authentication with EAP failed, deleting IKE_SA"); - SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA"); - build_notify(AUTHENTICATION_FAILED, response, TRUE); - return DESTROY_ME; - } - - if (authenticator->build(authenticator, this->init_response, - this->nonce_i, &auth_response) != SUCCESS) - { - SIG(IKE_UP_FAILED, "EAP authentication data generation failed, deleting IKE_SA"); - SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA"); - build_notify(AUTHENTICATION_FAILED, response, TRUE); - return DESTROY_ME; - } - response->add_payload(response, (payload_t*)auth_response); - - SIG(IKE_UP_SUCCESS, "IKE_SA '%s' established between %H[%D]...%H[%D]", - this->ike_sa->get_name(this->ike_sa), me, my_id, other, other_id); - this->ike_sa->set_state(this->ike_sa, IKE_ESTABLISHED); - - setup_child_sa(this, response); - - return SUCCESS; - } - - /* check if we have all payloads (AUTH is not checked, not required with EAP) */ - if (!(idi_request && sa_request && tsi_request && tsr_request)) - { - build_notify(INVALID_SYNTAX, response, TRUE); - SIG(IKE_UP_FAILED, "request message incomplete, deleting IKE_SA"); - SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA"); - return DESTROY_ME; - } - - { /* process ID payload */ - other_id = idi_request->get_identification(idi_request); - if (idr_request) - { - my_id = idr_request->get_identification(idr_request); - } - else - { - my_id = identification_create_from_encoding(ID_ANY, chunk_empty); - } - } - - { /* get a policy and process traffic selectors */ - linked_list_t *my_ts, *other_ts; - - my_ts = tsr_request->get_traffic_selectors(tsr_request); - other_ts = tsi_request->get_traffic_selectors(tsi_request); - - this->policy = charon->policies->get_policy(charon->policies, - my_id, other_id, - my_ts, other_ts, - me, other); - - if (this->policy) - { - this->tsr = this->policy->select_my_traffic_selectors(this->policy, my_ts, me); - this->tsi = this->policy->select_other_traffic_selectors(this->policy, other_ts, other); - } - my_ts->destroy_offset(my_ts, offsetof(traffic_selector_t, destroy)); - other_ts->destroy_offset(other_ts, offsetof(traffic_selector_t, destroy)); - - /* TODO: We should check somehow if we have a policy, but with other - * traffic selectors. Then we would create a IKE_SA without a CHILD_SA. */ - if (this->policy == NULL) - { - SIG(IKE_UP_FAILED, "no acceptable policy for IDs %D - %D found, " - "deleting IKE_SA", my_id, other_id); - SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA"); - my_id->destroy(my_id); - other_id->destroy(other_id); - build_notify(AUTHENTICATION_FAILED, response, TRUE); - return DESTROY_ME; - } - my_id->destroy(my_id); - - /* get my id from policy, which must contain a fully qualified valid id */ - my_id = this->policy->get_my_id(this->policy); - this->ike_sa->set_my_id(this->ike_sa, my_id->clone(my_id)); - this->ike_sa->set_other_id(this->ike_sa, other_id); - - idr_response = id_payload_create_from_identification(FALSE, my_id); - response->add_payload(response, (payload_t*)idr_response); - } - - if (this->policy->get_auth_method(this->policy) == AUTH_RSA && - this->connection->get_cert_policy(this->connection) != CERT_NEVER_SEND) - { /* build certificate payload */ - x509_t *cert; - cert_payload_t *cert_payload; - - cert = charon->credentials->get_certificate(charon->credentials, my_id); - if (cert) - { - cert_payload = cert_payload_create_from_x509(cert); - response->add_payload(response, (payload_t *)cert_payload); - } - else - { - DBG1(DBG_IKE, "could not find my certificate, cert payload omitted"); - } - } - - if (cert_request) - { /* process certificate payload */ - import_certificate(cert_request); - } - - { /* process SA payload */ - linked_list_t *proposal_list; - - /* get proposals from request, and select one with ours */ - proposal_list = sa_request->get_proposals(sa_request); - DBG2(DBG_IKE, "selecting proposals:"); - this->proposal = this->policy->select_proposal(this->policy, proposal_list); - proposal_list->destroy_offset(proposal_list, offsetof(proposal_t, destroy)); - - /* do we have a proposal? */ - if (this->proposal == NULL) - { - SIG(CHILD_UP_FAILED, "CHILD_SA proposals unacceptable, no CHILD_SA created"); - DBG1(DBG_IKE, "adding NO_PROPOSAL_CHOSEN notify to response"); - build_notify(NO_PROPOSAL_CHOSEN, response, FALSE); - this->build_child = FALSE; - } - /* do we have traffic selectors? */ - else if (this->tsi->get_count(this->tsi) == 0 || this->tsr->get_count(this->tsr) == 0) - { - SIG(CHILD_UP_FAILED, "CHILD_SA traffic selectors unacceptable, no CHILD_SA created"); - DBG1(DBG_IKE, "adding TS_UNACCEPTABLE notify to response"); - build_notify(TS_UNACCEPTABLE, response, FALSE); - this->build_child = FALSE; - } - } - - if (!this->eap_only || this->policy->get_auth_method(this->policy) != AUTH_EAP) - { /* build response AUTH payload when not using a mutual EAP authentication */ - auth_payload_t *auth_response; - authenticator_t *authenticator; - auth_method_t auth_method; - status_t status; - - auth_method = this->policy->get_auth_method(this->policy); - if (auth_method == AUTH_EAP) - { - SIG(IKE_UP_FAILED, - "peer does not support EAP only authentication, deleting IKE_SA"); - SIG(CHILD_UP_FAILED, - "initiating CHILD_SA failed, unable to create IKE_SA"); - build_notify(AUTHENTICATION_FAILED, response, TRUE); - return DESTROY_ME; - } - - authenticator = authenticator_create(this->ike_sa, auth_method); - if (authenticator == NULL) - { - SIG(IKE_UP_FAILED, "auth method %N not supported, deleting IKE_SA", - auth_method_names, auth_method); - SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA"); - build_notify(AUTHENTICATION_FAILED, response, TRUE); - return DESTROY_ME; - } - status = authenticator->build(authenticator, this->init_response, - this->nonce_i, &auth_response); - authenticator->destroy(authenticator); - if (status != SUCCESS) - { - SIG(IKE_UP_FAILED, "authentication data generation failed, deleting IKE_SA"); - SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA"); - build_notify(AUTHENTICATION_FAILED, response, TRUE); - return DESTROY_ME; - } - response->add_payload(response, (payload_t*)auth_response); - } - - if (auth_request) - { /* process auth payload, if not using EAP */ - authenticator_t *authenticator; - auth_method_t auth_method; - status_t status; - - auth_method = auth_request->get_auth_method(auth_request); - authenticator = authenticator_create(this->ike_sa, auth_method); - if (authenticator == NULL) - { - SIG(IKE_UP_FAILED, "auth method %N not supported, deleting IKE_SA", - auth_method_names, auth_method); - SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA"); - return DESTROY_ME; - } - status = authenticator->verify(authenticator, this->init_request, - this->nonce_r, auth_request); - authenticator->destroy(authenticator); - if (status != SUCCESS) - { - SIG(IKE_UP_FAILED, "authentication failed, deleting IKE_SA"); - SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA"); - build_notify(AUTHENTICATION_FAILED, response, TRUE); - return DESTROY_ME; - } - - SIG(IKE_UP_SUCCESS, "IKE_SA '%s' established between %H[%D]...%H[%D]", - this->ike_sa->get_name(this->ike_sa), me, my_id, other, other_id); - this->ike_sa->set_state(this->ike_sa, IKE_ESTABLISHED); - - /* set up CHILD_SA if negotiation succeded */ - if (this->build_child) - { - setup_child_sa(this, response); - } - - this->peer_authenticated = TRUE; - } - else - { - /* if no AUTH payload was included, we start with an EAP exchange. - * eap_response is a request in the EAP meaning, but is - * contained in a IKEv2 response */ - eap_payload_t *eap_response; - private_ike_auth_t *next_auth; - eap_type_t eap_type; - - eap_type = this->policy->get_eap_type(this->policy); - this->eap_auth = eap_authenticator_create(this->ike_sa); - status = this->eap_auth->initiate(this->eap_auth, eap_type, &eap_response); - response->add_payload(response, (payload_t*)eap_response); - - if (status == FAILED) - { - /* EAP initiaton failed, we send the EAP_FAILURE message and quit */ - return DESTROY_ME; - } - /* we send an EAP request. to handle the reply, we reschedule - * this transaction, as it knows how to handle the reply */ - next_auth = clone_for_eap(this); - next_auth->message_id = this->message_id + 1; - *next = (transaction_t*)next_auth; - } - return SUCCESS; -} - -/** - * Implementation of transaction_t.conclude - */ -static status_t conclude(private_ike_auth_t *this, message_t *response, - transaction_t **next) -{ - iterator_t *payloads; - payload_t *payload; - host_t *me, *other; - identification_t *other_id, *my_id; - ts_payload_t *tsi_payload = NULL; - ts_payload_t *tsr_payload = NULL; - id_payload_t *idr_payload = NULL; - cert_payload_t *cert_payload = NULL; - auth_payload_t *auth_payload = NULL; - sa_payload_t *sa_payload = NULL; - eap_payload_t *eap_payload = NULL; - status_t status; - - /* check message type */ - if (response->get_exchange_type(response) != IKE_AUTH) - { - SIG(IKE_UP_FAILED, "IKE_AUTH response of invalid type, deleting IKE_SA"); - SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA"); - return DESTROY_ME; - } - - me = this->ike_sa->get_my_host(this->ike_sa); - other = this->ike_sa->get_other_host(this->ike_sa); - my_id = this->ike_sa->get_my_id(this->ike_sa); - other_id = this->ike_sa->get_other_id(this->ike_sa); - - /* Iterate over all payloads to collect them */ - payloads = response->get_payload_iterator(response); - while (payloads->iterate(payloads, (void**)&payload)) - { - switch (payload->get_type(payload)) - { - case ID_RESPONDER: - idr_payload = (id_payload_t*)payload; - break; - case AUTHENTICATION: - auth_payload = (auth_payload_t*)payload; - break; - case CERTIFICATE: - cert_payload = (cert_payload_t*)payload; - break; - case SECURITY_ASSOCIATION: - sa_payload = (sa_payload_t*)payload; - break; - case TRAFFIC_SELECTOR_INITIATOR: - tsi_payload = (ts_payload_t*)payload; - break; - case TRAFFIC_SELECTOR_RESPONDER: - tsr_payload = (ts_payload_t*)payload; - break; - case EXTENSIBLE_AUTHENTICATION: - eap_payload = (eap_payload_t*)payload; - break; - case NOTIFY: - { - status = process_notifies(this, (notify_payload_t*)payload); - if (status == FAILED) - { - payloads->destroy(payloads); - /* we return SUCCESS, as transaction completet */ - return SUCCESS; - } - if (status == DESTROY_ME) - { - SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA"); - payloads->destroy(payloads); - return status; - } - break; - } - default: - { - DBG1(DBG_IKE, "ignoring payload %N", - payload_type_names, payload->get_type(payload)); - break; - } - } - } - payloads->destroy(payloads); - - if (idr_payload) - { /* process idr payload */ - identification_t *configured_other_id; - int wildcards; - - other_id = idr_payload->get_identification(idr_payload); - configured_other_id = this->policy->get_other_id(this->policy); - - if (!other_id->matches(other_id, configured_other_id, &wildcards)) - { - other_id->destroy(other_id); - SIG(IKE_UP_FAILED, "other peer uses unacceptable ID (%D, excepted " - "%D), deleting IKE_SA", other_id, configured_other_id); - SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA"); - return DESTROY_ME; - } - /* update other ID. It was already set, but may contain wildcards */ - this->ike_sa->set_other_id(this->ike_sa, other_id); - } - - if (cert_payload) - { /* process cert payload */ - import_certificate(cert_payload); - } - - if (auth_payload && idr_payload) - { /* authenticate peer */ - authenticator_t *authenticator; - auth_method_t auth_method; - status_t status; - - my_id = this->policy->get_my_id(this->policy); - auth_method = auth_payload->get_auth_method(auth_payload); - authenticator = authenticator_create(this->ike_sa, auth_method); - if (authenticator == NULL) - { - SIG(IKE_UP_FAILED, "auth method %N not supported, deleting IKE_SA", - auth_method_names, auth_method); - SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA"); - return DESTROY_ME; - } - status = authenticator->verify(authenticator, this->init_response, - this->nonce_i, auth_payload); - authenticator->destroy(authenticator); - if (status != SUCCESS) - { - SIG(IKE_UP_FAILED, "authentication of '%D' with %N failed, " - "deleting IKE_SA", other_id, auth_method_names, auth_method); - SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA"); - return DESTROY_ME; - } - this->peer_authenticated = TRUE; - } - - if (eap_payload && this->eap_auth) - { - switch (this->eap_auth->process(this->eap_auth, eap_payload, &this->eap_next)) - { - case SUCCESS: - { - /* EAP message was EAP_SUCCESS, send AUTH in next transaction */ - DBG2(DBG_IKE, "EAP authentication exchanges completed successful"); - this->eap_next = NULL; - /* fall through */ - } - case NEED_MORE: - { - /* EAP message was a EAP_REQUEST, handle it in next transaction */ - private_ike_auth_t *next_auth = clone_for_eap(this); - next_auth->message_id = this->message_id + 1; - *next = (transaction_t*)next_auth; - return SUCCESS; - } - case FAILED: - default: - { - /* EAP message was EAP_FAILURE */ - SIG(IKE_UP_FAILED, "EAP authentication failed, deleting IKE_SA"); - SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA"); - return DESTROY_ME; - } - } - } - - if (!(auth_payload && sa_payload && tsi_payload && tsr_payload)) - { - SIG(IKE_UP_FAILED, "response message incomplete, deleting IKE_SA"); - SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA"); - return DESTROY_ME; - } - - /* if we do EAP authentication and a AUTH payload comes in, verify it */ - if (this->eap_auth && - (this->eap_auth->is_mutual(this->eap_auth) || this->peer_authenticated)) - { - authenticator_t *authenticator = (authenticator_t*)this->eap_auth; - - if (authenticator->verify(authenticator, this->init_response, - this->nonce_i, auth_payload) != SUCCESS) - { - SIG(IKE_UP_FAILED, "authentication with EAP failed, deleting IKE_SA"); - SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA"); - return DESTROY_ME; - } - this->peer_authenticated = TRUE; - } - - if (!this->peer_authenticated) - { - SIG(IKE_UP_FAILED, "server didn't send authentication data, deleting IKE_SA"); - SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA"); - return DESTROY_ME; - } - - SIG(IKE_UP_SUCCESS, "IKE_SA '%s' established between %H[%D]...%H[%D]", - this->ike_sa->get_name(this->ike_sa), me, my_id, other, other_id); - this->ike_sa->set_state(this->ike_sa, IKE_ESTABLISHED); - - { /* process traffic selectors for us */ - linked_list_t *ts_received = tsi_payload->get_traffic_selectors(tsi_payload); - this->tsi = this->policy->select_my_traffic_selectors(this->policy, ts_received, me); - ts_received->destroy_offset(ts_received, offsetof(traffic_selector_t, destroy)); - } - - { /* process traffic selectors for other */ - linked_list_t *ts_received = tsr_payload->get_traffic_selectors(tsr_payload); - this->tsr = this->policy->select_other_traffic_selectors(this->policy, ts_received, other); - ts_received->destroy_offset(ts_received, offsetof(traffic_selector_t, destroy)); - } - - { /* process sa payload */ - linked_list_t *proposal_list; - - proposal_list = sa_payload->get_proposals(sa_payload); - /* we have to re-check here if other's selection is valid */ - this->proposal = this->policy->select_proposal(this->policy, proposal_list); - proposal_list->destroy_offset(proposal_list, offsetof(proposal_t, destroy)); - - /* everything fine to create CHILD? */ - if (this->proposal == NULL || - this->tsi->get_count(this->tsi) == 0 || - this->tsr->get_count(this->tsr) == 0 || - !this->build_child) - { - SIG(CHILD_UP_FAILED, "CHILD_SA negotiation failed, no CHILD_SA built"); - } - else - { - /* check mode if it is acceptable */ - switch (this->mode) - { - case MODE_TUNNEL: - /* is the default */ - break; - case MODE_TRANSPORT: - /* TODO: we should close the CHILD_SA if negotiated - * mode is not acceptable for us */ - if (!ts_list_is_host(this->tsi, me) || - !ts_list_is_host(this->tsr, other)) - { - this->mode = MODE_TUNNEL; - DBG1(DBG_IKE, "not using tranport mode, not host-to-host"); - } - else if (this->ike_sa->is_natt_enabled(this->ike_sa)) - { - this->mode = MODE_TUNNEL; - DBG1(DBG_IKE, "not using tranport mode, as connection NATed"); - } - break; - case MODE_BEET: - if (!ts_list_is_host(this->tsi, NULL) || - !ts_list_is_host(this->tsr, NULL)) - { - this->mode = MODE_TUNNEL; - DBG1(DBG_IKE, "not using BEET mode, not host-to-host"); - } - break; - } - - if (install_child_sa(this, TRUE) != SUCCESS) - { - SIG(CHILD_UP_FAILED, "installing CHILD_SA '%s' failed, no CHILD_SA built", - this->policy->get_name(this->policy)); - /* TODO: we should send a DELETE for that CHILD to stay - * synchronous with the peer */ - } - else - { - SIG(CHILD_UP_SUCCESS, "CHILD_SA '%s' created", - this->policy->get_name(this->policy)); - } - } - } - return SUCCESS; -} - -/** - * implements transaction_t.destroy - */ -static void destroy(private_ike_auth_t *this) -{ - DESTROY_IF(this->message); - DESTROY_IF(this->proposal); - DESTROY_IF(this->child_sa); - DESTROY_IF(this->policy); - DESTROY_IF(this->connection); - DESTROY_IF(this->cacerts); - if (this->eap_auth) - { - this->eap_auth->authenticator_interface.destroy(&this->eap_auth->authenticator_interface); - } - DESTROY_IF(this->eap_next); - if (this->tsi) - { - this->tsi->destroy_offset(this->tsi, offsetof(traffic_selector_t, destroy)); - } - if (this->tsr) - { - this->tsr->destroy_offset(this->tsr, offsetof(traffic_selector_t, destroy)); - } - chunk_free(&this->nonce_i); - chunk_free(&this->nonce_r); - chunk_free(&this->init_request); - chunk_free(&this->init_response); - free(this); -} - -/* - * Described in header. - */ -ike_auth_t *ike_auth_create(ike_sa_t *ike_sa) -{ - private_ike_auth_t *this = malloc_thing(private_ike_auth_t); - - /* transaction interface functions */ - this->public.transaction.get_request = (status_t(*)(transaction_t*,message_t**))get_request; - this->public.transaction.get_response = (status_t(*)(transaction_t*,message_t*,message_t**,transaction_t**))get_response; - this->public.transaction.conclude = (status_t(*)(transaction_t*,message_t*,transaction_t**))conclude; - this->public.transaction.get_message_id = (u_int32_t(*)(transaction_t*))get_message_id; - this->public.transaction.requested = (u_int32_t(*)(transaction_t*))requested; - this->public.transaction.destroy = (void(*)(transaction_t*))destroy; - - /* public functions */ - this->public.set_config = (void(*)(ike_auth_t*,connection_t*,policy_t*))set_config; - this->public.set_reqid = (void(*)(ike_auth_t*,u_int32_t))set_reqid; - this->public.set_nonces = (void(*)(ike_auth_t*,chunk_t,chunk_t))set_nonces; - this->public.set_init_messages = (void(*)(ike_auth_t*,chunk_t,chunk_t))set_init_messages; - - /* private data */ - this->ike_sa = ike_sa; - this->message_id = 0; - this->message = NULL; - this->requested = 0; - this->nonce_i = chunk_empty; - this->nonce_r = chunk_empty; - this->init_request = chunk_empty; - this->init_response = chunk_empty; - this->child_sa = NULL; - this->proposal = NULL; - this->policy = NULL; - this->connection = NULL; - this->tsi = NULL; - this->tsr = NULL; - this->build_child = TRUE; - this->eap_auth = NULL; - this->eap_next = NULL; - this->eap_only = FALSE; - this->peer_authenticated = FALSE; - this->reqid = 0; - this->cacerts = linked_list_create(); - this->mode = MODE_TUNNEL; - - return &this->public; -} diff --git a/src/charon/sa/transactions/ike_auth.h b/src/charon/sa/transactions/ike_auth.h deleted file mode 100644 index 490359eda..000000000 --- a/src/charon/sa/transactions/ike_auth.h +++ /dev/null @@ -1,110 +0,0 @@ -/** - * @file ike_auth.h - * - * @brief Interface of transaction ike_auth. - * - */ - -/* - * 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 IKE_AUTH_H_ -#define IKE_AUTH_H_ - -typedef struct ike_auth_t ike_auth_t; - -#include <sa/ike_sa.h> -#include <sa/transactions/transaction.h> - -/** - * @brief A transaction for the second message exchange to authenticate an IKE_SA. - * - * The second transaction is encrypted and authenticates the peers. It also - * sets up a first CHILD_SA. - * - * @b Constructors: - * - ike_auth_create() - * - transaction_create() with the appropriate message - * - * @ingroup transactions - */ -struct ike_auth_t { - - /** - * The transaction_t interface. - */ - transaction_t transaction; - - /** - * @brief Set the config used for the ike_auth exchange. - * - * The connection definition is used to complete IKE_SA setup, the - * policy defines the CHILD_SA which is created along with the ike_auth - * exchange. - * - * @param this calling object - * @param connection connection definition - * @param policy policy definition - */ - void (*set_config) (ike_auth_t* this, - connection_t *connection, policy_t *policy); - - /** - * @brief Set the reqid used for CHILD_SA setup. - * - * The first two message exchanges may set up an associated - * CHILD_SA. If we acquire, we must use the same reqid as the - * installed policy. - * - * @param this calling object - * @param reqid reqid to use for the CHILD_SA - */ - void (*set_reqid) (ike_auth_t* this, u_int32_t reqid); - - /** - * @brief Set the nonces used in the previous ike_sa_init transaction. - * - * The nonces are used to create the authentication data. - * - * @param this calling object - * @param nonce_i initiator chosen nonce - * @param nonce_r responder chosen nonce - */ - void (*set_nonces) (ike_auth_t* this, chunk_t nonce_i, chunk_t nonce_r); - - /** - * @brief Set the messages used in the previous ike_sa_init transaction. - * - * The messages are used to create the authentication data. - * - * @param this calling object - * @param request encoded request message as a chunk - * @param response encoded response message as a chunk - */ - void (*set_init_messages) (ike_auth_t* this, chunk_t request, chunk_t response); -}; - -/** - * @brief Create a new transaction which processes IKE_AUTH exchanges. - * - * @param ike_sa assigned IKE_SA - * @return created ike_auth transaction - * - * @ingroup transactions - */ -ike_auth_t *ike_auth_create(ike_sa_t *ike_sa); - -#endif /* IKE_AUTH_H_ */ diff --git a/src/charon/sa/transactions/ike_sa_init.c b/src/charon/sa/transactions/ike_sa_init.c deleted file mode 100644 index b5728a986..000000000 --- a/src/charon/sa/transactions/ike_sa_init.c +++ /dev/null @@ -1,1121 +0,0 @@ -/** - * @file ike_sa_init.c - * - * @brief Implementation of ike_sa_init_t transaction. - * - */ - -/* - * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger - * 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 "ike_sa_init.h" - -#include <string.h> - -#include <daemon.h> -#include <crypto/diffie_hellman.h> -#include <crypto/hashers/hasher.h> -#include <encoding/payloads/sa_payload.h> -#include <encoding/payloads/ke_payload.h> -#include <encoding/payloads/nonce_payload.h> -#include <sa/transactions/ike_auth.h> -#include <queues/jobs/delete_ike_sa_job.h> -#include <queues/jobs/rekey_ike_sa_job.h> - - -typedef struct private_ike_sa_init_t private_ike_sa_init_t; - -/** - * Private members of a ike_sa_init_t object.. - */ -struct private_ike_sa_init_t { - - /** - * Public methods and transaction_t interface. - */ - ike_sa_init_t public; - - /** - * Assigned IKE_SA. - */ - ike_sa_t *ike_sa; - - /** - * Message sent by our peer, if already generated - */ - message_t *message; - - /** - * Message ID this transaction uses - */ - u_int32_t message_id; - - /** - * Times we did send the request - */ - u_int32_t requested; - - /** - * Next transaction followed to this one. May be IKE_AUTH, - * or a IKE_SA_INIT retry - */ - transaction_t **next; - - /** - * Diffie hellman object used to generate public DH value. - */ - diffie_hellman_t *diffie_hellman; - - /** - * initiator chosen nonce - */ - chunk_t nonce_i; - - /** - * responder chosen nonce - */ - chunk_t nonce_r; - - /** - * connection definition used for initiation - */ - connection_t *connection; - - /** - * policy definition forwarded to ike_auth transaction - */ - policy_t *policy; - - /** - * Negotiated proposal used for IKE_SA - */ - proposal_t *proposal; - - /** - * Reqid to pass to IKE_AUTH, used for created CHILD_SA - */ - u_int32_t reqid; - - /** - * Unique ID for to enumerate all IKE_SAs in its name - */ - u_int32_t unique_id; - - /** - * Randomizer to generate nonces - */ - randomizer_t *randomizer; - - /** - * Hasher used to build NAT detection hashes - */ - hasher_t *nat_hasher; - - /** - * Precomputed NAT hash for source address - */ - chunk_t natd_src_hash; - - /** - * Precomputed NAT hash for destination address - */ - chunk_t natd_dst_hash; - - /** - * Did we process any NAT detection notifys for a source address? - */ - bool natd_src_seen; - - /** - * Did we process any NAT detection notifys for a destination address? - */ - bool natd_dst_seen; - - /** - * Have we found a matching source address NAT hash? - */ - bool natd_src_matched; - - /** - * Have we found a matching destination address NAT hash? - */ - bool natd_dst_matched; -}; - -/** - * Implementation of ike_sa_init_t.use_dh_group. - */ -static bool use_dh_group(private_ike_sa_init_t *this, diffie_hellman_group_t dh_group) -{ - if (this->connection->check_dh_group(this->connection, dh_group)) - { - this->diffie_hellman = diffie_hellman_create(dh_group); - if (this->diffie_hellman) - { - return TRUE; - } - } - return FALSE; -} - -/** - * Implementation of ike_sa_init_t.set_config. - */ -static void set_config(private_ike_sa_init_t *this, - connection_t *connection, policy_t *policy) -{ - this->connection = connection; - this->policy = policy; -} - -/** - * Implementation of ike_sa_init_t.set_reqid. - */ -static void set_reqid(private_ike_sa_init_t *this, u_int32_t reqid) -{ - this->reqid = reqid; -} - -/** - * Implementation of transaction_t.get_message_id. - */ -static u_int32_t get_message_id(private_ike_sa_init_t *this) -{ - return this->message_id; -} - -/** - * Implementation of transaction_t.requested. - */ -static u_int32_t requested(private_ike_sa_init_t *this) -{ - return this->requested++; -} - -/** - * Build NAT detection hash for a host - */ -static chunk_t generate_natd_hash(private_ike_sa_init_t *this, - ike_sa_id_t * ike_sa_id, host_t *host) -{ - chunk_t natd_chunk, spi_i_chunk, spi_r_chunk, addr_chunk, port_chunk; - chunk_t natd_hash; - u_int64_t spi_i, spi_r; - u_int16_t port; - - /* prepare all requred chunks */ - spi_i = ike_sa_id->get_initiator_spi(ike_sa_id); - spi_r = ike_sa_id->get_responder_spi(ike_sa_id); - spi_i_chunk.ptr = (void*)&spi_i; - spi_i_chunk.len = sizeof(spi_i); - spi_r_chunk.ptr = (void*)&spi_r; - spi_r_chunk.len = sizeof(spi_r); - port = htons(host->get_port(host)); - port_chunk.ptr = (void*)&port; - port_chunk.len = sizeof(port); - addr_chunk = host->get_address(host); - - /* natd_hash = SHA1( spi_i | spi_r | address | port ) */ - natd_chunk = chunk_cat("cccc", spi_i_chunk, spi_r_chunk, addr_chunk, port_chunk); - this->nat_hasher->allocate_hash(this->nat_hasher, natd_chunk, &natd_hash); - DBG3(DBG_IKE, "natd_chunk %B", &natd_chunk); - DBG3(DBG_IKE, "natd_hash %B", &natd_hash); - - chunk_free(&natd_chunk); - return natd_hash; -} - -/** - * Build a NAT detection notify payload. - */ -static notify_payload_t *build_natd_payload(private_ike_sa_init_t *this, - notify_type_t type, host_t *host) -{ - chunk_t hash; - notify_payload_t *notify; - ike_sa_id_t *ike_sa_id; - - ike_sa_id = this->ike_sa->get_id(this->ike_sa); - notify = notify_payload_create(); - notify->set_notify_type(notify, type); - hash = generate_natd_hash(this, ike_sa_id, host); - notify->set_notification_data(notify, hash); - chunk_free(&hash); - - return notify; -} - -/** - * Implementation of transaction_t.get_request. - */ -static status_t get_request(private_ike_sa_init_t *this, message_t **result) -{ - message_t *request; - host_t *me, *other; - identification_t *my_id, *other_id; - char name[64]; - - /* check if we already have built a message (retransmission) */ - if (this->message) - { - *result = this->message; - return SUCCESS; - } - - me = this->ike_sa->get_my_host(this->ike_sa); - other = this->ike_sa->get_other_host(this->ike_sa); - - /* we already set up the IDs. Mine is already fully qualified, other - * will be updated in the ike_auth transaction */ - my_id = this->policy->get_my_id(this->policy); - other_id = this->policy->get_other_id(this->policy); - this->ike_sa->set_my_id(this->ike_sa, my_id->clone(my_id)); - this->ike_sa->set_other_id(this->ike_sa, other_id->clone(other_id)); - if (snprintf(name, sizeof(name), "%s{%d}", - this->connection->get_name(this->connection), - this->unique_id) > 0) - { - this->ike_sa->set_name(this->ike_sa, name); - } - - /* setting up a IKE_SA implicitly requires setup of a CHILD_SA */ - SIG(IKE_UP_START, "initiating IKE_SA '%s' between %H[%D]...%H[%D]", - this->connection->get_name(this->connection), me, my_id, other, other_id); - SIG(CHILD_UP_START, "establishing CHILD_SA '%s' along with IKE_SA", - this->policy->get_name(this->policy)); - - /* build the request */ - request = message_create(); - request->set_source(request, me->clone(me)); - request->set_destination(request, other->clone(other)); - request->set_exchange_type(request, IKE_SA_INIT); - request->set_request(request, TRUE); - request->set_message_id(request, this->message_id); - request->set_ike_sa_id(request, this->ike_sa->get_id(this->ike_sa)); - /* apply for caller */ - *result = request; - /* store for retransmission */ - this->message = request; - - /* if the DH group is set via use_dh_group(), we already have a DH object */ - if (!this->diffie_hellman) - { - diffie_hellman_group_t dh_group; - - dh_group = this->connection->get_dh_group(this->connection); - this->diffie_hellman = diffie_hellman_create(dh_group); - if (this->diffie_hellman == NULL) - { - SIG(IKE_UP_FAILED, "DH group %N not supported, aborting", - diffie_hellman_group_names, dh_group); - SIG(CHILD_UP_FAILED, - "initiating CHILD_SA failed, unable to create IKE_SA"); - return DESTROY_ME; - } - } - - { /* build the SA payload from proposals */ - sa_payload_t *sa_payload; - linked_list_t *proposal_list; - - proposal_list = this->connection->get_proposals(this->connection); - sa_payload = sa_payload_create_from_proposal_list(proposal_list); - proposal_list->destroy_offset(proposal_list, offsetof(proposal_t, destroy)); - - request->add_payload(request, (payload_t*)sa_payload); - } - - { /* build the KE payload from the DH object */ - ke_payload_t *ke_payload; - - ke_payload = ke_payload_create_from_diffie_hellman(this->diffie_hellman); - - request->add_payload(request, (payload_t*)ke_payload); - } - - { /* build the NONCE payload for us (initiator) */ - nonce_payload_t *nonce_payload; - - if (this->randomizer->allocate_pseudo_random_bytes(this->randomizer, - NONCE_SIZE, &this->nonce_i) != SUCCESS) - { - SIG(IKE_UP_FAILED, "could not generate nonce, aborting"); - SIG(CHILD_UP_FAILED, - "initiating CHILD_SA failed, unable to create IKE_SA"); - return DESTROY_ME; - } - nonce_payload = nonce_payload_create(); - nonce_payload->set_nonce(nonce_payload, this->nonce_i); - - request->add_payload(request, (payload_t*)nonce_payload); - } - - { /* build NAT_DETECTION notifys */ - notify_payload_t *notify; - linked_list_t *list; - host_t *host; - - /* N(NAT_DETECTION_SOURCE_IP)+ - * we include only one notify if our address is defined, but all - * possible if not */ - host = this->ike_sa->get_my_host(this->ike_sa); - if (host->is_anyaddr(host)) - { - /* TODO: we could get the src address from netlink */ - list = charon->socket->create_local_address_list(charon->socket); - while (list->remove_first(list, (void**)&host) == SUCCESS) - { - notify = build_natd_payload(this, NAT_DETECTION_SOURCE_IP, host); - host->destroy(host); - request->add_payload(request, (payload_t*)notify); - } - list->destroy(list); - } - else - { - notify = build_natd_payload(this, NAT_DETECTION_SOURCE_IP, host); - request->add_payload(request, (payload_t*)notify); - } - - /* N(NAT_DETECTION_DESTINATION_IP) */ - notify = build_natd_payload(this, NAT_DETECTION_DESTINATION_IP, other); - request->add_payload(request, (payload_t*)notify); - } - - this->ike_sa->set_state(this->ike_sa, IKE_CONNECTING); - return SUCCESS; -} - -/** - * Handle all kind of notifys - */ -static status_t process_notifys(private_ike_sa_init_t *this, notify_payload_t *notify_payload) -{ - chunk_t notification_data; - notify_type_t notify_type = notify_payload->get_notify_type(notify_payload); - - DBG2(DBG_IKE, "process notify type %N", notify_type_names, notify_type); - - switch (notify_type) - { - case NO_PROPOSAL_CHOSEN: - { - SIG(IKE_UP_FAILED, - "received a NO_PROPOSAL_CHOSEN notify, deleting IKE_SA"); - return DESTROY_ME; - } - case INVALID_MAJOR_VERSION: - { - SIG(IKE_UP_FAILED, - "received a INVALID_MAJOR_VERSION notify, deleting IKE_SA"); - return DESTROY_ME; - } - case INVALID_KE_PAYLOAD: - { - chunk_t notify_data; - diffie_hellman_group_t dh_group, old_dh_group; - ike_sa_init_t *retry; - - old_dh_group = this->connection->get_dh_group(this->connection); - notify_data = notify_payload->get_notification_data(notify_payload); - dh_group = ntohs(*((u_int16_t*)notify_data.ptr)); - - DBG1(DBG_IKE, "peer didn't accept DH group %N, it requested %N", - diffie_hellman_group_names, old_dh_group, - diffie_hellman_group_names, dh_group); - if (!this->connection->check_dh_group(this->connection, dh_group)) - { - SIG(IKE_UP_FAILED, "DH group %N not acceptable, aborting", - diffie_hellman_group_names, dh_group); - return DESTROY_ME; - } - retry = ike_sa_init_create(this->ike_sa); - retry->set_config(retry, this->connection, this->policy); - this->connection = NULL; - this->policy = NULL; - retry->use_dh_group(retry, dh_group); - *this->next = (transaction_t*)retry; - return FAILED; - } - case NAT_DETECTION_DESTINATION_IP: - { - this->natd_dst_seen = TRUE; - if (this->natd_dst_matched) - { - return SUCCESS; - } - notification_data = notify_payload->get_notification_data(notify_payload); - if (chunk_equals(notification_data, this->natd_dst_hash)) - { - this->natd_dst_matched = TRUE; - DBG2(DBG_IKE, "NAT-D dst hash match"); - } - else - { - DBG2(DBG_IKE, "NAT-D dst hash mismatch"); - } - return SUCCESS; - } - case NAT_DETECTION_SOURCE_IP: - { - this->natd_src_seen = TRUE;; - if (this->natd_src_matched) - { - return SUCCESS; - } - notification_data = notify_payload->get_notification_data(notify_payload); - if (chunk_equals(notification_data, this->natd_src_hash)) - { - this->natd_src_matched = TRUE; - DBG2(DBG_IKE, "NAT-D src hash match"); - } - else - { - DBG2(DBG_IKE, "NAT-D src hash mismatch"); - } - return SUCCESS; - } - default: - { - if (notify_type < 16383) - { - SIG(IKE_UP_FAILED, "received %N notify error, deleting IKE_SA", - notify_type_names, notify_type); - return DESTROY_ME; - } - else - { - DBG1(DBG_IKE, "received %N notify, ignored", - notify_type_names, notify_type); - return SUCCESS; - } - } - } -} - -/** - * Implementation of transaction_t.get_response. - */ -static status_t get_response(private_ike_sa_init_t *this, - message_t *request, message_t **result, - transaction_t **next) -{ - host_t *me, *other; - message_t *response; - status_t status; - iterator_t *payloads; - payload_t *payload; - sa_payload_t *sa_request = NULL; - ke_payload_t *ke_request = NULL; - nonce_payload_t *nonce_request = NULL; - ike_sa_id_t *ike_sa_id; - u_int32_t timeout; - char name[64]; - - /* check if we already have built a response (retransmission) */ - if (this->message) - { - *result = this->message; - return SUCCESS; - } - - me = request->get_destination(request); - other = request->get_source(request); - this->message_id = request->get_message_id(request); - - SIG(IKE_UP_START, "establishing IKE_SA between %H...%H", me, other); - - /* set up response */ - response = message_create(); - response->set_source(response, me->clone(me)); - response->set_destination(response, other->clone(other)); - response->set_exchange_type(response, IKE_SA_INIT); - response->set_request(response, FALSE); - response->set_message_id(response, this->message_id); - response->set_ike_sa_id(response, this->ike_sa->get_id(this->ike_sa)); - this->message = response; - *result = response; - - /* check message type */ - if (request->get_exchange_type(request) != IKE_SA_INIT) - { - SIG(IKE_UP_FAILED, "IKE_SA_INIT request of invalid type, deleting IKE_SA"); - return DESTROY_ME; - } - - /* this is the first message to process, find a connection for IKE_SA */ - this->connection = charon->connections->get_connection_by_hosts( - charon->connections, me, other); - if (this->connection == NULL) - { - notify_payload_t *notify = notify_payload_create(); - notify->set_notify_type(notify, NO_PROPOSAL_CHOSEN); - response->add_payload(response, (payload_t*)notify); - - SIG(IKE_UP_FAILED, "no connection for hosts %H...%H found, " - "deleting IKE_SA", me, other); - return DESTROY_ME; - } - - if (snprintf(name, sizeof(name), "%s{%d}", - this->connection->get_name(this->connection), - this->unique_id) > 0) - { - this->ike_sa->set_name(this->ike_sa, name); - } - this->ike_sa->apply_connection(this->ike_sa, this->connection); - - /* Precompute NAT-D hashes for incoming NAT notify comparison */ - ike_sa_id = request->get_ike_sa_id(request); - this->natd_dst_hash = generate_natd_hash(this, ike_sa_id, me); - this->natd_src_hash = generate_natd_hash(this, ike_sa_id, other); - - /* Iterate over all payloads. */ - payloads = request->get_payload_iterator(request); - while (payloads->iterate(payloads, (void**)&payload)) - { - switch (payload->get_type(payload)) - { - case SECURITY_ASSOCIATION: - sa_request = (sa_payload_t*)payload; - break; - case KEY_EXCHANGE: - ke_request = (ke_payload_t*)payload; - break; - case NONCE: - nonce_request = (nonce_payload_t*)payload; - break; - case NOTIFY: - { - status = process_notifys(this, (notify_payload_t*)payload); - if (status == FAILED) - { - payloads->destroy(payloads); - /* we return SUCCESS, returned FAILED means do next transaction */ - return SUCCESS; - } - if (status == DESTROY_ME) - { - payloads->destroy(payloads); - return DESTROY_ME; - } - break; - } - default: - { - DBG2(DBG_IKE, "ignoring %N payload", - payload_type_names, payload->get_type(payload)); - break; - } - } - } - payloads->destroy(payloads); - - /* check if we have all payloads */ - if (!(sa_request && ke_request && nonce_request)) - { - notify_payload_t *notify = notify_payload_create(); - notify->set_notify_type(notify, INVALID_SYNTAX); - response->add_payload(response, (payload_t*)notify); - SIG(IKE_UP_FAILED, "received request message incomplete, deleting IKE_SA"); - return DESTROY_ME; - } - - { /* process SA payload: - * ------------------- - * - extract proposals - * - select our most preferred proposal found in extracted - * - if no matches, return NO_PROPOSAL_CHOSEN - * - add sa payload with selected proposal - */ - sa_payload_t* sa_response; - linked_list_t *proposal_list; - - proposal_list = sa_request->get_proposals(sa_request); - this->proposal = this->connection->select_proposal(this->connection, proposal_list); - proposal_list->destroy_offset(proposal_list, offsetof(proposal_t, destroy)); - if (this->proposal == NULL) - { - notify_payload_t *notify = notify_payload_create(); - notify->set_notify_type(notify, NO_PROPOSAL_CHOSEN); - response->add_payload(response, (payload_t*)notify); - SIG(IKE_UP_FAILED, "request did not contain any acceptable " - "proposals, deleting IKE_SA"); - return DESTROY_ME; - } - sa_response = sa_payload_create_from_proposal(this->proposal); - response->add_payload(response, (payload_t *)sa_response); - } - - { /* process KE payload: - * -------------------- - * - check if used group match the selected proposal - * - if not, stop with INVALID_KE_PAYLOAD - * - apply others public value to complete diffie hellman exchange - * - add our public value to response - */ - diffie_hellman_group_t used_group; - ke_payload_t *ke_response; - - used_group = ke_request->get_dh_group_number(ke_request); - - if (!this->connection->check_dh_group(this->connection, used_group) || - (this->diffie_hellman = diffie_hellman_create(used_group)) == NULL) - { - u_int16_t notify_group; - chunk_t notify_chunk; - notify_payload_t *notify; - iterator_t *iterator; - payload_t *payload; - - notify_group = this->connection->get_dh_group(this->connection); - SIG(IKE_UP_FAILED, "request used inacceptable DH group %N, sending " - "INVALID_KE_PAYLOAD with %N, deleting IKE_SA", - diffie_hellman_group_names, used_group, - diffie_hellman_group_names, notify_group); - - /* remove already added payloads */ - iterator = response->get_payload_iterator(response); - while (iterator->iterate(iterator, (void**)&payload)) - { - iterator->remove(iterator); - payload->destroy(payload); - } - iterator->destroy(iterator); - - notify_group = htons(notify_group); - notify_chunk.ptr = (u_int8_t*)¬ify_group; - notify_chunk.len = sizeof(notify_group); - notify = notify_payload_create(); - notify->set_notify_type(notify, INVALID_KE_PAYLOAD); - notify->set_notification_data(notify, notify_chunk); - response->add_payload(response, (payload_t*)notify); - return DESTROY_ME; - } - this->diffie_hellman->set_other_public_value(this->diffie_hellman, - ke_request->get_key_exchange_data(ke_request)); - - /* build response */ - ke_response = ke_payload_create_from_diffie_hellman(this->diffie_hellman); - response->add_payload(response, (payload_t*)ke_response); - } - - { /* process nonce payload: - * ---------------------- - * - get nonce from payload - * - generate own nonce and add to reply - */ - nonce_payload_t *nonce_response; - - this->nonce_i = nonce_request->get_nonce(nonce_request); - - /* build response nonce */ - if (this->randomizer->allocate_pseudo_random_bytes(this->randomizer, - NONCE_SIZE, &this->nonce_r) != SUCCESS) - { - notify_payload_t *notify = notify_payload_create(); - notify->set_notify_type(notify, NO_PROPOSAL_CHOSEN); - response->add_payload(response, (payload_t*)notify); - SIG(IKE_UP_FAILED, "could not create nonce, deleting IKE_SA"); - return DESTROY_ME; - } - nonce_response = nonce_payload_create(); - nonce_response->set_nonce(nonce_response, this->nonce_r); - response->add_payload(response, (payload_t *)nonce_response); - } - - { /* processs NATT stuff: - * -------------------- - * - check if we or other is behind NAT - * - enable NATT if so - * - build NAT detection notifys for reply - */ - notify_payload_t *notify; - - if ((!this->natd_src_seen && this->natd_dst_seen) || - (this->natd_src_seen && !this->natd_dst_seen)) - { - notify = notify_payload_create(); - notify->set_notify_type(notify, INVALID_SYNTAX); - response->add_payload(response, (payload_t*)notify); - SIG(IKE_UP_FAILED, "request contained invalid number of NAT-D" - "payloads, deleting IKE_SA"); - return DESTROY_ME; - } - if (this->natd_dst_seen && !this->natd_dst_matched) - { - this->ike_sa->enable_natt(this->ike_sa, TRUE); - } - if (this->natd_src_seen && !this->natd_src_matched) - { - this->ike_sa->enable_natt(this->ike_sa, FALSE); - } - /* build response NAT DETECTION notifys, if remote supports it */ - if (this->natd_src_seen || this->natd_dst_seen) - { - /* N(NAT_DETECTION_SOURCE_IP) */ - notify = build_natd_payload(this, NAT_DETECTION_SOURCE_IP, me); - response->add_payload(response, (payload_t*)notify); - - /* N(NAT_DETECTION_DESTINATION_IP) */ - notify = build_natd_payload(this, NAT_DETECTION_DESTINATION_IP, other); - response->add_payload(response, (payload_t*)notify); - } - } - - /* derive all the keys used in the IKE_SA */ - if (this->ike_sa->derive_keys(this->ike_sa, this->proposal, - this->diffie_hellman, - this->nonce_i, this->nonce_r, - FALSE, NULL, NULL) != SUCCESS) - { - notify_payload_t *notify = notify_payload_create(); - notify->set_notify_type(notify, NO_PROPOSAL_CHOSEN); - response->add_payload(response, (payload_t*)notify); - SIG(IKE_UP_FAILED, "error creating transforms from proposal, deleting IKE_SA"); - return DESTROY_ME; - } - - this->ike_sa->set_lifetimes(this->ike_sa, - this->connection->get_reauth(this->connection), - this->connection->get_soft_lifetime(this->connection), - this->connection->get_hard_lifetime(this->connection)); - - { /* create ike_auth transaction, which will store informations for us */ - packet_t *response_packet; - chunk_t request_chunk, response_chunk; - ike_auth_t *ike_auth; - - /* we normally do not generate the message. But we need the generated message - * for authentication in the next state, so we do it here. This is not problematic, - * as we don't use a crypter/signer in ike_sa_init... */ - if (response->generate(response, NULL, NULL, &response_packet) != SUCCESS) - { - SIG(IKE_UP_FAILED, "error in response generation, deleting IKE_SA"); - return DESTROY_ME; - } - response_packet->destroy(response_packet); - request_chunk = request->get_packet_data(request); - response_chunk = response->get_packet_data(response); - - /* create next transaction, for which we except a message */ - ike_auth = ike_auth_create(this->ike_sa); - ike_auth->set_config(ike_auth, this->connection, this->policy); - ike_auth->set_reqid(ike_auth, this->reqid); - this->connection = NULL; - this->policy = NULL; - ike_auth->set_nonces(ike_auth, - chunk_clone(this->nonce_i), - chunk_clone(this->nonce_r)); - ike_auth->set_init_messages(ike_auth, request_chunk, response_chunk); - *next = (transaction_t*)ike_auth; - } - - /* everything went fine. Now we set a timeout to destroy half initiated IKE_SAs */ - timeout = charon->configuration->get_half_open_ike_sa_timeout(charon->configuration); - if (timeout) - { - job_t *job = (job_t*)delete_ike_sa_job_create( - this->ike_sa->get_id(this->ike_sa), FALSE); - charon->event_queue->add_relative(charon->event_queue, job, timeout); - } - /* set new state */ - this->ike_sa->set_state(this->ike_sa, IKE_CONNECTING); - - return SUCCESS; -} - - -/** - * Implementation of transaction_t.conclude - */ -static status_t conclude(private_ike_sa_init_t *this, message_t *response, - transaction_t **next) -{ - u_int64_t responder_spi; - ike_sa_id_t *ike_sa_id; - iterator_t *payloads; - payload_t *payload; - host_t *me, *other; - sa_payload_t *sa_payload = NULL; - ke_payload_t *ke_payload = NULL; - nonce_payload_t *nonce_payload = NULL; - status_t status; - - /* check message type */ - if (response->get_exchange_type(response) != IKE_SA_INIT) - { - SIG(IKE_UP_FAILED, "IKE_SA_INIT response of invalid type, deleting IKE_SA"); - SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA"); - return DESTROY_ME; - } - - /* allow setting of next transaction in other functions */ - this->next = next; - - me = this->ike_sa->get_my_host(this->ike_sa); - other = this->ike_sa->get_other_host(this->ike_sa); - - /* check if SPI has been updated, but apply only if all goes ok later */ - responder_spi = response->get_responder_spi(response); - if (responder_spi == 0) - { - SIG(IKE_UP_FAILED, "response contained a SPI of zero, deleting IKE_SA"); - SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA"); - return DESTROY_ME; - } - - /* Precompute NAT-D hashes for later comparison */ - ike_sa_id = response->get_ike_sa_id(response); - this->natd_src_hash = generate_natd_hash(this, ike_sa_id, other); - this->natd_dst_hash = generate_natd_hash(this, ike_sa_id, me); - - /* Iterate over all payloads to collect them */ - payloads = response->get_payload_iterator(response); - while (payloads->iterate(payloads, (void**)&payload)) - { - switch (payload->get_type(payload)) - { - case SECURITY_ASSOCIATION: - { - sa_payload = (sa_payload_t*)payload; - break; - } - case KEY_EXCHANGE: - { - ke_payload = (ke_payload_t*)payload; - break; - } - case NONCE: - { - nonce_payload = (nonce_payload_t*)payload; - break; - } - case NOTIFY: - { - status = process_notifys(this, (notify_payload_t*)payload); - if (status == FAILED) - { - payloads->destroy(payloads); - /* we return SUCCESS, returned FAILED means do next transaction */ - return SUCCESS; - } - if (status == DESTROY_ME) - { - SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA"); - payloads->destroy(payloads); - return status; - } - break; - } - default: - { - DBG1(DBG_IKE, "ignoring payload %N", - payload_type_names, payload->get_type(payload)); - break; - } - } - } - payloads->destroy(payloads); - - if (!(nonce_payload && sa_payload && ke_payload)) - { - SIG(IKE_UP_FAILED, "response message incomplete, deleting IKE_SA"); - SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA"); - return DESTROY_ME; - } - - { /* process SA payload: - * ------------------- - * - get proposals from it - * - check if peer selected a proposal - * - verify it's selection againts our set - */ - linked_list_t *proposal_list; - - /* get the list of selected proposals, the peer has to select only one proposal */ - proposal_list = sa_payload->get_proposals (sa_payload); - if (proposal_list->get_count(proposal_list) != 1) - { - SIG(IKE_UP_FAILED, "response did not contain a single proposal, deleting IKE_SA"); - SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA"); - proposal_list->destroy_offset(proposal_list, offsetof(proposal_t, destroy)); - return DESTROY_ME; - } - - /* we have to re-check if the others selection is valid */ - this->proposal = this->connection->select_proposal(this->connection, proposal_list); - proposal_list->destroy_offset(proposal_list, offsetof(proposal_t, destroy)); - - if (this->proposal == NULL) - { - SIG(IKE_UP_FAILED, "peer selected a proposal we did not offer, deleting IKE_SA"); - SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA"); - return DESTROY_ME; - } - } - - { /* process KE payload: - * ------------------- - * - extract others public value - * - complete diffie-hellman exchange - */ - this->diffie_hellman->set_other_public_value(this->diffie_hellman, - ke_payload->get_key_exchange_data(ke_payload)); - } - - { /* process NONCE payload: - * ---------------------- - * - extract nonce used for key derivation */ - this->nonce_r = nonce_payload->get_nonce(nonce_payload); - } - - { /* process NATT stuff: - * ------------------- - * - check if we or other is NATted - * - switch to port 4500 if so - */ - if ((!this->natd_dst_seen && this->natd_src_seen) || - (this->natd_dst_seen && !this->natd_src_seen)) - { - SIG(IKE_UP_FAILED, "request contained invalid number of NAT-D payloads, deleting IKE_SA"); - SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA"); - return DESTROY_ME; - } - if (this->natd_src_seen && !this->natd_src_matched) - { - this->ike_sa->enable_natt(this->ike_sa, FALSE); - } - if (this->natd_dst_seen && !this->natd_dst_matched) - { - this->ike_sa->enable_natt(this->ike_sa, TRUE); - } - if (this->ike_sa->is_natt_enabled(this->ike_sa)) - { - me = this->ike_sa->get_my_host(this->ike_sa); - me->set_port(me, IKEV2_NATT_PORT); - other = this->ike_sa->get_other_host(this->ike_sa); - other->set_port(other, IKEV2_NATT_PORT); - - DBG2(DBG_IKE, "switching to port %d", IKEV2_NATT_PORT); - } - } - - /* because we are original initiator we have to update the responder SPI to the new one */ - ike_sa_id = this->ike_sa->get_id(this->ike_sa); - ike_sa_id->set_responder_spi(ike_sa_id, responder_spi); - - /* derive all the keys used in the IKE_SA */ - if (this->ike_sa->derive_keys(this->ike_sa, this->proposal, - this->diffie_hellman, - this->nonce_i, this->nonce_r, - TRUE, NULL, NULL) != SUCCESS) - { - SIG(IKE_UP_FAILED, "error creating transforms from proposal, deleting IKE_SA"); - SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA"); - return DESTROY_ME; - } - - this->ike_sa->set_lifetimes(this->ike_sa, - this->connection->get_reauth(this->connection), - this->connection->get_soft_lifetime(this->connection), - this->connection->get_hard_lifetime(this->connection)); - - { /* create ike_auth transaction, which will continue IKE_SA setup */ - chunk_t request_chunk, response_chunk; - ike_auth_t *ike_auth; - - request_chunk = this->message->get_packet_data(this->message); - response_chunk = response->get_packet_data(response); - - /* create next transaction, for which we except a message */ - ike_auth = ike_auth_create(this->ike_sa); - ike_auth->set_config(ike_auth, this->connection, this->policy); - ike_auth->set_reqid(ike_auth, this->reqid); - this->connection = NULL; - this->policy = NULL; - ike_auth->set_nonces(ike_auth, - chunk_clone(this->nonce_i), - chunk_clone(this->nonce_r)); - ike_auth->set_init_messages(ike_auth, request_chunk, response_chunk); - *next = (transaction_t*)ike_auth; - } - - return SUCCESS; -} - -static void destroy(private_ike_sa_init_t *this) -{ - DESTROY_IF(this->message); - DESTROY_IF(this->diffie_hellman); - DESTROY_IF(this->proposal); - DESTROY_IF(this->connection); - DESTROY_IF(this->policy); - chunk_free(&this->nonce_i); - chunk_free(&this->nonce_r); - this->randomizer->destroy(this->randomizer); - this->nat_hasher->destroy(this->nat_hasher); - chunk_free(&this->natd_src_hash); - chunk_free(&this->natd_dst_hash); - free(this); -} - -/* - * Described in header. - */ -ike_sa_init_t *ike_sa_init_create(ike_sa_t *ike_sa) -{ - static u_int unique_id = 0; - private_ike_sa_init_t *this = malloc_thing(private_ike_sa_init_t); - - /* transaction interface functions */ - this->public.transaction.get_request = (status_t(*)(transaction_t*,message_t**))get_request; - this->public.transaction.get_response = (status_t(*)(transaction_t*,message_t*,message_t**,transaction_t**))get_response; - this->public.transaction.conclude = (status_t(*)(transaction_t*,message_t*,transaction_t**))conclude; - this->public.transaction.get_message_id = (u_int32_t(*)(transaction_t*))get_message_id; - this->public.transaction.requested = (u_int32_t(*)(transaction_t*))requested; - this->public.transaction.destroy = (void(*)(transaction_t*))destroy; - - /* public functions */ - this->public.set_config = (void(*)(ike_sa_init_t*,connection_t*,policy_t*))set_config; - this->public.set_reqid = (void(*)(ike_sa_init_t*,u_int32_t))set_reqid; - this->public.use_dh_group = (bool(*)(ike_sa_init_t*,diffie_hellman_group_t))use_dh_group; - - /* private data */ - this->ike_sa = ike_sa; - this->message_id = 0; - this->message = NULL; - this->requested = 0; - this->diffie_hellman = NULL; - this->nonce_i = chunk_empty; - this->nonce_r = chunk_empty; - this->connection = NULL; - this->policy = NULL; - this->proposal = NULL; - this->unique_id = ++unique_id; - this->reqid = 0; - this->randomizer = randomizer_create(); - this->nat_hasher = hasher_create(HASH_SHA1); - this->natd_src_hash = chunk_empty; - this->natd_dst_hash = chunk_empty; - this->natd_src_seen = FALSE; - this->natd_dst_seen = FALSE; - this->natd_src_matched = FALSE; - this->natd_dst_matched = FALSE; - - return &this->public; -} diff --git a/src/charon/sa/transactions/ike_sa_init.h b/src/charon/sa/transactions/ike_sa_init.h deleted file mode 100644 index ab820fea5..000000000 --- a/src/charon/sa/transactions/ike_sa_init.h +++ /dev/null @@ -1,97 +0,0 @@ -/** - * @file ike_sa_init.h - * - * @brief Interface of transaction ike_sa_init. - * - */ - -/* - * 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 IKE_SA_INIT_H_ -#define IKE_SA_INIT_H_ - -typedef struct ike_sa_init_t ike_sa_init_t; - -#include <sa/ike_sa.h> -#include <sa/transactions/transaction.h> - -/** - * @brief A transaction for the first message exchange to set up an IKE_SA. - * - * @b Constructors: - * - ike_sa_init_create() - * - transaction_create() with the appropriate message - * - * @ingroup transactions - */ -struct ike_sa_init_t { - - /** - * The transaction_t interface. - */ - transaction_t transaction; - - /** - * @brief Set connection & policy to use for initiation. - * - * The policy is not used directly, but forwarded to the - * ike_auth transaction. - * - * @param this calling object - * @param connection connection to use for initiation - * @param policy policy used in ike_auth transaction - */ - void (*set_config) (ike_sa_init_t* this, - connection_t *connection, policy_t *policy); - - /** - * @brief Set the reqid used for CHILD_SA setup. - * - * The first two message exchanges may set up an associated - * CHILD_SA. If we acquire, we must use the same reqid as the - * installed policy. This requid is passed to the ike_auth - * transaction which creates the CHILD_AS. - * - * @param this calling object - * @param reqid reqid to use for the CHILD_SA - */ - void (*set_reqid) (ike_sa_init_t* this, u_int32_t reqid); - - /** - * @brief Set the Diffie Hellman group to use for initiating. - * - * If a first exchange fails with a INVALID_KE_PAYLOAD, the second - * try uses the DH group proposed by the responder. - * - * @param this calling object - * @param dh_group diffie hellman group to use - * @return FALSE, if DH group not allowed/supported - */ - bool (*use_dh_group) (ike_sa_init_t* this, diffie_hellman_group_t dh_group); -}; - -/** - * @brief Create a new transaction which processes IKE_SA_INIT exchanges. - * - * @param ike_sa assigned IKE_SA - * @return created ike_sa_init transaction - * - * @ingroup transactions - */ -ike_sa_init_t *ike_sa_init_create(ike_sa_t *ike_sa); - -#endif /* IKE_SA_INIT_H_ */ diff --git a/src/charon/sa/transactions/rekey_ike_sa.c b/src/charon/sa/transactions/rekey_ike_sa.c deleted file mode 100644 index 23e7e5638..000000000 --- a/src/charon/sa/transactions/rekey_ike_sa.c +++ /dev/null @@ -1,889 +0,0 @@ -/** - * @file rekey_ike_sa.c - * - * @brief Implementation of rekey_ike_sa_t transaction. - * - */ - -/* - * 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 "rekey_ike_sa.h" - -#include <string.h> - -#include <daemon.h> -#include <encoding/payloads/sa_payload.h> -#include <encoding/payloads/nonce_payload.h> -#include <encoding/payloads/ke_payload.h> -#include <sa/transactions/delete_ike_sa.h> -#include <utils/randomizer.h> - - -typedef struct private_rekey_ike_sa_t private_rekey_ike_sa_t; - -/** - * Private members of a rekey_ike_sa_t object.. - */ -struct private_rekey_ike_sa_t { - - /** - * Public methods and transaction_t interface. - */ - rekey_ike_sa_t public; - - /** - * Assigned IKE_SA. - */ - ike_sa_t *ike_sa; - - /** - * Message sent by our peer, if already generated - */ - message_t *message; - - /** - * Message ID this transaction uses - */ - u_int32_t message_id; - - /** - * Times we did send the request - */ - u_int32_t requested; - - /** - * IKE_SA we set up, replaces ike_sa - */ - ike_sa_t *new_sa; - - /** - * Connection used to replace IKE_SA - */ - connection_t *connection; - - /** - * initiator chosen nonce - */ - chunk_t nonce_i; - - /** - * responder chosen nonce - */ - chunk_t nonce_r; - - /** - * lower of the nonces of a simultaneus rekeying request - */ - chunk_t nonce_s; - - /** - * Diffie hellman to generate new shared secret - */ - diffie_hellman_t *diffie_hellman; - - /** - * negotiated proposal to use - */ - proposal_t *proposal; - - /** - * Have we lost the simultaneous rekeying nonce compare? - */ - bool lost; - - /** - * source of randomness for nonces - */ - randomizer_t *randomizer; - - /** - * next transaction processed by the IKE_SA - */ - transaction_t **next; -}; - -/** - * Implementation of transaction_t.get_message_id. - */ -static u_int32_t get_message_id(private_rekey_ike_sa_t *this) -{ - return this->message_id; -} - -/** - * Implementation of transaction_t.requested. - */ -static u_int32_t requested(private_rekey_ike_sa_t *this) -{ - return this->requested++; -} - - -/** - * Implementation of rekey_ike_sa_t.use_dh_group. - */ -static void use_dh_group(private_rekey_ike_sa_t *this, diffie_hellman_group_t dh_group) -{ - this->diffie_hellman = diffie_hellman_create(dh_group); -} - -/** - * Implementation of rekey_ike_sa_t.cancel. - */ -static void cancel(private_rekey_ike_sa_t *this) -{ - this->lost = TRUE; -} - -/** - * Implementation of transaction_t.get_request. - */ -static status_t get_request(private_rekey_ike_sa_t *this, message_t **result) -{ - message_t *request; - host_t *me, *other; - - /* check if we already have built a message (retransmission) */ - if (this->message) - { - *result = this->message; - return SUCCESS; - } - - /* check for correct state, except when retrying with another dh group */ - if (this->ike_sa->get_state(this->ike_sa) != IKE_ESTABLISHED && - !this->diffie_hellman) - { - DBG1(DBG_IKE, "tried to rekey in state %N, aborted", - ike_sa_state_names, this->ike_sa->get_state(this->ike_sa)); - return FAILED; - } - - me = this->ike_sa->get_my_host(this->ike_sa); - other = this->ike_sa->get_other_host(this->ike_sa); - - /* build the request */ - request = message_create(); - request->set_source(request, me->clone(me)); - request->set_destination(request, other->clone(other)); - request->set_exchange_type(request, CREATE_CHILD_SA); - request->set_request(request, TRUE); - request->set_ike_sa_id(request, this->ike_sa->get_id(this->ike_sa)); - *result = request; - this->message = request; - - { /* build SA payload */ - sa_payload_t *sa_payload; - linked_list_t *proposals; - ike_sa_id_t *ike_sa_id; - iterator_t *iterator; - proposal_t *proposal; - u_int64_t spi; - - /* get a connection to replace current IKE_SA */ - this->connection = charon->connections->get_connection_by_name( - charon->connections, - this->ike_sa->get_name(this->ike_sa)); - /* if connection lookup by name fails, try it with the hosts */ - if (this->connection == NULL) - { - this->connection = charon->connections->get_connection_by_hosts( - charon->connections, - me, other); - if (this->connection == NULL) - { - DBG1(DBG_IKE, "no connection found to rekey IKE_SA"); - return FAILED; - } - } - - /* create a new SA */ - ike_sa_id = ike_sa_id_create(0, 0, TRUE); - this->new_sa = charon->ike_sa_manager->checkout(charon->ike_sa_manager, - ike_sa_id); - spi = ike_sa_id->get_initiator_spi(ike_sa_id); - ike_sa_id->destroy(ike_sa_id); - - proposals = this->connection->get_proposals(this->connection); - iterator = proposals->create_iterator(proposals, TRUE); - while (iterator->iterate(iterator, (void**)&proposal)) - { - proposal->set_spi(proposal, spi); - } - iterator->destroy(iterator); - - sa_payload = sa_payload_create_from_proposal_list(proposals); - proposals->destroy_offset(proposals, offsetof(proposal_t, destroy)); - request->add_payload(request, (payload_t*)sa_payload); - } - - { /* build the NONCE payload for us (initiator) */ - nonce_payload_t *nonce_payload; - - if (this->randomizer->allocate_pseudo_random_bytes(this->randomizer, - NONCE_SIZE, &this->nonce_i) != SUCCESS) - { - return FAILED; - } - nonce_payload = nonce_payload_create(); - nonce_payload->set_nonce(nonce_payload, this->nonce_i); - request->add_payload(request, (payload_t*)nonce_payload); - } - - /* if the DH group is set via use_dh_group(), we already have a DH object */ - if (!this->diffie_hellman) - { - diffie_hellman_group_t dh_group; - - dh_group = this->connection->get_dh_group(this->connection); - this->diffie_hellman = diffie_hellman_create(dh_group); - if (this->diffie_hellman == NULL) - { - DBG1(DBG_IKE, "DH group %N not supported, aborting", - diffie_hellman_group_names, dh_group); - return FAILED; - } - } - - { /* build the KE payload from the DH object */ - ke_payload_t *ke_payload; - - ke_payload = ke_payload_create_from_diffie_hellman(this->diffie_hellman); - request->add_payload(request, (payload_t*)ke_payload); - } - - this->message_id = this->ike_sa->get_next_message_id(this->ike_sa); - request->set_message_id(request, this->message_id); - - /* register us as rekeying to detect multiple rekeying */ - this->ike_sa->set_state(this->ike_sa, IKE_REKEYING); - this->ike_sa->set_rekeying_transaction(this->ike_sa, &this->public.transaction); - - return SUCCESS; -} - -/** - * Handle all kind of notifys - */ -static status_t process_notifys(private_rekey_ike_sa_t *this, notify_payload_t *notify_payload) -{ - notify_type_t notify_type = notify_payload->get_notify_type(notify_payload); - - DBG2(DBG_IKE,"process notify type %N", notify_type_names, notify_type); - - switch (notify_type) - { - case NO_PROPOSAL_CHOSEN: - { - DBG1(DBG_IKE, "received a NO_PROPOSAL_CHOSEN notify, IKE_SA rekeying failed"); - return FAILED; - } - case INVALID_KE_PAYLOAD: - { - chunk_t notify_data; - diffie_hellman_group_t dh_group, old_dh_group; - rekey_ike_sa_t *retry; - - old_dh_group = this->connection->get_dh_group(this->connection); - notify_data = notify_payload->get_notification_data(notify_payload); - dh_group = ntohs(*((u_int16_t*)notify_data.ptr)); - - DBG1(DBG_IKE, "peer didn't accept DH group %N, it requested %N", - diffie_hellman_group_names, old_dh_group, - diffie_hellman_group_names, dh_group); - if (!this->connection->check_dh_group(this->connection, dh_group)) - { - DBG1(DBG_IKE, "requested DH group not acceptable, IKE_SA rekeying failed"); - return FAILED; - } - retry = rekey_ike_sa_create(this->ike_sa); - retry->use_dh_group(retry, dh_group); - *this->next = (transaction_t*)retry; - return FAILED; - } - default: - { - if (notify_type < 16383) - { - DBG1(DBG_IKE, "received %N notify error, IKE_SA rekeying failed", - notify_type_names, notify_type); - return FAILED; - } - else - { - DBG1(DBG_IKE, "received %N notify, ignored", - notify_type_names, notify_type); - return SUCCESS; - } - } - } -} - -/** - * Switch to the newly created IKE_SA - */ -static status_t switchto_new_sa(private_rekey_ike_sa_t* this, bool initiator) -{ - identification_t *my_id, *other_id; - host_t *my_host, *other_host; - char *name; - - my_id = this->ike_sa->get_my_id(this->ike_sa); - other_id = this->ike_sa->get_other_id(this->ike_sa); - my_host = this->ike_sa->get_my_host(this->ike_sa); - other_host = this->ike_sa->get_other_host(this->ike_sa); - name = this->ike_sa->get_name(this->ike_sa); - - this->new_sa->set_my_id(this->new_sa, my_id->clone(my_id)); - this->new_sa->set_other_id(this->new_sa, other_id->clone(other_id)); - this->new_sa->set_my_host(this->new_sa, my_host->clone(my_host)); - this->new_sa->set_other_host(this->new_sa, other_host->clone(other_host)); - this->new_sa->set_name(this->new_sa, name); - - if (this->new_sa->derive_keys(this->new_sa, this->proposal, - this->diffie_hellman, - this->nonce_i, this->nonce_r, initiator, - this->ike_sa->get_child_prf(this->ike_sa), - this->ike_sa->get_prf(this->ike_sa) - ) != SUCCESS) - { - return FAILED; - } - - this->new_sa->apply_connection(this->new_sa, this->connection); - this->new_sa->set_state(this->new_sa, IKE_ESTABLISHED); - this->new_sa->set_lifetimes(this->new_sa, - this->connection->get_reauth(this->connection), - this->connection->get_soft_lifetime(this->connection), - this->connection->get_hard_lifetime(this->connection)); - return SUCCESS; -} - -/** - * Build a notify message. - */ -static void build_notify(notify_type_t type, chunk_t data, message_t *message, bool flush_message) -{ - notify_payload_t *notify; - - if (flush_message) - { - payload_t *payload; - iterator_t *iterator = message->get_payload_iterator(message); - while (iterator->iterate(iterator, (void**)&payload)) - { - payload->destroy(payload); - iterator->remove(iterator); - } - iterator->destroy(iterator); - } - - notify = notify_payload_create(); - notify->set_notify_type(notify, type); - notify->set_notification_data(notify, data); - message->add_payload(message, (payload_t*)notify); -} - -/** - * Implementation of transaction_t.get_response. - */ -static status_t get_response(private_rekey_ike_sa_t *this, message_t *request, - message_t **result, transaction_t **next) -{ - host_t *me, *other; - message_t *response; - status_t status; - iterator_t *payloads, *iterator; - payload_t *payload; - child_sa_t *child_sa; - sa_payload_t *sa_request = NULL; - nonce_payload_t *nonce_request = NULL; - ke_payload_t *ke_request = NULL; - nonce_payload_t *nonce_response; - - /* check if we already have built a response (retransmission) */ - if (this->message) - { - *result = this->message; - return SUCCESS; - } - - me = this->ike_sa->get_my_host(this->ike_sa); - other = this->ike_sa->get_other_host(this->ike_sa); - this->message_id = request->get_message_id(request); - - /* set up response */ - response = message_create(); - response->set_source(response, me->clone(me)); - response->set_destination(response, other->clone(other)); - response->set_exchange_type(response, CREATE_CHILD_SA); - response->set_request(response, FALSE); - response->set_message_id(response, this->message_id); - response->set_ike_sa_id(response, this->ike_sa->get_id(this->ike_sa)); - this->message = response; - *result = response; - - /* check message type */ - if (request->get_exchange_type(request) != CREATE_CHILD_SA) - { - DBG1(DBG_IKE, "CREATE_CHILD_SA response of invalid type, aborted"); - return FAILED; - } - - /* if we already initiate a delete, we do not allow rekeying */ - if (this->ike_sa->get_state(this->ike_sa) == IKE_DELETING) - { - build_notify(NO_PROPOSAL_CHOSEN, chunk_empty, response, TRUE); - DBG1(DBG_IKE, "unable to rekey, as delete in progress. Sending NO_PROPOSAL_CHOSEN"); - return FAILED; - } - - /* if we have a CHILD which is "half-open", we do not allow rekeying */ - iterator = this->ike_sa->create_child_sa_iterator(this->ike_sa); - while (iterator->iterate(iterator, (void**)&child_sa)) - { - child_sa_state_t state = child_sa->get_state(child_sa); - if (state == CHILD_CREATED || - state == CHILD_REKEYING || - state == CHILD_DELETING) - { - build_notify(NO_PROPOSAL_CHOSEN, chunk_empty, response, TRUE); - DBG1(DBG_IKE, "unable to rekey, one CHILD_SA is half open. Sending NO_PROPOSAL_CHOSEN"); - iterator->destroy(iterator); - return FAILED; - } - } - iterator->destroy(iterator); - - /* apply for notify processing */ - this->next = next; - - - /* get a connection to replace current IKE_SA */ - this->connection = charon->connections->get_connection_by_name( - charon->connections, this->ike_sa->get_name(this->ike_sa)); - /* if connection lookup by name fails, try it with the hosts */ - if (this->connection == NULL) - { - this->connection = charon->connections->get_connection_by_hosts( - charon->connections, me, other); - if (this->connection == NULL) - { - DBG1(DBG_IKE, "no connection found to rekey IKE_SA, sending NO_RROPOSAL_CHOSEN"); - build_notify(NO_PROPOSAL_CHOSEN, chunk_empty, response, TRUE); - return FAILED; - } - } - - /* Iterate over all payloads. */ - payloads = request->get_payload_iterator(request); - while (payloads->iterate(payloads, (void**)&payload)) - { - switch (payload->get_type(payload)) - { - case SECURITY_ASSOCIATION: - sa_request = (sa_payload_t*)payload; - break; - case NONCE: - nonce_request = (nonce_payload_t*)payload; - break; - case KEY_EXCHANGE: - { - ke_request = (ke_payload_t*)payload; - break; - } - case NOTIFY: - { - status = process_notifys(this, (notify_payload_t*)payload); - if (status != SUCCESS) - { - payloads->destroy(payloads); - return status; - } - break; - } - default: - { - DBG1(DBG_IKE, "ignoring %N payload", - payload_type_names, payload->get_type(payload)); - break; - } - } - } - payloads->destroy(payloads); - - /* check if we have all payloads */ - if (!(sa_request && nonce_request && ke_request)) - { - build_notify(INVALID_SYNTAX, chunk_empty, response, TRUE); - DBG1(DBG_IKE, "request message incomplete, IKE_SA rekeying failed"); - return FAILED; - } - - { /* process nonce payload */ - this->nonce_i = nonce_request->get_nonce(nonce_request); - if (this->randomizer->allocate_pseudo_random_bytes(this->randomizer, - NONCE_SIZE, &this->nonce_r) != SUCCESS) - { - build_notify(NO_PROPOSAL_CHOSEN, chunk_empty, response, TRUE); - return FAILED; - } - nonce_response = nonce_payload_create(); - nonce_response->set_nonce(nonce_response, this->nonce_r); - } - - { /* process SA payload */ - linked_list_t *proposal_list; - sa_payload_t *sa_response; - u_int64_t spi; - ike_sa_id_t *ike_sa_id; - - sa_response = sa_payload_create(); - /* get proposals from request, and select one with ours */ - proposal_list = sa_request->get_proposals(sa_request); - DBG2(DBG_IKE, "selecting proposals:"); - this->proposal = this->connection->select_proposal(this->connection, proposal_list); - proposal_list->destroy_offset(proposal_list, offsetof(proposal_t, destroy)); - - /* do we have a proposal? */ - if (this->proposal == NULL) - { - DBG1(DBG_IKE, "no proposals acceptable to rekey IKE_SA, sending NO_PROPOSAL_CHOSEN"); - build_notify(NO_PROPOSAL_CHOSEN, chunk_empty, response, TRUE); - return FAILED; - } - - /* create IKE_SA with new SPIs */ - spi = this->proposal->get_spi(this->proposal); - ike_sa_id = ike_sa_id_create(spi, 0, FALSE); - this->new_sa = charon->ike_sa_manager->checkout(charon->ike_sa_manager, - ike_sa_id); - spi = ike_sa_id->get_responder_spi(ike_sa_id); - ike_sa_id->destroy(ike_sa_id); - this->proposal->set_spi(this->proposal, spi); - - sa_response->add_proposal(sa_response, this->proposal); - response->add_payload(response, (payload_t*)sa_response); - /* add nonce after sa payload */ - response->add_payload(response, (payload_t *)nonce_response); - } - - { /* process KE payload */ - diffie_hellman_group_t used_group; - ke_payload_t *ke_response; - - used_group = ke_request->get_dh_group_number(ke_request); - - if (!this->connection->check_dh_group(this->connection, used_group) || - (this->diffie_hellman = diffie_hellman_create(used_group)) == NULL) - { - u_int16_t notify_group; - chunk_t notify_chunk; - - notify_group = this->connection->get_dh_group(this->connection); - DBG1(DBG_IKE, "request used inacceptable DH group %N, sending " - "INVALID_KE_PAYLOAD with %N", - diffie_hellman_group_names, used_group, - diffie_hellman_group_names, notify_group); - - notify_group = htons(notify_group); - notify_chunk.ptr = (u_int8_t*)¬ify_group; - notify_chunk.len = sizeof(notify_group); - build_notify(INVALID_KE_PAYLOAD, notify_chunk, response, TRUE); - return FAILED; - } - this->diffie_hellman->set_other_public_value(this->diffie_hellman, - ke_request->get_key_exchange_data(ke_request)); - - /* build response */ - ke_response = ke_payload_create_from_diffie_hellman(this->diffie_hellman); - response->add_payload(response, (payload_t*)ke_response); - } - - status = switchto_new_sa(this, FALSE); - if (status != SUCCESS) - { - return status; - } - - /* IKE_SA successfully created. If another transaction is already rekeying - * this SA, our lower nonce must be registered for a later nonce compare. */ - { - private_rekey_ike_sa_t *other; - - other = (private_rekey_ike_sa_t*) - this->ike_sa->get_rekeying_transaction(this->ike_sa); - if (other) - { - /* store our lower nonce in the simultaneus transaction, we - * will later compare it against his nonces when we calls conclude(). - * We do not adopt childrens yet, as we don't know if we'll win - * the race... - */ - if (memcmp(this->nonce_i.ptr, this->nonce_r.ptr, - min(this->nonce_i.len, this->nonce_r.len)) < 0) - { - other->nonce_s = chunk_clone(this->nonce_i); - } - else - { - other->nonce_s = chunk_clone(this->nonce_r); - } - /* overwrite "other" in IKE_SA, allows "other" to access "this" */ - this->ike_sa->set_rekeying_transaction(this->ike_sa, &this->public.transaction); - } - else - { - /* if we have no simultaneus transaction, we can safely adopt - * all children and complete. */ - this->new_sa->adopt_children(this->new_sa, this->ike_sa); - charon->ike_sa_manager->checkin(charon->ike_sa_manager, this->new_sa); - this->new_sa = NULL; - } - this->ike_sa->set_state(this->ike_sa, IKE_REKEYING); - } - - return SUCCESS; -} - -/** - * Implementation of transaction_t.conclude - */ -static status_t conclude(private_rekey_ike_sa_t *this, message_t *response, - transaction_t **next) -{ - iterator_t *payloads; - payload_t *payload; - host_t *me, *other; - sa_payload_t *sa_payload = NULL; - nonce_payload_t *nonce_payload = NULL; - ke_payload_t *ke_payload = NULL; - private_rekey_ike_sa_t *other_trans; - status_t status; - - /* check message type */ - if (response->get_exchange_type(response) != CREATE_CHILD_SA) - { - DBG1(DBG_IKE, "CREATE_CHILD_SA response of invalid type, aborting"); - return FAILED; - } - - me = this->ike_sa->get_my_host(this->ike_sa); - other = this->ike_sa->get_other_host(this->ike_sa); - - /* apply for notify processing */ - this->next = next; - - /* Iterate over all payloads to collect them */ - payloads = response->get_payload_iterator(response); - while (payloads->iterate(payloads, (void**)&payload)) - { - switch (payload->get_type(payload)) - { - case SECURITY_ASSOCIATION: - sa_payload = (sa_payload_t*)payload; - break; - case NONCE: - nonce_payload = (nonce_payload_t*)payload; - break; - case KEY_EXCHANGE: - ke_payload = (ke_payload_t*)payload; - break; - case NOTIFY: - { - status = process_notifys(this, (notify_payload_t*)payload); - if (status != SUCCESS) - { - payloads->destroy(payloads); - return status; - } - break; - } - default: - { - DBG1(DBG_IKE, "ignoring %N payload", - payload_type_names, payload->get_type(payload)); - break; - } - } - } - payloads->destroy(payloads); - - if (!(sa_payload && nonce_payload && ke_payload)) - { - DBG1(DBG_IKE, "response message incomplete, rekeying IKE_SA failed"); - return FAILED; - } - - { /* process NONCE payload */ - this->nonce_r = nonce_payload->get_nonce(nonce_payload); - } - - { /* process SA payload */ - linked_list_t *proposal_list; - ike_sa_id_t *ike_sa_id; - u_int64_t spi; - - proposal_list = sa_payload->get_proposals(sa_payload); - /* we have to re-check here if other's selection is valid */ - this->proposal = this->connection->select_proposal(this->connection, proposal_list); - proposal_list->destroy_offset(proposal_list, offsetof(proposal_t, destroy)); - - if (this->proposal == NULL) - { - DBG1(DBG_IKE, "no proposal selected, rekeying IKE_SA failed"); - return FAILED; - } - spi = this->proposal->get_spi(this->proposal); - ike_sa_id = this->new_sa->get_id(this->new_sa); - ike_sa_id->set_responder_spi(ike_sa_id, spi); - } - - { /* process KE payload */ - this->diffie_hellman->set_other_public_value(this->diffie_hellman, - ke_payload->get_key_exchange_data(ke_payload)); - } - - if (switchto_new_sa(this, TRUE) != SUCCESS) - { - /* this should not happen. But if, we destroy the new SAs */ - this->new_sa->set_state(this->new_sa, IKE_REKEYING); - *next = (transaction_t*)delete_ike_sa_create(this->new_sa); - return FAILED; - } - - /* IKE_SA successfully created. If the other peer initiated rekeying - * in the meantime, we detect this by comparing the rekeying_transaction - * of the SA. If it changed, we are not alone. Then we must compare the nonces. - * If no simultaneous rekeying is going on, we just initiate the delete of - * the superseded SA. */ - other_trans = (private_rekey_ike_sa_t*) - this->ike_sa->get_rekeying_transaction(this->ike_sa); - this->ike_sa->set_rekeying_transaction(this->ike_sa, NULL); - - if (this->nonce_s.ptr) - { /* simlutaneous rekeying is going on, not so good */ - chunk_t this_lowest; - - /* first get our lowest nonce */ - if (memcmp(this->nonce_i.ptr, this->nonce_r.ptr, - min(this->nonce_i.len, this->nonce_r.len)) < 0) - { - this_lowest = this->nonce_i; - } - else - { - this_lowest = this->nonce_r; - } - /* then compare against other lowest nonce */ - if (memcmp(this_lowest.ptr, this->nonce_s.ptr, - min(this_lowest.len, this->nonce_s.len)) < 0) - { - DBG1(DBG_IKE, "detected simultaneous IKE_SA rekeying, deleting ours"); - this->lost = TRUE; - } - else - { - DBG1(DBG_IKE, "detected simultaneous IKE_SA rekeying, but ours is preferred"); - } - if (this->lost) - { - /* the other has won, he gets our children */ - other_trans->new_sa->adopt_children(other_trans->new_sa, this->ike_sa); - /* we have lost simlutaneous rekeying, delete the SA we just have created */ - this->new_sa->set_state(this->new_sa, IKE_REKEYING); - *next = (transaction_t*)delete_ike_sa_create(this->new_sa); - } - else - { - other_trans->new_sa->set_state(other_trans->new_sa, IKE_REKEYING); - } - /* other trans' SA is still not checked in, so do it now. It's SA will get - * deleted by remote peer. */ - charon->ike_sa_manager->checkin(charon->ike_sa_manager, other_trans->new_sa); - other_trans->new_sa = NULL; - } - - if (!this->lost) - { - /* we have won. delete old IKE_SA, and migrate all children */ - this->new_sa->adopt_children(this->new_sa, this->ike_sa); - *next = (transaction_t*)delete_ike_sa_create(this->ike_sa); - } - - charon->ike_sa_manager->checkin(charon->ike_sa_manager, this->new_sa); - this->new_sa = NULL; - - return SUCCESS; -} - -/** - * implements transaction_t.destroy - */ -static void destroy(private_rekey_ike_sa_t *this) -{ - if (this->new_sa) - { - charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, - this->new_sa); - } - DESTROY_IF(this->message); - DESTROY_IF(this->connection); - DESTROY_IF(this->diffie_hellman); - DESTROY_IF(this->proposal); - chunk_free(&this->nonce_i); - chunk_free(&this->nonce_r); - chunk_free(&this->nonce_s); - this->randomizer->destroy(this->randomizer); - free(this); -} - -/* - * Described in header. - */ -rekey_ike_sa_t *rekey_ike_sa_create(ike_sa_t *ike_sa) -{ - private_rekey_ike_sa_t *this = malloc_thing(private_rekey_ike_sa_t); - - /* transaction interface functions */ - this->public.transaction.get_request = (status_t(*)(transaction_t*,message_t**))get_request; - this->public.transaction.get_response = (status_t(*)(transaction_t*,message_t*,message_t**,transaction_t**))get_response; - this->public.transaction.conclude = (status_t(*)(transaction_t*,message_t*,transaction_t**))conclude; - this->public.transaction.get_message_id = (u_int32_t(*)(transaction_t*))get_message_id; - this->public.transaction.requested = (u_int32_t(*)(transaction_t*))requested; - this->public.transaction.destroy = (void(*)(transaction_t*))destroy; - - /* public functions */ - this->public.use_dh_group = (void(*)(rekey_ike_sa_t*,diffie_hellman_group_t))use_dh_group; - this->public.cancel = (void(*)(rekey_ike_sa_t*))cancel; - - /* private data */ - this->ike_sa = ike_sa; - this->message_id = 0; - this->message = NULL; - this->requested = 0; - this->nonce_i = chunk_empty; - this->nonce_r = chunk_empty; - this->nonce_s = chunk_empty; - this->new_sa = NULL; - this->lost = FALSE; - this->connection = NULL; - this->randomizer = randomizer_create(); - this->diffie_hellman = NULL; - this->proposal = NULL; - - return &this->public; -} diff --git a/src/charon/sa/transactions/rekey_ike_sa.h b/src/charon/sa/transactions/rekey_ike_sa.h deleted file mode 100644 index dbb65237e..000000000 --- a/src/charon/sa/transactions/rekey_ike_sa.h +++ /dev/null @@ -1,81 +0,0 @@ -/** - * @file rekey_ike_sa.h - * - * @brief Interface of transaction rekey_ike_sa. - * - */ - -/* - * 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 REKEY_IKE_SA_H -#define REKEY_IKE_SA_H - -typedef struct rekey_ike_sa_t rekey_ike_sa_t; - -#include <sa/ike_sa.h> -#include <sa/transactions/transaction.h> -#include <crypto/diffie_hellman.h> - - -/** - * @brief A transaction to rekey an established IKE_SA - * - * @b Constructors: - * - rekey_ike_sa_create() - * - transaction_create() with the appropriate message - * - * @ingroup transactions - */ -struct rekey_ike_sa_t { - - /** - * The transaction_t interface. - */ - transaction_t transaction; - - /** - * @brief Set the Diffie Hellman group to use for initiating. - * - * If a first exchange fails with a INVALID_KE_PAYLOAD, the second - * try uses the DH group proposed by the responder. - * - * @param this calling object - * @param dh_group diffie hellman group to use - */ - void (*use_dh_group) (rekey_ike_sa_t* this, diffie_hellman_group_t dh_group); - - /** - * @brief Cancel the request. - * - * Cancelling the request will set a flag in the transaction. - * - * @param this calling object - * @param child_sa CHILD_SA to rekey - */ - void (*cancel) (rekey_ike_sa_t* this); -}; - -/** - * @brief Create a new transaction to rekey an existing IKE_SA. - * - * @param ike_sa existing IKE_SA - * @return created rekey_ike_sa transaction - * - * @ingroup transactions - */ -rekey_ike_sa_t *rekey_ike_sa_create(ike_sa_t *ike_sa); - -#endif /* REKEY_IKE_SA_H */ diff --git a/src/charon/sa/transactions/transaction.c b/src/charon/sa/transactions/transaction.c deleted file mode 100644 index d4da1cdcb..000000000 --- a/src/charon/sa/transactions/transaction.c +++ /dev/null @@ -1,167 +0,0 @@ -/** - * @file transaction.c - * - * @brief Generic contstructor for the different transaction types. - * - */ - -/* - * 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 "transaction.h" - -#include <sa/child_sa.h> -#include <sa/transactions/ike_sa_init.h> -#include <sa/transactions/ike_auth.h> -#include <sa/transactions/delete_ike_sa.h> -#include <sa/transactions/create_child_sa.h> -#include <sa/transactions/delete_child_sa.h> -#include <sa/transactions/dead_peer_detection.h> -#include <sa/transactions/rekey_ike_sa.h> -#include <encoding/payloads/ts_payload.h> -#include <encoding/payloads/sa_payload.h> -#include <encoding/payloads/nonce_payload.h> -#include <encoding/payloads/notify_payload.h> -#include <encoding/payloads/delete_payload.h> - -/* - * see header file - */ -transaction_t *transaction_create(ike_sa_t *ike_sa, message_t *request) -{ - iterator_t *iterator; - payload_t *current; - transaction_t *transaction = NULL; - - if (!request->get_request(request)) - { - return NULL; - } - - switch (request->get_exchange_type(request)) - { - case IKE_SA_INIT: - { - if (ike_sa->get_state(ike_sa) == IKE_CREATED) - { - transaction = (transaction_t*)ike_sa_init_create(ike_sa); - } - break; - } - case IKE_AUTH: - { - /* IKE_AUTH is always created in IKE_SA_INIT, it never should - * appear alone */ - break; - } - case CREATE_CHILD_SA: - { - if (ike_sa->get_state(ike_sa) < IKE_ESTABLISHED) - { - break; - } - /* check protocol of SA payload */ - iterator = request->get_payload_iterator(request); - while (iterator->iterate(iterator, (void**)¤t)) - { - if (current->get_type(current) == SECURITY_ASSOCIATION) - { - iterator_t *prop_iter; - proposal_substructure_t *prop_struct; - sa_payload_t *sa_payload = (sa_payload_t*)current; - - prop_iter = sa_payload->create_proposal_substructure_iterator(sa_payload, TRUE); - if (prop_iter->iterate(prop_iter, (void**)&prop_struct)) - { - switch (prop_struct->get_protocol_id(prop_struct)) - { - case PROTO_IKE: - transaction = (transaction_t*) - rekey_ike_sa_create(ike_sa); - break; - case PROTO_AH: - case PROTO_ESP: - transaction = (transaction_t*) - create_child_sa_create(ike_sa); - break; - default: - break; - } - } - prop_iter->destroy(prop_iter); - } - if (transaction) - { - break; - } - } - iterator->destroy(iterator); - break; - } - case INFORMATIONAL: - { - if (ike_sa->get_state(ike_sa) < IKE_ESTABLISHED) - { - break; - } - u_int payload_count = 0; - iterator = request->get_payload_iterator(request); - while (iterator->iterate(iterator, (void**)¤t)) - { - payload_count++; - switch (current->get_type(current)) - { - case DELETE: - { - delete_payload_t *delete_payload = (delete_payload_t*)current; - switch (delete_payload->get_protocol_id(delete_payload)) - { - case PROTO_IKE: - transaction = (transaction_t*) - delete_ike_sa_create(ike_sa); - break; - case PROTO_AH: - case PROTO_ESP: - transaction = (transaction_t*) - delete_child_sa_create(ike_sa); - break; - default: - break; - } - break; - } - default: - break; - } - if (transaction) - { - break; - } - } - iterator->destroy(iterator); - /* empty informationals are used for dead peer detection in - * IKEv2. We use a special transaction for it. */ - if (payload_count == 0) - { - transaction = (transaction_t*) - dead_peer_detection_create(ike_sa); - } - break; - } - default: - break; - } - return transaction; -} diff --git a/src/charon/sa/transactions/transaction.h b/src/charon/sa/transactions/transaction.h deleted file mode 100644 index 4a401e12b..000000000 --- a/src/charon/sa/transactions/transaction.h +++ /dev/null @@ -1,180 +0,0 @@ -/** - * @file transaction.h - * - * @brief Interface transaction_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 TRANSACTION_H_ -#define TRANSACTION_H_ - -typedef struct transaction_t transaction_t; - -#include <library.h> -#include <encoding/message.h> -#include <sa/ike_sa.h> - -/** - * @brief This interface represents a transaction an established IKE_SA can do. - * - * To every transaction, a message ID is associated. IKEv2 uses strict message - * IDs, which are equal for a request/response pair in a transaction. - * An initiator of a transaction does the following: - * - create the transaction using a specific constructor - * - call request() to get the message for initiaton - * - call conclude() to process received reply - * The other peer does the following: - * - create a transanction using the generic transaction constructor - * - call respond() to get a reply to send - * - * The responder must not destroy the transaction, until the - * initiator initiates another transaction (or a number of transactions - * > window size). This allows us to redo a transaction in case of a - * message loss. The initiator can destroy the the transaction once - * the conclude() function is called. - * - * @b Constructors: - * - transaction_create() - * - ike_sa_init_create() - * - ike_auth_create() - * - * @ingroup transactions - */ -struct transaction_t { - - /** - * @brief Get the request to use for initiating the transaction. - * - * A transaction creates a request only once. The request is stored - * internally and may be queried multiple times for retransmission. - * The transaction is not responsible for generating/encrypting the - * message, this is the job of the caller. But it MAY be already - * generated when calling get_request() the second time. - * - * @param this calling object - * @param[out] request resultin request - * @return - * - FAILED if transaction failed - * - DESTROY_ME if transaction failed and IKE SA - * must be deleted - * - SUCCESS - */ - status_t (*get_request) (transaction_t *this, message_t **request); - - /** - * @brief Build the response for a received request. - * - * A transaction creates a response only once for a unique request. - * This allows the use of get_response multiple times for retransmission - * purposes. - * The transaction is not responsible for generating/encrypting the - * response, nor is it responsible for decrypting/parsing the request. - * This is the job of the caller. But the response MAY be already - * generated when calling get_request() the second time. - * The initiator waits for a response, so we send one in every case. This - * means response points always to a valid message. This message - * may not be modified or destroyed, it gets destroyed along with the - * transaction. - * The get_response() function may return a next transaction. This allows - * passing of informations from one transaction to a next one. - * - * @param this calling object - * @param request received request - * @param[out] response resulting response - * @param[out] next transaction expected as next, or NULL - * @return - * - FAILED if transaction failed - * - DESTROY_ME if transaction failed and IKE SA - * must be deleted - * - SUCCESS - */ - status_t (*get_response) (transaction_t *this, message_t *request, - message_t **response, transaction_t **next); - - /** - * @brief Conclude an initiated transaction with a received response. - * - * The response must be decrypted and parsed. The conclude function - * may return a new transaction. This transaction has to be executed - * next to complete a multi-exchange scenario. It allows a clean - * transaction mechanism, as the transaction knows best whats to do - * after it completes. It must only be executed if conclude returns - * SUCCESS. - * - * @param this calling object - * @param response received response - * @param[out] next transaction to execute as next, or NULL - * @return - * - FAILED if transaction failed - * - DESTROY_ME if transaction failed and IKE SA - * must be deleted - * - SUCCESS - */ - status_t (*conclude) (transaction_t *this, message_t *response, - transaction_t **next); - - /** - * @brief Get the message ID associated with this transaction. - * - * Every transaction consists of a message pair with the same - * message ID. This ID can be queried with get_message_id(). - * - * @param this calling object - * @return message id - */ - u_int32_t (*get_message_id) (transaction_t *this); - - /** - * @brief Times we already sent the request (retransmitted). - * - * The transaction stores an internal counter to see how - * many times we sent the request. This counter is incremented - * each time after a call to requested(). - * - * @param this calling object - * @return message id - */ - u_int32_t (*requested) (transaction_t *this); - - /** - * @brief Destroys a transaction_t object. - * - * @param this calling object - */ - void (*destroy) (transaction_t *this); -}; - -/** - * @brief Create a transaction instance based on a received request. - * - * Incoming requests are handled by a transaction. But as we don't - * know what kind of transaction we use for a specific request, we use - * a generic constructor. This constructor decides which instance will - * handle the transaction, and creates it. - * - * @param ike_sa ike_sa associated with this transaction - * @param request received request - * @return - * - created transaction, or - * - NULL no transaction needed - * - * @ingroup transactions - */ -transaction_t *transaction_create(ike_sa_t *ike_sa, message_t* request); - -#endif /* TRANSACTION_H_ */ diff --git a/src/charon/threads/kernel_interface.c b/src/charon/threads/kernel_interface.c index 3ec2bd58e..e595108ee 100644 --- a/src/charon/threads/kernel_interface.c +++ b/src/charon/threads/kernel_interface.c @@ -6,7 +6,9 @@ */ /* - * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger + * Copyright (C) 2006-2007 Fabian Hartmann, Noah Heusser + * Copyright (C) 2006-2007 Tobias Brunner + * Copyright (C) 2006 Daniel Roethlisberger * Copyright (C) 2005-2006 Martin Willi * Copyright (C) 2005 Jan Hutter * Hochschule fuer Technik Rapperswil @@ -36,6 +38,9 @@ #include <fcntl.h> #include <errno.h> #include <string.h> +#include <net/if.h> +#include <sys/ioctl.h> +#include <ifaddrs.h> #include "kernel_interface.h" @@ -152,7 +157,21 @@ char* lookup_algorithm(kernel_algorithm_t *kernel_algo, return NULL; } +typedef struct rt_refcount_t rt_refcount_t; +struct rt_refcount_t { + /** Index of the interface the route is bound to */ + int if_index; + + /** Source ip of the route */ + host_t *src_ip; + + /** Destination net */ + chunk_t dst_net; + + /** Destination net prefixlen */ + u_int8_t prefixlen; +}; typedef struct kernel_policy_t kernel_policy_t; @@ -170,15 +189,33 @@ struct kernel_policy_t { /** parameters of installed policy */ struct xfrm_selector sel; + /** associated route installed for this policy */ + rt_refcount_t *route; + /** by how many CHILD_SA's this policy is used */ u_int refcount; }; +typedef struct vip_refcount_t vip_refcount_t; + +/** + * Reference counter for for virtual ips. + */ +struct vip_refcount_t { + /** Index of the interface the ip is bound to */ + u_int8_t if_index; + + /** The ip address */ + host_t *ip; + + /** Number of times this ip is used */ + u_int refcount; +}; typedef struct private_kernel_interface_t private_kernel_interface_t; /** - * Private Variables and Functions of kernel_interface class. + * Private variables and functions of kernel_interface class. */ struct private_kernel_interface_t { /** @@ -197,9 +234,14 @@ struct private_kernel_interface_t { pthread_mutex_t pol_mutex; /** - * Netlink communication socket. + * Netlink communication socket for XFRM IPsec. */ - int socket; + int xfrm_socket; + + /** + * Netlink communication socket for routing & addresses. + */ + int rt_socket; /** * Process id of kernel thread @@ -217,9 +259,14 @@ struct private_kernel_interface_t { linked_list_t *responses; /** - * Thread which receives messages. + * Thread which receives xfrm messages. + */ + pthread_t xfrm_thread; + + /** + * Thread which receives rt messages. */ - pthread_t thread; + pthread_t rt_thread; /** * Mutex locks access to replies list. @@ -230,35 +277,56 @@ struct private_kernel_interface_t { * Condvar allows signaling of threads waiting for a reply. */ pthread_cond_t condvar; -}; + + /** + * List of reference counter objects for all virtual ips. + */ + linked_list_t *vips; + /** + * Mutex to lock access to vip list. + */ + pthread_mutex_t vip_mutex; +}; /** - * Send a message down to the kernel and wait for its response + * Sends a message down to the kernel and waits for its response */ static status_t send_message(private_kernel_interface_t *this, - struct nlmsghdr *request, struct nlmsghdr **response) + struct nlmsghdr *request, + struct nlmsghdr **response, + int socket) { size_t length; struct sockaddr_nl addr; request->nlmsg_seq = ++this->seq; - request->nlmsg_pid = 0; + request->nlmsg_pid = getpid(); memset(&addr, 0, sizeof(struct sockaddr_nl)); addr.nl_family = AF_NETLINK; addr.nl_pid = 0; addr.nl_groups = 0; + + /* + // set timeout to 10 secs + struct timespec tm; + tm.tv_sec = 10; + */ - length = sendto(this->socket,(void *)request, request->nlmsg_len, 0, + length = sendto(socket,(void *)request, request->nlmsg_len, 0, (struct sockaddr *)&addr, sizeof(addr)); + DBG2(DBG_IKE, "%d bytes sent to kernel", length); if (length < 0) { + DBG1(DBG_IKE,"0 byte could be sent"); return FAILED; } else if (length != request->nlmsg_len) { + DBG1(DBG_IKE,"Request length %d does not match the sent bytes %d", + request->nlmsg_len, length); return FAILED; } @@ -290,6 +358,8 @@ static status_t send_message(private_kernel_interface_t *this, break; } /* TODO: we should time out, if something goes wrong!??? */ + //if(pthread_cond_timedwait(&(this->condvar), &(this->rep_mutex), &tm) == ETIMEDOUT) + // return FAILED; pthread_cond_wait(&(this->condvar), &(this->rep_mutex)); } @@ -299,46 +369,73 @@ static status_t send_message(private_kernel_interface_t *this, } /** - * Implementation of private_kernel_interface_t.receive_messages. + * Reads from a netlink socket and returns the message in a buffer. */ -static void receive_messages(private_kernel_interface_t *this) +static void netlink_package_receiver(int socket, unsigned char *response, int response_size) { - while(TRUE) + while (TRUE) { - unsigned char response[BUFFER_SIZE]; - struct nlmsghdr *hdr, *listed_response; - while (TRUE) + struct sockaddr_nl addr; + socklen_t addr_length; + size_t length; + addr_length = sizeof(addr); + + length = recvfrom(socket, response, response_size, 0, (struct sockaddr*)&addr, &addr_length); + if (length < 0) { - struct sockaddr_nl addr; - socklen_t addr_length; - size_t length; - - addr_length = sizeof(addr); - - length = recvfrom(this->socket, &response, sizeof(response), 0, (struct sockaddr*)&addr, &addr_length); - if (length < 0) - { - if (errno == EINTR) - { - /* interrupted, try again */ - continue; - } - charon->kill(charon, "receiving from netlink socket failed"); - } - if (!NLMSG_OK((struct nlmsghdr *)response, length)) + if (errno == EINTR) { - /* bad netlink message */ + /* interrupted, try again */ continue; } - if (addr.nl_pid != 0) - { - /* not from kernel. not interested, try another one */ - continue; - } - /* good message, handle it */ - break; + charon->kill(charon, "receiving from netlink socket failed\n"); } + if (!NLMSG_OK((struct nlmsghdr *)response, length)) + { + /* bad netlink message */ + continue; + } + + if (addr.nl_pid != 0) + { + /* not from kernel. not interested, try another one */ + continue; + } + /* good message, handle it */ + return; + } +} + +/** + * Takes a Netlink package from the response buffer and writes it to this->responses. + * Then it signals all waiting threads. + */ +static void add_to_package_list(private_kernel_interface_t *this, unsigned char *response) +{ + struct nlmsghdr *hdr = (struct nlmsghdr*)response; + /* add response to queue */ + struct nlmsghdr *listed_response = malloc(hdr->nlmsg_len); + memcpy(listed_response, response, hdr->nlmsg_len); + + pthread_mutex_lock(&(this->rep_mutex)); + this->responses->insert_last(this->responses, (void*)listed_response); + pthread_mutex_unlock(&(this->rep_mutex)); + /* signal ALL waiting threads */ + pthread_cond_broadcast(&(this->condvar)); +} + +/** + * Receives packages from this->xfrm_socket and puts them to this->package_list + */ +static void receive_xfrm_messages(private_kernel_interface_t *this) +{ + while(TRUE) + { + unsigned char response[BUFFER_SIZE]; + struct nlmsghdr *hdr; + netlink_package_receiver(this->xfrm_socket, response, sizeof(response)); + /* we handle ACQUIRE and EXPIRE messages directly */ hdr = (struct nlmsghdr*)response; if (hdr->nlmsg_type == XFRM_MSG_ACQUIRE) @@ -396,28 +493,47 @@ static void receive_messages(private_kernel_interface_t *this) } charon->job_queue->add(charon->job_queue, job); } - /* NLMSG_ERROR is sent back for acknowledge (or on error), an - * XFRM_MSG_NEWSA is returned when we alloc spis and when - * updating SAs. - * XFRM_MSG_NEWPOLICY is returned when we query a policy. - * list these responses for the sender - */ - else if (hdr->nlmsg_type == NLMSG_ERROR || - hdr->nlmsg_type == XFRM_MSG_NEWSA || - hdr->nlmsg_type == XFRM_MSG_NEWPOLICY) + /* NLMSG_ERROR is sent back for acknowledge (or on error). + * XFRM_MSG_NEWSA is returned when we alloc spis and when + * updating SAs. + * XFRM_MSG_NEWPOLICY is returned when we query a policy. + */ + else if (hdr->nlmsg_type == NLMSG_ERROR + || hdr->nlmsg_type == XFRM_MSG_NEWSA + || hdr->nlmsg_type == XFRM_MSG_NEWPOLICY) { - /* add response to queue */ - listed_response = malloc(hdr->nlmsg_len); - memcpy(listed_response, &response, hdr->nlmsg_len); - - pthread_mutex_lock(&(this->rep_mutex)); - this->responses->insert_last(this->responses, (void*)listed_response); - pthread_mutex_unlock(&(this->rep_mutex)); - /* signal ALL waiting threads */ - pthread_cond_broadcast(&(this->condvar)); + add_to_package_list(this, response); + } + /* we are not interested in anything other. + * anyway, move on to the next message + */ + continue; + } +} + +/** + * Receives packages from this->rt_socket and puts them to this->package_list + */ +static void receive_rt_messages(private_kernel_interface_t *this) +{ + while(TRUE) + { + unsigned char response[BUFFER_SIZE]; + struct nlmsghdr *hdr; + netlink_package_receiver(this->rt_socket,response,BUFFER_SIZE); + + hdr = (struct nlmsghdr*)response; + /* NLMSG_ERROR is sent back for acknowledge (or on error). + * RTM_NEWROUTE is returned when we add a route. + */ + if (hdr->nlmsg_type == NLMSG_ERROR || + hdr->nlmsg_type == RTM_NEWROUTE) + { + add_to_package_list(this, response); } /* we are not interested in anything other. - * anyway, move on to the next message */ + * anyway, move on to the next message. + */ continue; } } @@ -464,7 +580,7 @@ static status_t get_spi(private_kernel_interface_t *this, userspi->min = 0xc0000000; userspi->max = 0xcFFFFFFF; - if (send_message(this, hdr, &response) != SUCCESS) + if (send_message(this, hdr, &response, this->xfrm_socket) != SUCCESS) { DBG1(DBG_KNL, "netlink communication failed"); return FAILED; @@ -627,12 +743,12 @@ static status_t add_sa(private_kernel_interface_t *this, * -> does that mean that NAT-T encap doesn't work in transport mode? * No. The reason the kernel ignores NAT-OA is that it recomputes * (or, rather, just ignores) the checksum. If packets pass - * the IPSec checks it marks them "checksum ok" so OA isn't needed. */ + * the IPsec checks it marks them "checksum ok" so OA isn't needed. */ rthdr = XFRM_RTA_NEXT(rthdr); } - if (send_message(this, hdr, &response) != SUCCESS) + if (send_message(this, hdr, &response, this->xfrm_socket) != SUCCESS) { DBG1(DBG_KNL, "netlink communication failed"); return FAILED; @@ -684,7 +800,7 @@ static status_t update_sa( sa_id->proto = (protocol == PROTO_ESP) ? KERNEL_ESP : KERNEL_AH; sa_id->family = dst->get_family(dst); - if (send_message(this, hdr, &update) != SUCCESS) + if (send_message(this, hdr, &update, this->xfrm_socket) != SUCCESS) { DBG1(DBG_KNL, "netlink communication failed"); return FAILED; @@ -744,7 +860,7 @@ static status_t update_sa( } } - if (send_message(this, update, &response) != SUCCESS) + if (send_message(this, update, &response, this->xfrm_socket) != SUCCESS) { DBG1(DBG_KNL, "netlink communication failed"); free(update); @@ -798,7 +914,7 @@ static status_t query_sa(private_kernel_interface_t *this, host_t *dst, sa_id->proto = (protocol == PROTO_ESP) ? KERNEL_ESP : KERNEL_AH; sa_id->family = dst->get_family(dst); - if (send_message(this, hdr, &response) != SUCCESS) + if (send_message(this, hdr, &response, this->xfrm_socket) != SUCCESS) { DBG1(DBG_KNL, "netlink communication failed"); return FAILED; @@ -850,7 +966,7 @@ static status_t del_sa(private_kernel_interface_t *this, host_t *dst, sa_id->proto = (protocol == PROTO_ESP) ? KERNEL_ESP : KERNEL_AH; sa_id->family = dst->get_family(dst); - if (send_message(this, hdr, &response) != SUCCESS) + if (send_message(this, hdr, &response, this->xfrm_socket) != SUCCESS) { DBG1(DBG_KNL, "netlink communication failed"); return FAILED; @@ -880,7 +996,7 @@ static void ts2subnet(traffic_selector_t* ts, /* there is no way to do this cleanly, as the address range may * be anything else but a subnet. We use from_addr as subnet * and try to calculate a usable subnet mask. - */ + */ int byte, bit; bool found = FALSE; chunk_t from, to; @@ -891,7 +1007,8 @@ static void ts2subnet(traffic_selector_t* ts, *mask = (size * 8); /* go trough all bits of the addresses, beginning in the front. - * As longer as they equal, the subnet gets larger */ + * as long as they are equal, the subnet gets larger + */ for (byte = 0; byte < size; byte++) { for (bit = 7; bit >= 0; bit--) @@ -963,6 +1080,106 @@ static struct xfrm_selector ts2selector(traffic_selector_t *src, } /** + * Tries to find an ip address of a local interface that is included in the + * supplied traffic selector. + */ +static status_t find_addr_by_ts(traffic_selector_t *ts, host_t **ip) +{ + host_t *try = NULL; + +#ifdef HAVE_GETIFADDRS + struct ifaddrs *list; + struct ifaddrs *cur; + + if (getifaddrs(&list) < 0) + { + return FAILED; + } + + for (cur = list; cur != NULL; cur = cur->ifa_next) + { + if (!(cur->ifa_flags & IFF_UP) || !cur->ifa_addr) + { + /* ignore interfaces which are down or have no address assigned */ + continue; + } + + try = host_create_from_sockaddr(cur->ifa_addr); + + if (try && ts->includes(ts, try)) + { + if (ip) + { + *ip = try; + } + freeifaddrs(list); + return SUCCESS; + } + + DESTROY_IF(try); + } + freeifaddrs(list); + return FAILED; +#else /* !HAVE_GETIFADDRS */ + + /* only IPv4 supported yet */ + if (ts->get_type != TS_IPV4_ADDR_RANGE) + { + return FAILED; + } + + int skt = socket(PF_INET, SOCK_DGRAM, 0); + struct ifconf conf; + struct ifreq reqs[16]; + + conf.ifc_len = sizeof(reqs); + conf.ifc_req = reqs; + + if (ioctl(skt, SIOCGIFCONF, &conf) == -1) + { + DBG1(DBG_NET, "checking address using ioctl() failed: %m"); + close(skt); + return FAILED; + } + close(skt); + + while (conf.ifc_len >= sizeof(struct ifreq)) + { + /* only IPv4 supported yet */ + if (conf.ifc_req->ifr_addr.sa_family != AF_INET) + { + continue; + } + + try = host_create_from_sockaddr(conf.ifc_req->ifr_addr); + + if (try && ts->includes(ts, try)) + { + if (ip) + { + *ip = try; + } + + return SUCCESS; + } + + DESTROY_IF(try); + + conf.ifc_len -= sizeof(struct ifreq); + conf.ifc_req++; + } + return FAILED; +#endif /* HAVE_GETIFADDRS */ +} + +/** + * forward declarations + */ +static status_t manage_srcroute(private_kernel_interface_t*,int,int,rt_refcount_t*); +static int get_iface(private_kernel_interface_t*,host_t*); +static void rt_refcount_destroy(rt_refcount_t*); + +/** * Implementation of kernel_interface_t.add_policy. */ static status_t add_policy(private_kernel_interface_t *this, @@ -980,7 +1197,6 @@ static status_t add_policy(private_kernel_interface_t *this, struct nlmsghdr *response; struct xfrm_userpolicy_info *policy_info; struct nlmsghdr *hdr; - status_t status = SUCCESS; /* create a policy */ policy = malloc_thing(kernel_policy_t); @@ -1075,7 +1291,7 @@ static status_t add_policy(private_kernel_interface_t *this, host2xfrm(src, &tmpl->saddr); host2xfrm(dst, &tmpl->id.daddr); - if (send_message(this, hdr, &response) != SUCCESS) + if (send_message(this, hdr, &response, this->xfrm_socket) != SUCCESS) { DBG1(DBG_KNL, "netlink communication failed"); return FAILED; @@ -1083,17 +1299,45 @@ static status_t add_policy(private_kernel_interface_t *this, else if (response->nlmsg_type != NLMSG_ERROR) { DBG1(DBG_KNL, "netlink request XFRM_MSG_UPDPOLICY not acknowledged"); - status = FAILED; + free(response); + return FAILED; } else if (((struct nlmsgerr*)NLMSG_DATA(response))->error) { DBG1(DBG_KNL, "netlink request XFRM_MSG_UPDPOLICY got an error: %s", strerror(-((struct nlmsgerr*)NLMSG_DATA(response))->error)); - status = FAILED; + free(response); + return FAILED; } + + if (direction == POLICY_FWD) + { + policy->route = malloc_thing(rt_refcount_t); + if (find_addr_by_ts(dst_ts, &policy->route->src_ip) == SUCCESS) + { + policy->route->if_index = get_iface(this, src); + policy->route->dst_net = chunk_alloc(policy->sel.family == AF_INET ? 4 : 16); + memcpy(policy->route->dst_net.ptr, &policy->sel.saddr, policy->route->dst_net.len); + policy->route->prefixlen = policy->sel.prefixlen_s; + + if (manage_srcroute(this, RTM_NEWROUTE, NLM_F_CREATE | NLM_F_EXCL, + policy->route) != SUCCESS) + { + DBG1(DBG_KNL, "error installing route"); + rt_refcount_destroy(policy->route); + policy->route = NULL; + } + } + else + { + free(policy->route); + policy->route = NULL; + } + } + free(response); - return status; + return SUCCESS; } /** @@ -1124,7 +1368,7 @@ static status_t query_policy(private_kernel_interface_t *this, policy_id->sel = ts2selector(src_ts, dst_ts); policy_id->dir = direction; - if (send_message(this, hdr, &response) != SUCCESS) + if (send_message(this, hdr, &response, this->xfrm_socket) != SUCCESS) { DBG1(DBG_KNL, "netlink communication failed"); return FAILED; @@ -1166,12 +1410,12 @@ static status_t del_policy(private_kernel_interface_t *this, policy_dir_t direction) { kernel_policy_t *current, policy, *to_delete = NULL; + rt_refcount_t *route; unsigned char request[BUFFER_SIZE]; struct nlmsghdr *response; struct nlmsghdr *hdr; struct xfrm_userpolicy_id *policy_id; iterator_t *iterator; - status_t status = SUCCESS; DBG2(DBG_KNL, "deleting policy"); @@ -1221,9 +1465,10 @@ static status_t del_policy(private_kernel_interface_t *this, policy_id->sel = to_delete->sel; policy_id->dir = direction; + route = to_delete->route; free(to_delete); - if (send_message(this, hdr, &response) != SUCCESS) + if (send_message(this, hdr, &response, this->xfrm_socket) != SUCCESS) { DBG1(DBG_KNL, "netlink communication failed"); return FAILED; @@ -1231,27 +1476,347 @@ static status_t del_policy(private_kernel_interface_t *this, else if (response->nlmsg_type != NLMSG_ERROR) { DBG1(DBG_KNL, "netlink request XFRM_MSG_DELPOLICY not acknowledged"); - status = FAILED; + free(response); + return FAILED; } else if (((struct nlmsgerr*)NLMSG_DATA(response))->error) { DBG1(DBG_KNL, "netlink request XFRM_MSG_DELPOLICY got an error: %s", strerror(-((struct nlmsgerr*)NLMSG_DATA(response))->error)); - status = FAILED; + free(response); + return FAILED; + } + + if (route) + { + if (manage_srcroute(this, RTM_DELROUTE, 0, route) != SUCCESS) + { + DBG1(DBG_KNL, "error uninstalling route"); + } + rt_refcount_destroy(route); } free(response); - return status; + return SUCCESS; +} + +/** + * Sends an RT_NETLINK request to the kernel. + */ +static status_t send_rtrequest(private_kernel_interface_t *this, struct nlmsghdr *hdr) +{ + struct nlmsghdr *response; + + if (send_message(this, hdr, &response, this->rt_socket) != SUCCESS) + { + DBG1(DBG_KNL, "netlink communication failed"); + return FAILED; + } + else if (((struct nlmsgerr*)NLMSG_DATA(response))->error) + { + DBG1(DBG_KNL, "netlink request got an error: %s (%d)", + strerror(-((struct nlmsgerr*)NLMSG_DATA(response))->error), + -((struct nlmsgerr*)NLMSG_DATA(response))->error); + free(response); + return FAILED; + } + + free(response); + return SUCCESS; +} + +/** + * Creates an rtattr and adds it to the netlink message. + */ +static status_t add_rtattr(struct nlmsghdr *hdr, int max_len, + int rta_type, void *data, int data_len) +{ + struct rtattr *rta; + + if (NLMSG_ALIGN(hdr->nlmsg_len) + RTA_ALIGN(data_len) > max_len) + { + DBG1(DBG_KNL, "netlink message exceeded bound of %d", max_len); + return FAILED; + } + + rta = (struct rtattr*)(((char*)hdr) + NLMSG_ALIGN(hdr->nlmsg_len)); + + rta->rta_type = rta_type; + rta->rta_len = RTA_LENGTH(data_len); + memcpy(RTA_DATA(rta), data, data_len); + + hdr->nlmsg_len = NLMSG_ALIGN(hdr->nlmsg_len) + rta->rta_len; + return SUCCESS; +} + +/** + * Manages the creation and deletion of ip addresses on an interface. + * By setting the appropriate nlmsg_type, the ip will be set or unset. + */ +static status_t manage_ipaddr(private_kernel_interface_t *this, int nlmsg_type, + int flags, int if_index, host_t *ip) +{ + unsigned char request[BUFFER_SIZE]; + struct nlmsghdr *hdr; + struct ifaddrmsg *msg; + chunk_t chunk; + + DBG2(DBG_KNL, "adding virtual IP %H to interface %d", ip, if_index); + + memset(&request, 0, sizeof(request)); + + chunk = ip->get_address(ip); + + hdr = (struct nlmsghdr*)request; + hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | flags; + hdr->nlmsg_type = nlmsg_type; + hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)); + + msg = (struct ifaddrmsg*)NLMSG_DATA(hdr); + msg->ifa_family = ip->get_family(ip); + msg->ifa_flags = 0; + msg->ifa_prefixlen = 8 * chunk.len; + msg->ifa_scope = RT_SCOPE_UNIVERSE; + msg->ifa_index = if_index; + + if (add_rtattr(hdr, sizeof(request), IFA_LOCAL, + chunk.ptr, chunk.len) != SUCCESS) + { + return FAILED; + } + + return send_rtrequest(this, hdr); +} + +static int get_iface(private_kernel_interface_t *this, host_t* ip) +{ + unsigned char request[BUFFER_SIZE]; + struct nlmsghdr *hdr; + struct rtmsg *msg; + struct rtattr* rta; + chunk_t chunk; + int ifindex = 0; + + DBG2(DBG_KNL, "getting interface for %H", ip); + + memset(&request, 0, sizeof(request)); + + chunk = ip->get_address(ip); + + hdr = (struct nlmsghdr*)request; + hdr->nlmsg_flags = NLM_F_REQUEST; + hdr->nlmsg_type = RTM_GETROUTE; + hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); + + msg = (struct rtmsg*)NLMSG_DATA(hdr); + msg->rtm_family = ip->get_family(ip); + msg->rtm_table = 0; + msg->rtm_protocol = 0; + msg->rtm_scope = 0; + msg->rtm_type = 0; + msg->rtm_src_len = 0; + msg->rtm_dst_len = 8 * chunk.len; + msg->rtm_tos = 0; + msg->rtm_flags = RT_TABLE_UNSPEC | RTPROT_UNSPEC; + + if (add_rtattr(hdr, sizeof(request), RTA_DST, + chunk.ptr, chunk.len) != SUCCESS) + { + return 0; + } + + if(send_message(this, hdr, &hdr, this->rt_socket) != SUCCESS) + { + return 0; + } + rta = (struct rtattr*)(NLMSG_DATA(hdr) + NLMSG_LENGTH(sizeof(struct rtmsg))); + + while(RTA_OK(rta, hdr->nlmsg_len)) + { + if(rta->rta_type == RTA_OIF) + { + ifindex = *((int*)RTA_DATA(rta)); + break; + } + rta = RTA_NEXT(rta, hdr->nlmsg_len); + } + free(hdr); + if (ifindex == 0) + { + DBG1(DBG_KNL, "address %H not reachable, unable to get interface", ip); + } + return ifindex; +} + +/** + * Manages source routes in the routing table. + * By setting the appropriate nlmsg_type, the route will be set or unset. + */ +static status_t manage_srcroute(private_kernel_interface_t *this, + int nlmsg_type, int flags, rt_refcount_t *route) +{ + struct nlmsghdr *hdr; + struct rtmsg *msg; + unsigned char request[BUFFER_SIZE]; + chunk_t src; + + memset(&request, 0, sizeof(request)); + + hdr = (struct nlmsghdr*)request; + hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | flags; + hdr->nlmsg_type = nlmsg_type; + hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); + + msg = (struct rtmsg*)NLMSG_DATA(hdr); + msg->rtm_family = route->src_ip->get_family(route->src_ip); + msg->rtm_dst_len = route->prefixlen; + msg->rtm_table = RT_TABLE_MAIN; + msg->rtm_protocol = RTPROT_STATIC; + msg->rtm_type = RTN_UNICAST; + msg->rtm_scope = RT_SCOPE_UNIVERSE; + + src = route->src_ip->get_address(route->src_ip); + + if (add_rtattr(hdr, sizeof(request), RTA_DST, + route->dst_net.ptr, route->dst_net.len) != SUCCESS) + { + return FAILED; + } + + if (add_rtattr(hdr, sizeof(request), RTA_PREFSRC, + src.ptr, src.len) != SUCCESS) + { + return FAILED; + } + + if (add_rtattr(hdr, sizeof(request), RTA_OIF, + &route->if_index, sizeof(route->if_index)) != SUCCESS) + { + return FAILED; + } + + return send_rtrequest(this, hdr); +} + +/** + * destroy an rt_refcount object + */ +static void rt_refcount_destroy(rt_refcount_t *this) +{ + this->src_ip->destroy(this->src_ip); + chunk_free(&this->dst_net); + free(this); +} + +/** + * destroy a vip_refcount object + */ +static void vip_refcount_destroy(vip_refcount_t *this) +{ + this->ip->destroy(this->ip); + free(this); +} + +/** + * Implementation of kernel_interface_t.add_ip. + */ +static status_t add_ip(private_kernel_interface_t *this, + host_t *virtual_ip, host_t *dst_ip) +{ + int targetif; + vip_refcount_t *listed; + iterator_t *iterator; + + DBG2(DBG_KNL, "adding ip addr: %H", virtual_ip); + + targetif = get_iface(this, dst_ip); + if (targetif == 0) + { + return FAILED; + } + + /* beware of deadlocks (e.g. send/receive packets while holding the lock) */ + iterator = this->vips->create_iterator_locked(this->vips, &(this->vip_mutex)); + while (iterator->iterate(iterator, (void**)&listed)) + { + if (listed->if_index == targetif && + virtual_ip->ip_equals(virtual_ip, listed->ip)) + { + listed->refcount++; + iterator->destroy(iterator); + return SUCCESS; + } + } + iterator->destroy(iterator); + + if (manage_ipaddr(this, RTM_NEWADDR, NLM_F_CREATE | NLM_F_EXCL, + targetif, virtual_ip) == SUCCESS) + { + listed = malloc_thing(vip_refcount_t); + listed->ip = virtual_ip->clone(virtual_ip); + listed->if_index = targetif; + listed->refcount = 1; + this->vips->insert_last(this->vips, listed); + return SUCCESS; + } + + return FAILED; +} + +/** + * Implementation of kernel_interface_t.del_ip. + */ +static status_t del_ip(private_kernel_interface_t *this, + host_t *virtual_ip, host_t *dst_ip) +{ + int targetif; + vip_refcount_t *listed; + iterator_t *iterator; + + DBG2(DBG_KNL, "deleting ip addr: %H", virtual_ip); + + targetif = get_iface(this, dst_ip); + if (targetif == 0) + { + return FAILED; + } + + /* beware of deadlocks (e.g. send/receive packets while holding the lock) */ + iterator = this->vips->create_iterator_locked(this->vips, &(this->vip_mutex)); + while (iterator->iterate(iterator, (void**)&listed)) + { + if (listed->if_index == targetif && + virtual_ip->ip_equals(virtual_ip, listed->ip)) + { + listed->refcount--; + if (listed->refcount == 0) + { + iterator->remove(iterator); + vip_refcount_destroy(listed); + iterator->destroy(iterator); + return manage_ipaddr(this, RTM_DELADDR, 0, targetif, virtual_ip); + } + iterator->destroy(iterator); + return SUCCESS; + } + } + iterator->destroy(iterator); + + return FAILED; } /** * Implementation of kernel_interface_t.destroy. */ static void destroy(private_kernel_interface_t *this) -{ - pthread_cancel(this->thread); - pthread_join(this->thread, NULL); - close(this->socket); +{ + pthread_cancel(this->xfrm_thread); + pthread_join(this->xfrm_thread, NULL); + pthread_cancel(this->rt_thread); + pthread_join(this->rt_thread, NULL); + close(this->xfrm_socket); + close(this->rt_socket); + this->vips->destroy_function(this->vips, (void*)vip_refcount_destroy); this->responses->destroy(this->responses); this->policies->destroy(this->policies); free(this); @@ -1262,7 +1827,8 @@ static void destroy(private_kernel_interface_t *this) */ kernel_interface_t *kernel_interface_create() { - struct sockaddr_nl addr; + struct sockaddr_nl addr_xfrm; + struct sockaddr_nl addr_rt; private_kernel_interface_t *this = malloc_thing(private_kernel_interface_t); /* public functions */ @@ -1274,45 +1840,88 @@ kernel_interface_t *kernel_interface_create() this->public.add_policy = (status_t(*)(kernel_interface_t*,host_t*,host_t*,traffic_selector_t*,traffic_selector_t*,policy_dir_t,protocol_id_t,u_int32_t,bool,mode_t,bool))add_policy; this->public.query_policy = (status_t(*)(kernel_interface_t*,traffic_selector_t*,traffic_selector_t*,policy_dir_t,u_int32_t*))query_policy; this->public.del_policy = (status_t(*)(kernel_interface_t*,traffic_selector_t*,traffic_selector_t*,policy_dir_t))del_policy; + this->public.add_ip = (status_t(*)(kernel_interface_t*,host_t*,host_t*)) add_ip; + this->public.del_ip = (status_t(*)(kernel_interface_t*,host_t*,host_t*)) del_ip; this->public.destroy = (void(*)(kernel_interface_t*)) destroy; /* private members */ this->pid = getpid(); this->responses = linked_list_create(); + this->vips = linked_list_create(); this->policies = linked_list_create(); pthread_mutex_init(&(this->rep_mutex),NULL); pthread_mutex_init(&(this->pol_mutex),NULL); + pthread_mutex_init(&(this->vip_mutex),NULL); pthread_cond_init(&(this->condvar),NULL); this->seq = 0; - /* open netlink socket */ - this->socket = socket(PF_NETLINK, SOCK_RAW, NETLINK_XFRM); - if (this->socket <= 0) + /* open xfrm netlink socket */ + this->xfrm_socket = socket(PF_NETLINK, SOCK_RAW, NETLINK_XFRM); + if (this->xfrm_socket <= 0) { - this->responses->destroy(this->responses); - free(this); - charon->kill(charon, "Unable to create netlink socket"); + DBG1(DBG_KNL, "Unable to create xfrm netlink socket"); + goto kill; } - /* bind the socket and reqister for ACQUIRE & EXPIRE */ - addr.nl_family = AF_NETLINK; - addr.nl_pid = getpid(); - addr.nl_groups = XFRMGRP_ACQUIRE | XFRMGRP_EXPIRE; - if (bind(this->socket, (struct sockaddr*)&addr, sizeof(addr)) != 0) + /* bind the xfrm socket and reqister for ACQUIRE & EXPIRE */ + addr_xfrm.nl_family = AF_NETLINK; + addr_xfrm.nl_pid = getpid(); + addr_xfrm.nl_groups = XFRMGRP_ACQUIRE | XFRMGRP_EXPIRE; + if (bind(this->xfrm_socket, (struct sockaddr*)&addr_xfrm, sizeof(addr_xfrm))) { - this->responses->destroy(this->responses); - close(this->socket); - free(this); - charon->kill(charon, "Unable to bind netlink socket"); + DBG1(DBG_KNL, "Unable to bind xfrm netlink socket"); + goto kill_xfrm; } - if (pthread_create(&this->thread, NULL, (void*(*)(void*))receive_messages, this) != 0) + if (pthread_create(&this->xfrm_thread, NULL, + (void*(*)(void*))receive_xfrm_messages, this)) + { + DBG1(DBG_KNL, "Unable to create xfrm netlink thread"); + goto kill_xfrm; + } + + /* open rt netlink socket */ + this->rt_socket = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (this->rt_socket <= 0) + { + DBG1(DBG_KNL, "Unable to create rt netlink socket"); + goto kill_xfrm_all; + } + + /* bind the socket_rt */ + addr_rt.nl_family = AF_NETLINK; + addr_rt.nl_pid = getpid(); + addr_rt.nl_groups = 0; + if (bind(this->rt_socket, (struct sockaddr*)&addr_rt, sizeof(addr_rt))) { - this->responses->destroy(this->responses); - close(this->socket); - free(this); - charon->kill(charon, "Unable to create netlink thread"); + DBG1(DBG_KNL, "Unable to bind rt netlink socket"); + goto kill_rt; } + if (pthread_create(&this->rt_thread, NULL, + (void*(*)(void*))receive_rt_messages, this)) + { + DBG1(DBG_KNL, "Unable to create rt netlink thread"); + goto kill_rt; + } + return &this->public; + +kill_rt: + close(this->rt_socket); +kill_xfrm_all: + pthread_cancel(this->xfrm_thread); + pthread_join(this->xfrm_thread, NULL); +kill_xfrm: + close(this->xfrm_socket); +kill: + this->responses->destroy(this->responses); + this->policies->destroy(this->policies); + this->vips->destroy(this->vips); + free(this); + charon->kill(charon, "Unable to create kernel_interface"); + return NULL; } + +/* vim: set ts=4 sw=4 noet: */ + diff --git a/src/charon/threads/kernel_interface.h b/src/charon/threads/kernel_interface.h index 991d8e17c..805a2b89d 100644 --- a/src/charon/threads/kernel_interface.h +++ b/src/charon/threads/kernel_interface.h @@ -264,6 +264,40 @@ struct kernel_interface_t { policy_dir_t direction); /** + * @brief Add a virtual IP to an interface. + * + * Virtual IPs are attached to an interface. If an IP is added multiple + * times, the IP is refcounted and not removed until del_ip() was called + * as many times as add_ip(). + * The virtual IP is attached to the interface used to reach a specified + * destination host. + * + * @param this calling object + * @param virtual_ip virtual ip address to assign + * @param dst_ip destination host to select outgoing interface + * @return + * - SUCCESS + * - FAILED if kernel comm failed + */ + status_t (*add_ip) (kernel_interface_t *this, host_t *virtual_ip, + host_t *dst_ip); + + /** + * @brief Remove a virtual IP from an interface. + * + * The kernel interface uses refcounting, see add_ip(). + * + * @param this calling object + * @param virtual_ip virtual ip address to assign + * @param dst_ip destination host to select outgoing interface + * @return + * - SUCCESS + * - FAILED if kernel comm failed + */ + status_t (*del_ip) (kernel_interface_t *this, host_t *virtual_ip, + host_t *dst_ip); + + /** * @brief Destroys a kernel_interface object. * * @param kernel_interface_t calling object diff --git a/src/charon/threads/stroke_interface.c b/src/charon/threads/stroke_interface.c index a4ae03301..c491be975 100755 --- a/src/charon/threads/stroke_interface.c +++ b/src/charon/threads/stroke_interface.c @@ -197,6 +197,7 @@ static void stroke_add_conn(stroke_msg_t *msg, FILE *out) bool my_ca_same = FALSE; bool other_ca_same =FALSE; host_t *my_host, *other_host, *my_subnet, *other_subnet; + host_t *my_vip = NULL, *other_vip = NULL; proposal_t *proposal; traffic_selector_t *my_ts, *other_ts; @@ -205,6 +206,8 @@ static void stroke_add_conn(stroke_msg_t *msg, FILE *out) pop_string(msg, &msg->add_conn.other.address); pop_string(msg, &msg->add_conn.me.subnet); pop_string(msg, &msg->add_conn.other.subnet); + pop_string(msg, &msg->add_conn.me.sourceip); + pop_string(msg, &msg->add_conn.other.sourceip); pop_string(msg, &msg->add_conn.me.id); pop_string(msg, &msg->add_conn.other.id); pop_string(msg, &msg->add_conn.me.cert); @@ -223,6 +226,8 @@ static void stroke_add_conn(stroke_msg_t *msg, FILE *out) DBG2(DBG_CFG, " right=%s", msg->add_conn.other.address); DBG2(DBG_CFG, " leftsubnet=%s", msg->add_conn.me.subnet); DBG2(DBG_CFG, " rightsubnet=%s", msg->add_conn.other.subnet); + DBG2(DBG_CFG, " leftsourceip=%s", msg->add_conn.me.sourceip); + DBG2(DBG_CFG, " rightsourceip=%s", msg->add_conn.other.sourceip); DBG2(DBG_CFG, " leftid=%s", msg->add_conn.me.id); DBG2(DBG_CFG, " rightid=%s", msg->add_conn.other.id); DBG2(DBG_CFG, " leftcert=%s", msg->add_conn.me.cert); @@ -303,7 +308,13 @@ static void stroke_add_conn(stroke_msg_t *msg, FILE *out) my_subnet->destroy(my_subnet); goto destroy_ids; } - + + if (msg->add_conn.me.virtual_ip) + { + my_vip = host_create_from_string(msg->add_conn.me.sourceip, 0); + } + other_vip = host_create_from_string(msg->add_conn.other.sourceip, 0); + my_ts = traffic_selector_create_from_subnet(my_subnet, msg->add_conn.me.subnet ? msg->add_conn.me.subnet_mask : 0, msg->add_conn.me.protocol, msg->add_conn.me.port); @@ -429,7 +440,7 @@ static void stroke_add_conn(stroke_msg_t *msg, FILE *out) connection->add_proposal(connection, proposal); } - policy = policy_create(msg->add_conn.name, my_id, other_id, + policy = policy_create(msg->add_conn.name, my_id, other_id, my_vip, other_vip, msg->add_conn.auth_method, msg->add_conn.eap_type, msg->add_conn.rekey.ipsec_lifetime, msg->add_conn.rekey.ipsec_lifetime - msg->add_conn.rekey.margin, @@ -559,16 +570,7 @@ static void stroke_initiate(stroke_msg_t *msg, FILE *out) return; } - job = initiate_job_create(connection, NULL, policy); - /* - if (msg->output_verbosity < 0) - { - TODO: detach immediately if verbosity is SILENT. Local credential store - is not threadsave yet, so this would cause crashes!! - charon->job_queue->add(charon->job_queue, (job_t*)job); - return; -}*/ - + job = initiate_job_create(connection, policy); charon->bus->set_listen_state(charon->bus, TRUE); charon->job_queue->add(charon->job_queue, (job_t*)job); while (TRUE) @@ -664,10 +666,100 @@ static void stroke_route(stroke_msg_t *msg, FILE *out, bool route) */ static void stroke_terminate(stroke_msg_t *msg, FILE *out) { + char *string, *pos = NULL, *name = NULL; + u_int32_t id = 0; + bool child; + int len; + status_t status = SUCCESS;; + ike_sa_t *ike_sa; + pop_string(msg, &(msg->terminate.name)); - DBG1(DBG_CFG, "received stroke: terminate '%s'", msg->terminate.name); + string = msg->terminate.name; + DBG1(DBG_CFG, "received stroke: terminate '%s'", string); - charon->ike_sa_manager->delete_by_name(charon->ike_sa_manager, msg->terminate.name); + len = strlen(string); + if (len < 1) + { + DBG1(DBG_CFG, "error parsing string"); + return; + } + switch (string[len-1]) + { + case '}': + child = TRUE; + pos = strchr(string, '{'); + break; + case ']': + child = FALSE; + pos = strchr(string, '['); + break; + default: + name = string; + child = FALSE; + break; + } + + if (name) + { /* must be a single name */ + DBG1(DBG_CFG, "check out by single name '%s'", name); + ike_sa = charon->ike_sa_manager->checkout_by_name(charon->ike_sa_manager, + name, child); + } + else if (pos == string + len - 2) + { /* must be name[] or name{} */ + string[len-2] = '\0'; + DBG1(DBG_CFG, "check out by name '%s'", string); + ike_sa = charon->ike_sa_manager->checkout_by_name(charon->ike_sa_manager, + string, child); + } + else + { /* must be name[123] or name{23} */ + string[len-1] = '\0'; + id = atoi(pos + 1); + if (id == 0) + { + DBG1(DBG_CFG, "error parsing string"); + return; + } + DBG1(DBG_CFG, "check out by id '%d'", id); + ike_sa = charon->ike_sa_manager->checkout_by_id(charon->ike_sa_manager, + id, child); + } + if (ike_sa == NULL) + { + DBG1(DBG_CFG, "no such IKE_SA found"); + return; + } + + if (!child) + { + status = ike_sa->delete(ike_sa); + } + else + { + child_sa_t *child_sa; + iterator_t *iterator = ike_sa->create_child_sa_iterator(ike_sa); + while (iterator->iterate(iterator, (void**)&child_sa)) + { + if ((id && id == child_sa->get_reqid(child_sa)) || + (string && streq(string, child_sa->get_name(child_sa)))) + { + u_int32_t spi = child_sa->get_spi(child_sa, TRUE); + protocol_id_t proto = child_sa->get_protocol(child_sa); + + status = ike_sa->delete_child_sa(ike_sa, proto, spi); + break; + } + } + iterator->destroy(iterator); + } + if (status == DESTROY_ME) + { + charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, + ike_sa); + return; + } + charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); } /** diff --git a/src/libstrongswan/utils/host.c b/src/libstrongswan/utils/host.c index f9be454fa..8cbfd6ab8 100644 --- a/src/libstrongswan/utils/host.c +++ b/src/libstrongswan/utils/host.c @@ -6,7 +6,8 @@ */ /* - * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger + * Copyright (C) 2006-2007 Tobias Brunner + * Copyright (C) 2006 Daniel Roethlisberger * Copyright (C) 2005-2006 Martin Willi * Copyright (C) 2005 Jan Hutter * Hochschule fuer Technik Rapperswil @@ -45,7 +46,7 @@ struct private_host_t { union { /** generic type */ struct sockaddr address; - /** maximux sockaddr size */ + /** maximum sockaddr size */ struct sockaddr_storage address_max; /** IPv4 address */ struct sockaddr_in address4; @@ -495,3 +496,31 @@ host_t *host_create_from_sockaddr(sockaddr_t *sockaddr) free(this); return NULL; } + +/* + * Described in header. + */ +host_t *host_create_any(int family) +{ + private_host_t *this = host_create_empty(); + + memset(&this->address_max, 0, sizeof(struct sockaddr_storage)); + this->address.sa_family = family; + + switch (family) + { + case AF_INET: + { + this->socklen = sizeof(struct sockaddr_in); + return &(this->public); + } + case AF_INET6: + { + this->socklen = sizeof(struct sockaddr_in6); + return &this->public; + } + default: + break; + } + return NULL; +} diff --git a/src/libstrongswan/utils/host.h b/src/libstrongswan/utils/host.h index 20b5c6345..461300438 100644 --- a/src/libstrongswan/utils/host.h +++ b/src/libstrongswan/utils/host.h @@ -6,7 +6,8 @@ */ /* - * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger + * Copyright (C) 2006-2007 Tobias Brunner + * Copyright (C) 2006 Daniel Roethlisberger * Copyright (C) 2005-2006 Martin Willi * Copyright (C) 2005 Jan Hutter * Hochschule fuer Technik Rapperswil @@ -216,4 +217,16 @@ host_t *host_create_from_chunk(int family, chunk_t address, u_int16_t port); */ host_t *host_create_from_sockaddr(sockaddr_t *sockaddr); +/** + * @brief Create a host without an address, a "any" host. + * + * @param family family of the any host + * @return + * - host_t object + * - NULL, if family not supported. + * + * @ingroup network + */ +host_t *host_create_any(int family); + #endif /*HOST_H_*/ diff --git a/src/libstrongswan/utils/identification.c b/src/libstrongswan/utils/identification.c index 6c77b41e8..341af39c0 100644 --- a/src/libstrongswan/utils/identification.c +++ b/src/libstrongswan/utils/identification.c @@ -497,7 +497,10 @@ bool match_dn(chunk_t a, chunk_t b, int *wildcards) bool next_a, next_b; /* initialize wildcard counter */ - *wildcards = 0; + if (wildcards) + { + *wildcards = 0; + } /* initialize DN parsing */ if (init_rdn(a, &rdn_a, &attribute_a, &next_a) != SUCCESS @@ -522,7 +525,10 @@ bool match_dn(chunk_t a, chunk_t b, int *wildcards) /* does rdn_b contain a wildcard? */ if (value_b.len == 1 && *value_b.ptr == '*') { - (*wildcards)++; + if (wildcards) + { + (*wildcards)++; + } continue; } /* same lengths for values */ @@ -549,7 +555,10 @@ bool match_dn(chunk_t a, chunk_t b, int *wildcards) } /* the two DNs match! */ - *wildcards = min(*wildcards, MAX_WILDCARDS); + if (wildcards) + { + *wildcards = min(*wildcards, MAX_WILDCARDS); + } return TRUE; } @@ -750,10 +759,16 @@ static bool matches_binary(private_identification_t *this, { if (other->type == ID_ANY) { - *wildcards = MAX_WILDCARDS; + if (wildcards) + { + *wildcards = MAX_WILDCARDS; + } return TRUE; } - *wildcards = 0; + if (wildcards) + { + *wildcards = 0; + } return this->type == other->type && chunk_equals(this->encoded, other->encoded); } @@ -769,7 +784,10 @@ static bool matches_string(private_identification_t *this, if (other->type == ID_ANY) { - *wildcards = MAX_WILDCARDS; + if (wildcards) + { + *wildcards = MAX_WILDCARDS; + } return TRUE; } @@ -779,7 +797,10 @@ static bool matches_string(private_identification_t *this, /* try a binary comparison first */ if (equals_binary(this, other)) { - *wildcards = 0; + if (wildcards) + { + *wildcards = 0; + } return TRUE; } @@ -789,7 +810,10 @@ static bool matches_string(private_identification_t *this, /* check for single wildcard at the head of the string */ if (*other->encoded.ptr == '*') { - *wildcards = 1; + if (wildcards) + { + *wildcards = 1; + } /* single asterisk matches any string */ if (len-- == 1) @@ -809,7 +833,10 @@ static bool matches_string(private_identification_t *this, static bool matches_any(private_identification_t *this, private_identification_t *other, int *wildcards) { - *wildcards = 0; + if (wildcards) + { + *wildcards = 0; + } return other->type == ID_ANY; } @@ -822,7 +849,10 @@ static bool matches_dn(private_identification_t *this, { if (other->type == ID_ANY) { - *wildcards = MAX_WILDCARDS; + if (wildcards) + { + *wildcards = MAX_WILDCARDS; + } return TRUE; } diff --git a/src/libstrongswan/utils/identification.h b/src/libstrongswan/utils/identification.h index 80fc27d7c..59c568eaf 100644 --- a/src/libstrongswan/utils/identification.h +++ b/src/libstrongswan/utils/identification.h @@ -182,7 +182,7 @@ struct identification_t { * * @param this the ID without wildcard * @param other the ID containing a wildcard - * @param wildcards returns the number of wildcards + * @param wildcards returns the number of wildcards, may be NULL * @return TRUE if match is found */ bool (*matches) (identification_t *this, identification_t *other, int *wildcards); diff --git a/src/libstrongswan/utils/iterator.h b/src/libstrongswan/utils/iterator.h index 51a8d6061..02a15c534 100644 --- a/src/libstrongswan/utils/iterator.h +++ b/src/libstrongswan/utils/iterator.h @@ -24,6 +24,19 @@ #ifndef ITERATOR_H_ #define ITERATOR_H_ +#include <library.h> + +/** + * @brief Iterator hook function prototype. + * + * @param param user supplied parameter + * @param in the value the hook receives from the iterator + * @param out the value supplied as a result to the iterator + * @return TRUE to return "out", FALSE to skip this value + */ +typedef bool (iterator_hook_t)(void *param, void *in, void **out); + + typedef struct iterator_t iterator_t; /** @@ -76,8 +89,10 @@ struct iterator_t { * * @param this calling object * @param hook iterator hook which manipulates the iterated value + * @param param user supplied parameter to pass back to the hook */ - void (*set_iterator_hook) (iterator_t *this, void*(*hook)(void*)); + void (*set_iterator_hook) (iterator_t *this, iterator_hook_t *hook, + void *param); /** * @brief Inserts a new item before the given iterator position. diff --git a/src/libstrongswan/utils/linked_list.c b/src/libstrongswan/utils/linked_list.c index 8c5068870..de043a02e 100644 --- a/src/libstrongswan/utils/linked_list.c +++ b/src/libstrongswan/utils/linked_list.c @@ -132,7 +132,12 @@ struct private_iterator_t { /** * iteration hook */ - void* (*hook)(void*); + iterator_hook_t *hook; + + /** + * user parameter for iterator hook + */ + void *hook_param; }; /** @@ -146,23 +151,27 @@ static int get_list_count(private_iterator_t *this) /** * default iterator hook which does nothing */ -static void *iterator_hook(void *value) +static bool iterator_hook(void *param, void *in, void **out) { - return value; + *out = in; + return TRUE; } /** * Implementation of iterator_t.set_iterator_hook. */ -static void set_iterator_hook(private_iterator_t *this, void*(*hook)(void*)) +static void set_iterator_hook(private_iterator_t *this, iterator_hook_t *hook, + void* param) { if (hook == NULL) { this->hook = iterator_hook; + this->hook_param = NULL; } else { this->hook = hook; + this->hook_param = param; } } @@ -178,7 +187,10 @@ static bool iterate(private_iterator_t *this, void** value) if (this->current == NULL) { this->current = (this->forward) ? this->list->first : this->list->last; - *value = this->hook(this->current->value); + if (!this->hook(this->hook_param, this->current->value, value)) + { + return iterate(this, value); + } return TRUE; } if (this->forward) @@ -188,16 +200,21 @@ static bool iterate(private_iterator_t *this, void** value) return FALSE; } this->current = this->current->next; - *value = this->hook(this->current->value); + if (!this->hook(this->hook_param, this->current->value, value)) + { + return iterate(this, value); + } return TRUE; } - /* backward */ if (this->current->previous == NULL) { return FALSE; } this->current = this->current->previous; - *value = this->hook(this->current->value); + if (!this->hook(this->hook_param, this->current->value, value)) + { + return iterate(this, value); + } return TRUE; } @@ -225,11 +242,15 @@ static status_t remove_(private_iterator_t *this) { return NOT_FOUND; } - /* find out the new iterator position */ - if (this->current->previous != NULL) + /* find out the new iterator position, depending on iterator direction */ + if (this->forward && this->current->previous != NULL) { new_current = this->current->previous; } + else if (!this->forward && this->current->next != NULL) + { + new_current = this->current->next; + } else { new_current = NULL; @@ -679,7 +700,7 @@ static iterator_t *create_iterator(private_linked_list_t *linked_list, bool forw this->public.get_count = (int (*) (iterator_t*)) get_list_count; this->public.iterate = (bool (*) (iterator_t*, void **value)) iterate; - this->public.set_iterator_hook = (void(*)(iterator_t*, void*(*)(void*)))set_iterator_hook; + this->public.set_iterator_hook = (void(*)(iterator_t*, iterator_hook_t*, void*))set_iterator_hook; this->public.insert_before = (void (*) (iterator_t*, void *item)) insert_before; this->public.insert_after = (void (*) (iterator_t*, void *item)) insert_after; this->public.replace = (status_t (*) (iterator_t*, void **, void *)) replace; diff --git a/src/starter/confread.c b/src/starter/confread.c index 9f1453394..9e83581fa 100644 --- a/src/starter/confread.c +++ b/src/starter/confread.c @@ -252,7 +252,8 @@ kw_end(starter_conn_t *conn, starter_end_t *end, kw_token_t token plog("# natip and sourceip cannot be defined at the same time"); goto err; } - if (streq(value, "%modeconfig") || streq(value, "%modecfg")) + if (streq(value, "%modeconfig") || streq(value, "%modecfg") || + streq(value, "%config") || streq(value, "%cfg")) { end->modecfg = TRUE; } diff --git a/src/starter/starterstroke.c b/src/starter/starterstroke.c index 28d777a21..79ad33f61 100644 --- a/src/starter/starterstroke.c +++ b/src/starter/starterstroke.c @@ -166,6 +166,9 @@ static void starter_stroke_add_end(stroke_msg_t *msg, stroke_end_t *msg_end, sta msg_end->hostaccess = conn_end->hostaccess; msg_end->protocol = conn_end->protocol; msg_end->port = conn_end->port; + msg_end->virtual_ip = conn_end->modecfg; + ip_address2string(&conn_end->srcip, buffer, sizeof(buffer)); + msg_end->sourceip = push_string(msg, buffer); } int starter_stroke_add_conn(starter_conn_t *conn) diff --git a/src/stroke/stroke.c b/src/stroke/stroke.c index 3d186bd0c..1fed6e281 100644 --- a/src/stroke/stroke.c +++ b/src/stroke/stroke.c @@ -127,6 +127,8 @@ static int add_connection(char *name, msg.add_conn.me.address = push_string(&msg, my_addr); msg.add_conn.me.subnet = push_string(&msg, my_net); msg.add_conn.me.subnet_mask = my_netmask; + msg.add_conn.me.sourceip = NULL; + msg.add_conn.me.virtual_ip = 0; msg.add_conn.me.cert = NULL; msg.add_conn.me.ca = NULL; msg.add_conn.me.sendcert = 1; @@ -138,6 +140,8 @@ static int add_connection(char *name, msg.add_conn.other.address = push_string(&msg, other_addr); msg.add_conn.other.subnet = push_string(&msg, other_net); msg.add_conn.other.subnet_mask = other_netmask; + msg.add_conn.other.sourceip = NULL; + msg.add_conn.other.virtual_ip = 0; msg.add_conn.other.cert = NULL; msg.add_conn.other.ca = NULL; msg.add_conn.other.sendcert = 1; diff --git a/src/stroke/stroke.h b/src/stroke/stroke.h index ced655583..57ddf29be 100644 --- a/src/stroke/stroke.h +++ b/src/stroke/stroke.h @@ -100,6 +100,8 @@ struct stroke_end_t { char *ca; char *updown; char *address; + char *sourceip; + u_int8_t virtual_ip; char *subnet; int subnet_mask; int sendcert; |