diff options
91 files changed, 3096 insertions, 842 deletions
@@ -20,6 +20,29 @@ strongswan-5.0.1 PAM directly anymore, but can use any XAuth backend to verify credentials, including xauth-pam. +- An EAP-Nak payload is returned by clients if the gateway requests an EAP + method that the client does not support. Clients can also request a specific + EAP method by configuring that method with leftauth. + +- The eap-dynamic plugin handles EAP-Nak payloads returned by clients and uses + these to select a different EAP method supported/requested by the client. + The plugin initially requests the first registered method or the first method + configured with charon.plugins.eap-dynamic.preferred. + +- The new left/rightdns options specify connection specific DNS servers to + request/respond in IKEv2 configuration payloads or IKEv2 mode config. leftdns + can be any (comma separated) combination of %config4 and %config6 to request + multiple servers, both for IPv4 and IPv6. rightdns takes a list of DNS server + IP addresses to return. + +- The left/rightsourceip options now accept multiple addresses or pools. + leftsourceip can be any (comma separated) combination of %config4, %config6 + or fixed IP addresses to request. rightsourceip accepts multiple explicitly + specified or referenced named pools. + +- Multiple connections can now share a single address pool when they use the + same definition in one of the rightsourceip pools. + - The autotools build has been migrated to use a config.h header. strongSwan development headers will get installed during "make install" if --with-dev-headers has been passed to ./configure. diff --git a/configure.in b/configure.in index 2ab96e078..f233b0ab4 100644 --- a/configure.in +++ b/configure.in @@ -16,7 +16,7 @@ dnl =========================== dnl initialize & set some vars dnl =========================== -AC_INIT(strongSwan,5.0.1dr3) +AC_INIT(strongSwan,5.0.1dr4) AM_INIT_AUTOMAKE(tar-ustar) AC_CONFIG_MACRO_DIR([m4/config]) AC_CONFIG_HEADERS([config.h]) @@ -154,6 +154,7 @@ ARG_ENABL_SET([eap-tls], [enable EAP TLS authentication module.]) ARG_ENABL_SET([eap-ttls], [enable EAP TTLS authentication module.]) ARG_ENABL_SET([eap-peap], [enable EAP PEAP authentication module.]) ARG_ENABL_SET([eap-tnc], [enable EAP TNC trusted network connect module.]) +ARG_ENABL_SET([eap-dynamic], [enable dynamic EAP proxy module.]) ARG_ENABL_SET([eap-radius], [enable RADIUS proxy authentication module.]) ARG_DISBL_SET([xauth-generic], [disable generic XAuth backend.]) ARG_ENABL_SET([xauth-eap], [enable XAuth backend using EAP methods to verify passwords.]) @@ -922,6 +923,7 @@ ADD_PLUGIN([eap-simaka-reauth], [c charon]) ADD_PLUGIN([eap-md5], [c charon nm]) ADD_PLUGIN([eap-gtc], [c charon nm]) ADD_PLUGIN([eap-mschapv2], [c charon nm]) +ADD_PLUGIN([eap-dynamic], [c charon]) ADD_PLUGIN([eap-radius], [c charon]) ADD_PLUGIN([eap-tls], [c charon nm]) ADD_PLUGIN([eap-ttls], [c charon nm]) @@ -1055,6 +1057,7 @@ AM_CONDITIONAL(USE_EAP_TLS, test x$eap_tls = xtrue) AM_CONDITIONAL(USE_EAP_TTLS, test x$eap_ttls = xtrue) AM_CONDITIONAL(USE_EAP_PEAP, test x$eap_peap = xtrue) AM_CONDITIONAL(USE_EAP_TNC, test x$eap_tnc = xtrue) +AM_CONDITIONAL(USE_EAP_DYNAMIC, test x$eap_dynamic = xtrue) AM_CONDITIONAL(USE_EAP_RADIUS, test x$eap_radius = xtrue) AM_CONDITIONAL(USE_XAUTH_GENERIC, test x$xauth_generic = xtrue) AM_CONDITIONAL(USE_XAUTH_EAP, test x$xauth_eap = xtrue) @@ -1222,6 +1225,7 @@ AC_OUTPUT( src/libcharon/Makefile src/libcharon/plugins/eap_aka/Makefile src/libcharon/plugins/eap_aka_3gpp2/Makefile + src/libcharon/plugins/eap_dynamic/Makefile src/libcharon/plugins/eap_identity/Makefile src/libcharon/plugins/eap_md5/Makefile src/libcharon/plugins/eap_gtc/Makefile diff --git a/man/ipsec.conf.5.in b/man/ipsec.conf.5.in index ca77ee7de..7c336c451 100644 --- a/man/ipsec.conf.5.in +++ b/man/ipsec.conf.5.in @@ -523,6 +523,7 @@ an optional EAP method can be appended. Currently defined methods are .BR eap-sim , .BR eap-tls , .BR eap-ttls , +.BR eap-dynamic , and .BR eap-radius . Alternatively, IANA assigned EAP method numbers are accepted. Vendor specific @@ -585,6 +586,16 @@ Comma separated list of certificate policy OIDs the peer's certificate must have. OIDs are specified using the numerical dotted representation. .TP +.BR leftdns " = <servers>" +Comma separated list of DNS server addresses to exchange as configuration +attributes. On the initiator, a server is a fixed IPv4 / IPv6 address, or +.B %config4 +/ +.B %config6 +to request attributes without an address. On the responder, +only fixed IPv4 /IPv6 addresses are allowed and define DNS servers assigned +to the client. +.TP .BR leftfirewall " = yes | " no whether the left participant is doing forwarding-firewalling (including masquerading) using iptables for traffic from \fIleftsubnet\fR, @@ -690,19 +701,19 @@ and the latter meaning that the peer must send a certificate request payload in order to get a certificate in return. .TP -.BR leftsourceip " = %config | %cfg | %modeconfig | %modecfg | <ip address>" -The internal source IP to use in a tunnel, also known as virtual IP. If the -value is one of the synonyms +.BR leftsourceip " = %config4 | %config6 | <ip address>" +Comma separated list of internal source IPs to use in a tunnel, also known as +virtual IP. If the value is one of the synonyms .BR %config , .BR %cfg , .BR %modeconfig , or .BR %modecfg , -an address is requested from the peer. +an address (from the tunnel address family) is requested from the peer. .TP .BR rightsourceip " = %config | <network>/<netmask> | %poolname" -The internal source IP to use in a tunnel for the remote peer. If the -value is +Comma separated list of internal source IPs to use in a tunnel for the remote +peer. If the value is .B %config on the responder side, the initiator must propose an address which is then echoed back. Also supported are address pools expressed as diff --git a/man/strongswan.conf.5.in b/man/strongswan.conf.5.in index 4fba2344b..8df6cf1fa 100644 --- a/man/strongswan.conf.5.in +++ b/man/strongswan.conf.5.in @@ -303,6 +303,16 @@ enable loaded duplicheck plugin .BR charon.plugins.eap-aka-3ggp2.seq_check .TP +.BR charon.plugins.eap-dynamic.preferred +The preferred EAP method(s) to be used. If it is not given the first +registered method will be used initially. If a comma separated list is given +the methods are tried in the given order before trying the rest of the +registered methods. +.TP +.BR charon.plugins.eap-dynamic.prefer_user " [no]" +If enabled the EAP methods proposed in an EAP-Nak message sent by the peer are +preferred over the methods registered locally. +.TP .BR charon.plugins.eap-gtc.backend " [pam]" XAuth backend to be used for credential verification .TP diff --git a/src/_updown/_updown.in b/src/_updown/_updown.in index 2c742c010..3a40e2110 100644 --- a/src/_updown/_updown.in +++ b/src/_updown/_updown.in @@ -73,8 +73,12 @@ # just the host, this will be 255.255.255.255. # # PLUTO_MY_SOURCEIP -# if non-empty, then the source address for the route will be -# set to this IP address. +# PLUTO_MY_SOURCEIP4_$i +# PLUTO_MY_SOURCEIP6_$i +# contains IPv4/IPv6 virtual IP received from a responder, +# $i enumerates from 1 to the number of IP per address family. +# PLUTO_MY_SOURCEIP is a legacy variable and equals to the first +# virtual IP, IPv4 or IPv6. # # PLUTO_MY_PROTOCOL # is the IP protocol that will be transported. @@ -128,6 +132,12 @@ # contains the remote UDP port in the case of ESP_IN_UDP # encapsulation # +# PLUTO_DNS4_$i +# PLUTO_DNS6_$i +# contains IPv4/IPv6 DNS server attribute received from a +# responder, $i enumerates from 1 to the number of servers per +# address family. +# # define a minimum PATH environment in case it is not set PATH="/sbin:/bin:/usr/sbin:/usr/bin:@sbindir@" diff --git a/src/charon-nm/nm/nm_handler.c b/src/charon-nm/nm/nm_handler.c index 408129ebe..28aa04b31 100644 --- a/src/charon-nm/nm/nm_handler.c +++ b/src/charon-nm/nm/nm_handler.c @@ -92,15 +92,17 @@ static bool enumerate_dns(enumerator_t *this, } METHOD(attribute_handler_t, create_attribute_enumerator, enumerator_t*, - private_nm_handler_t *this, identification_t *server, host_t *vip) + private_nm_handler_t *this, identification_t *server, linked_list_t *vips) { - if (vip && vip->get_family(vip) == AF_INET) - { /* no IPv6 attributes yet */ - enumerator_t *enumerator = malloc_thing(enumerator_t); - /* enumerate DNS attribute first ... */ - enumerator->enumerate = (void*)enumerate_dns; - enumerator->destroy = (void*)free; + if (vips->get_count(vips)) + { + enumerator_t *enumerator; + INIT(enumerator, + /* enumerate DNS attribute first ... */ + .enumerate = (void*)enumerate_dns, + .destroy = (void*)free, + ); return enumerator; } return enumerator_create_empty(); diff --git a/src/charon-nm/nm/nm_service.c b/src/charon-nm/nm/nm_service.c index ccfa210a0..461837c0c 100644 --- a/src/charon-nm/nm/nm_service.c +++ b/src/charon-nm/nm/nm_service.c @@ -507,8 +507,11 @@ static gboolean connect_(NMVPNPlugin *plugin, NMConnection *connection, 600, 600, /* jitter, over 10min */ TRUE, FALSE, /* mobike, aggressive */ 0, 0, /* DPD delay, timeout */ - virtual ? host_create_from_string("0.0.0.0", 0) : NULL, - NULL, FALSE, NULL, NULL); /* pool, mediation */ + FALSE, NULL, NULL); /* mediation */ + if (virtual) + { + peer_cfg->add_virtual_ip(peer_cfg, host_create_from_string("0.0.0.0", 0)); + } auth = auth_cfg_create(); auth->add(auth, AUTH_RULE_AUTH_CLASS, auth_class); auth->add(auth, AUTH_RULE_IDENTITY, user); diff --git a/src/conftest/config.c b/src/conftest/config.c index a3f23f3a4..cbc6ac05f 100644 --- a/src/conftest/config.c +++ b/src/conftest/config.c @@ -253,7 +253,7 @@ static peer_cfg_t *load_peer_config(private_config_t *this, ike_cfg = load_ike_config(this, settings, config); peer_cfg = peer_cfg_create(config, IKEV2, ike_cfg, CERT_ALWAYS_SEND, UNIQUE_NO, 1, 0, 0, 0, 0, FALSE, FALSE, 0, 0, - NULL, NULL, FALSE, NULL, NULL); + FALSE, NULL, NULL); auth = auth_cfg_create(); auth->add(auth, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_PUBKEY); diff --git a/src/frontends/android/jni/libandroidbridge/backend/android_attr.c b/src/frontends/android/jni/libandroidbridge/backend/android_attr.c index e8c506950..f9e96a76c 100644 --- a/src/frontends/android/jni/libandroidbridge/backend/android_attr.c +++ b/src/frontends/android/jni/libandroidbridge/backend/android_attr.c @@ -80,7 +80,7 @@ METHOD(enumerator_t, enumerate_dns, bool, } METHOD(attribute_handler_t, create_attribute_enumerator, enumerator_t*, - private_android_attr_t *this, identification_t *server, host_t *vip) + private_android_attr_t *this, identification_t *server, linked_list_t *vips) { enumerator_t *enumerator; diff --git a/src/frontends/android/jni/libandroidbridge/backend/android_service.c b/src/frontends/android/jni/libandroidbridge/backend/android_service.c index 29b6d45af..f62aea0e8 100644 --- a/src/frontends/android/jni/libandroidbridge/backend/android_service.c +++ b/src/frontends/android/jni/libandroidbridge/backend/android_service.c @@ -250,21 +250,36 @@ static bool setup_tun_device(private_android_service_t *this, ike_sa_t *ike_sa, child_sa_t *child_sa) { vpnservice_builder_t *builder; + enumerator_t *enumerator; + bool vip_found = FALSE; host_t *vip; int tunfd; DBG1(DBG_DMN, "setting up TUN device for CHILD_SA %s{%u}", child_sa->get_name(child_sa), child_sa->get_reqid(child_sa)); - vip = ike_sa->get_virtual_ip(ike_sa, TRUE); - if (!vip || vip->is_anyaddr(vip)) + + builder = charonservice->get_vpnservice_builder(charonservice); + + enumerator = ike_sa->create_virtual_ip_enumerator(ike_sa, TRUE); + while (enumerator->enumerate(enumerator, &vip)) + { + if (!vip->is_anyaddr(vip)) + { + if (!builder->add_address(builder, vip)) + { + break; + } + vip_found = TRUE; + } + } + enumerator->destroy(enumerator); + + if (!vip_found) { DBG1(DBG_DMN, "setting up TUN device failed, no virtual IP found"); return FALSE; } - - builder = charonservice->get_vpnservice_builder(charonservice); - if (!builder->add_address(builder, vip) || - !add_routes(builder, child_sa) || + if (!add_routes(builder, child_sa) || !builder->set_mtu(builder, TUN_DEFAULT_MTU)) { return FALSE; @@ -442,8 +457,8 @@ static job_requeue_t initiate(private_android_service_t *this) 600, 600, /* jitter, over 10min */ TRUE, FALSE, /* mobike, aggressive */ 0, 0, /* DPD delay, timeout */ - host_create_from_string("0.0.0.0", 0) /* virt */, - NULL, FALSE, NULL, NULL); /* pool, mediation */ + FALSE, NULL, NULL); /* mediation */ + peer_cfg->add_virtual_ip(peer_cfg, host_create_from_string("0.0.0.0", 0)); /* local auth config */ if (streq("ikev2-eap", this->type)) diff --git a/src/libcharon/Makefile.am b/src/libcharon/Makefile.am index eada68bf5..3869066a8 100644 --- a/src/libcharon/Makefile.am +++ b/src/libcharon/Makefile.am @@ -316,6 +316,13 @@ if MONOLITHIC endif endif +if USE_EAP_DYNAMIC + SUBDIRS += plugins/eap_dynamic +if MONOLITHIC + libcharon_la_LIBADD += plugins/eap_dynamic/libstrongswan-eap-dynamic.la +endif +endif + if USE_EAP_RADIUS SUBDIRS += plugins/eap_radius if MONOLITHIC diff --git a/src/libcharon/config/peer_cfg.c b/src/libcharon/config/peer_cfg.c index d16aedc79..59869eab2 100644 --- a/src/libcharon/config/peer_cfg.c +++ b/src/libcharon/config/peer_cfg.c @@ -141,14 +141,14 @@ struct private_peer_cfg_t { u_int32_t dpd_timeout; /** - * virtual IP to use locally + * List of virtual IPs (host_t*) to request */ - host_t *virtual_ip; + linked_list_t *vips; /** - * pool to acquire configuration attributes from + * List of pool names to use for virtual IP lookup */ - char *pool; + linked_list_t *pools; /** * local authentication configs (rulesets) @@ -409,16 +409,28 @@ METHOD(peer_cfg_t, get_dpd_timeout, u_int32_t, return this->dpd_timeout; } -METHOD(peer_cfg_t, get_virtual_ip, host_t*, +METHOD(peer_cfg_t, add_virtual_ip, void, + private_peer_cfg_t *this, host_t *vip) +{ + this->vips->insert_last(this->vips, vip); +} + +METHOD(peer_cfg_t, create_virtual_ip_enumerator, enumerator_t*, private_peer_cfg_t *this) { - return this->virtual_ip; + return this->vips->create_enumerator(this->vips); } -METHOD(peer_cfg_t, get_pool, char*, +METHOD(peer_cfg_t, add_pool, void, + private_peer_cfg_t *this, char *name) +{ + this->pools->insert_last(this->pools, strdup(name)); +} + +METHOD(peer_cfg_t, create_pool_enumerator, enumerator_t*, private_peer_cfg_t *this) { - return this->pool; + return this->pools->create_enumerator(this->pools); } METHOD(peer_cfg_t, add_auth_cfg, void, @@ -521,6 +533,10 @@ static bool auth_cfg_equal(private_peer_cfg_t *this, private_peer_cfg_t *other) METHOD(peer_cfg_t, equals, bool, private_peer_cfg_t *this, private_peer_cfg_t *other) { + enumerator_t *e1, *e2; + host_t *vip1, *vip2; + char *pool1, *pool2; + if (this == other) { return TRUE; @@ -530,6 +546,43 @@ METHOD(peer_cfg_t, equals, bool, return FALSE; } + if (this->vips->get_count(this->vips) != other->vips->get_count(other->vips)) + { + return FALSE; + } + e1 = create_virtual_ip_enumerator(this); + e2 = create_virtual_ip_enumerator(other); + if (e1->enumerate(e1, &vip1) && e2->enumerate(e2, &vip2)) + { + if (!vip1->ip_equals(vip1, vip2)) + { + e1->destroy(e1); + e2->destroy(e2); + return FALSE; + } + } + e1->destroy(e1); + e2->destroy(e2); + + if (this->pools->get_count(this->pools) != + other->pools->get_count(other->pools)) + { + return FALSE; + } + e1 = create_pool_enumerator(this); + e2 = create_pool_enumerator(other); + if (e1->enumerate(e1, &pool1) && e2->enumerate(e2, &pool2)) + { + if (!streq(pool1, pool2)) + { + e1->destroy(e1); + e2->destroy(e2); + return FALSE; + } + } + e1->destroy(e1); + e2->destroy(e2); + return ( this->ike_version == other->ike_version && this->cert_policy == other->cert_policy && @@ -541,11 +594,6 @@ METHOD(peer_cfg_t, equals, bool, this->jitter_time == other->jitter_time && this->over_time == other->over_time && this->dpd == other->dpd && - (this->virtual_ip == other->virtual_ip || - (this->virtual_ip && other->virtual_ip && - this->virtual_ip->equals(this->virtual_ip, other->virtual_ip))) && - (this->pool == other->pool || - (this->pool && other->pool && streq(this->pool, other->pool))) && auth_cfg_equal(this, other) #ifdef ME && this->mediation == other->mediation && @@ -572,18 +620,18 @@ METHOD(peer_cfg_t, destroy, void, this->ike_cfg->destroy(this->ike_cfg); this->child_cfgs->destroy_offset(this->child_cfgs, offsetof(child_cfg_t, destroy)); - DESTROY_IF(this->virtual_ip); this->local_auth->destroy_offset(this->local_auth, offsetof(auth_cfg_t, destroy)); this->remote_auth->destroy_offset(this->remote_auth, offsetof(auth_cfg_t, destroy)); + this->vips->destroy_offset(this->vips, offsetof(host_t, destroy)); + this->pools->destroy_function(this->pools, free); #ifdef ME DESTROY_IF(this->mediated_by); DESTROY_IF(this->peer_id); #endif /* ME */ this->mutex->destroy(this->mutex); free(this->name); - free(this->pool); free(this); } } @@ -597,8 +645,8 @@ peer_cfg_t *peer_cfg_create(char *name, ike_version_t ike_version, u_int32_t rekey_time, u_int32_t reauth_time, u_int32_t jitter_time, u_int32_t over_time, bool mobike, bool aggressive, u_int32_t dpd, - u_int32_t dpd_timeout, host_t *virtual_ip, - char *pool, bool mediation, peer_cfg_t *mediated_by, + u_int32_t dpd_timeout, + bool mediation, peer_cfg_t *mediated_by, identification_t *peer_id) { private_peer_cfg_t *this; @@ -631,8 +679,10 @@ peer_cfg_t *peer_cfg_create(char *name, ike_version_t ike_version, .use_aggressive = _use_aggressive, .get_dpd = _get_dpd, .get_dpd_timeout = _get_dpd_timeout, - .get_virtual_ip = _get_virtual_ip, - .get_pool = _get_pool, + .add_virtual_ip = _add_virtual_ip, + .create_virtual_ip_enumerator = _create_virtual_ip_enumerator, + .add_pool = _add_pool, + .create_pool_enumerator = _create_pool_enumerator, .add_auth_cfg = _add_auth_cfg, .create_auth_cfg_enumerator = _create_auth_cfg_enumerator, .equals = (void*)_equals, @@ -660,8 +710,8 @@ peer_cfg_t *peer_cfg_create(char *name, ike_version_t ike_version, .aggressive = aggressive, .dpd = dpd, .dpd_timeout = dpd_timeout, - .virtual_ip = virtual_ip, - .pool = strdupnull(pool), + .vips = linked_list_create(), + .pools = linked_list_create(), .local_auth = linked_list_create(), .remote_auth = linked_list_create(), .refcount = 1, diff --git a/src/libcharon/config/peer_cfg.h b/src/libcharon/config/peer_cfg.h index 572153505..f65b91258 100644 --- a/src/libcharon/config/peer_cfg.h +++ b/src/libcharon/config/peer_cfg.h @@ -278,23 +278,34 @@ struct peer_cfg_t { u_int32_t (*get_dpd_timeout) (peer_cfg_t *this); /** - * Get a virtual IP for the local peer. + * Add a virtual IP to request as initiator. * - * If no virtual IP should be used, NULL is returned. %any means to request - * a virtual IP using configuration payloads. A specific address is also - * used for a request and may be changed by the server. + * @param vip virtual IP to request, may be %any or %any6 + */ + void (*add_virtual_ip)(peer_cfg_t *this, host_t *vip); + + /** + * Create an enumerator over virtual IPs to request. + * + * The returned enumerator enumerates over IPs added with add_virtual_ip(). + * + * @return enumerator over host_t* + */ + enumerator_t* (*create_virtual_ip_enumerator)(peer_cfg_t *this); + + /** + * Add a pool name this configuration uses to select virtual IPs. * - * @param suggestion NULL, %any or specific - * @return virtual IP, %any or NULL + * @param name pool name to use for virtual IP lookup */ - host_t* (*get_virtual_ip) (peer_cfg_t *this); + void (*add_pool)(peer_cfg_t *this, char *name); /** - * Get the name of the pool to acquire configuration attributes from. + * Create an enumerator over pool names of this config. * - * @return pool name, NULL if none defined + * @return enumerator over char* */ - char* (*get_pool)(peer_cfg_t *this); + enumerator_t* (*create_pool_enumerator)(peer_cfg_t *this); #ifdef ME /** @@ -374,8 +385,6 @@ struct peer_cfg_t { * @param aggressive use/accept aggressive mode with IKEv1 * @param dpd DPD check interval, 0 to disable * @param dpd_timeout DPD timeout interval (IKEv1 only), if 0 default applies - * @param virtual_ip virtual IP for local host, or NULL - * @param pool pool name to get configuration attributes from, or NULL * @param mediation TRUE if this is a mediation connection * @param mediated_by peer_cfg_t of the mediation connection to mediate through * @param peer_id ID that identifies our peer at the mediation server @@ -387,8 +396,8 @@ peer_cfg_t *peer_cfg_create(char *name, ike_version_t ike_version, u_int32_t rekey_time, u_int32_t reauth_time, u_int32_t jitter_time, u_int32_t over_time, bool mobike, bool aggressive, u_int32_t dpd, - u_int32_t dpd_timeout, host_t *virtual_ip, - char *pool, bool mediation, peer_cfg_t *mediated_by, + u_int32_t dpd_timeout, + bool mediation, peer_cfg_t *mediated_by, identification_t *peer_id); #endif /** PEER_CFG_H_ @}*/ diff --git a/src/libcharon/encoding/payloads/eap_payload.c b/src/libcharon/encoding/payloads/eap_payload.c index 1b9a5c802..dd2e25795 100644 --- a/src/libcharon/encoding/payloads/eap_payload.c +++ b/src/libcharon/encoding/payloads/eap_payload.c @@ -1,4 +1,5 @@ /* + * Copyright (C) 2012 Tobias Brunner * Copyright (C) 2005-2010 Martin Willi * Copyright (C) 2005 Jan Hutter * Hochschule fuer Technik Rapperswil @@ -20,6 +21,7 @@ #include <daemon.h> #include <eap/eap.h> +#include <bio/bio_writer.h> typedef struct private_eap_payload_t private_eap_payload_t; @@ -217,28 +219,93 @@ METHOD(eap_payload_t, get_identifier, u_int8_t, return 0; } +/** + * Get the current type at the given offset into this->data. + * @return the new offset or 0 if failed + */ +static size_t extract_type(private_eap_payload_t *this, size_t offset, + eap_type_t *type, u_int32_t *vendor) +{ + if (this->data.len > offset) + { + *vendor = 0; + *type = this->data.ptr[offset]; + if (*type != EAP_EXPANDED) + { + return offset + 1; + } + if (this->data.len >= offset + 8) + { + *vendor = untoh32(this->data.ptr + offset) & 0x00FFFFFF; + *type = untoh32(this->data.ptr + offset + 4); + return offset + 8; + } + } + return 0; +} + METHOD(eap_payload_t, get_type, eap_type_t, private_eap_payload_t *this, u_int32_t *vendor) { eap_type_t type; *vendor = 0; - if (this->data.len > 4) + if (extract_type(this, 4, &type, vendor)) { - type = this->data.ptr[4]; - if (type != EAP_EXPANDED) - { - return type; - } - if (this->data.len >= 12) - { - *vendor = untoh32(this->data.ptr + 4) & 0x00FFFFFF; - return untoh32(this->data.ptr + 8); - } + return type; } return 0; } +/** + * Type enumerator + */ +typedef struct { + /** public interface */ + enumerator_t public; + /** payload */ + private_eap_payload_t *payload; + /** current offset in the data */ + size_t offset; +} type_enumerator_t; + +METHOD(enumerator_t, enumerate_types, bool, + type_enumerator_t *this, eap_type_t *type, u_int32_t *vendor) +{ + this->offset = extract_type(this->payload, this->offset, type, vendor); + return this->offset; +} + +METHOD(eap_payload_t, get_types, enumerator_t*, + private_eap_payload_t *this) +{ + type_enumerator_t *enumerator; + eap_type_t type; + u_int32_t vendor; + size_t offset; + + offset = extract_type(this, 4, &type, &vendor); + if (offset && type == EAP_NAK) + { + INIT(enumerator, + .public = { + .enumerate = (void*)_enumerate_types, + .destroy = (void*)free, + }, + .payload = this, + .offset = offset, + ); + return &enumerator->public; + } + return enumerator_create_empty(); +} + +METHOD(eap_payload_t, is_expanded, bool, + private_eap_payload_t *this) +{ + return this->data.len > 4 ? this->data.ptr[4] == EAP_EXPANDED : FALSE; +} + METHOD2(payload_t, eap_payload_t, destroy, void, private_eap_payload_t *this) { @@ -270,6 +337,8 @@ eap_payload_t *eap_payload_create() .get_code = _get_code, .get_identifier = _get_identifier, .get_type = _get_type, + .get_types = _get_types, + .is_expanded = _is_expanded, .destroy = _destroy, }, .next_payload = NO_PAYLOAD, @@ -313,15 +382,81 @@ eap_payload_t *eap_payload_create_code(eap_code_t code, u_int8_t identifier) return eap_payload_create_data(data); } +/** + * Write the given type either expanded or not + */ +static void write_type(bio_writer_t *writer, eap_type_t type, u_int32_t vendor, + bool expanded) +{ + if (expanded) + { + writer->write_uint8(writer, EAP_EXPANDED); + writer->write_uint24(writer, vendor); + writer->write_uint32(writer, type); + } + else + { + writer->write_uint8(writer, type); + } +} + /* * Described in header */ -eap_payload_t *eap_payload_create_nak(u_int8_t identifier) +eap_payload_t *eap_payload_create_nak(u_int8_t identifier, eap_type_t type, + u_int32_t vendor, bool expanded) { - chunk_t data; + enumerator_t *enumerator; + eap_type_t reg_type; + u_int32_t reg_vendor; + bio_writer_t *writer; + chunk_t length, data; + bool added_any = FALSE, found_vendor = FALSE; + eap_payload_t *payload; + + writer = bio_writer_create(12); + writer->write_uint8(writer, EAP_RESPONSE); + writer->write_uint8(writer, identifier); + length = writer->skip(writer, 2); + + write_type(writer, EAP_NAK, 0, expanded); + + enumerator = charon->eap->create_enumerator(charon->eap, EAP_PEER); + while (enumerator->enumerate(enumerator, ®_type, ®_vendor)) + { + if ((type && type != reg_type) || + (type && vendor && vendor != reg_vendor)) + { /* the preferred type is only sent if we actually find it */ + continue; + } + if (!reg_vendor || expanded) + { + write_type(writer, reg_type, reg_vendor, expanded); + added_any = TRUE; + } + else if (reg_vendor) + { /* found vendor specifc method, but this is not an expanded Nak */ + found_vendor = TRUE; + } + } + enumerator->destroy(enumerator); - data = chunk_from_chars(EAP_RESPONSE, identifier, 0, 0, EAP_NAK); - htoun16(data.ptr + 2, data.len); - return eap_payload_create_data(data); + if (found_vendor) + { /* request an expanded authentication type */ + write_type(writer, EAP_EXPANDED, 0, expanded); + added_any = TRUE; + } + if (!added_any) + { /* no methods added */ + write_type(writer, 0, 0, expanded); + } + + /* set length */ + data = writer->get_buf(writer); + htoun16(length.ptr, data.len); + + payload = eap_payload_create_data(data); + writer->destroy(writer); + return payload; } diff --git a/src/libcharon/encoding/payloads/eap_payload.h b/src/libcharon/encoding/payloads/eap_payload.h index 52bc7ac5e..e8ed1c5e7 100644 --- a/src/libcharon/encoding/payloads/eap_payload.h +++ b/src/libcharon/encoding/payloads/eap_payload.h @@ -1,4 +1,5 @@ /* + * Copyright (C) 2012 Tobias Brunner * Copyright (C) 2005-2006 Martin Willi * Copyright (C) 2005 Jan Hutter * Hochschule fuer Technik Rapperswil @@ -82,6 +83,21 @@ struct eap_payload_t { eap_type_t (*get_type) (eap_payload_t *this, u_int32_t *vendor); /** + * Enumerate the EAP method types contained in an EAP-Nak (i.e. get_type() + * returns EAP_NAK). + * + * @return enumerator over (eap_type_t type, u_int32_t vendor) + */ + enumerator_t* (*get_types) (eap_payload_t *this); + + /** + * Check if the EAP method type is encoded in the Expanded Type format. + * + * @return TRUE if in Expanded Type format + */ + bool (*is_expanded) (eap_payload_t *this); + + /** * Destroys an eap_payload_t object. */ void (*destroy) (eap_payload_t *this); @@ -126,8 +142,12 @@ eap_payload_t *eap_payload_create_code(eap_code_t code, u_int8_t identifier); * Creates an eap_payload_t EAP_RESPONSE containing an EAP_NAK. * * @param identifier EAP identifier to use in payload + * @param type preferred auth type, 0 to send all supported types + * @param vendor vendor identifier for auth type, 0 for default + * @param expanded TRUE to send an expanded Nak * @return eap_payload_t object */ -eap_payload_t *eap_payload_create_nak(u_int8_t identifier); +eap_payload_t *eap_payload_create_nak(u_int8_t identifier, eap_type_t type, + u_int32_t vendor, bool expanded); #endif /** EAP_PAYLOAD_H_ @}*/ diff --git a/src/libcharon/plugins/android/android_handler.c b/src/libcharon/plugins/android/android_handler.c index a53962f16..f1d3045ca 100644 --- a/src/libcharon/plugins/android/android_handler.c +++ b/src/libcharon/plugins/android/android_handler.c @@ -196,7 +196,7 @@ METHOD(enumerator_t, enumerate_dns, bool, } METHOD(attribute_handler_t, create_attribute_enumerator, enumerator_t *, - android_handler_t *this, identification_t *id, host_t *vip) + android_handler_t *this, identification_t *id, linked_list_t *vips) { enumerator_t *enumerator; diff --git a/src/libcharon/plugins/android/android_service.c b/src/libcharon/plugins/android/android_service.c index f2d8ddce9..81628b80a 100644 --- a/src/libcharon/plugins/android/android_service.c +++ b/src/libcharon/plugins/android/android_service.c @@ -275,8 +275,8 @@ static job_requeue_t initiate(private_android_service_t *this) 600, 600, /* jitter, over 10min */ TRUE, FALSE, /* mobike, aggressive */ 0, 0, /* DPD delay, timeout */ - host_create_from_string("0.0.0.0", 0) /* virt */, - NULL, FALSE, NULL, NULL); /* pool, mediation */ + FALSE, NULL, NULL); /* mediation */ + peer_cfg->add_virtual_ip(peer_cfg, host_create_from_string("0.0.0.0", 0)); auth = auth_cfg_create(); auth->add(auth, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_EAP); diff --git a/src/libcharon/plugins/dhcp/dhcp_provider.c b/src/libcharon/plugins/dhcp/dhcp_provider.c index a6a887780..e46cc4d90 100644 --- a/src/libcharon/plugins/dhcp/dhcp_provider.c +++ b/src/libcharon/plugins/dhcp/dhcp_provider.c @@ -84,7 +84,7 @@ METHOD(attribute_provider_t, acquire_address, host_t*, private_dhcp_provider_t *this, char *pool, identification_t *id, host_t *requested) { - if (streq(pool, "dhcp")) + if (streq(pool, "dhcp") && requested->get_family(requested) == AF_INET) { dhcp_transaction_t *transaction, *old; host_t *vip; @@ -110,7 +110,7 @@ METHOD(attribute_provider_t, release_address, bool, private_dhcp_provider_t *this, char *pool, host_t *address, identification_t *id) { - if (streq(pool, "dhcp")) + if (streq(pool, "dhcp") && address->get_family(address) == AF_INET) { dhcp_transaction_t *transaction; @@ -129,18 +129,25 @@ METHOD(attribute_provider_t, release_address, bool, } METHOD(attribute_provider_t, create_attribute_enumerator, enumerator_t*, - private_dhcp_provider_t *this, char *pool, identification_t *id, - host_t *vip) + private_dhcp_provider_t *this, linked_list_t *pools, identification_t *id, + linked_list_t *vips) { - dhcp_transaction_t *transaction; + dhcp_transaction_t *transaction = NULL; + enumerator_t *enumerator; + host_t *vip; - if (!vip) + this->mutex->lock(this->mutex); + enumerator = vips->create_enumerator(vips); + while (enumerator->enumerate(enumerator, &vip)) { - return NULL; + transaction = this->transactions->get(this->transactions, + (void*)hash_id_host(id, vip)); + if (transaction) + { + break; + } } - this->mutex->lock(this->mutex); - transaction = this->transactions->get(this->transactions, - (void*)hash_id_host(id, vip)); + enumerator->destroy(enumerator); if (!transaction) { this->mutex->unlock(this->mutex); diff --git a/src/libcharon/plugins/eap_dynamic/Makefile.am b/src/libcharon/plugins/eap_dynamic/Makefile.am new file mode 100644 index 000000000..0d07fbf35 --- /dev/null +++ b/src/libcharon/plugins/eap_dynamic/Makefile.am @@ -0,0 +1,16 @@ + +INCLUDES = -I$(top_srcdir)/src/libstrongswan -I$(top_srcdir)/src/libhydra \ + -I$(top_srcdir)/src/libcharon + +AM_CFLAGS = -rdynamic + +if MONOLITHIC +noinst_LTLIBRARIES = libstrongswan-eap-dynamic.la +else +plugin_LTLIBRARIES = libstrongswan-eap-dynamic.la +endif + +libstrongswan_eap_dynamic_la_SOURCES = \ + eap_dynamic_plugin.h eap_dynamic_plugin.c eap_dynamic.h eap_dynamic.c + +libstrongswan_eap_dynamic_la_LDFLAGS = -module -avoid-version diff --git a/src/libcharon/plugins/eap_dynamic/eap_dynamic.c b/src/libcharon/plugins/eap_dynamic/eap_dynamic.c new file mode 100644 index 000000000..d24cbd128 --- /dev/null +++ b/src/libcharon/plugins/eap_dynamic/eap_dynamic.c @@ -0,0 +1,393 @@ +/* + * Copyright (C) 2012 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "eap_dynamic.h" + +#include <daemon.h> +#include <library.h> + +typedef struct private_eap_dynamic_t private_eap_dynamic_t; + +/** + * Private data of an eap_dynamic_t object. + */ +struct private_eap_dynamic_t { + + /** + * Public authenticator_t interface. + */ + eap_dynamic_t public; + + /** + * ID of the server + */ + identification_t *server; + + /** + * ID of the peer + */ + identification_t *peer; + + /** + * Our supported EAP types (as eap_vendor_type_t*) + */ + linked_list_t *types; + + /** + * EAP types supported by peer, if any + */ + linked_list_t *other_types; + + /** + * Prefer types sent by peer + */ + bool prefer_peer; + + /** + * The proxied EAP method + */ + eap_method_t *method; +}; + +/** + * Compare two eap_vendor_type_t objects + */ +static bool entry_matches(eap_vendor_type_t *item, eap_vendor_type_t *other) +{ + return item->type == other->type && item->vendor == other->vendor; +} + +/** + * Load the given EAP method + */ +static eap_method_t *load_method(private_eap_dynamic_t *this, + eap_type_t type, u_int32_t vendor) +{ + eap_method_t *method; + + method = charon->eap->create_instance(charon->eap, type, vendor, EAP_SERVER, + this->server, this->peer); + if (!method) + { + if (vendor) + { + DBG1(DBG_IKE, "loading vendor specific EAP method %d-%d failed", + type, vendor); + } + else + { + DBG1(DBG_IKE, "loading %N method failed", eap_type_names, type); + } + } + return method; +} + +/** + * Select the first method we can instantiate and is supported by both peers. + */ +static void select_method(private_eap_dynamic_t *this) +{ + eap_vendor_type_t *entry; + linked_list_t *outer = this->types, *inner = this->other_types; + char *who = "peer"; + + if (this->other_types && this->prefer_peer) + { + outer = this->other_types; + inner = this->types; + who = "us"; + } + + while (outer->remove_first(outer, (void*)&entry) == SUCCESS) + { + if (inner) + { + if (inner->find_first(inner, (void*)entry_matches, + NULL, entry) != SUCCESS) + { + if (entry->vendor) + { + DBG2(DBG_IKE, "proposed vendor specific EAP method %d-%d " + "not supported by %s, skipped", entry->type, + entry->vendor, who); + } + else + { + DBG2(DBG_IKE, "proposed %N method not supported by %s, " + "skipped", eap_type_names, entry->type, who); + } + free(entry); + continue; + } + } + this->method = load_method(this, entry->type, entry->vendor); + if (this->method) + { + if (entry->vendor) + { + DBG1(DBG_IKE, "vendor specific EAP method %d-%d selected", + entry->type, entry->vendor); + } + else + { + DBG1(DBG_IKE, "%N method selected", eap_type_names, + entry->type); + } + free(entry); + break; + } + free(entry); + } +} + +METHOD(eap_method_t, initiate, status_t, + private_eap_dynamic_t *this, eap_payload_t **out) +{ + if (!this->method) + { + select_method(this); + if (!this->method) + { + DBG1(DBG_IKE, "no supported EAP method found"); + return FAILED; + } + } + return this->method->initiate(this->method, out); +} + +METHOD(eap_method_t, process, status_t, + private_eap_dynamic_t *this, eap_payload_t *in, eap_payload_t **out) +{ + eap_type_t received_type, type; + u_int32_t received_vendor, vendor; + + received_type = in->get_type(in, &received_vendor); + if (received_vendor == 0 && received_type == EAP_NAK) + { + enumerator_t *enumerator; + + DBG1(DBG_IKE, "received %N, selecting a different EAP method", + eap_type_names, EAP_NAK); + + if (this->other_types) + { /* we already received a Nak or a proper response before */ + DBG1(DBG_IKE, "%N is not supported in this state", eap_type_names, + EAP_NAK); + return FAILED; + } + + this->other_types = linked_list_create(); + enumerator = in->get_types(in); + while (enumerator->enumerate(enumerator, &type, &vendor)) + { + eap_vendor_type_t *entry; + + if (!type) + { + DBG1(DBG_IKE, "peer does not support any other EAP methods"); + enumerator->destroy(enumerator); + return FAILED; + } + INIT(entry, + .type = type, + .vendor = vendor, + ); + this->other_types->insert_last(this->other_types, entry); + } + enumerator->destroy(enumerator); + + /* restart with a different method */ + this->method->destroy(this->method); + this->method = NULL; + return initiate(this, out); + } + if (!this->other_types) + { /* so we don't handle EAP-Naks later */ + this->other_types = linked_list_create(); + } + if (this->method) + { + return this->method->process(this->method, in, out); + } + return FAILED; +} + +METHOD(eap_method_t, get_type, eap_type_t, + private_eap_dynamic_t *this, u_int32_t *vendor) +{ + if (this->method) + { + return this->method->get_type(this->method, vendor); + } + *vendor = 0; + return EAP_DYNAMIC; +} + +METHOD(eap_method_t, get_msk, status_t, + private_eap_dynamic_t *this, chunk_t *msk) +{ + if (this->method) + { + return this->method->get_msk(this->method, msk); + } + return FAILED; +} + +METHOD(eap_method_t, get_identifier, u_int8_t, + private_eap_dynamic_t *this) +{ + if (this->method) + { + return this->method->get_identifier(this->method); + } + return 0; +} + +METHOD(eap_method_t, set_identifier, void, + private_eap_dynamic_t *this, u_int8_t identifier) +{ + if (this->method) + { + this->method->set_identifier(this->method, identifier); + } +} + +METHOD(eap_method_t, is_mutual, bool, + private_eap_dynamic_t *this) +{ + if (this->method) + { + return this->method->is_mutual(this->method); + } + return FALSE; +} + +METHOD(eap_method_t, destroy, void, + private_eap_dynamic_t *this) +{ + DESTROY_IF(this->method); + this->types->destroy_function(this->types, (void*)free); + DESTROY_FUNCTION_IF(this->other_types, (void*)free); + this->server->destroy(this->server); + this->peer->destroy(this->peer); + free(this); +} + +/** + * Parse preferred EAP types + */ +static void handle_preferred_eap_types(private_eap_dynamic_t *this, + char *methods) +{ + enumerator_t *enumerator; + eap_vendor_type_t *type, *entry; + linked_list_t *preferred; + char *method; + + /* parse preferred EAP methods, format: type[-vendor], ... */ + preferred = linked_list_create(); + enumerator = enumerator_create_token(methods, ",", " "); + while (enumerator->enumerate(enumerator, &method)) + { + type = eap_vendor_type_from_string(method); + if (type) + { + preferred->insert_last(preferred, type); + } + } + enumerator->destroy(enumerator); + + enumerator = this->types->create_enumerator(this->types); + while (preferred->remove_last(preferred, (void**)&type) == SUCCESS) + { /* move (supported) types to the front, maintain the preferred order */ + this->types->reset_enumerator(this->types, enumerator); + while (enumerator->enumerate(enumerator, &entry)) + { + if (entry_matches(entry, type)) + { + this->types->remove_at(this->types, enumerator); + this->types->insert_first(this->types, entry); + break; + } + } + free(type); + } + enumerator->destroy(enumerator); + preferred->destroy(preferred); +} + +/** + * Get all supported EAP methods + */ +static void get_supported_eap_types(private_eap_dynamic_t *this) +{ + enumerator_t *enumerator; + eap_type_t type; + u_int32_t vendor; + + enumerator = charon->eap->create_enumerator(charon->eap, EAP_SERVER); + while (enumerator->enumerate(enumerator, &type, &vendor)) + { + eap_vendor_type_t *entry; + + INIT(entry, + .type = type, + .vendor = vendor, + ); + this->types->insert_last(this->types, entry); + } + enumerator->destroy(enumerator); +} + +/* + * Defined in header + */ +eap_dynamic_t *eap_dynamic_create(identification_t *server, + identification_t *peer) +{ + private_eap_dynamic_t *this; + char *preferred; + + INIT(this, + .public = { + .interface = { + .initiate = _initiate, + .process = _process, + .get_type = _get_type, + .is_mutual = _is_mutual, + .get_msk = _get_msk, + .get_identifier = _get_identifier, + .set_identifier = _set_identifier, + .destroy = _destroy, + }, + }, + .peer = peer->clone(peer), + .server = server->clone(server), + .types = linked_list_create(), + .prefer_peer = lib->settings->get_bool(lib->settings, + "%s.plugins.eap-dynamic.prefer_peer", FALSE, charon->name), + ); + + /* get all supported EAP methods */ + get_supported_eap_types(this); + /* move preferred methods to the front */ + preferred = lib->settings->get_str(lib->settings, + "%s.plugins.eap-dynamic.preferred", NULL, charon->name); + if (preferred) + { + handle_preferred_eap_types(this, preferred); + } + return &this->public; +} diff --git a/src/libcharon/plugins/eap_dynamic/eap_dynamic.h b/src/libcharon/plugins/eap_dynamic/eap_dynamic.h new file mode 100644 index 000000000..35db4fa26 --- /dev/null +++ b/src/libcharon/plugins/eap_dynamic/eap_dynamic.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2012 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/** + * @defgroup eap_dynamic_i eap_dynamic + * @{ @ingroup eap_dynamic + */ + +#ifndef EAP_DYNAMIC_H_ +#define EAP_DYNAMIC_H_ + +typedef struct eap_dynamic_t eap_dynamic_t; + +#include <sa/eap/eap_method.h> + +/** + * Implementation of the eap_method_t interface for a virtual EAP method that + * proxies other EAP methods and supports the selection of the actual method + * by the client. + */ +struct eap_dynamic_t { + + /** + * Implemented eap_method_t interface + */ + eap_method_t interface; +}; + +/** + * Create a dynamic EAP proxy serving any supported real method which is also + * supported (or selected) by the client. + * + * @param server ID of the EAP server + * @param peer ID of the EAP client + * @return eap_dynamic_t object + */ +eap_dynamic_t *eap_dynamic_create(identification_t *server, + identification_t *peer); + +#endif /** EAP_DYNAMIC_H_ @}*/ diff --git a/src/libcharon/plugins/eap_dynamic/eap_dynamic_plugin.c b/src/libcharon/plugins/eap_dynamic/eap_dynamic_plugin.c new file mode 100644 index 000000000..d6f38b666 --- /dev/null +++ b/src/libcharon/plugins/eap_dynamic/eap_dynamic_plugin.c @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2012 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "eap_dynamic_plugin.h" + +#include "eap_dynamic.h" + +#include <daemon.h> + +METHOD(plugin_t, get_name, char*, + eap_dynamic_plugin_t *this) +{ + return "eap-dynamic"; +} + +METHOD(plugin_t, get_features, int, + eap_dynamic_plugin_t *this, plugin_feature_t *features[]) +{ + static plugin_feature_t f[] = { + PLUGIN_CALLBACK(eap_method_register, eap_dynamic_create), + PLUGIN_PROVIDE(EAP_SERVER, EAP_DYNAMIC), + }; + *features = f; + return countof(f); +} + +METHOD(plugin_t, destroy, void, + eap_dynamic_plugin_t *this) +{ + free(this); +} + +/* + * see header file + */ +plugin_t *eap_dynamic_plugin_create() +{ + eap_dynamic_plugin_t *this; + + INIT(this, + .plugin = { + .get_name = _get_name, + .get_features = _get_features, + .destroy = _destroy, + }, + ); + + return &this->plugin; +} + diff --git a/src/libcharon/plugins/eap_dynamic/eap_dynamic_plugin.h b/src/libcharon/plugins/eap_dynamic/eap_dynamic_plugin.h new file mode 100644 index 000000000..9b124d8d2 --- /dev/null +++ b/src/libcharon/plugins/eap_dynamic/eap_dynamic_plugin.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2012 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/** + * @defgroup eap_dynamic eap_dynamic + * @ingroup cplugins + * + * @defgroup eap_dynamic_plugin eap_dynamic_plugin + * @{ @ingroup eap_dynamic + */ + +#ifndef EAP_DYNAMIC_PLUGIN_H_ +#define EAP_DYNAMIC_PLUGIN_H_ + +#include <plugins/plugin.h> + +typedef struct eap_dynamic_plugin_t eap_dynamic_plugin_t; + +/** + * EAP plugin that can use any supported EAP method the client supports or + * prefers to use. + */ +struct eap_dynamic_plugin_t { + + /** + * implements plugin interface + */ + plugin_t plugin; +}; + +#endif /** EAP_DYNAMIC_PLUGIN_H_ @}*/ diff --git a/src/libcharon/plugins/eap_peap/eap_peap_peer.c b/src/libcharon/plugins/eap_peap/eap_peap_peer.c index 72e201fb6..79fd667cb 100644 --- a/src/libcharon/plugins/eap_peap/eap_peap_peer.c +++ b/src/libcharon/plugins/eap_peap/eap_peap_peer.c @@ -85,7 +85,7 @@ METHOD(tls_application_t, process, status_t, default: return FAILED; } - + in = eap_payload_create_data(data); DBG3(DBG_IKE, "%B", &data); chunk_free(&data); @@ -151,7 +151,8 @@ METHOD(tls_application_t, process, status_t, if (!this->ph2_method) { DBG1(DBG_IKE, "EAP method not supported"); - this->out = eap_payload_create_nak(in->get_identifier(in)); + this->out = eap_payload_create_nak(in->get_identifier(in), 0, 0, + in->is_expanded(in)); in->destroy(in); return NEED_MORE; } diff --git a/src/libcharon/plugins/eap_radius/eap_radius_accounting.c b/src/libcharon/plugins/eap_radius/eap_radius_accounting.c index 243c76304..7690cf9f8 100644 --- a/src/libcharon/plugins/eap_radius/eap_radius_accounting.c +++ b/src/libcharon/plugins/eap_radius/eap_radius_accounting.c @@ -149,6 +149,7 @@ static bool send_message(private_eap_radius_accounting_t *this, */ static void add_ike_sa_parameters(radius_message_t *message, ike_sa_t *ike_sa) { + enumerator_t *enumerator; host_t *vip; char buf[64]; chunk_t data; @@ -157,17 +158,25 @@ static void add_ike_sa_parameters(radius_message_t *message, ike_sa_t *ike_sa) message->add(message, RAT_USER_NAME, chunk_create(buf, strlen(buf))); snprintf(buf, sizeof(buf), "%#H", ike_sa->get_other_host(ike_sa)); message->add(message, RAT_CALLING_STATION_ID, chunk_create(buf, strlen(buf))); - vip = ike_sa->get_virtual_ip(ike_sa, FALSE); - if (vip && vip->get_family(vip) == AF_INET) - { - message->add(message, RAT_FRAMED_IP_ADDRESS, vip->get_address(vip)); - } - if (vip && vip->get_family(vip) == AF_INET6) + + enumerator = ike_sa->create_virtual_ip_enumerator(ike_sa, FALSE); + while (enumerator->enumerate(enumerator, &vip)) { - /* we currently assign /128 prefixes, only (reserved, length) */ - data = chunk_from_chars(0, 128); - data = chunk_cata("cc", data, vip->get_address(vip)); - message->add(message, RAT_FRAMED_IPV6_PREFIX, data); + switch (vip->get_family(vip)) + { + case AF_INET: + message->add(message, RAT_FRAMED_IP_ADDRESS, + vip->get_address(vip)); + break; + case AF_INET6: + /* we currently assign /128 prefixes, only (reserved, length) */ + data = chunk_from_chars(0, 128); + data = chunk_cata("cc", data, vip->get_address(vip)); + message->add(message, RAT_FRAMED_IPV6_PREFIX, data); + break; + default: + break; + } } } diff --git a/src/libcharon/plugins/eap_ttls/eap_ttls_peer.c b/src/libcharon/plugins/eap_ttls/eap_ttls_peer.c index 767111b3e..00a4da3f8 100644 --- a/src/libcharon/plugins/eap_ttls/eap_ttls_peer.c +++ b/src/libcharon/plugins/eap_ttls/eap_ttls_peer.c @@ -138,7 +138,7 @@ METHOD(tls_application_t, process, status_t, chunk_free(&avp_data); } while (eap_pos < eap_data.len); - + in = eap_payload_create_data(eap_data); chunk_free(&eap_data); payload = (payload_t*)in; @@ -192,7 +192,8 @@ METHOD(tls_application_t, process, status_t, if (!this->method) { DBG1(DBG_IKE, "EAP method not supported"); - this->out = eap_payload_create_nak(in->get_identifier(in)); + this->out = eap_payload_create_nak(in->get_identifier(in), 0, 0, + in->is_expanded(in)); in->destroy(in); return NEED_MORE; } diff --git a/src/libcharon/plugins/ha/ha_attribute.c b/src/libcharon/plugins/ha/ha_attribute.c index 64bd2b8b7..f18c58b6a 100644 --- a/src/libcharon/plugins/ha/ha_attribute.c +++ b/src/libcharon/plugins/ha/ha_attribute.c @@ -181,6 +181,12 @@ METHOD(attribute_provider_t, acquire_address, host_t*, pool = get_pool(this, name); if (pool) { + if (pool->base->get_family(pool->base) != + requested->get_family(requested)) + { + this->mutex->unlock(this->mutex); + return NULL; + } for (byte = 0; byte < pool->size / 8; byte++) { if (pool->mask[byte] != 0xFF) diff --git a/src/libcharon/plugins/ha/ha_dispatcher.c b/src/libcharon/plugins/ha/ha_dispatcher.c index 8b6ec9180..f07b4ea21 100644 --- a/src/libcharon/plugins/ha/ha_dispatcher.c +++ b/src/libcharon/plugins/ha/ha_dispatcher.c @@ -344,10 +344,10 @@ static void process_ike_update(private_ha_dispatcher_t *this, ike_sa->set_other_host(ike_sa, value.host->clone(value.host)); break; case HA_LOCAL_VIP: - ike_sa->set_virtual_ip(ike_sa, TRUE, value.host); + ike_sa->add_virtual_ip(ike_sa, TRUE, value.host); break; case HA_REMOTE_VIP: - ike_sa->set_virtual_ip(ike_sa, FALSE, value.host); + ike_sa->add_virtual_ip(ike_sa, FALSE, value.host); received_vip = TRUE; break; case HA_PEER_ADDR: @@ -413,18 +413,24 @@ static void process_ike_update(private_ha_dispatcher_t *this, } if (received_vip) { + enumerator_t *pools, *vips; host_t *vip; char *pool; peer_cfg = ike_sa->get_peer_cfg(ike_sa); - vip = ike_sa->get_virtual_ip(ike_sa, FALSE); - if (peer_cfg && vip) + if (peer_cfg) { - pool = peer_cfg->get_pool(peer_cfg); - if (pool) + pools = peer_cfg->create_pool_enumerator(peer_cfg); + while (pools->enumerate(pools, &pool)) { - this->attr->reserve(this->attr, pool, vip); + vips = ike_sa->create_virtual_ip_enumerator(ike_sa, FALSE); + while (vips->enumerate(vips, &vip)) + { + this->attr->reserve(this->attr, pool, vip); + } + vips->destroy(vips); } + pools->destroy(pools); } } if (ike_sa->get_version(ike_sa) == IKEV1) diff --git a/src/libcharon/plugins/ha/ha_ike.c b/src/libcharon/plugins/ha/ha_ike.c index d523e8919..442a3a23d 100644 --- a/src/libcharon/plugins/ha/ha_ike.c +++ b/src/libcharon/plugins/ha/ha_ike.c @@ -241,6 +241,34 @@ METHOD(listener_t, ike_state_change, bool, return TRUE; } +/** + * Send a virtual IP sync message for remote VIPs + */ +static void sync_vips(private_ha_ike_t *this, ike_sa_t *ike_sa) +{ + ha_message_t *m = NULL; + enumerator_t *enumerator; + host_t *vip; + + enumerator = ike_sa->create_virtual_ip_enumerator(ike_sa, FALSE); + while (enumerator->enumerate(enumerator, &vip)) + { + if (!m) + { + m = ha_message_create(HA_IKE_UPDATE); + m->add_attribute(m, HA_IKE_ID, ike_sa->get_id(ike_sa)); + } + m->add_attribute(m, HA_REMOTE_VIP, vip); + } + enumerator->destroy(enumerator); + + if (m) + { + this->socket->push(this->socket, m); + this->cache->cache(this->cache, ike_sa, m); + } +} + METHOD(listener_t, message_hook, bool, private_ha_ike_t *this, ike_sa_t *ike_sa, message_t *message, bool incoming, bool plain) @@ -276,18 +304,7 @@ METHOD(listener_t, message_hook, bool, { /* After IKE_SA has been established, sync peers virtual IP. * We cannot sync it in the state_change hook, it is installed later. * TODO: where to sync local VIP? */ - ha_message_t *m; - host_t *vip; - - vip = ike_sa->get_virtual_ip(ike_sa, FALSE); - if (vip) - { - m = ha_message_create(HA_IKE_UPDATE); - m->add_attribute(m, HA_IKE_ID, ike_sa->get_id(ike_sa)); - m->add_attribute(m, HA_REMOTE_VIP, vip); - this->socket->push(this->socket, m); - this->cache->cache(this->cache, ike_sa, m); - } + sync_vips(this, ike_sa); } } if (!plain && ike_sa->get_version(ike_sa) == IKEV1) @@ -296,7 +313,6 @@ METHOD(listener_t, message_hook, bool, keymat_v1_t *keymat; u_int32_t mid; chunk_t iv; - host_t *vip; mid = message->get_message_id(message); if (mid == 0) @@ -313,15 +329,7 @@ METHOD(listener_t, message_hook, bool, } if (!incoming && message->get_exchange_type(message) == TRANSACTION) { - vip = ike_sa->get_virtual_ip(ike_sa, FALSE); - if (vip) - { - m = ha_message_create(HA_IKE_UPDATE); - m->add_attribute(m, HA_IKE_ID, ike_sa->get_id(ike_sa)); - m->add_attribute(m, HA_REMOTE_VIP, vip); - this->socket->push(this->socket, m); - this->cache->cache(this->cache, ike_sa, m); - } + sync_vips(this, ike_sa); } } if (plain && ike_sa->get_version(ike_sa) == IKEV1 && diff --git a/src/libcharon/plugins/ha/ha_tunnel.c b/src/libcharon/plugins/ha/ha_tunnel.c index ad458caad..541dd9313 100644 --- a/src/libcharon/plugins/ha/ha_tunnel.c +++ b/src/libcharon/plugins/ha/ha_tunnel.c @@ -209,7 +209,7 @@ static void setup_tunnel(private_ha_tunnel_t *this, ike_cfg->add_proposal(ike_cfg, proposal_create_default(PROTO_IKE)); peer_cfg = peer_cfg_create("ha", IKEV2, ike_cfg, CERT_NEVER_SEND, UNIQUE_KEEP, 0, 86400, 0, 7200, 3600, FALSE, FALSE, 30, - 0, NULL, NULL, FALSE, NULL, NULL); + 0, FALSE, NULL, NULL); auth_cfg = auth_cfg_create(); auth_cfg->add(auth_cfg, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_PSK); diff --git a/src/libcharon/plugins/load_tester/load_tester_config.c b/src/libcharon/plugins/load_tester/load_tester_config.c index f5da8b892..735f17985 100644 --- a/src/libcharon/plugins/load_tester/load_tester_config.c +++ b/src/libcharon/plugins/load_tester/load_tester_config.c @@ -268,8 +268,15 @@ static peer_cfg_t* generate_config(private_load_tester_config_t *this, uint num) FALSE, FALSE, /* mobike, aggressive mode */ this->dpd_delay, /* dpd_delay */ this->dpd_timeout, /* dpd_timeout */ - this->vip ? this->vip->clone(this->vip) : NULL, - this->pool, FALSE, NULL, NULL); + FALSE, NULL, NULL); + if (this->vip) + { + peer_cfg->add_virtual_ip(peer_cfg, this->vip->clone(this->vip)); + } + if (this->pool) + { + peer_cfg->add_pool(peer_cfg, this->pool); + } if (num) { /* initiator */ generate_auth_cfg(this, this->initiator_auth, peer_cfg, TRUE, num); diff --git a/src/libcharon/plugins/maemo/maemo_service.c b/src/libcharon/plugins/maemo/maemo_service.c index f638a81b7..cb2fc9ebb 100644 --- a/src/libcharon/plugins/maemo/maemo_service.c +++ b/src/libcharon/plugins/maemo/maemo_service.c @@ -335,8 +335,8 @@ static gboolean initiate_connection(private_maemo_service_t *this, 600, 600, /* jitter, over 10min */ TRUE, FALSE, /* mobike, aggressive */ 0, 0, /* DPD delay, timeout */ - host_create_from_string("0.0.0.0", 0) /* virt */, - NULL, FALSE, NULL, NULL); /* pool, mediation */ + FALSE, NULL, NULL); /* mediation */ + peer_cfg->add_virtual_ip(peer_cfg, host_create_from_string("0.0.0.0", 0)); auth = auth_cfg_create(); auth->add(auth, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_EAP); diff --git a/src/libcharon/plugins/medcli/medcli_config.c b/src/libcharon/plugins/medcli/medcli_config.c index 829c8f05e..a1825effc 100644 --- a/src/libcharon/plugins/medcli/medcli_config.c +++ b/src/libcharon/plugins/medcli/medcli_config.c @@ -129,7 +129,6 @@ METHOD(backend_t, get_peer_cfg_by_name, peer_cfg_t*, this->rekey*5, this->rekey*3, /* jitter, overtime */ TRUE, FALSE, /* mobike, aggressive */ this->dpd, 0, /* DPD delay, timeout */ - NULL, NULL, /* vip, pool */ TRUE, NULL, NULL); /* mediation, med by, peer id */ e->destroy(e); @@ -167,7 +166,6 @@ METHOD(backend_t, get_peer_cfg_by_name, peer_cfg_t*, this->rekey*5, this->rekey*3, /* jitter, overtime */ TRUE, FALSE, /* mobike, aggressive */ this->dpd, 0, /* DPD delay, timeout */ - NULL, NULL, /* vip, pool */ FALSE, med_cfg, /* mediation, med by */ identification_create_from_encoding(ID_KEY_ID, other)); @@ -243,7 +241,6 @@ METHOD(enumerator_t, peer_enumerator_enumerate, bool, this->rekey*5, this->rekey*3, /* jitter, overtime */ TRUE, FALSE, /* mobike, aggressive */ this->dpd, 0, /* DPD delay, timeout */ - NULL, NULL, /* vip, pool */ FALSE, NULL, NULL); /* mediation, med by, peer id */ auth = auth_cfg_create(); diff --git a/src/libcharon/plugins/medsrv/medsrv_config.c b/src/libcharon/plugins/medsrv/medsrv_config.c index ebc142de2..ff33c53e1 100644 --- a/src/libcharon/plugins/medsrv/medsrv_config.c +++ b/src/libcharon/plugins/medsrv/medsrv_config.c @@ -94,7 +94,6 @@ METHOD(backend_t, create_peer_cfg_enumerator, enumerator_t*, this->rekey*5, this->rekey*3, /* jitter, overtime */ TRUE, FALSE, /* mobike, aggressiv */ this->dpd, 0, /* DPD delay, timeout */ - NULL, NULL, /* vip, pool */ TRUE, NULL, NULL); /* mediation, med by, peer id */ e->destroy(e); diff --git a/src/libcharon/plugins/sql/sql_config.c b/src/libcharon/plugins/sql/sql_config.c index 890077157..c614c679e 100644 --- a/src/libcharon/plugins/sql/sql_config.c +++ b/src/libcharon/plugins/sql/sql_config.c @@ -333,6 +333,7 @@ static peer_cfg_t *build_peer_cfg(private_sql_config_t *this, enumerator_t *e, mediation, mediated_by, p_type; chunk_t l_data, r_data, p_data; char *name, *virtual, *pool; + enumerator_t *enumerator; while (e->enumerate(e, &id, &name, &ike_cfg, &l_type, &l_data, &r_type, &r_data, @@ -371,8 +372,23 @@ static peer_cfg_t *build_peer_cfg(private_sql_config_t *this, enumerator_t *e, peer_cfg = peer_cfg_create( name, IKEV2, ike, cert_policy, uniqueid, keyingtries, rekeytime, reauthtime, jitter, overtime, - mobike, FALSE, dpd_delay, 0, vip, pool, + mobike, FALSE, dpd_delay, 0, mediation, mediated_cfg, peer_id); + if (vip) + { + peer_cfg->add_virtual_ip(peer_cfg, vip); + } + if (pool) + { + /* attr-sql used comma separated pools, but we now completely + * support multiple pools directly. Support old SQL configs: */ + enumerator = enumerator_create_token(pool, ",", " "); + while (enumerator->enumerate(enumerator, &pool)) + { + peer_cfg->add_pool(peer_cfg, pool); + } + enumerator->destroy(enumerator); + } auth = auth_cfg_create(); auth->add(auth, AUTH_RULE_AUTH_CLASS, auth_method); auth->add(auth, AUTH_RULE_IDENTITY, local_id); diff --git a/src/libcharon/plugins/stroke/Makefile.am b/src/libcharon/plugins/stroke/Makefile.am index e561224e9..cebcd984f 100644 --- a/src/libcharon/plugins/stroke/Makefile.am +++ b/src/libcharon/plugins/stroke/Makefile.am @@ -21,6 +21,7 @@ libstrongswan_stroke_la_SOURCES = \ stroke_cred.h stroke_cred.c \ stroke_ca.h stroke_ca.c \ stroke_attribute.h stroke_attribute.c \ + stroke_handler.h stroke_handler.c \ stroke_list.h stroke_list.c libstrongswan_stroke_la_LDFLAGS = -module -avoid-version diff --git a/src/libcharon/plugins/stroke/stroke_attribute.c b/src/libcharon/plugins/stroke/stroke_attribute.c index 1e4615e12..3e012230a 100644 --- a/src/libcharon/plugins/stroke/stroke_attribute.c +++ b/src/libcharon/plugins/stroke/stroke_attribute.c @@ -17,7 +17,6 @@ #include "stroke_attribute.h" #include <daemon.h> -#include <attributes/mem_pool.h> #include <utils/linked_list.h> #include <threading/rwlock.h> @@ -39,12 +38,37 @@ struct private_stroke_attribute_t { linked_list_t *pools; /** + * List of connection specific attributes, as attributes_t + */ + linked_list_t *attrs; + + /** * rwlock to lock access to pools */ rwlock_t *lock; }; /** + * Attributes assigned to a connection + */ +typedef struct { + /** name of the connection */ + char *name; + /** list of DNS attributes, as host_t */ + linked_list_t *dns; +} attributes_t; + +/** + * Destroy an attributes_t entry + */ +static void attributes_destroy(attributes_t *this) +{ + this->dns->destroy_offset(this->dns, offsetof(host_t, destroy)); + free(this->name); + free(this); +} + +/** * find a pool by name */ static mem_pool_t *find_pool(private_stroke_attribute_t *this, char *name) @@ -66,11 +90,12 @@ static mem_pool_t *find_pool(private_stroke_attribute_t *this, char *name) } METHOD(attribute_provider_t, acquire_address, host_t*, - private_stroke_attribute_t *this, char *name, identification_t *id, - host_t *requested) + private_stroke_attribute_t *this, char *name, identification_t *id, + host_t *requested) { mem_pool_t *pool; host_t *addr = NULL; + this->lock->read_lock(this->lock); pool = find_pool(this, name); if (pool) @@ -82,11 +107,12 @@ METHOD(attribute_provider_t, acquire_address, host_t*, } METHOD(attribute_provider_t, release_address, bool, - private_stroke_attribute_t *this, char *name, host_t *address, - identification_t *id) + private_stroke_attribute_t *this, char *name, host_t *address, + identification_t *id) { mem_pool_t *pool; bool found = FALSE; + this->lock->read_lock(this->lock); pool = find_pool(this, name); if (pool) @@ -97,56 +123,162 @@ METHOD(attribute_provider_t, release_address, bool, return found; } -METHOD(stroke_attribute_t, add_pool, void, - private_stroke_attribute_t *this, stroke_msg_t *msg) +/** + * Filter function to convert host to DNS configuration attributes + */ +static bool attr_filter(void *lock, host_t **in, + configuration_attribute_type_t *type, + void *dummy, chunk_t *data) { - if (msg->add_conn.other.sourceip_mask) + host_t *host = *in; + + switch (host->get_family(host)) { - mem_pool_t *pool; - host_t *base = NULL; - u_int32_t bits = 0; + case AF_INET: + *type = INTERNAL_IP4_DNS; + break; + case AF_INET6: + *type = INTERNAL_IP6_DNS; + break; + default: + return FALSE; + } + *data = host->get_address(host); + return TRUE; +} - /* if %config, add an empty pool, otherwise */ - if (msg->add_conn.other.sourceip) +METHOD(attribute_provider_t, create_attribute_enumerator, enumerator_t*, + private_stroke_attribute_t *this, linked_list_t *pools, + identification_t *id, linked_list_t *vips) +{ + ike_sa_t *ike_sa; + peer_cfg_t *peer_cfg; + enumerator_t *enumerator; + attributes_t *attr; + + ike_sa = charon->bus->get_sa(charon->bus); + if (ike_sa) + { + peer_cfg = ike_sa->get_peer_cfg(ike_sa); + this->lock->read_lock(this->lock); + enumerator = this->attrs->create_enumerator(this->attrs); + while (enumerator->enumerate(enumerator, &attr)) { - DBG1(DBG_CFG, "adding virtual IP address pool '%s': %s/%d", - msg->add_conn.name, msg->add_conn.other.sourceip, - msg->add_conn.other.sourceip_mask); - base = host_create_from_string(msg->add_conn.other.sourceip, 0); - if (!base) + if (streq(attr->name, peer_cfg->get_name(peer_cfg))) { - DBG1(DBG_CFG, "virtual IP address invalid, discarded"); - return; + enumerator->destroy(enumerator); + return enumerator_create_filter( + attr->dns->create_enumerator(attr->dns), + (void*)attr_filter, this->lock, + (void*)this->lock->unlock); } - bits = msg->add_conn.other.sourceip_mask; } - pool = mem_pool_create(msg->add_conn.name, base, bits); - DESTROY_IF(base); - - this->lock->write_lock(this->lock); - this->pools->insert_last(this->pools, pool); + enumerator->destroy(enumerator); this->lock->unlock(this->lock); } + return enumerator_create_empty(); } -METHOD(stroke_attribute_t, del_pool, void, - private_stroke_attribute_t *this, stroke_msg_t *msg) +METHOD(stroke_attribute_t, add_pool, void, + private_stroke_attribute_t *this, mem_pool_t *pool) { enumerator_t *enumerator; - mem_pool_t *pool; + mem_pool_t *current; + host_t *base; + int size; + + base = pool->get_base(pool); + size = pool->get_size(pool); this->lock->write_lock(this->lock); + enumerator = this->pools->create_enumerator(this->pools); - while (enumerator->enumerate(enumerator, &pool)) + while (enumerator->enumerate(enumerator, ¤t)) { - if (streq(msg->del_conn.name, pool->get_name(pool))) + if (base && current->get_base(current) && + base->ip_equals(base, current->get_base(current)) && + size == current->get_size(current)) { - this->pools->remove_at(this->pools, enumerator); pool->destroy(pool); + pool = NULL; + DBG1(DBG_CFG, "reusing virtual IP address pool %H/%d", base, size); + break; + } + } + enumerator->destroy(enumerator); + + if (pool) + { + if (base) + { + DBG1(DBG_CFG, "adding virtual IP address pool %H/%d", base, size); + } + this->pools->insert_last(this->pools, pool); + } + + this->lock->unlock(this->lock); +} + +METHOD(stroke_attribute_t, add_dns, void, + private_stroke_attribute_t *this, stroke_msg_t *msg) +{ + if (msg->add_conn.other.dns) + { + enumerator_t *enumerator; + attributes_t *attr = NULL; + host_t *host; + char *token; + + enumerator = enumerator_create_token(msg->add_conn.other.dns, ",", " "); + while (enumerator->enumerate(enumerator, &token)) + { + host = host_create_from_string(token, 0); + if (host) + { + if (!attr) + { + INIT(attr, + .name = strdup(msg->add_conn.name), + .dns = linked_list_create(), + ); + } + attr->dns->insert_last(attr->dns, host); + } + else + { + DBG1(DBG_CFG, "ignoring invalid DNS address '%s'", token); + } + } + enumerator->destroy(enumerator); + if (attr) + { + this->lock->write_lock(this->lock); + this->attrs->insert_last(this->attrs, attr); + this->lock->unlock(this->lock); + } + } +} + +METHOD(stroke_attribute_t, del_dns, void, + private_stroke_attribute_t *this, stroke_msg_t *msg) +{ + enumerator_t *enumerator; + attributes_t *attr; + + this->lock->write_lock(this->lock); + + enumerator = this->attrs->create_enumerator(this->attrs); + while (enumerator->enumerate(enumerator, &attr)) + { + if (streq(msg->del_conn.name, attr->name)) + { + this->attrs->remove_at(this->attrs, enumerator); + attributes_destroy(attr); break; } } enumerator->destroy(enumerator); + this->lock->unlock(this->lock); } @@ -158,6 +290,11 @@ static bool pool_filter(void *lock, mem_pool_t **poolp, const char **name, void *d3, u_int *offline) { mem_pool_t *pool = *poolp; + + if (pool->get_size(pool) == 0) + { + return FALSE; + } *name = pool->get_name(pool); *size = pool->get_size(pool); *online = pool->get_online(pool); @@ -166,7 +303,7 @@ static bool pool_filter(void *lock, mem_pool_t **poolp, const char **name, } METHOD(stroke_attribute_t, create_pool_enumerator, enumerator_t*, - private_stroke_attribute_t *this) + private_stroke_attribute_t *this) { this->lock->read_lock(this->lock); return enumerator_create_filter(this->pools->create_enumerator(this->pools), @@ -175,7 +312,7 @@ METHOD(stroke_attribute_t, create_pool_enumerator, enumerator_t*, } METHOD(stroke_attribute_t, create_lease_enumerator, enumerator_t*, - private_stroke_attribute_t *this, char *name) + private_stroke_attribute_t *this, char *name) { mem_pool_t *pool; this->lock->read_lock(this->lock); @@ -190,10 +327,11 @@ METHOD(stroke_attribute_t, create_lease_enumerator, enumerator_t*, } METHOD(stroke_attribute_t, destroy, void, - private_stroke_attribute_t *this) + private_stroke_attribute_t *this) { this->lock->destroy(this->lock); this->pools->destroy_offset(this->pools, offsetof(mem_pool_t, destroy)); + this->attrs->destroy_function(this->attrs, (void*)attributes_destroy); free(this); } @@ -209,15 +347,17 @@ stroke_attribute_t *stroke_attribute_create() .provider = { .acquire_address = _acquire_address, .release_address = _release_address, - .create_attribute_enumerator = enumerator_create_empty, + .create_attribute_enumerator = _create_attribute_enumerator, }, .add_pool = _add_pool, - .del_pool = _del_pool, + .add_dns = _add_dns, + .del_dns = _del_dns, .create_pool_enumerator = _create_pool_enumerator, .create_lease_enumerator = _create_lease_enumerator, .destroy = _destroy, }, .pools = linked_list_create(), + .attrs = linked_list_create(), .lock = rwlock_create(RWLOCK_TYPE_DEFAULT), ); diff --git a/src/libcharon/plugins/stroke/stroke_attribute.h b/src/libcharon/plugins/stroke/stroke_attribute.h index 249a9899b..f1b9d135b 100644 --- a/src/libcharon/plugins/stroke/stroke_attribute.h +++ b/src/libcharon/plugins/stroke/stroke_attribute.h @@ -23,6 +23,7 @@ #include <stroke_msg.h> #include <attributes/attribute_provider.h> +#include <attributes/mem_pool.h> typedef struct stroke_attribute_t stroke_attribute_t; @@ -37,18 +38,28 @@ struct stroke_attribute_t { attribute_provider_t provider; /** - * Add a virtual IP address pool. + * Add a memory pool to this virtual IP backend. * - * @param msg stroke message + * The pool gets owned by the provider, or destroyed if such a pool + * is already registered. + * + * @param pool virtual IP pool to add + */ + void (*add_pool)(stroke_attribute_t *this, mem_pool_t *pool); + + /** + * Add connection specific DNS servers. + * + * @param msg stroke add message */ - void (*add_pool)(stroke_attribute_t *this, stroke_msg_t *msg); + void (*add_dns)(stroke_attribute_t *this, stroke_msg_t *msg); /** - * Remove a virtual IP address pool. + * Remove connection specific DNS servers. * - * @param msg stroke message + * @param msg stroke del message */ - void (*del_pool)(stroke_attribute_t *this, stroke_msg_t *msg); + void (*del_dns)(stroke_attribute_t *this, stroke_msg_t *msg); /** * Create an enumerator over installed pools. diff --git a/src/libcharon/plugins/stroke/stroke_config.c b/src/libcharon/plugins/stroke/stroke_config.c index bf93f2c34..e3c78f750 100644 --- a/src/libcharon/plugins/stroke/stroke_config.c +++ b/src/libcharon/plugins/stroke/stroke_config.c @@ -52,6 +52,11 @@ struct private_stroke_config_t { * credentials */ stroke_cred_t *cred; + + /** + * Virtual IP pool / DNS backend + */ + stroke_attribute_t *attributes; }; METHOD(backend_t, create_peer_cfg_enumerator, enumerator_t*, @@ -563,51 +568,20 @@ static auth_cfg_t *build_auth_cfg(private_stroke_config_t *this, } else if (strneq(auth, "eap", 3)) { - enumerator_t *enumerator; - char *str; - int i = 0, type = 0, vendor; + eap_vendor_type_t *type; cfg->add(cfg, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_EAP); - /* parse EAP string, format: eap[-type[-vendor]] */ - enumerator = enumerator_create_token(auth, "-", " "); - while (enumerator->enumerate(enumerator, &str)) + type = eap_vendor_type_from_string(auth); + if (type) { - switch (i) + cfg->add(cfg, AUTH_RULE_EAP_TYPE, type->type); + if (type->vendor) { - case 1: - type = eap_type_from_string(str); - if (!type) - { - type = atoi(str); - if (!type) - { - DBG1(DBG_CFG, "unknown EAP method: %s", str); - break; - } - } - cfg->add(cfg, AUTH_RULE_EAP_TYPE, type); - break; - case 2: - if (type) - { - vendor = atoi(str); - if (vendor) - { - cfg->add(cfg, AUTH_RULE_EAP_VENDOR, vendor); - } - else - { - DBG1(DBG_CFG, "unknown EAP vendor: %s", str); - } - } - break; - default: - break; + cfg->add(cfg, AUTH_RULE_EAP_VENDOR, type->vendor); } - i++; + free(type); } - enumerator->destroy(enumerator); if (msg->add_conn.eap_identity) { @@ -649,7 +623,6 @@ static peer_cfg_t *build_peer_cfg(private_stroke_config_t *this, { identification_t *peer_id = NULL; peer_cfg_t *mediated_by = NULL; - host_t *vip = NULL; unique_policy_t unique; u_int32_t rekey = 0, reauth = 0, over, jitter; peer_cfg_t *peer_cfg; @@ -708,49 +681,6 @@ static peer_cfg_t *build_peer_cfg(private_stroke_config_t *this, { rekey = msg->add_conn.rekey.ike_lifetime - over; } - if (msg->add_conn.me.sourceip_mask) - { - if (msg->add_conn.me.sourceip) - { - vip = host_create_from_string(msg->add_conn.me.sourceip, 0); - } - if (!vip) - { /* if it is set to something like %poolname, request an address */ - if (msg->add_conn.me.subnets) - { /* use the same family as in local subnet, if any */ - if (strchr(msg->add_conn.me.subnets, '.')) - { - vip = host_create_any(AF_INET); - } - else - { - vip = host_create_any(AF_INET6); - } - } - else if (msg->add_conn.other.subnets) - { /* use the same family as in remote subnet, if any */ - if (strchr(msg->add_conn.other.subnets, '.')) - { - vip = host_create_any(AF_INET); - } - else - { - vip = host_create_any(AF_INET6); - } - } - else - { - if (strchr(ike_cfg->get_my_addr(ike_cfg, NULL), ':')) - { - vip = host_create_any(AF_INET6); - } - else - { - vip = host_create_any(AF_INET); - } - } - } - } switch (msg->add_conn.unique) { case 1: /* yes */ @@ -778,10 +708,126 @@ static peer_cfg_t *build_peer_cfg(private_stroke_config_t *this, msg->add_conn.rekey.tries, rekey, reauth, jitter, over, msg->add_conn.mobike, msg->add_conn.aggressive, msg->add_conn.dpd.delay, msg->add_conn.dpd.timeout, - vip, msg->add_conn.other.sourceip_mask ? - msg->add_conn.name : msg->add_conn.other.sourceip, msg->add_conn.ikeme.mediation, mediated_by, peer_id); + if (msg->add_conn.other.sourceip) + { + enumerator_t *enumerator; + char *token; + + enumerator = enumerator_create_token(msg->add_conn.other.sourceip, + ",", " "); + while (enumerator->enumerate(enumerator, &token)) + { + if (streq(token, "%modeconfig") || streq(token, "%modecfg") || + streq(token, "%config") || streq(token, "%cfg") || + streq(token, "%config4") || streq(token, "%config6")) + { + /* empty pool, uses connection name */ + this->attributes->add_pool(this->attributes, + mem_pool_create(msg->add_conn.name, NULL, 0)); + peer_cfg->add_pool(peer_cfg, msg->add_conn.name); + } + else if (*token == '%') + { + /* external named pool */ + peer_cfg->add_pool(peer_cfg, token + 1); + } + else + { + /* in-memory pool, named using CIDR notation */ + host_t *base; + int bits; + + base = host_create_from_subnet(token, &bits); + if (base) + { + this->attributes->add_pool(this->attributes, + mem_pool_create(token, base, bits)); + peer_cfg->add_pool(peer_cfg, token); + base->destroy(base); + } + else + { + DBG1(DBG_CFG, "IP pool %s invalid, ignored", token); + } + } + } + enumerator->destroy(enumerator); + } + + if (msg->add_conn.me.sourceip) + { + enumerator_t *enumerator; + char *token; + + enumerator = enumerator_create_token(msg->add_conn.me.sourceip, ",", " "); + while (enumerator->enumerate(enumerator, &token)) + { + host_t *vip = NULL; + + if (streq(token, "%modeconfig") || streq(token, "%modecfg") || + streq(token, "%config") || streq(token, "%cfg")) + { /* try to deduce an address family */ + if (msg->add_conn.me.subnets) + { /* use the same family as in local subnet, if any */ + if (strchr(msg->add_conn.me.subnets, '.')) + { + vip = host_create_any(AF_INET); + } + else + { + vip = host_create_any(AF_INET6); + } + } + else if (msg->add_conn.other.subnets) + { /* use the same family as in remote subnet, if any */ + if (strchr(msg->add_conn.other.subnets, '.')) + { + vip = host_create_any(AF_INET); + } + else + { + vip = host_create_any(AF_INET6); + } + } + else + { + if (strchr(ike_cfg->get_my_addr(ike_cfg, NULL), ':')) + { + vip = host_create_any(AF_INET6); + } + else + { + vip = host_create_any(AF_INET); + } + } + } + else if (streq(token, "%config4")) + { + vip = host_create_any(AF_INET); + } + else if (streq(token, "%config6")) + { + vip = host_create_any(AF_INET6); + } + else + { + vip = host_create_from_string(token, 0); + if (vip) + { + DBG1(DBG_CFG, "ignored invalid subnet token: %s", token); + } + } + + if (vip) + { + peer_cfg->add_virtual_ip(peer_cfg, vip); + } + } + enumerator->destroy(enumerator); + } + /* build leftauth= */ auth_cfg = build_auth_cfg(this, msg, TRUE, TRUE); if (auth_cfg) @@ -1240,7 +1286,8 @@ METHOD(stroke_config_t, destroy, void, /* * see header file */ -stroke_config_t *stroke_config_create(stroke_ca_t *ca, stroke_cred_t *cred) +stroke_config_t *stroke_config_create(stroke_ca_t *ca, stroke_cred_t *cred, + stroke_attribute_t *attributes) { private_stroke_config_t *this; @@ -1260,6 +1307,7 @@ stroke_config_t *stroke_config_create(stroke_ca_t *ca, stroke_cred_t *cred) .mutex = mutex_create(MUTEX_TYPE_RECURSIVE), .ca = ca, .cred = cred, + .attributes = attributes, ); return &this->public; diff --git a/src/libcharon/plugins/stroke/stroke_config.h b/src/libcharon/plugins/stroke/stroke_config.h index 450d517f3..894e03ce4 100644 --- a/src/libcharon/plugins/stroke/stroke_config.h +++ b/src/libcharon/plugins/stroke/stroke_config.h @@ -26,6 +26,7 @@ #include <stroke_msg.h> #include "stroke_ca.h" #include "stroke_cred.h" +#include "stroke_attribute.h" typedef struct stroke_config_t stroke_config_t; @@ -71,6 +72,7 @@ struct stroke_config_t { /** * Create a stroke_config instance. */ -stroke_config_t *stroke_config_create(stroke_ca_t *ca, stroke_cred_t *cred); +stroke_config_t *stroke_config_create(stroke_ca_t *ca, stroke_cred_t *cred, + stroke_attribute_t *attributes); #endif /** STROKE_CONFIG_H_ @}*/ diff --git a/src/libcharon/plugins/stroke/stroke_control.c b/src/libcharon/plugins/stroke/stroke_control.c index 163a2a750..233d4088f 100644 --- a/src/libcharon/plugins/stroke/stroke_control.c +++ b/src/libcharon/plugins/stroke/stroke_control.c @@ -407,10 +407,10 @@ METHOD(stroke_control_t, rekey, void, METHOD(stroke_control_t, terminate_srcip, void, private_stroke_control_t *this, stroke_msg_t *msg, FILE *out) { - enumerator_t *enumerator; + enumerator_t *enumerator, *vips; ike_sa_t *ike_sa; host_t *start = NULL, *end = NULL, *vip; - chunk_t chunk_start, chunk_end = chunk_empty, chunk_vip; + chunk_t chunk_start, chunk_end = chunk_empty, chunk; if (msg->terminate_srcip.start) { @@ -438,33 +438,40 @@ METHOD(stroke_control_t, terminate_srcip, void, charon->controller, TRUE); while (enumerator->enumerate(enumerator, &ike_sa)) { - vip = ike_sa->get_virtual_ip(ike_sa, FALSE); - if (!vip) - { - continue; - } - if (!end) + bool match = FALSE; + + vips = ike_sa->create_virtual_ip_enumerator(ike_sa, FALSE); + while (vips->enumerate(vips, &vip)) { - if (!vip->ip_equals(vip, start)) + if (!end) { - continue; + if (vip->ip_equals(vip, start)) + { + match = TRUE; + break; + } } - } - else - { - chunk_vip = vip->get_address(vip); - if (chunk_vip.len != chunk_start.len || - chunk_vip.len != chunk_end.len || - memcmp(chunk_vip.ptr, chunk_start.ptr, chunk_vip.len) < 0 || - memcmp(chunk_vip.ptr, chunk_end.ptr, chunk_vip.len) > 0) + else { - continue; + chunk = vip->get_address(vip); + if (chunk.len == chunk_start.len && + chunk.len == chunk_end.len && + memcmp(chunk.ptr, chunk_start.ptr, chunk.len) >= 0 && + memcmp(chunk.ptr, chunk_end.ptr, chunk.len) <= 0) + { + match = TRUE; + break; + } } } + vips->destroy(vips); - /* schedule delete asynchronously */ - lib->processor->queue_job(lib->processor, (job_t*) + if (match) + { + /* schedule delete asynchronously */ + lib->processor->queue_job(lib->processor, (job_t*) delete_ike_sa_job_create(ike_sa->get_id(ike_sa), TRUE)); + } } enumerator->destroy(enumerator); start->destroy(start); diff --git a/src/libcharon/plugins/stroke/stroke_handler.c b/src/libcharon/plugins/stroke/stroke_handler.c new file mode 100644 index 000000000..523151efb --- /dev/null +++ b/src/libcharon/plugins/stroke/stroke_handler.c @@ -0,0 +1,231 @@ +/* + * Copyright (C) 2012 Martin Willi + * Copyright (C) 2012 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "stroke_handler.h" + +#include <daemon.h> +#include <utils/linked_list.h> +#include <threading/rwlock.h> + +typedef struct private_stroke_handler_t private_stroke_handler_t; + +/** + * Private data of an stroke_handler_t object. + */ +struct private_stroke_handler_t { + + /** + * Public stroke_handler_t interface. + */ + stroke_handler_t public; + + /** + * List of connection specific attributes, as attributes_t + */ + linked_list_t *attrs; + + /** + * rwlock to lock access to pools + */ + rwlock_t *lock; +}; + +/** + * Attributes assigned to a connection + */ +typedef struct { + /** name of the connection */ + char *name; + /** list of DNS attributes, as host_t */ + linked_list_t *dns; +} attributes_t; + +/** + * Destroy an attributes_t entry + */ +static void attributes_destroy(attributes_t *this) +{ + this->dns->destroy_offset(this->dns, offsetof(host_t, destroy)); + free(this->name); + free(this); +} + +/** + * Filter function to convert host to DNS configuration attributes + */ +static bool attr_filter(void *lock, host_t **in, + configuration_attribute_type_t *type, + void *dummy, chunk_t *data) +{ + host_t *host = *in; + + switch (host->get_family(host)) + { + case AF_INET: + *type = INTERNAL_IP4_DNS; + break; + case AF_INET6: + *type = INTERNAL_IP6_DNS; + break; + default: + return FALSE; + } + if (host->is_anyaddr(host)) + { + *data = chunk_empty; + } + else + { + *data = host->get_address(host); + } + return TRUE; +} + +METHOD(attribute_handler_t, create_attribute_enumerator, enumerator_t*, + private_stroke_handler_t *this, identification_t *server, + linked_list_t *vips) +{ + ike_sa_t *ike_sa; + peer_cfg_t *peer_cfg; + enumerator_t *enumerator; + attributes_t *attr; + + ike_sa = charon->bus->get_sa(charon->bus); + if (ike_sa) + { + peer_cfg = ike_sa->get_peer_cfg(ike_sa); + this->lock->read_lock(this->lock); + enumerator = this->attrs->create_enumerator(this->attrs); + while (enumerator->enumerate(enumerator, &attr)) + { + if (streq(attr->name, peer_cfg->get_name(peer_cfg))) + { + enumerator->destroy(enumerator); + return enumerator_create_filter( + attr->dns->create_enumerator(attr->dns), + (void*)attr_filter, this->lock, + (void*)this->lock->unlock); + } + } + enumerator->destroy(enumerator); + this->lock->unlock(this->lock); + } + return enumerator_create_empty(); +} + +METHOD(stroke_handler_t, add_attributes, void, + private_stroke_handler_t *this, stroke_msg_t *msg) +{ + if (msg->add_conn.me.dns) + { + enumerator_t *enumerator; + attributes_t *attr = NULL; + host_t *host; + char *token; + + enumerator = enumerator_create_token(msg->add_conn.me.dns, ",", " "); + while (enumerator->enumerate(enumerator, &token)) + { + if (streq(token, "%config") || streq(token, "%config4")) + { + host = host_create_any(AF_INET); + } + else if (streq(token, "%config6")) + { + host = host_create_any(AF_INET6); + } + else + { + host = host_create_from_string(token, 0); + } + if (host) + { + if (!attr) + { + INIT(attr, + .name = strdup(msg->add_conn.name), + .dns = linked_list_create(), + ); + } + attr->dns->insert_last(attr->dns, host); + } + else + { + DBG1(DBG_CFG, "ignoring invalid DNS address '%s'", token); + } + } + enumerator->destroy(enumerator); + if (attr) + { + this->lock->write_lock(this->lock); + this->attrs->insert_last(this->attrs, attr); + this->lock->unlock(this->lock); + } + } +} + +METHOD(stroke_handler_t, del_attributes, void, + private_stroke_handler_t *this, stroke_msg_t *msg) +{ + enumerator_t *enumerator; + attributes_t *attr; + + this->lock->write_lock(this->lock); + enumerator = this->attrs->create_enumerator(this->attrs); + while (enumerator->enumerate(enumerator, &attr)) + { + if (streq(msg->del_conn.name, attr->name)) + { + this->attrs->remove_at(this->attrs, enumerator); + attributes_destroy(attr); + break; + } + } + enumerator->destroy(enumerator); + this->lock->unlock(this->lock); +} + +METHOD(stroke_handler_t, destroy, void, + private_stroke_handler_t *this) +{ + this->lock->destroy(this->lock); + this->attrs->destroy_function(this->attrs, (void*)attributes_destroy); + free(this); +} + +/** + * See header + */ +stroke_handler_t *stroke_handler_create() +{ + private_stroke_handler_t *this; + + INIT(this, + .public = { + .handler = { + .handle = (void*)return_false, + .release = (void*)return_false, + .create_attribute_enumerator = _create_attribute_enumerator, + }, + .add_attributes = _add_attributes, + .del_attributes = _del_attributes, + .destroy = _destroy, + }, + .attrs = linked_list_create(), + .lock = rwlock_create(RWLOCK_TYPE_DEFAULT), + ); + + return &this->public; +} diff --git a/src/libcharon/plugins/stroke/stroke_handler.h b/src/libcharon/plugins/stroke/stroke_handler.h new file mode 100644 index 000000000..ab76f80b0 --- /dev/null +++ b/src/libcharon/plugins/stroke/stroke_handler.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2012 Martin Willi + * Copyright (C) 2012 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/** + * @defgroup stroke_handler stroke_handler + * @{ @ingroup stroke + */ + +#ifndef STROKE_HANDLER_H_ +#define STROKE_HANDLER_H_ + +#include <stroke_msg.h> +#include <attributes/attribute_handler.h> + +typedef struct stroke_handler_t stroke_handler_t; + +/** + * Handler requesting DNS attributes as defined with leftdns option. + */ +struct stroke_handler_t { + + /** + * Implements the attribute_handler_t interface + */ + attribute_handler_t handler; + + /** + * Add connection specific configuration attributes. + * + * @param msg stroke message + */ + void (*add_attributes)(stroke_handler_t *this, stroke_msg_t *msg); + + /** + * Remove connection specific configuration attributes. + * + * @param msg stroke message + */ + void (*del_attributes)(stroke_handler_t *this, stroke_msg_t *msg); + + /** + * Destroy a stroke_handler_t. + */ + void (*destroy)(stroke_handler_t *this); +}; + +/** + * Create a stroke_handler instance. + */ +stroke_handler_t *stroke_handler_create(); + +#endif /** STROKE_HANDLER_H_ @}*/ diff --git a/src/libcharon/plugins/stroke/stroke_socket.c b/src/libcharon/plugins/stroke/stroke_socket.c index 6bf65cc7e..241f0fbf6 100644 --- a/src/libcharon/plugins/stroke/stroke_socket.c +++ b/src/libcharon/plugins/stroke/stroke_socket.c @@ -37,6 +37,7 @@ #include "stroke_cred.h" #include "stroke_ca.h" #include "stroke_attribute.h" +#include "stroke_handler.h" #include "stroke_list.h" /** @@ -99,6 +100,11 @@ struct private_stroke_socket_t { stroke_attribute_t *attribute; /** + * attribute handler (requests only) + */ + stroke_handler_t *handler; + + /** * controller to control daemon */ stroke_control_t *control; @@ -171,6 +177,7 @@ static void pop_end(stroke_msg_t *msg, const char* label, stroke_end_t *end) pop_string(msg, &end->address); pop_string(msg, &end->subnets); pop_string(msg, &end->sourceip); + pop_string(msg, &end->dns); pop_string(msg, &end->auth); pop_string(msg, &end->auth2); pop_string(msg, &end->id); @@ -188,6 +195,7 @@ static void pop_end(stroke_msg_t *msg, const char* label, stroke_end_t *end) DBG2(DBG_CFG, " %s=%s", label, end->address); DBG2(DBG_CFG, " %ssubnet=%s", label, end->subnets); DBG2(DBG_CFG, " %ssourceip=%s", label, end->sourceip); + DBG2(DBG_CFG, " %sdns=%s", label, end->dns); DBG2(DBG_CFG, " %sauth=%s", label, end->auth); DBG2(DBG_CFG, " %sauth2=%s", label, end->auth2); DBG2(DBG_CFG, " %sid=%s", label, end->id); @@ -235,7 +243,8 @@ static void stroke_add_conn(private_stroke_socket_t *this, stroke_msg_t *msg) DBG2(DBG_CFG, " keyexchange=ikev%u", msg->add_conn.version); this->config->add(this->config, msg); - this->attribute->add_pool(this->attribute, msg); + this->attribute->add_dns(this->attribute, msg); + this->handler->add_attributes(this->handler, msg); } /** @@ -247,7 +256,8 @@ static void stroke_del_conn(private_stroke_socket_t *this, stroke_msg_t *msg) DBG1(DBG_CFG, "received stroke: delete connection '%s'", msg->del_conn.name); this->config->del(this->config, msg); - this->attribute->del_pool(this->attribute, msg); + this->attribute->del_dns(this->attribute, msg); + this->handler->del_attributes(this->handler, msg); } /** @@ -787,10 +797,12 @@ METHOD(stroke_socket_t, destroy, void, lib->credmgr->remove_set(lib->credmgr, &this->cred->set); charon->backends->remove_backend(charon->backends, &this->config->backend); hydra->attributes->remove_provider(hydra->attributes, &this->attribute->provider); + hydra->attributes->remove_handler(hydra->attributes, &this->handler->handler); this->cred->destroy(this->cred); this->ca->destroy(this->ca); this->config->destroy(this->config); this->attribute->destroy(this->attribute); + this->handler->destroy(this->handler); this->control->destroy(this->control); this->list->destroy(this->list); free(this); @@ -817,8 +829,9 @@ stroke_socket_t *stroke_socket_create() this->cred = stroke_cred_create(); this->attribute = stroke_attribute_create(); + this->handler = stroke_handler_create(); this->ca = stroke_ca_create(this->cred); - this->config = stroke_config_create(this->ca, this->cred); + this->config = stroke_config_create(this->ca, this->cred, this->attribute); this->control = stroke_control_create(); this->list = stroke_list_create(this->attribute); @@ -833,6 +846,7 @@ stroke_socket_t *stroke_socket_create() lib->credmgr->add_set(lib->credmgr, &this->cred->set); charon->backends->add_backend(charon->backends, &this->config->backend); hydra->attributes->add_provider(hydra->attributes, &this->attribute->provider); + hydra->attributes->add_handler(hydra->attributes, &this->handler->handler); lib->processor->queue_job(lib->processor, (job_t*)callback_job_create_with_prio((callback_job_cb_t)receive, this, diff --git a/src/libcharon/plugins/uci/uci_config.c b/src/libcharon/plugins/uci/uci_config.c index 5b698b8b2..1201f568e 100644 --- a/src/libcharon/plugins/uci/uci_config.c +++ b/src/libcharon/plugins/uci/uci_config.c @@ -178,7 +178,6 @@ METHOD(enumerator_t, peer_enumerator_enumerate, bool, 1800, 900, /* jitter, overtime */ TRUE, FALSE, /* mobike, aggressive */ 60, 0, /* DPD delay, timeout */ - NULL, NULL, /* vip, pool */ FALSE, NULL, NULL); /* mediation, med by, peer id */ auth = auth_cfg_create(); auth->add(auth, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_PSK); diff --git a/src/libcharon/plugins/updown/Makefile.am b/src/libcharon/plugins/updown/Makefile.am index 312c8d7e8..30683d83e 100644 --- a/src/libcharon/plugins/updown/Makefile.am +++ b/src/libcharon/plugins/updown/Makefile.am @@ -12,6 +12,7 @@ endif libstrongswan_updown_la_SOURCES = \ updown_plugin.h updown_plugin.c \ + updown_handler.h updown_handler.c \ updown_listener.h updown_listener.c libstrongswan_updown_la_LDFLAGS = -module -avoid-version diff --git a/src/libcharon/plugins/updown/updown_handler.c b/src/libcharon/plugins/updown/updown_handler.c new file mode 100644 index 000000000..b2ac02e85 --- /dev/null +++ b/src/libcharon/plugins/updown/updown_handler.c @@ -0,0 +1,243 @@ +/* + * Copyright (C) 2012 Martin Willi + * Copyright (C) 2012 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "updown_handler.h" + +#include <daemon.h> +#include <utils/linked_list.h> +#include <threading/rwlock.h> + +typedef struct private_updown_handler_t private_updown_handler_t; + +/** + * Private data of an updown_handler_t object. + */ +struct private_updown_handler_t { + + /** + * Public updown_handler_t interface. + */ + updown_handler_t public; + + /** + * List of connection specific attributes, as attributes_t + */ + linked_list_t *attrs; + + /** + * rwlock to lock access to pools + */ + rwlock_t *lock; +}; + +/** + * Attributes assigned to an IKE_SA + */ +typedef struct { + /** unique IKE_SA identifier */ + u_int id; + /** list of DNS attributes, as host_t */ + linked_list_t *dns; +} attributes_t; + +/** + * Destroy an attributes_t entry + */ +static void attributes_destroy(attributes_t *this) +{ + this->dns->destroy_offset(this->dns, offsetof(host_t, destroy)); + free(this); +} + +METHOD(attribute_handler_t, handle, bool, + private_updown_handler_t *this, identification_t *server, + configuration_attribute_type_t type, chunk_t data) +{ + attributes_t *current, *attr = NULL; + enumerator_t *enumerator; + ike_sa_t *ike_sa; + host_t *host; + + ike_sa = charon->bus->get_sa(charon->bus); + if (!ike_sa) + { + return FALSE; + } + switch (type) + { + case INTERNAL_IP4_DNS: + host = host_create_from_chunk(AF_INET, data, 0); + break; + case INTERNAL_IP6_DNS: + host = host_create_from_chunk(AF_INET6, data, 0); + break; + default: + return FALSE; + } + if (!host) + { + return FALSE; + } + + this->lock->write_lock(this->lock); + enumerator = this->attrs->create_enumerator(this->attrs); + while (enumerator->enumerate(enumerator, ¤t)) + { + if (current->id == ike_sa->get_unique_id(ike_sa)) + { + attr = current; + } + } + enumerator->destroy(enumerator); + + if (!attr) + { + INIT(attr, + .id = ike_sa->get_unique_id(ike_sa), + .dns = linked_list_create(), + ); + this->attrs->insert_last(this->attrs, attr); + } + attr->dns->insert_last(attr->dns, host); + this->lock->unlock(this->lock); + + return TRUE; +} + +METHOD(attribute_handler_t, release, void, + private_updown_handler_t *this, identification_t *server, + configuration_attribute_type_t type, chunk_t data) +{ + attributes_t *attr; + enumerator_t *enumerator, *servers; + ike_sa_t *ike_sa; + host_t *host; + bool found = FALSE; + int family; + + switch (type) + { + case INTERNAL_IP4_DNS: + family = AF_INET; + break; + case INTERNAL_IP6_DNS: + family = AF_INET6; + break; + default: + return; + } + + ike_sa = charon->bus->get_sa(charon->bus); + if (ike_sa) + { + this->lock->write_lock(this->lock); + enumerator = this->attrs->create_enumerator(this->attrs); + while (enumerator->enumerate(enumerator, &attr)) + { + if (attr->id == ike_sa->get_unique_id(ike_sa)) + { + servers = attr->dns->create_enumerator(attr->dns); + while (servers->enumerate(servers, &host)) + { + if (host->get_family(host) == family && + chunk_equals(data, host->get_address(host))) + { + attr->dns->remove_at(attr->dns, servers); + host->destroy(host); + found = TRUE; + break; + } + } + servers->destroy(servers); + if (attr->dns->get_count(attr->dns) == 0) + { + this->attrs->remove_at(this->attrs, enumerator); + attributes_destroy(attr); + break; + } + } + if (found) + { + break; + } + } + enumerator->destroy(enumerator); + this->lock->unlock(this->lock); + } +} + +METHOD(updown_handler_t, create_dns_enumerator, enumerator_t*, + private_updown_handler_t *this, u_int id) +{ + attributes_t *attr; + enumerator_t *enumerator; + ike_sa_t *ike_sa; + + ike_sa = charon->bus->get_sa(charon->bus); + if (!ike_sa) + { + return FALSE; + } + + this->lock->read_lock(this->lock); + enumerator = this->attrs->create_enumerator(this->attrs); + while (enumerator->enumerate(enumerator, &attr)) + { + if (attr->id == ike_sa->get_unique_id(ike_sa)) + { + enumerator->destroy(enumerator); + return enumerator_create_cleaner( + attr->dns->create_enumerator(attr->dns), + (void*)this->lock->unlock, this->lock); + } + } + enumerator->destroy(enumerator); + this->lock->unlock(this->lock); + + return enumerator_create_empty(); +} + + +METHOD(updown_handler_t, destroy, void, + private_updown_handler_t *this) +{ + this->lock->destroy(this->lock); + this->attrs->destroy_function(this->attrs, (void*)attributes_destroy); + free(this); +} + +/** + * See header + */ +updown_handler_t *updown_handler_create() +{ + private_updown_handler_t *this; + + INIT(this, + .public = { + .handler = { + .handle = _handle, + .release = _release, + .create_attribute_enumerator = enumerator_create_empty, + }, + .create_dns_enumerator = _create_dns_enumerator, + .destroy = _destroy, + }, + .attrs = linked_list_create(), + .lock = rwlock_create(RWLOCK_TYPE_DEFAULT), + ); + + return &this->public; +} diff --git a/src/libcharon/plugins/updown/updown_handler.h b/src/libcharon/plugins/updown/updown_handler.h new file mode 100644 index 000000000..d4de880b8 --- /dev/null +++ b/src/libcharon/plugins/updown/updown_handler.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2012 Martin Willi + * Copyright (C) 2012 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/** + * @defgroup updown_handler updown_handler + * @{ @ingroup updown + */ + +#ifndef UPDOWN_HANDLER_H_ +#define UPDOWN_HANDLER_H_ + +#include <attributes/attribute_handler.h> + +typedef struct updown_handler_t updown_handler_t; + +/** + * Handler storing configuration attributes to pass to updown script. + */ +struct updown_handler_t { + + /** + * Implements the attribute_handler_t interface + */ + attribute_handler_t handler; + + /** + * Create an enumerator over received DNS servers. + * + * @param id unique IKE_SA identifier to get attributes for + * @return enumerator over host_t* + */ + enumerator_t* (*create_dns_enumerator)(updown_handler_t *this, u_int id); + + /** + * Destroy a updown_handler_t. + */ + void (*destroy)(updown_handler_t *this); +}; + +/** + * Create a updown_handler instance. + */ +updown_handler_t *updown_handler_create(); + +#endif /** UPDOWN_HANDLER_H_ @}*/ diff --git a/src/libcharon/plugins/updown/updown_listener.c b/src/libcharon/plugins/updown/updown_listener.c index 2bd757ec7..2f9f2ef91 100644 --- a/src/libcharon/plugins/updown/updown_listener.c +++ b/src/libcharon/plugins/updown/updown_listener.c @@ -38,6 +38,11 @@ struct private_updown_listener_t { * List of cached interface names */ linked_list_t *iface_cache; + + /** + * DNS attribute handler + */ + updown_handler_t *handler; }; typedef struct cache_entry_t cache_entry_t; @@ -90,6 +95,85 @@ static char* uncache_iface(private_updown_listener_t *this, u_int32_t reqid) return iface; } +/** + * Create variables for handled DNS attributes + */ +static char *make_dns_vars(private_updown_listener_t *this, ike_sa_t *ike_sa) +{ + enumerator_t *enumerator; + host_t *host; + int v4 = 0, v6 = 0; + char total[512] = "", current[64]; + + if (!this->handler) + { + return strdup(""); + } + + enumerator = this->handler->create_dns_enumerator(this->handler, + ike_sa->get_unique_id(ike_sa)); + while (enumerator->enumerate(enumerator, &host)) + { + switch (host->get_family(host)) + { + case AF_INET: + snprintf(current, sizeof(current), + "PLUTO_DNS4_%d='%H' ", ++v4, host); + break; + case AF_INET6: + snprintf(current, sizeof(current), + "PLUTO_DNS6_%d='%H' ", ++v6, host); + break; + default: + continue; + } + strncat(total, current, sizeof(total) - strlen(total) - 1); + } + enumerator->destroy(enumerator); + + return strdup(total); +} + +/** + * Create variables for local virtual IPs + */ +static char *make_vip_vars(private_updown_listener_t *this, ike_sa_t *ike_sa) +{ + enumerator_t *enumerator; + host_t *host; + int v4 = 0, v6 = 0; + char total[512] = "", current[64]; + bool first = TRUE; + + enumerator = ike_sa->create_virtual_ip_enumerator(ike_sa, TRUE); + while (enumerator->enumerate(enumerator, &host)) + { + if (first) + { /* legacy variable for first VIP */ + snprintf(current, sizeof(current), + "PLUTO_MY_SOURCEIP='%H' ", host); + strncat(total, current, sizeof(total) - strlen(total) - 1); + } + switch (host->get_family(host)) + { + case AF_INET: + snprintf(current, sizeof(current), + "PLUTO_MY_SOURCEIP4_%d='%H' ", ++v4, host); + break; + case AF_INET6: + snprintf(current, sizeof(current), + "PLUTO_MY_SOURCEIP6_%d='%H' ", ++v6, host); + break; + default: + continue; + } + strncat(total, current, sizeof(total) - strlen(total) - 1); + } + enumerator->destroy(enumerator); + + return strdup(total); +} + METHOD(listener_t, child_updown, bool, private_updown_listener_t *this, ike_sa_t *ike_sa, child_sa_t *child_sa, bool up) @@ -97,11 +181,10 @@ METHOD(listener_t, child_updown, bool, traffic_selector_t *my_ts, *other_ts; enumerator_t *enumerator; child_cfg_t *config; - host_t *vip, *me, *other; + host_t *me, *other; char *script; config = child_sa->get_config(child_sa); - vip = ike_sa->get_virtual_ip(ike_sa, TRUE); script = config->get_updown(config); me = ike_sa->get_my_host(ike_sa); other = ike_sa->get_other_host(ike_sa); @@ -117,7 +200,7 @@ METHOD(listener_t, child_updown, bool, char command[1024]; host_t *my_client, *other_client; u_int8_t my_client_mask, other_client_mask; - char *virtual_ip, *iface, *mark_in, *mark_out, *udp_enc; + char *virtual_ip, *iface, *mark_in, *mark_out, *udp_enc, *dns; mark_t mark; bool is_host, is_ipv6; FILE *shell; @@ -125,20 +208,7 @@ METHOD(listener_t, child_updown, bool, my_ts->to_subnet(my_ts, &my_client, &my_client_mask); other_ts->to_subnet(other_ts, &other_client, &other_client_mask); - if (vip) - { - if (asprintf(&virtual_ip, "PLUTO_MY_SOURCEIP='%H' ", vip) < 0) - { - virtual_ip = NULL; - } - } - else - { - if (asprintf(&virtual_ip, "") < 0) - { - virtual_ip = NULL; - } - } + virtual_ip = make_vip_vars(this, ike_sa); /* check for the presence of an inbound mark */ mark = config->get_mark(config, TRUE); @@ -209,6 +279,8 @@ METHOD(listener_t, child_updown, bool, iface = uncache_iface(this, child_sa->get_reqid(child_sa)); } + dns = make_dns_vars(this, ike_sa); + /* determine IPv4/IPv6 and client/host situation */ is_host = my_ts->is_host(my_ts, me); is_ipv6 = is_host ? (me->get_family(me) == AF_INET6) : @@ -239,6 +311,7 @@ METHOD(listener_t, child_updown, bool, "%s" "%s" "%s" + "%s" "%s", up ? "up" : "down", is_host ? "-host" : "-client", @@ -259,6 +332,7 @@ METHOD(listener_t, child_updown, bool, mark_out, udp_enc, config->get_hostaccess(config) ? "PLUTO_HOST_ACCESS='1' " : "", + dns, script); my_client->destroy(my_client); other_client->destroy(other_client); @@ -266,6 +340,7 @@ METHOD(listener_t, child_updown, bool, free(mark_in); free(mark_out); free(udp_enc); + free(dns); free(iface); DBG3(DBG_CHD, "running updown script: %s", command); @@ -315,7 +390,7 @@ METHOD(updown_listener_t, destroy, void, /** * See header */ -updown_listener_t *updown_listener_create() +updown_listener_t *updown_listener_create(updown_handler_t *handler) { private_updown_listener_t *this; @@ -327,6 +402,7 @@ updown_listener_t *updown_listener_create() .destroy = _destroy, }, .iface_cache = linked_list_create(), + .handler = handler, ); return &this->public; diff --git a/src/libcharon/plugins/updown/updown_listener.h b/src/libcharon/plugins/updown/updown_listener.h index 5b866c4e5..2d9b56ade 100644 --- a/src/libcharon/plugins/updown/updown_listener.h +++ b/src/libcharon/plugins/updown/updown_listener.h @@ -23,6 +23,8 @@ #include <bus/bus.h> +#include "updown_handler.h" + typedef struct updown_listener_t updown_listener_t; /** @@ -44,6 +46,6 @@ struct updown_listener_t { /** * Create a updown_listener instance. */ -updown_listener_t *updown_listener_create(); +updown_listener_t *updown_listener_create(updown_handler_t *handler); #endif /** UPDOWN_LISTENER_H_ @}*/ diff --git a/src/libcharon/plugins/updown/updown_plugin.c b/src/libcharon/plugins/updown/updown_plugin.c index 2ce2d3257..e1f0d1380 100644 --- a/src/libcharon/plugins/updown/updown_plugin.c +++ b/src/libcharon/plugins/updown/updown_plugin.c @@ -15,8 +15,10 @@ #include "updown_plugin.h" #include "updown_listener.h" +#include "updown_handler.h" #include <daemon.h> +#include <hydra.h> typedef struct private_updown_plugin_t private_updown_plugin_t; @@ -34,6 +36,11 @@ struct private_updown_plugin_t { * Listener interface, listens to CHILD_SA state changes */ updown_listener_t *listener; + + /** + * Attribute handler, to pass DNS servers to updown + */ + updown_handler_t *handler; }; METHOD(plugin_t, get_name, char*, @@ -47,6 +54,12 @@ METHOD(plugin_t, destroy, void, { charon->bus->remove_listener(charon->bus, &this->listener->listener); this->listener->destroy(this->listener); + if (this->handler) + { + hydra->attributes->remove_handler(hydra->attributes, + &this->handler->handler); + this->handler->destroy(this->handler); + } free(this); } @@ -65,9 +78,16 @@ plugin_t *updown_plugin_create() .destroy = _destroy, }, }, - .listener = updown_listener_create(), ); + if (lib->settings->get_bool(lib->settings, + "charon.plugins.updown.dns_handler", FALSE)) + { + this->handler = updown_handler_create(); + hydra->attributes->add_handler(hydra->attributes, + &this->handler->handler); + } + this->listener = updown_listener_create(this->handler); charon->bus->add_listener(charon->bus, &this->listener->listener); return &this->public.plugin; diff --git a/src/libcharon/processing/jobs/migrate_job.c b/src/libcharon/processing/jobs/migrate_job.c index 57f344980..2ebfc6714 100644 --- a/src/libcharon/processing/jobs/migrate_job.c +++ b/src/libcharon/processing/jobs/migrate_job.c @@ -79,9 +79,10 @@ METHOD(job_t, execute, job_requeue_t, } if (ike_sa) { - enumerator_t *children; + enumerator_t *children, *enumerator; child_sa_t *child_sa; host_t *host; + linked_list_t *vips; children = ike_sa->create_child_sa_enumerator(ike_sa); while (children->enumerate(children, (void**)&child_sa)) @@ -104,14 +105,22 @@ METHOD(job_t, execute, job_requeue_t, host->set_port(host, IKEV2_UDP_PORT); ike_sa->set_other_host(ike_sa, host); - if (child_sa->update(child_sa, this->local, this->remote, - ike_sa->get_virtual_ip(ike_sa, TRUE), + vips = linked_list_create(); + enumerator = ike_sa->create_virtual_ip_enumerator(ike_sa, TRUE); + while (enumerator->enumerate(enumerator, &host)) + { + vips->insert_last(vips, host); + } + enumerator->destroy(enumerator); + + if (child_sa->update(child_sa, this->local, this->remote, vips, ike_sa->has_condition(ike_sa, COND_NAT_ANY)) == NOT_SUPPORTED) { ike_sa->rekey_child_sa(ike_sa, child_sa->get_protocol(child_sa), child_sa->get_spi(child_sa, TRUE)); } charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); + vips->destroy(vips); } else { diff --git a/src/libcharon/sa/child_sa.c b/src/libcharon/sa/child_sa.c index d5adb2527..c3fbd8b53 100644 --- a/src/libcharon/sa/child_sa.c +++ b/src/libcharon/sa/child_sa.c @@ -819,9 +819,18 @@ METHOD(child_sa_t, add_policies, status_t, return status; } +/** + * Callback to reinstall a virtual IP + */ +static void reinstall_vip(host_t *vip, host_t *me) +{ + hydra->kernel_interface->del_ip(hydra->kernel_interface, vip); + hydra->kernel_interface->add_ip(hydra->kernel_interface, vip, me); +} + METHOD(child_sa_t, update, status_t, - private_child_sa_t *this, host_t *me, host_t *other, host_t *vip, - bool encap) + private_child_sa_t *this, host_t *me, host_t *other, linked_list_t *vips, + bool encap) { child_sa_state_t old; bool transport_proxy_mode; @@ -927,13 +936,7 @@ METHOD(child_sa_t, update, status_t, /* we reinstall the virtual IP to handle interface roaming * correctly */ - if (vip) - { - hydra->kernel_interface->del_ip(hydra->kernel_interface, - vip); - hydra->kernel_interface->add_ip(hydra->kernel_interface, - vip, me); - } + vips->invoke_function(vips, (void*)reinstall_vip, me); /* reinstall updated policies */ install_policies_internal(this, me, other, my_ts, other_ts, diff --git a/src/libcharon/sa/child_sa.h b/src/libcharon/sa/child_sa.h index 712a4765c..dae3f2c18 100644 --- a/src/libcharon/sa/child_sa.h +++ b/src/libcharon/sa/child_sa.h @@ -346,12 +346,12 @@ struct child_sa_t { * * @param me the new local host * @param other the new remote host - * @param vip virtual IP, if any + * @param vips list of local virtual IPs * @param TRUE to use UDP encapsulation for NAT traversal * @return SUCCESS or FAILED */ status_t (*update)(child_sa_t *this, host_t *me, host_t *other, - host_t *vip, bool encap); + linked_list_t *vips, bool encap); /** * Destroys a child_sa. */ diff --git a/src/libcharon/sa/eap/eap_manager.c b/src/libcharon/sa/eap/eap_manager.c index d38754e01..520c0ce56 100644 --- a/src/libcharon/sa/eap/eap_manager.c +++ b/src/libcharon/sa/eap/eap_manager.c @@ -1,4 +1,5 @@ /* + * Copyright (C) 2012 Tobias Brunner * Copyright (C) 2008 Martin Willi * Hochschule fuer Technik Rapperswil * @@ -104,6 +105,44 @@ METHOD(eap_manager_t, remove_method, void, this->lock->unlock(this->lock); } +/** + * filter the registered methods + */ +static bool filter_methods(uintptr_t role, eap_entry_t **entry, + eap_type_t *type, void *in, u_int32_t *vendor) +{ + if ((*entry)->role != (eap_role_t)role) + { + return FALSE; + } + if ((*entry)->vendor == 0 && + ((*entry)->type < 4 || (*entry)->type == EAP_EXPANDED || + (*entry)->type > EAP_EXPERIMENTAL)) + { /* filter invalid types */ + return FALSE; + } + if (type) + { + *type = (*entry)->type; + } + if (vendor) + { + *vendor = (*entry)->vendor; + } + return TRUE; +} + +METHOD(eap_manager_t, create_enumerator, enumerator_t*, + private_eap_manager_t *this, eap_role_t role) +{ + this->lock->read_lock(this->lock); + return enumerator_create_cleaner( + enumerator_create_filter( + this->methods->create_enumerator(this->methods), + (void*)filter_methods, (void*)(uintptr_t)role, NULL), + (void*)this->lock->unlock, this->lock); +} + METHOD(eap_manager_t, create_instance, eap_method_t*, private_eap_manager_t *this, eap_type_t type, u_int32_t vendor, eap_role_t role, identification_t *server, identification_t *peer) @@ -150,6 +189,7 @@ eap_manager_t *eap_manager_create() .public = { .add_method = _add_method, .remove_method = _remove_method, + .create_enumerator = _create_enumerator, .create_instance = _create_instance, .destroy = _destroy, }, diff --git a/src/libcharon/sa/eap/eap_manager.h b/src/libcharon/sa/eap/eap_manager.h index 868eaef06..e318ef57a 100644 --- a/src/libcharon/sa/eap/eap_manager.h +++ b/src/libcharon/sa/eap/eap_manager.h @@ -1,4 +1,5 @@ /* + * Copyright (C) 2012 Tobias Brunner * Copyright (C) 2008 Martin Willi * Hochschule fuer Technik Rapperswil * @@ -54,6 +55,17 @@ struct eap_manager_t { void (*remove_method)(eap_manager_t *this, eap_constructor_t constructor); /** + * Enumerate the registered EAP authentication methods for the given role. + * + * @note Only authentication types are enumerated (e.g. EAP-Identity is not + * even though it is registered as method with this manager). + * + * @param role EAP role of methods to enumerate + * @return enumerator over (eap_type_t type, u_int32_t vendor) + */ + enumerator_t* (*create_enumerator)(eap_manager_t *this, eap_role_t role); + + /** * Create a new EAP method instance. * * @param type type of the EAP method diff --git a/src/libcharon/sa/ike_sa.c b/src/libcharon/sa/ike_sa.c index 1e3d00f02..7672accc4 100644 --- a/src/libcharon/sa/ike_sa.c +++ b/src/libcharon/sa/ike_sa.c @@ -182,14 +182,14 @@ struct private_ike_sa_t { keymat_t *keymat; /** - * Virtual IP on local host, if any + * Virtual IPs on local host */ - host_t *my_virtual_ip; + linked_list_t *my_vips; /** - * Virtual IP on remote host, if any + * Virtual IPs on remote host */ - host_t *other_virtual_ip; + linked_list_t *other_vips; /** * List of configuration attributes (attribute_entry_t) @@ -735,7 +735,7 @@ METHOD(ike_sa_t, get_keymat, keymat_t*, return this->keymat; } -METHOD(ike_sa_t, set_virtual_ip, void, +METHOD(ike_sa_t, add_virtual_ip, void, private_ike_sa_t *this, bool local, host_t *ip) { if (local) @@ -744,39 +744,27 @@ METHOD(ike_sa_t, set_virtual_ip, void, if (hydra->kernel_interface->add_ip(hydra->kernel_interface, ip, this->my_host) == SUCCESS) { - if (this->my_virtual_ip) - { - DBG1(DBG_IKE, "removing old virtual IP %H", this->my_virtual_ip); - hydra->kernel_interface->del_ip(hydra->kernel_interface, - this->my_virtual_ip); - } - DESTROY_IF(this->my_virtual_ip); - this->my_virtual_ip = ip->clone(ip); + this->my_vips->insert_last(this->my_vips, 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); + this->other_vips->insert_last(this->other_vips, ip->clone(ip)); } } -METHOD(ike_sa_t, get_virtual_ip, host_t*, +METHOD(ike_sa_t, create_virtual_ip_enumerator, enumerator_t*, private_ike_sa_t *this, bool local) { if (local) { - return this->my_virtual_ip; - } - else - { - return this->other_virtual_ip; + return this->my_vips->create_enumerator(this->my_vips); } + return this->other_vips->create_enumerator(this->other_vips); } METHOD(ike_sa_t, add_peer_address, void, @@ -909,7 +897,7 @@ METHOD(ike_sa_t, update_hosts, void, while (enumerator->enumerate(enumerator, (void**)&child_sa)) { if (child_sa->update(child_sa, this->my_host, - this->other_host, this->my_virtual_ip, + this->other_host, this->my_vips, has_condition(this, COND_NAT_ANY)) == NOT_SUPPORTED) { this->public.rekey_child_sa(&this->public, @@ -1458,7 +1446,7 @@ METHOD(ike_sa_t, reauth, status_t, if (!has_condition(this, COND_ORIGINAL_INITIATOR)) { DBG1(DBG_IKE, "initiator did not reauthenticate as requested"); - if (this->other_virtual_ip != NULL || + if (this->other_vips->get_count(this->other_vips) != 0 || has_condition(this, COND_XAUTH_AUTHENTICATED) || has_condition(this, COND_EAP_AUTHENTICATED) #ifdef ME @@ -1562,7 +1550,7 @@ METHOD(ike_sa_t, reestablish, status_t, /* check if we are able to reestablish this IKE_SA */ if (!has_condition(this, COND_ORIGINAL_INITIATOR) && - (this->other_virtual_ip != NULL || + (this->other_vips->get_count(this->other_vips) != 0 || has_condition(this, COND_EAP_AUTHENTICATED) #ifdef ME || this->is_mediation_server @@ -1585,11 +1573,12 @@ METHOD(ike_sa_t, reestablish, status_t, host = this->my_host; new->set_my_host(new, host->clone(host)); /* if we already have a virtual IP, we reuse it */ - host = this->my_virtual_ip; - if (host) + enumerator = this->my_vips->create_enumerator(this->my_vips); + while (enumerator->enumerate(enumerator, &host)) { - new->set_virtual_ip(new, TRUE, host); + new->add_virtual_ip(new, TRUE, host); } + enumerator->destroy(enumerator); #ifdef ME if (this->peer_cfg->is_mediation(this->peer_cfg)) @@ -1732,7 +1721,7 @@ METHOD(ike_sa_t, set_auth_lifetime, status_t, * We send the notify in IKE_AUTH if not yet ESTABLISHED. */ send_update = this->state == IKE_ESTABLISHED && this->version == IKEV2 && !has_condition(this, COND_ORIGINAL_INITIATOR) && - (this->other_virtual_ip != NULL || + (this->other_vips->get_count(this->other_vips) != 0 || has_condition(this, COND_EAP_AUTHENTICATED)); if (lifetime < diff) @@ -1937,6 +1926,7 @@ METHOD(ike_sa_t, inherit, void, attribute_entry_t *entry; enumerator_t *enumerator; auth_cfg_t *cfg; + host_t *vip; /* apply hosts and ids */ this->my_host->destroy(this->my_host); @@ -1948,16 +1938,15 @@ METHOD(ike_sa_t, inherit, void, 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) + /* apply assigned virtual IPs... */ + while (this->my_vips->remove_last(this->my_vips, (void**)&vip) == SUCCESS) { - this->my_virtual_ip = other->my_virtual_ip; - other->my_virtual_ip = NULL; + other->my_vips->insert_first(other->my_vips, vip); } - if (other->other_virtual_ip) + while (this->other_vips->remove_last(this->other_vips, + (void**)&vip) == SUCCESS) { - this->other_virtual_ip = other->other_virtual_ip; - other->other_virtual_ip = NULL; + other->other_vips->insert_first(other->other_vips, vip); } /* authentication information */ @@ -2032,6 +2021,7 @@ METHOD(ike_sa_t, destroy, void, private_ike_sa_t *this) { attribute_entry_t *entry; + host_t *vip; charon->bus->set_sa(charon->bus, &this->public); @@ -2056,22 +2046,34 @@ METHOD(ike_sa_t, destroy, void, DESTROY_IF(this->keymat); - if (this->my_virtual_ip) + while (this->my_vips->remove_last(this->my_vips, (void**)&vip) == SUCCESS) { - hydra->kernel_interface->del_ip(hydra->kernel_interface, - this->my_virtual_ip); - this->my_virtual_ip->destroy(this->my_virtual_ip); + hydra->kernel_interface->del_ip(hydra->kernel_interface, vip); + vip->destroy(vip); } - if (this->other_virtual_ip) + this->my_vips->destroy(this->my_vips); + while (this->other_vips->remove_last(this->other_vips, + (void**)&vip) == SUCCESS) { - if (this->peer_cfg && this->peer_cfg->get_pool(this->peer_cfg)) + if (this->peer_cfg) { - hydra->attributes->release_address(hydra->attributes, - this->peer_cfg->get_pool(this->peer_cfg), - this->other_virtual_ip, get_other_eap_id(this)); + enumerator_t *enumerator; + char *pool; + + enumerator = this->peer_cfg->create_pool_enumerator(this->peer_cfg); + while (enumerator->enumerate(enumerator, &pool)) + { + if (hydra->attributes->release_address(hydra->attributes, pool, + vip, get_other_eap_id(this))) + { + break; + } + } + enumerator->destroy(enumerator); } - this->other_virtual_ip->destroy(this->other_virtual_ip); + vip->destroy(vip); } + this->other_vips->destroy(this->other_vips); this->peer_addresses->destroy_offset(this->peer_addresses, offsetof(host_t, destroy)); #ifdef ME @@ -2190,8 +2192,8 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id, bool initiator, .generate_message = _generate_message, .reset = _reset, .get_unique_id = _get_unique_id, - .set_virtual_ip = _set_virtual_ip, - .get_virtual_ip = _get_virtual_ip, + .add_virtual_ip = _add_virtual_ip, + .create_virtual_ip_enumerator = _create_virtual_ip_enumerator, .add_configuration_attribute = _add_configuration_attribute, .set_kmaddress = _set_kmaddress, .create_task_enumerator = _create_task_enumerator, @@ -2226,6 +2228,8 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id, bool initiator, .other_auths = linked_list_create(), .unique_id = ++unique_id, .peer_addresses = linked_list_create(), + .my_vips = linked_list_create(), + .other_vips = linked_list_create(), .attributes = linked_list_create(), .keepalive_interval = lib->settings->get_time(lib->settings, "%s.keep_alive", KEEPALIVE_INTERVAL, charon->name), diff --git a/src/libcharon/sa/ike_sa.h b/src/libcharon/sa/ike_sa.h index de9e0ede4..d2a394b6c 100644 --- a/src/libcharon/sa/ike_sa.h +++ b/src/libcharon/sa/ike_sa.h @@ -931,7 +931,7 @@ struct ike_sa_t { status_t (*set_auth_lifetime)(ike_sa_t *this, u_int32_t lifetime); /** - * Set the virtual IP to use for this IKE_SA and its children. + * Add a 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. @@ -939,15 +939,15 @@ struct ike_sa_t { * @param local TRUE to set local address, FALSE for remote * @param ip IP to set as virtual IP */ - void (*set_virtual_ip) (ike_sa_t *this, bool local, host_t *ip); + void (*add_virtual_ip) (ike_sa_t *this, bool local, host_t *ip); /** - * Get the virtual IP configured. + * Create an enumerator over virtual IPs. * * @param local TRUE to get local virtual IP, FALSE for remote - * @return host_t *virtual IP + * @return enumerator over host_t* */ - host_t* (*get_virtual_ip) (ike_sa_t *this, bool local); + enumerator_t* (*create_virtual_ip_enumerator) (ike_sa_t *this, bool local); /** * Register a configuration attribute to the IKE_SA. diff --git a/src/libcharon/sa/ikev1/phase1.c b/src/libcharon/sa/ikev1/phase1.c index e5a48ec49..4096141ec 100644 --- a/src/libcharon/sa/ikev1/phase1.c +++ b/src/libcharon/sa/ikev1/phase1.c @@ -609,6 +609,34 @@ METHOD(phase1_t, get_id, identification_t*, return id; } +METHOD(phase1_t, has_virtual_ip, bool, + private_phase1_t *this, peer_cfg_t *peer_cfg) +{ + enumerator_t *enumerator; + bool found = FALSE; + host_t *host; + + enumerator = peer_cfg->create_virtual_ip_enumerator(peer_cfg); + found = enumerator->enumerate(enumerator, &host); + enumerator->destroy(enumerator); + + return found; +} + +METHOD(phase1_t, has_pool, bool, + private_phase1_t *this, peer_cfg_t *peer_cfg) +{ + enumerator_t *enumerator; + bool found = FALSE; + char *pool; + + enumerator = peer_cfg->create_pool_enumerator(peer_cfg); + found = enumerator->enumerate(enumerator, &pool); + enumerator->destroy(enumerator); + + return found; +} + METHOD(phase1_t, save_sa_payload, bool, private_phase1_t *this, message_t *message) { @@ -748,6 +776,8 @@ phase1_t *phase1_create(ike_sa_t *ike_sa, bool initiator) .get_auth_method = _get_auth_method, .get_id = _get_id, .select_config = _select_config, + .has_virtual_ip = _has_virtual_ip, + .has_pool = _has_pool, .verify_auth = _verify_auth, .build_auth = _build_auth, .save_sa_payload = _save_sa_payload, diff --git a/src/libcharon/sa/ikev1/phase1.h b/src/libcharon/sa/ikev1/phase1.h index f1e95d88c..eaf8908e7 100644 --- a/src/libcharon/sa/ikev1/phase1.h +++ b/src/libcharon/sa/ikev1/phase1.h @@ -109,6 +109,22 @@ struct phase1_t { identification_t* (*get_id)(phase1_t *this, peer_cfg_t *peer_cfg, bool local); /** + * Check if peer config has virtual IPs pool assigned. + * + * @param peer_cfg peer_config to check + * @return TRUE if peer config contains at least one pool + */ + bool (*has_pool)(phase1_t *this, peer_cfg_t *peer_cfg); + + /** + * Check if peer config has virtual IPs to request + * + * @param peer_cfg peer_config to check + * @return TRUE if peer config contains at least one virtual IP + */ + bool (*has_virtual_ip)(phase1_t *this, peer_cfg_t *peer_cfg); + + /** * Extract and store SA payload bytes from encoded message. * * @param message message to extract SA payload bytes from diff --git a/src/libcharon/sa/ikev1/task_manager_v1.c b/src/libcharon/sa/ikev1/task_manager_v1.c index 3c84b5114..ea836b76e 100644 --- a/src/libcharon/sa/ikev1/task_manager_v1.c +++ b/src/libcharon/sa/ikev1/task_manager_v1.c @@ -346,15 +346,30 @@ METHOD(task_manager_t, retransmit, status_t, */ static bool mode_config_expected(private_task_manager_t *this) { + enumerator_t *enumerator; peer_cfg_t *peer_cfg; + char *pool; + host_t *host; peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa); - if (peer_cfg && peer_cfg->get_pool(peer_cfg)) + if (peer_cfg) { - if (!this->ike_sa->get_virtual_ip(this->ike_sa, FALSE)) - { + enumerator = peer_cfg->create_pool_enumerator(peer_cfg); + if (!enumerator->enumerate(enumerator, &pool)) + { /* no pool configured */ + enumerator->destroy(enumerator); + return FALSE; + } + enumerator->destroy(enumerator); + + enumerator = this->ike_sa->create_virtual_ip_enumerator(this->ike_sa, + FALSE); + if (!enumerator->enumerate(enumerator, &host)) + { /* have a pool, but no VIP assigned yet */ + enumerator->destroy(enumerator); return TRUE; } + enumerator->destroy(enumerator); } return FALSE; } @@ -1309,11 +1324,12 @@ METHOD(task_manager_t, queue_ike_reauth, void, new->set_other_host(new, host->clone(host)); host = this->ike_sa->get_my_host(this->ike_sa); new->set_my_host(new, host->clone(host)); - host = this->ike_sa->get_virtual_ip(this->ike_sa, TRUE); - if (host) + enumerator = this->ike_sa->create_virtual_ip_enumerator(this->ike_sa, TRUE); + while (enumerator->enumerate(enumerator, &host)) { - new->set_virtual_ip(new, TRUE, host); + new->add_virtual_ip(new, TRUE, host); } + enumerator->destroy(enumerator); enumerator = this->ike_sa->create_child_sa_enumerator(this->ike_sa); while (enumerator->enumerate(enumerator, &child_sa)) diff --git a/src/libcharon/sa/ikev1/tasks/aggressive_mode.c b/src/libcharon/sa/ikev1/tasks/aggressive_mode.c index 38962fc60..954dea880 100644 --- a/src/libcharon/sa/ikev1/tasks/aggressive_mode.c +++ b/src/libcharon/sa/ikev1/tasks/aggressive_mode.c @@ -320,7 +320,7 @@ METHOD(task_t, build_i, status_t, } break; } - if (this->peer_cfg->get_virtual_ip(this->peer_cfg)) + if (this->ph1->has_virtual_ip(this->ph1, this->peer_cfg)) { this->ike_sa->queue_task(this->ike_sa, (task_t*)mode_config_create(this->ike_sa, TRUE)); @@ -497,8 +497,8 @@ METHOD(task_t, process_r, status_t, this->ike_sa->get_id(this->ike_sa))); break; } - if (this->peer_cfg->get_pool(this->peer_cfg) == NULL && - this->peer_cfg->get_virtual_ip(this->peer_cfg)) + if (!this->ph1->has_pool(this->ph1, this->peer_cfg) && + this->ph1->has_virtual_ip(this->ph1, this->peer_cfg)) { this->ike_sa->queue_task(this->ike_sa, (task_t*)mode_config_create(this->ike_sa, TRUE)); diff --git a/src/libcharon/sa/ikev1/tasks/main_mode.c b/src/libcharon/sa/ikev1/tasks/main_mode.c index 539286938..9ccf9abf5 100644 --- a/src/libcharon/sa/ikev1/tasks/main_mode.c +++ b/src/libcharon/sa/ikev1/tasks/main_mode.c @@ -524,8 +524,8 @@ METHOD(task_t, build_r, status_t, this->ike_sa->get_id(this->ike_sa))); break; } - if (this->peer_cfg->get_pool(this->peer_cfg) == NULL && - this->peer_cfg->get_virtual_ip(this->peer_cfg)) + if (!this->ph1->has_pool(this->ph1, this->peer_cfg) && + this->ph1->has_virtual_ip(this->ph1, this->peer_cfg)) { this->ike_sa->queue_task(this->ike_sa, (task_t*)mode_config_create(this->ike_sa, TRUE)); @@ -658,7 +658,7 @@ METHOD(task_t, process_i, status_t, } break; } - if (this->peer_cfg->get_virtual_ip(this->peer_cfg)) + if (this->ph1->has_virtual_ip(this->ph1, this->peer_cfg)) { this->ike_sa->queue_task(this->ike_sa, (task_t*)mode_config_create(this->ike_sa, TRUE)); diff --git a/src/libcharon/sa/ikev1/tasks/mode_config.c b/src/libcharon/sa/ikev1/tasks/mode_config.c index ab3a81386..ef1b35c17 100644 --- a/src/libcharon/sa/ikev1/tasks/mode_config.c +++ b/src/libcharon/sa/ikev1/tasks/mode_config.c @@ -42,9 +42,9 @@ struct private_mode_config_t { bool initiator; /** - * virtual ip + * Received list of virtual IPs, host_t* */ - host_t *virtual_ip; + linked_list_t *vips; /** * list of attributes requested and its handler, entry_t @@ -174,8 +174,7 @@ static void process_attribute(private_mode_config_t *this, } if (ip) { - DESTROY_IF(this->virtual_ip); - this->virtual_ip = ip; + this->vips->insert_last(this->vips, ip); } break; } @@ -239,23 +238,44 @@ METHOD(task_t, build_i, status_t, peer_cfg_t *config; configuration_attribute_type_t type; chunk_t data; - host_t *vip; + linked_list_t *vips; + host_t *host; + + vips = linked_list_create(); /* reuse virtual IP if we already have one */ - vip = this->ike_sa->get_virtual_ip(this->ike_sa, TRUE); - if (!vip) + enumerator = this->ike_sa->create_virtual_ip_enumerator(this->ike_sa, TRUE); + while (enumerator->enumerate(enumerator, &host)) + { + vips->insert_last(vips, host); + } + enumerator->destroy(enumerator); + + if (vips->get_count(vips) == 0) { config = this->ike_sa->get_peer_cfg(this->ike_sa); - vip = config->get_virtual_ip(config); + enumerator = config->create_virtual_ip_enumerator(config); + while (enumerator->enumerate(enumerator, &host)) + { + vips->insert_last(vips, host); + } + enumerator->destroy(enumerator); } - if (vip) + + if (vips->get_count(vips)) { cp = cp_payload_create_type(CONFIGURATION_V1, CFG_REQUEST); - cp->add_attribute(cp, build_vip(vip)); + enumerator = vips->create_enumerator(vips); + while (enumerator->enumerate(enumerator, &host)) + { + cp->add_attribute(cp, build_vip(host)); + } + enumerator->destroy(enumerator); } - enumerator = hydra->attributes->create_initiator_enumerator(hydra->attributes, - this->ike_sa->get_other_id(this->ike_sa), vip); + enumerator = hydra->attributes->create_initiator_enumerator( + hydra->attributes, + this->ike_sa->get_other_id(this->ike_sa), vips); while (enumerator->enumerate(enumerator, &handler, &type, &data)) { configuration_attribute_t *ca; @@ -279,6 +299,8 @@ METHOD(task_t, build_i, status_t, } enumerator->destroy(enumerator); + vips->destroy(vips); + if (cp) { message->add_payload(message, (payload_t*)cp); @@ -299,37 +321,64 @@ METHOD(task_t, build_r, status_t, enumerator_t *enumerator; configuration_attribute_type_t type; chunk_t value; - host_t *vip = NULL; cp_payload_t *cp = NULL; peer_cfg_t *config; identification_t *id; + linked_list_t *vips, *pools; + host_t *requested; id = this->ike_sa->get_other_eap_id(this->ike_sa); - config = this->ike_sa->get_peer_cfg(this->ike_sa); - if (this->virtual_ip) + vips = linked_list_create(); + pools = linked_list_create(); + + enumerator = this->vips->create_enumerator(this->vips); + while (enumerator->enumerate(enumerator, &requested)) { - DBG1(DBG_IKE, "peer requested virtual IP %H", this->virtual_ip); - if (config->get_pool(config)) - { - vip = hydra->attributes->acquire_address(hydra->attributes, - config->get_pool(config), id, this->virtual_ip); - } - cp = cp_payload_create_type(CONFIGURATION_V1, CFG_REPLY); - if (vip) + enumerator_t *poolenum; + char *pool; + host_t *found = NULL; + + /* query all pools until we get an address */ + DBG1(DBG_IKE, "peer requested virtual IP %H", requested); + + poolenum = config->create_pool_enumerator(config); + while (poolenum->enumerate(poolenum, &pool)) { - DBG1(DBG_IKE, "assigning virtual IP %H to peer '%Y'", vip, id); - this->ike_sa->set_virtual_ip(this->ike_sa, FALSE, vip); - cp->add_attribute(cp, build_vip(vip)); + found = hydra->attributes->acquire_address(hydra->attributes, + pool, id, requested); + if (!found) + { + continue; + } + DBG1(DBG_IKE, "assigning virtual IP %H to peer '%Y'", found, id); + this->ike_sa->add_virtual_ip(this->ike_sa, FALSE, found); + if (!cp) + { + cp = cp_payload_create_type(CONFIGURATION_V1, CFG_REPLY); + } + cp->add_attribute(cp, build_vip(found)); + /* use pool to request other attributes */ + if (pools->find_first(pools, NULL, (void**)&pool) == NOT_FOUND) + { + pools->insert_last(pools, pool); + } + vips->insert_last(vips, found); + break; } - else + poolenum->destroy(poolenum); + + if (!found) { - DBG1(DBG_IKE, "no virtual IP found, sending empty config payload"); + DBG1(DBG_IKE, "no virtual IP found for %H requested by '%Y'", + requested, id); } } + enumerator->destroy(enumerator); + /* query registered providers for additional attributes to include */ enumerator = hydra->attributes->create_responder_enumerator( - hydra->attributes, config->get_pool(config), id, vip); + hydra->attributes, pools, id, vips); while (enumerator->enumerate(enumerator, &type, &value)) { if (!cp) @@ -343,25 +392,35 @@ METHOD(task_t, build_r, status_t, type, value)); } enumerator->destroy(enumerator); + vips->destroy_offset(vips, offsetof(host_t, destroy)); + vips->destroy(vips); if (cp) { cp->set_identifier(cp, this->identifier); message->add_payload(message, (payload_t*)cp); } - DESTROY_IF(vip); return SUCCESS; } METHOD(task_t, process_i, status_t, private_mode_config_t *this, message_t *message) { + enumerator_t *enumerator; + host_t *host; + process_payloads(this, message); - if (this->virtual_ip) + enumerator = this->vips->create_enumerator(this->vips); + while (enumerator->enumerate(enumerator, &host)) { - this->ike_sa->set_virtual_ip(this->ike_sa, TRUE, this->virtual_ip); + if (!host->is_anyaddr(host)) + { + this->ike_sa->add_virtual_ip(this->ike_sa, TRUE, host); + } } + enumerator->destroy(enumerator); + return SUCCESS; } @@ -374,10 +433,9 @@ METHOD(task_t, get_type, task_type_t, METHOD(task_t, migrate, void, private_mode_config_t *this, ike_sa_t *ike_sa) { - DESTROY_IF(this->virtual_ip); - this->ike_sa = ike_sa; - this->virtual_ip = NULL; + this->vips->destroy_offset(this->vips, offsetof(host_t, destroy)); + this->vips = linked_list_create(); this->requested->destroy_function(this->requested, free); this->requested = linked_list_create(); } @@ -385,7 +443,7 @@ METHOD(task_t, migrate, void, METHOD(task_t, destroy, void, private_mode_config_t *this) { - DESTROY_IF(this->virtual_ip); + this->vips->destroy_offset(this->vips, offsetof(host_t, destroy)); this->requested->destroy_function(this->requested, free); free(this); } @@ -408,6 +466,7 @@ mode_config_t *mode_config_create(ike_sa_t *ike_sa, bool initiator) .initiator = initiator, .ike_sa = ike_sa, .requested = linked_list_create(), + .vips = linked_list_create(), ); if (initiator) diff --git a/src/libcharon/sa/ikev1/tasks/quick_mode.c b/src/libcharon/sa/ikev1/tasks/quick_mode.c index 67d1b45f1..73a23f4ea 100644 --- a/src/libcharon/sa/ikev1/tasks/quick_mode.c +++ b/src/libcharon/sa/ikev1/tasks/quick_mode.c @@ -398,11 +398,12 @@ static traffic_selector_t* select_ts(private_quick_mode_t *this, bool local, linked_list_t *supplied) { traffic_selector_t *ts; + enumerator_t *enumerator; linked_list_t *list; host_t *host; - host = this->ike_sa->get_virtual_ip(this->ike_sa, local); - if (!host) + enumerator = this->ike_sa->create_virtual_ip_enumerator(this->ike_sa, local); + if (!enumerator->enumerate(enumerator, &host)) { if (local) { @@ -413,6 +414,7 @@ static traffic_selector_t* select_ts(private_quick_mode_t *this, bool local, host = this->ike_sa->get_other_host(this->ike_sa); } } + enumerator->destroy(enumerator); list = this->config->get_traffic_selectors(this->config, local, supplied, host); if (list->get_first(list, (void**)&ts) == SUCCESS) @@ -831,6 +833,30 @@ static void check_for_rekeyed_child(private_quick_mode_t *this) enumerator->destroy(enumerator); } +/** + * Get host to use for dynamic traffic selectors + */ +static host_t *get_dynamic_host(ike_sa_t *ike_sa, bool local) +{ + enumerator_t *enumerator; + host_t *host; + + enumerator = ike_sa->create_virtual_ip_enumerator(ike_sa, local); + if (!enumerator->enumerate(enumerator, &host)) + { + if (local) + { + host = ike_sa->get_my_host(ike_sa); + } + else + { + host = ike_sa->get_other_host(ike_sa); + } + } + enumerator->destroy(enumerator); + return host; +} + METHOD(task_t, process_r, status_t, private_quick_mode_t *this, message_t *message) { @@ -841,7 +867,6 @@ METHOD(task_t, process_r, status_t, sa_payload_t *sa_payload; linked_list_t *tsi, *tsr, *list = NULL; peer_cfg_t *peer_cfg; - host_t *me, *other; u_int16_t group; bool private; @@ -849,16 +874,6 @@ METHOD(task_t, process_r, status_t, { return FAILED; } - me = this->ike_sa->get_virtual_ip(this->ike_sa, TRUE); - if (!me) - { - me = this->ike_sa->get_my_host(this->ike_sa); - } - other = this->ike_sa->get_virtual_ip(this->ike_sa, FALSE); - if (!other) - { - other = this->ike_sa->get_other_host(this->ike_sa); - } peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa); tsi = linked_list_create(); tsr = linked_list_create(); @@ -866,7 +881,8 @@ METHOD(task_t, process_r, status_t, tsr->insert_last(tsr, this->tsr); this->tsi = this->tsr = NULL; this->config = peer_cfg->select_child_cfg(peer_cfg, tsr, tsi, - me, other); + get_dynamic_host(this->ike_sa, TRUE), + get_dynamic_host(this->ike_sa, FALSE)); if (this->config) { this->tsi = select_ts(this, FALSE, tsi); diff --git a/src/libcharon/sa/ikev2/authenticators/eap_authenticator.c b/src/libcharon/sa/ikev2/authenticators/eap_authenticator.c index 2c282dc06..aa0644033 100644 --- a/src/libcharon/sa/ikev2/authenticators/eap_authenticator.c +++ b/src/libcharon/sa/ikev2/authenticators/eap_authenticator.c @@ -1,4 +1,5 @@ /* + * Copyright (C) 2012 Tobias Brunner * Copyright (C) 2006-2009 Martin Willi * Hochschule fuer Technik Rapperswil * @@ -186,9 +187,9 @@ static eap_payload_t* server_initiate_eap(private_eap_authenticator_t *this, if (this->method) { action = "initiating"; - type = this->method->get_type(this->method, &vendor); if (this->method->initiate(this->method, &out) == NEED_MORE) { + type = this->method->get_type(this->method, &vendor); if (vendor) { DBG1(DBG_IKE, "initiating EAP vendor type %d-%d method (id 0x%02X)", @@ -201,6 +202,8 @@ static eap_payload_t* server_initiate_eap(private_eap_authenticator_t *this, } return out; } + /* type might have changed for virtual methods */ + type = this->method->get_type(this->method, &vendor); } if (vendor) { @@ -233,9 +236,10 @@ static void replace_eap_identity(private_eap_authenticator_t *this) static eap_payload_t* server_process_eap(private_eap_authenticator_t *this, eap_payload_t *in) { - eap_type_t type, received_type; - u_int32_t vendor, received_vendor; + eap_type_t type, received_type, conf_type; + u_int32_t vendor, received_vendor, conf_vendor; eap_payload_t *out; + auth_cfg_t *auth; if (in->get_code(in) != EAP_RESPONSE) { @@ -250,15 +254,25 @@ static eap_payload_t* server_process_eap(private_eap_authenticator_t *this, { if (received_vendor == 0 && received_type == EAP_NAK) { - DBG1(DBG_IKE, "received %N, sending %N", - eap_type_names, EAP_NAK, eap_code_names, EAP_FAILURE); + auth = this->ike_sa->get_auth_cfg(this->ike_sa, FALSE); + conf_type = (uintptr_t)auth->get(auth, AUTH_RULE_EAP_TYPE); + conf_vendor = (uintptr_t)auth->get(auth, AUTH_RULE_EAP_VENDOR); + if ((type == EAP_IDENTITY && !vendor) || + (type == conf_type && vendor == conf_vendor)) + { + DBG1(DBG_IKE, "received %N, sending %N", + eap_type_names, EAP_NAK, eap_code_names, EAP_FAILURE); + return eap_payload_create_code(EAP_FAILURE, + in->get_identifier(in)); + } + /* virtual methods handle NAKs in process() */ } else { DBG1(DBG_IKE, "received invalid EAP response, sending %N", eap_code_names, EAP_FAILURE); + return eap_payload_create_code(EAP_FAILURE, in->get_identifier(in)); } - return eap_payload_create_code(EAP_FAILURE, in->get_identifier(in)); } switch (this->method->process(this->method, in, &out)) @@ -302,6 +316,8 @@ static eap_payload_t* server_process_eap(private_eap_authenticator_t *this, return eap_payload_create_code(EAP_SUCCESS, in->get_identifier(in)); case FAILED: default: + /* type might have changed for virtual methods */ + type = this->method->get_type(this->method, &vendor); if (vendor) { DBG1(DBG_IKE, "EAP vendor specific method %d-%d failed for " @@ -324,8 +340,8 @@ static eap_payload_t* server_process_eap(private_eap_authenticator_t *this, static eap_payload_t* client_process_eap(private_eap_authenticator_t *this, eap_payload_t *in) { - eap_type_t type; - u_int32_t vendor; + eap_type_t type, conf_type; + u_int32_t vendor, conf_vendor; auth_cfg_t *auth; eap_payload_t *out; identification_t *id; @@ -357,9 +373,11 @@ static eap_payload_t* client_process_eap(private_eap_authenticator_t *this, this->method->destroy(this->method); this->method = NULL; } + /* FIXME: sending a Nak is not correct here as EAP_IDENTITY (1) is no + * EAP method (types 3-253, 255) */ DBG1(DBG_IKE, "%N not supported, sending EAP_NAK", eap_type_names, type); - return eap_payload_create_nak(in->get_identifier(in)); + return eap_payload_create_nak(in->get_identifier(in), 0, 0, FALSE); } if (this->method == NULL) { @@ -373,11 +391,31 @@ static eap_payload_t* client_process_eap(private_eap_authenticator_t *this, DBG1(DBG_IKE, "server requested %N authentication (id 0x%02X)", eap_type_names, type, in->get_identifier(in)); } + auth = this->ike_sa->get_auth_cfg(this->ike_sa, TRUE); + conf_type = (uintptr_t)auth->get(auth, AUTH_RULE_EAP_TYPE); + conf_vendor = (uintptr_t)auth->get(auth, AUTH_RULE_EAP_VENDOR); + if (conf_type != EAP_NAK && + (conf_type != type || conf_vendor != vendor)) + { + if (conf_vendor) + { + DBG1(DBG_IKE, "requesting EAP method %d-%d, sending EAP_NAK", + conf_type, conf_vendor); + } + else + { + DBG1(DBG_IKE, "requesting %N authentication, sending EAP_NAK", + eap_type_names, conf_type); + } + return eap_payload_create_nak(in->get_identifier(in), conf_type, + conf_vendor, in->is_expanded(in)); + } this->method = load_method(this, type, vendor, EAP_PEER); if (!this->method) { DBG1(DBG_IKE, "EAP method not supported, sending EAP_NAK"); - return eap_payload_create_nak(in->get_identifier(in)); + return eap_payload_create_nak(in->get_identifier(in), 0, 0, + in->is_expanded(in)); } } diff --git a/src/libcharon/sa/ikev2/tasks/child_create.c b/src/libcharon/sa/ikev2/tasks/child_create.c index 5c5468dab..d7d912fdc 100644 --- a/src/libcharon/sa/ikev2/tasks/child_create.c +++ b/src/libcharon/sa/ikev2/tasks/child_create.c @@ -285,6 +285,30 @@ static void schedule_inactivity_timeout(private_child_create_t *this) } /** + * Get host to use for dynamic traffic selectors + */ +static host_t *get_dynamic_host(ike_sa_t *ike_sa, bool local) +{ + enumerator_t *enumerator; + host_t *host; + + enumerator = ike_sa->create_virtual_ip_enumerator(ike_sa, local); + if (!enumerator->enumerate(enumerator, &host)) + { + if (local) + { + host = ike_sa->get_my_host(ike_sa); + } + else + { + host = ike_sa->get_other_host(ike_sa); + } + } + enumerator->destroy(enumerator); + return host; +} + +/** * Install a CHILD_SA for usage, return value: * - FAILED: no acceptable proposal * - INVALID_ARG: diffie hellman group inacceptable @@ -298,7 +322,7 @@ static status_t select_and_install(private_child_create_t *this, chunk_t encr_i = chunk_empty, encr_r = chunk_empty; chunk_t integ_i = chunk_empty, integ_r = chunk_empty; linked_list_t *my_ts, *other_ts; - host_t *me, *other, *other_vip, *my_vip; + host_t *me, *other; bool private; if (this->proposals == NULL) @@ -314,8 +338,6 @@ static status_t select_and_install(private_child_create_t *this, 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); private = this->ike_sa->supports_extension(this->ike_sa, EXT_STRONGSWAN); this->proposal = this->config->select_proposal(this->config, @@ -354,15 +376,6 @@ static status_t select_and_install(private_child_create_t *this, this->dh_group = MODP_NONE; } - if (my_vip == NULL) - { - my_vip = me; - } - if (other_vip == NULL) - { - other_vip = other; - } - if (this->initiator) { nonce_i = this->my_nonce; @@ -378,9 +391,9 @@ static status_t select_and_install(private_child_create_t *this, other_ts = this->tsi; } my_ts = this->config->get_traffic_selectors(this->config, TRUE, my_ts, - my_vip); + get_dynamic_host(this->ike_sa, TRUE)); other_ts = this->config->get_traffic_selectors(this->config, FALSE, other_ts, - other_vip); + get_dynamic_host(this->ike_sa, FALSE)); if (this->initiator) { @@ -723,7 +736,8 @@ static void process_payloads(private_child_create_t *this, message_t *message) METHOD(task_t, build_i, status_t, private_child_create_t *this, message_t *message) { - host_t *me, *other, *vip; + enumerator_t *enumerator; + host_t *vip; peer_cfg_t *peer_cfg; switch (message->get_exchange_type(message)) @@ -763,22 +777,10 @@ METHOD(task_t, build_i, status_t, this->config->get_name(this->config)); } - /* reuse virtual IP if we already have one */ - me = this->ike_sa->get_virtual_ip(this->ike_sa, TRUE); - if (me == NULL) - { - me = this->ike_sa->get_my_host(this->ike_sa); - } - other = this->ike_sa->get_virtual_ip(this->ike_sa, FALSE); - if (other == NULL) - { - other = this->ike_sa->get_other_host(this->ike_sa); - } - /* check if we want a virtual IP, but don't have one */ peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa); - vip = peer_cfg->get_virtual_ip(peer_cfg); - if (!this->reqid && vip) + enumerator = peer_cfg->create_virtual_ip_enumerator(peer_cfg); + if (!this->reqid && enumerator->enumerate(enumerator, &vip)) { /* propose a 0.0.0.0/0 or ::/0 subnet when we use virtual ip */ vip = host_create_any(vip->get_family(vip)); @@ -788,11 +790,12 @@ METHOD(task_t, build_i, status_t, } else { /* but narrow it for host2host / if we already have a vip */ - this->tsi = this->config->get_traffic_selectors(this->config, TRUE, - NULL, me); + this->tsi = this->config->get_traffic_selectors(this->config, TRUE, NULL, + get_dynamic_host(this->ike_sa, TRUE)); } - this->tsr = this->config->get_traffic_selectors(this->config, FALSE, - NULL, other); + enumerator->destroy(enumerator); + this->tsr = this->config->get_traffic_selectors(this->config, FALSE, NULL, + get_dynamic_host(this->ike_sa, FALSE)); if (this->packet_tsi) { @@ -948,20 +951,10 @@ METHOD(task_t, build_r, status_t, peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa); if (!this->config && peer_cfg && this->tsi && this->tsr) { - host_t *me, *other; - - me = this->ike_sa->get_virtual_ip(this->ike_sa, TRUE); - if (me == NULL) - { - me = this->ike_sa->get_my_host(this->ike_sa); - } - other = this->ike_sa->get_virtual_ip(this->ike_sa, FALSE); - if (other == NULL) - { - other = this->ike_sa->get_other_host(this->ike_sa); - } - this->config = peer_cfg->select_child_cfg(peer_cfg, this->tsr, - this->tsi, me, other); + this->config = peer_cfg->select_child_cfg(peer_cfg, + this->tsr, this->tsi, + get_dynamic_host(this->ike_sa, TRUE), + get_dynamic_host(this->ike_sa, FALSE)); } if (this->config == NULL) diff --git a/src/libcharon/sa/ikev2/tasks/ike_config.c b/src/libcharon/sa/ikev2/tasks/ike_config.c index 932b02d42..e788bb5f9 100644 --- a/src/libcharon/sa/ikev2/tasks/ike_config.c +++ b/src/libcharon/sa/ikev2/tasks/ike_config.c @@ -43,9 +43,9 @@ struct private_ike_config_t { bool initiator; /** - * virtual ip + * Received list of virtual IPs, host_t* */ - host_t *virtual_ip; + linked_list_t *vips; /** * list of attributes requested and its handler, entry_t @@ -170,8 +170,7 @@ static void process_attribute(private_ike_config_t *this, } if (ip) { - DESTROY_IF(this->virtual_ip); - this->virtual_ip = ip; + this->vips->insert_last(this->vips, ip); } break; } @@ -242,23 +241,45 @@ METHOD(task_t, build_i, status_t, peer_cfg_t *config; configuration_attribute_type_t type; chunk_t data; - host_t *vip; + linked_list_t *vips; + host_t *host; + + vips = linked_list_create(); /* reuse virtual IP if we already have one */ - vip = this->ike_sa->get_virtual_ip(this->ike_sa, TRUE); - if (!vip) + enumerator = this->ike_sa->create_virtual_ip_enumerator(this->ike_sa, + TRUE); + while (enumerator->enumerate(enumerator, &host)) + { + vips->insert_last(vips, host); + } + enumerator->destroy(enumerator); + + if (vips->get_count(vips) == 0) { config = this->ike_sa->get_peer_cfg(this->ike_sa); - vip = config->get_virtual_ip(config); + enumerator = config->create_virtual_ip_enumerator(config); + while (enumerator->enumerate(enumerator, &host)) + { + vips->insert_last(vips, host); + } + enumerator->destroy(enumerator); } - if (vip) + + if (vips->get_count(vips)) { cp = cp_payload_create_type(CONFIGURATION, CFG_REQUEST); - cp->add_attribute(cp, build_vip(vip)); + enumerator = vips->create_enumerator(vips); + while (enumerator->enumerate(enumerator, &host)) + { + cp->add_attribute(cp, build_vip(host)); + } + enumerator->destroy(enumerator); } - enumerator = hydra->attributes->create_initiator_enumerator(hydra->attributes, - this->ike_sa->get_other_id(this->ike_sa), vip); + enumerator = hydra->attributes->create_initiator_enumerator( + hydra->attributes, + this->ike_sa->get_other_id(this->ike_sa), vips); while (enumerator->enumerate(enumerator, &handler, &type, &data)) { configuration_attribute_t *ca; @@ -284,6 +305,8 @@ METHOD(task_t, build_i, status_t, } enumerator->destroy(enumerator); + vips->destroy(vips); + if (cp) { message->add_payload(message, (payload_t*)cp); @@ -310,40 +333,75 @@ METHOD(task_t, build_r, status_t, enumerator_t *enumerator; configuration_attribute_type_t type; chunk_t value; - host_t *vip = NULL; cp_payload_t *cp = NULL; peer_cfg_t *config; identification_t *id; + linked_list_t *vips, *pools; + host_t *requested; id = this->ike_sa->get_other_eap_id(this->ike_sa); - config = this->ike_sa->get_peer_cfg(this->ike_sa); - if (this->virtual_ip) + vips = linked_list_create(); + pools = linked_list_create(); + + enumerator = this->vips->create_enumerator(this->vips); + while (enumerator->enumerate(enumerator, &requested)) { - DBG1(DBG_IKE, "peer requested virtual IP %H", this->virtual_ip); - if (config->get_pool(config)) + enumerator_t *poolenum; + char *pool; + host_t *found = NULL; + + /* query all pools until we get an address */ + DBG1(DBG_IKE, "peer requested virtual IP %H", requested); + + poolenum = config->create_pool_enumerator(config); + while (poolenum->enumerate(poolenum, &pool)) { - vip = hydra->attributes->acquire_address(hydra->attributes, - config->get_pool(config), id, this->virtual_ip); + found = hydra->attributes->acquire_address(hydra->attributes, + pool, id, requested); + if (!found) + { + continue; + } + DBG1(DBG_IKE, "assigning virtual IP %H to peer '%Y'", found, id); + this->ike_sa->add_virtual_ip(this->ike_sa, FALSE, found); + if (!cp) + { + cp = cp_payload_create_type(CONFIGURATION, CFG_REPLY); + } + cp->add_attribute(cp, build_vip(found)); + /* use pool to request other attributes */ + if (pools->find_first(pools, NULL, (void**)&pool) == NOT_FOUND) + { + pools->insert_last(pools, pool); + } + vips->insert_last(vips, found); + break; } - if (vip == NULL) + poolenum->destroy(poolenum); + + if (!found) { - DBG1(DBG_IKE, "no virtual IP found, sending %N", - notify_type_names, INTERNAL_ADDRESS_FAILURE); - message->add_notify(message, FALSE, INTERNAL_ADDRESS_FAILURE, - chunk_empty); - return SUCCESS; + DBG1(DBG_IKE, "no virtual IP found for %H requested by '%Y'", + requested, id); } - DBG1(DBG_IKE, "assigning virtual IP %H to peer '%Y'", vip, id); - this->ike_sa->set_virtual_ip(this->ike_sa, FALSE, vip); + } + enumerator->destroy(enumerator); - cp = cp_payload_create_type(CONFIGURATION, CFG_REPLY); - cp->add_attribute(cp, build_vip(vip)); + if (this->vips->get_count(this->vips) && !vips->get_count(vips)) + { + DBG1(DBG_IKE, "no virtual IP found, sending %N", + notify_type_names, INTERNAL_ADDRESS_FAILURE); + message->add_notify(message, FALSE, INTERNAL_ADDRESS_FAILURE, + chunk_empty); + vips->destroy_offset(vips, offsetof(host_t, destroy)); + pools->destroy(pools); + return SUCCESS; } /* query registered providers for additional attributes to include */ enumerator = hydra->attributes->create_responder_enumerator( - hydra->attributes, config->get_pool(config), id, vip); + hydra->attributes, pools, id, vips); while (enumerator->enumerate(enumerator, &type, &value)) { if (!cp) @@ -357,12 +415,13 @@ METHOD(task_t, build_r, status_t, type, value)); } enumerator->destroy(enumerator); + vips->destroy_offset(vips, offsetof(host_t, destroy)); + pools->destroy(pools); if (cp) { message->add_payload(message, (payload_t*)cp); } - DESTROY_IF(vip); return SUCCESS; } return NEED_MORE; @@ -373,14 +432,20 @@ METHOD(task_t, process_i, status_t, { if (this->ike_sa->get_state(this->ike_sa) == IKE_ESTABLISHED) { /* in last IKE_AUTH exchange */ + enumerator_t *enumerator; + host_t *host; process_payloads(this, message); - if (this->virtual_ip && - !this->virtual_ip->is_anyaddr(this->virtual_ip)) + enumerator = this->vips->create_enumerator(this->vips); + while (enumerator->enumerate(enumerator, &host)) { - this->ike_sa->set_virtual_ip(this->ike_sa, TRUE, this->virtual_ip); + if (!host->is_anyaddr(host)) + { + this->ike_sa->add_virtual_ip(this->ike_sa, TRUE, host); + } } + enumerator->destroy(enumerator); return SUCCESS; } return NEED_MORE; @@ -395,10 +460,9 @@ METHOD(task_t, get_type, task_type_t, METHOD(task_t, migrate, void, private_ike_config_t *this, ike_sa_t *ike_sa) { - DESTROY_IF(this->virtual_ip); - this->ike_sa = ike_sa; - this->virtual_ip = NULL; + this->vips->destroy_offset(this->vips, offsetof(host_t, destroy)); + this->vips = linked_list_create(); this->requested->destroy_function(this->requested, free); this->requested = linked_list_create(); } @@ -406,7 +470,7 @@ METHOD(task_t, migrate, void, METHOD(task_t, destroy, void, private_ike_config_t *this) { - DESTROY_IF(this->virtual_ip); + this->vips->destroy_offset(this->vips, offsetof(host_t, destroy)); this->requested->destroy_function(this->requested, free); free(this); } @@ -428,6 +492,7 @@ ike_config_t *ike_config_create(ike_sa_t *ike_sa, bool initiator) }, .initiator = initiator, .ike_sa = ike_sa, + .vips = linked_list_create(), .requested = linked_list_create(), ); diff --git a/src/libcharon/sa/ikev2/tasks/ike_mobike.c b/src/libcharon/sa/ikev2/tasks/ike_mobike.c index 133bc296e..8c30ed96c 100644 --- a/src/libcharon/sa/ikev2/tasks/ike_mobike.c +++ b/src/libcharon/sa/ikev2/tasks/ike_mobike.c @@ -250,15 +250,26 @@ static void update_children(private_ike_mobike_t *this) { enumerator_t *enumerator; child_sa_t *child_sa; + linked_list_t *vips; + host_t *host; + + vips = linked_list_create(); + + enumerator = this->ike_sa->create_virtual_ip_enumerator(this->ike_sa, TRUE); + while (enumerator->enumerate(enumerator, &host)) + { + vips->insert_last(vips, host); + } + enumerator->destroy(enumerator); enumerator = this->ike_sa->create_child_sa_enumerator(this->ike_sa); while (enumerator->enumerate(enumerator, (void**)&child_sa)) { if (child_sa->update(child_sa, this->ike_sa->get_my_host(this->ike_sa), - this->ike_sa->get_other_host(this->ike_sa), - this->ike_sa->get_virtual_ip(this->ike_sa, TRUE), - this->ike_sa->has_condition(this->ike_sa, COND_NAT_ANY)) == NOT_SUPPORTED) + this->ike_sa->get_other_host(this->ike_sa), vips, + this->ike_sa->has_condition(this->ike_sa, + COND_NAT_ANY)) == NOT_SUPPORTED) { this->ike_sa->rekey_child_sa(this->ike_sa, child_sa->get_protocol(child_sa), @@ -266,6 +277,8 @@ static void update_children(private_ike_mobike_t *this) } } enumerator->destroy(enumerator); + + vips->destroy(vips); } /** diff --git a/src/libhydra/attributes/attribute_handler.h b/src/libhydra/attributes/attribute_handler.h index d042f47ef..6014ef0fa 100644 --- a/src/libhydra/attributes/attribute_handler.h +++ b/src/libhydra/attributes/attribute_handler.h @@ -22,8 +22,8 @@ #define ATTRIBUTE_HANDLER_H_ #include <chunk.h> -#include <utils/host.h> #include <utils/identification.h> +#include <utils/linked_list.h> #include "attributes.h" @@ -62,11 +62,11 @@ struct attribute_handler_t { * Enumerate attributes to request from a server. * * @param server server identity to request attributes from - * @param vip virtual IP we are requesting, if any + * @param vips list of virtual IPs (host_t*) we are requesting * @return enumerator (configuration_attribute_type_t, chunk_t) */ enumerator_t* (*create_attribute_enumerator)(attribute_handler_t *this, - identification_t *server, host_t *vip); + identification_t *server, linked_list_t *vips); }; #endif /** ATTRIBUTE_HANDLER_H_ @}*/ diff --git a/src/libhydra/attributes/attribute_manager.c b/src/libhydra/attributes/attribute_manager.c index 95520531e..64dc9c7c9 100644 --- a/src/libhydra/attributes/attribute_manager.c +++ b/src/libhydra/attributes/attribute_manager.c @@ -51,12 +51,12 @@ struct private_attribute_manager_t { * Data to pass to enumerator filters */ typedef struct { - /** attribute group pool */ - char *pool; + /** attribute group pools */ + linked_list_t *pools; /** server/peer identity */ identification_t *id; - /** requesting/assigned virtual IP */ - host_t *vip; + /** requesting/assigned virtual IPs */ + linked_list_t *vips; } enum_data_t; METHOD(attribute_manager_t, acquire_address, host_t*, @@ -80,14 +80,10 @@ METHOD(attribute_manager_t, acquire_address, host_t*, enumerator->destroy(enumerator); this->lock->unlock(this->lock); - if (!host) - { - DBG1(DBG_CFG, "acquiring address from pool '%s' failed", pool); - } return host; } -METHOD(attribute_manager_t, release_address, void, +METHOD(attribute_manager_t, release_address, bool, private_attribute_manager_t *this, char *pool, host_t *address, identification_t *id) { @@ -108,10 +104,7 @@ METHOD(attribute_manager_t, release_address, void, enumerator->destroy(enumerator); this->lock->unlock(this->lock); - if (!found) - { - DBG1(DBG_CFG, "releasing address to pool '%s' failed", pool); - } + return found; } /** @@ -120,19 +113,21 @@ METHOD(attribute_manager_t, release_address, void, static enumerator_t *responder_enum_create(attribute_provider_t *provider, enum_data_t *data) { - return provider->create_attribute_enumerator(provider, data->pool, - data->id, data->vip); + return provider->create_attribute_enumerator(provider, data->pools, + data->id, data->vips); } METHOD(attribute_manager_t, create_responder_enumerator, enumerator_t*, - private_attribute_manager_t *this, char *pool, identification_t *id, - host_t *vip) + private_attribute_manager_t *this, linked_list_t *pools, + identification_t *id, linked_list_t *vips) { - enum_data_t *data = malloc_thing(enum_data_t); + enum_data_t *data; - data->pool = pool; - data->id = id; - data->vip = vip; + INIT(data, + .pools = pools, + .id = id, + .vips = vips, + ); this->lock->read_lock(this->lock); return enumerator_create_cleaner( enumerator_create_nested( @@ -238,8 +233,8 @@ typedef struct { enumerator_t *inner; /** server ID we want attributes for */ identification_t *id; - /** virtual IP we are requesting along with attriubutes */ - host_t *vip; + /** virtual IPs we are requesting along with attriubutes */ + linked_list_t *vips; } initiator_enumerator_t; /** @@ -259,7 +254,7 @@ static bool initiator_enumerate(initiator_enumerator_t *this, } DESTROY_IF(this->inner); this->inner = this->handler->create_attribute_enumerator(this->handler, - this->id, this->vip); + this->id, this->vips); } /* inject the handler as additional attribute */ *handler = this->handler; @@ -278,20 +273,22 @@ static void initiator_destroy(initiator_enumerator_t *this) } METHOD(attribute_manager_t, create_initiator_enumerator, enumerator_t*, - private_attribute_manager_t *this, identification_t *id, host_t *vip) + private_attribute_manager_t *this, identification_t *id, linked_list_t *vips) { - initiator_enumerator_t *enumerator = malloc_thing(initiator_enumerator_t); + initiator_enumerator_t *enumerator; this->lock->read_lock(this->lock); - enumerator->public.enumerate = (void*)initiator_enumerate; - enumerator->public.destroy = (void*)initiator_destroy; - enumerator->this = this; - enumerator->id = id; - enumerator->vip = vip; - enumerator->outer = this->handlers->create_enumerator(this->handlers); - enumerator->inner = NULL; - enumerator->handler = NULL; + INIT(enumerator, + .public = { + .enumerate = (void*)initiator_enumerate, + .destroy = (void*)initiator_destroy, + }, + .this = this, + .id = id, + .vips = vips, + .outer = this->handlers->create_enumerator(this->handlers), + ); return &enumerator->public; } diff --git a/src/libhydra/attributes/attribute_manager.h b/src/libhydra/attributes/attribute_manager.h index 56afef7c6..8bc80ca11 100644 --- a/src/libhydra/attributes/attribute_manager.h +++ b/src/libhydra/attributes/attribute_manager.h @@ -54,20 +54,22 @@ struct attribute_manager_t { * @param pool pool name from which the address was acquired * @param address address to release * @param id peer identity to get address for + * @return TRUE if address released to pool */ - void (*release_address)(attribute_manager_t *this, + bool (*release_address)(attribute_manager_t *this, char *pool, host_t *address, identification_t *id); /** * Create an enumerator over attributes to hand out to a peer. * - * @param pool pool name to get attributes from + * @param pool list of pools names (char*) to query attributes from * @param id peer identity to hand out attributes to - * @param vip virtual IP to assign to peer, if any + * @param vip list of virtual IPs (host_t*) to assign to peer * @return enumerator (configuration_attribute_type_t, chunk_t) */ enumerator_t* (*create_responder_enumerator)(attribute_manager_t *this, - char *pool, identification_t *id, host_t *vip); + linked_list_t *pool, identification_t *id, + linked_list_t *vips); /** * Register an attribute provider to the manager. @@ -114,11 +116,11 @@ struct attribute_manager_t { * Create an enumerator over attributes to request from server. * * @param id server identity to hand out attributes to - * @param vip virtual IP going to request, if any + * @param vip list of virtual IPs (host_t*) going to request * @return enumerator (attribute_handler_t, ca_type_t, chunk_t) */ enumerator_t* (*create_initiator_enumerator)(attribute_manager_t *this, - identification_t *id, host_t *vip); + identification_t *id, linked_list_t *vips); /** * Register an attribute handler to the manager. diff --git a/src/libhydra/attributes/attribute_provider.h b/src/libhydra/attributes/attribute_provider.h index e4b4e13f3..327135ffe 100644 --- a/src/libhydra/attributes/attribute_provider.h +++ b/src/libhydra/attributes/attribute_provider.h @@ -23,6 +23,7 @@ #include <utils/host.h> #include <utils/identification.h> +#include <utils/linked_list.h> typedef struct attribute_provider_t attribute_provider_t; @@ -56,13 +57,14 @@ struct attribute_provider_t { /** * Create an enumerator over attributes to hand out to a peer. * - * @param pool pool name to get attributes from + * @param pool list of pools names (char*) to query attributes from * @param id peer ID - * @param vip virtual IP to assign to peer, if any + * @param vip list of virtual IPs (host_t*) to assign to peer * @return enumerator (configuration_attribute_type_t, chunk_t) */ enumerator_t* (*create_attribute_enumerator)(attribute_provider_t *this, - char *pool, identification_t *id, host_t *vip); + linked_list_t *pools, identification_t *id, + linked_list_t *vips); }; #endif /** ATTRIBUTE_PROVIDER_H_ @}*/ diff --git a/src/libhydra/attributes/mem_pool.c b/src/libhydra/attributes/mem_pool.c index f55b3a7d1..b2fed2703 100644 --- a/src/libhydra/attributes/mem_pool.c +++ b/src/libhydra/attributes/mem_pool.c @@ -162,6 +162,12 @@ METHOD(mem_pool_t, get_name, const char*, return this->name; } +METHOD(mem_pool_t, get_base, host_t*, + private_mem_pool_t *this) +{ + return this->base; +} + METHOD(mem_pool_t, get_size, u_int, private_mem_pool_t *this) { @@ -220,11 +226,9 @@ METHOD(mem_pool_t, acquire_address, host_t*, return requested->clone(requested); } - if (!requested->is_anyaddr(requested) && - requested->get_family(requested) != + if (requested->get_family(requested) != this->base->get_family(this->base)) { - DBG1(DBG_CFG, "IP pool address family mismatch"); return NULL; } @@ -463,6 +467,7 @@ mem_pool_t *mem_pool_create(char *name, host_t *base, int bits) INIT(this, .public = { .get_name = _get_name, + .get_base = _get_base, .get_size = _get_size, .get_online = _get_online, .get_offline = _get_offline, diff --git a/src/libhydra/attributes/mem_pool.h b/src/libhydra/attributes/mem_pool.h index bb963de93..7b7e58af7 100644 --- a/src/libhydra/attributes/mem_pool.h +++ b/src/libhydra/attributes/mem_pool.h @@ -39,6 +39,13 @@ struct mem_pool_t { const char* (*get_name)(mem_pool_t *this); /** + * Get the base (first) address of this pool. + * + * @return base address, internal host + */ + host_t* (*get_base)(mem_pool_t *this); + + /** * Get the size (i.e. number of addresses) of this pool. * * @return the size of this pool diff --git a/src/libhydra/plugins/attr/attr_provider.c b/src/libhydra/plugins/attr/attr_provider.c index 6af8b473b..673c72116 100644 --- a/src/libhydra/plugins/attr/attr_provider.c +++ b/src/libhydra/plugins/attr/attr_provider.c @@ -77,10 +77,10 @@ static bool attr_enum_filter(void *null, attribute_entry_t **in, } METHOD(attribute_provider_t, create_attribute_enumerator, enumerator_t*, - private_attr_provider_t *this, char *pool, - identification_t *id, host_t *vip) + private_attr_provider_t *this, linked_list_t *pools, + identification_t *id, linked_list_t *vips) { - if (vip) + if (vips->get_count(vips)) { this->lock->read_lock(this->lock); return enumerator_create_filter( diff --git a/src/libhydra/plugins/attr_sql/sql_attribute.c b/src/libhydra/plugins/attr_sql/sql_attribute.c index 714bbcd72..8055be71c 100644 --- a/src/libhydra/plugins/attr_sql/sql_attribute.c +++ b/src/libhydra/plugins/attr_sql/sql_attribute.c @@ -233,7 +233,7 @@ static host_t* get_lease(private_sql_attribute_t *this, char *name, } METHOD(attribute_provider_t, acquire_address, host_t*, - private_sql_attribute_t *this, char *names, identification_t *id, + private_sql_attribute_t *this, char *name, identification_t *id, host_t *requested) { host_t *address = NULL; @@ -242,59 +242,17 @@ METHOD(attribute_provider_t, acquire_address, host_t*, identity = get_identity(this, id); if (identity) { - /* check for a single pool first (no concatenation and enumeration) */ - if (strchr(names, ',') == NULL) + pool = get_pool(this, name, &timeout); + if (pool) { - pool = get_pool(this, names, &timeout); - if (pool) + /* check for an existing lease */ + address = check_lease(this, name, pool, identity); + if (address == NULL) { - /* check for an existing lease */ - address = check_lease(this, names, pool, identity); - if (address == NULL) - { - /* get an unallocated address or expired lease */ - address = get_lease(this, names, pool, timeout, identity); - } + /* get an unallocated address or expired lease */ + address = get_lease(this, name, pool, timeout, identity); } } - else - { - enumerator_t *enumerator; - char *name; - - /* in a first step check for an existing lease over all pools */ - enumerator = enumerator_create_token(names, ",", " "); - while (enumerator->enumerate(enumerator, &name)) - { - pool = get_pool(this, name, &timeout); - if (pool) - { - address = check_lease(this, name, pool, identity); - if (address) - { - enumerator->destroy(enumerator); - return address; - } - } - } - enumerator->destroy(enumerator); - - /* in a second step get an unallocated address or expired lease */ - enumerator = enumerator_create_token(names, ",", " "); - while (enumerator->enumerate(enumerator, &name)) - { - pool = get_pool(this, name, &timeout); - if (pool) - { - address = get_lease(this, name, pool, timeout, identity); - if (address) - { - break; - } - } - } - enumerator->destroy(enumerator); - } } return address; } @@ -303,50 +261,41 @@ METHOD(attribute_provider_t, release_address, bool, private_sql_attribute_t *this, char *name, host_t *address, identification_t *id) { - enumerator_t *enumerator; - bool found = FALSE; + u_int pool, timeout; time_t now = time(NULL); - enumerator = enumerator_create_token(name, ",", " "); - while (enumerator->enumerate(enumerator, &name)) + pool = get_pool(this, name, &timeout); + if (pool) { - u_int pool, timeout; - - pool = get_pool(this, name, &timeout); - if (pool) + if (this->history) { - if (this->history) - { - this->db->execute(this->db, NULL, - "INSERT INTO leases (address, identity, acquired, released)" - " SELECT id, identity, acquired, ? FROM addresses " - " WHERE pool = ? AND address = ?", - DB_UINT, now, DB_UINT, pool, - DB_BLOB, address->get_address(address)); - } - if (this->db->execute(this->db, NULL, - "UPDATE addresses SET released = ? WHERE " - "pool = ? AND address = ?", DB_UINT, time(NULL), - DB_UINT, pool, DB_BLOB, address->get_address(address)) > 0) - { - found = TRUE; - break; - } + this->db->execute(this->db, NULL, + "INSERT INTO leases (address, identity, acquired, released)" + " SELECT id, identity, acquired, ? FROM addresses " + " WHERE pool = ? AND address = ?", + DB_UINT, now, DB_UINT, pool, + DB_BLOB, address->get_address(address)); + } + if (this->db->execute(this->db, NULL, + "UPDATE addresses SET released = ? WHERE " + "pool = ? AND address = ?", DB_UINT, time(NULL), + DB_UINT, pool, DB_BLOB, address->get_address(address)) > 0) + { + return TRUE; } } - enumerator->destroy(enumerator); - return found; + return FALSE; } METHOD(attribute_provider_t, create_attribute_enumerator, enumerator_t*, - private_sql_attribute_t *this, char *names, identification_t *id, - host_t *vip) + private_sql_attribute_t *this, linked_list_t *pools, identification_t *id, + linked_list_t *vips) { enumerator_t *attr_enumerator = NULL; - if (vip) + if (vips->get_count(vips)) { - enumerator_t *names_enumerator; + enumerator_t *pool_enumerator; u_int count; char *name; @@ -357,8 +306,8 @@ METHOD(attribute_provider_t, create_attribute_enumerator, enumerator_t*, { u_int identity = get_identity(this, id); - names_enumerator = enumerator_create_token(names, ",", " "); - while (names_enumerator->enumerate(names_enumerator, &name)) + pool_enumerator = pools->create_enumerator(pools); + while (pool_enumerator->enumerate(pool_enumerator, &name)) { u_int attr_pool = get_attr_pool(this, name); if (!attr_pool) @@ -385,14 +334,14 @@ METHOD(attribute_provider_t, create_attribute_enumerator, enumerator_t*, DESTROY_IF(attr_enumerator); attr_enumerator = NULL; } - names_enumerator->destroy(names_enumerator); + pool_enumerator->destroy(pool_enumerator); } /* in a second step check for attributes that match name */ if (!attr_enumerator) { - names_enumerator = enumerator_create_token(names, ",", " "); - while (names_enumerator->enumerate(names_enumerator, &name)) + pool_enumerator = pools->create_enumerator(pools); + while (pool_enumerator->enumerate(pool_enumerator, &name)) { u_int attr_pool = get_attr_pool(this, name); if (!attr_pool) @@ -419,7 +368,7 @@ METHOD(attribute_provider_t, create_attribute_enumerator, enumerator_t*, DESTROY_IF(attr_enumerator); attr_enumerator = NULL; } - names_enumerator->destroy(names_enumerator); + pool_enumerator->destroy(pool_enumerator); } this->db->execute(this->db, NULL, "END TRANSACTION"); diff --git a/src/libhydra/plugins/resolve/resolve_handler.c b/src/libhydra/plugins/resolve/resolve_handler.c index 011ebbaaf..2bee45d0d 100644 --- a/src/libhydra/plugins/resolve/resolve_handler.c +++ b/src/libhydra/plugins/resolve/resolve_handler.c @@ -267,46 +267,71 @@ METHOD(attribute_handler_t, release, void, typedef struct { /** implements enumerator_t interface */ enumerator_t public; - /** virtual IP we are requesting */ - host_t *vip; + /** request IPv4 DNS? */ + bool v4; + /** request IPv6 DNS? */ + bool v6; } attribute_enumerator_t; static bool attribute_enumerate(attribute_enumerator_t *this, configuration_attribute_type_t *type, chunk_t *data) { - switch (this->vip->get_family(this->vip)) + if (this->v4) { - case AF_INET: - *type = INTERNAL_IP4_DNS; - break; - case AF_INET6: - *type = INTERNAL_IP6_DNS; - break; - default: - return FALSE; + *type = INTERNAL_IP4_DNS; + *data = chunk_empty; + this->v4 = FALSE; + return TRUE; } - *data = chunk_empty; - /* enumerate only once */ - this->public.enumerate = (void*)return_false; - return TRUE; + if (this->v6) + { + *type = INTERNAL_IP6_DNS; + *data = chunk_empty; + this->v6 = FALSE; + return TRUE; + } + return FALSE; } -METHOD(attribute_handler_t, create_attribute_enumerator, enumerator_t*, - private_resolve_handler_t *this, identification_t *server, host_t *vip) +/** + * Check if a list has a host of given family + */ +static bool has_host_family(linked_list_t *list, int family) { - if (vip) + enumerator_t *enumerator; + host_t *host; + bool found = FALSE; + + enumerator = list->create_enumerator(list); + while (enumerator->enumerate(enumerator, &host)) { - attribute_enumerator_t *enumerator; + if (host->get_family(host) == family) + { + found = TRUE; + break; + } + } + enumerator->destroy(enumerator); - enumerator = malloc_thing(attribute_enumerator_t); - enumerator->public.enumerate = (void*)attribute_enumerate; - enumerator->public.destroy = (void*)free; - enumerator->vip = vip; + return found; +} - return &enumerator->public; - } - return enumerator_create_empty(); +METHOD(attribute_handler_t, create_attribute_enumerator, enumerator_t*, + private_resolve_handler_t *this, identification_t *server, + linked_list_t *vips) +{ + attribute_enumerator_t *enumerator; + + INIT(enumerator, + .public = { + .enumerate = (void*)attribute_enumerate, + .destroy = (void*)free, + }, + .v4 = has_host_family(vips, AF_INET), + .v6 = has_host_family(vips, AF_INET6), + ); + return &enumerator->public; } METHOD(resolve_handler_t, destroy, void, diff --git a/src/libipsec/esp_context.c b/src/libipsec/esp_context.c index c7fb7ab2f..dc3ad3f8b 100644 --- a/src/libipsec/esp_context.c +++ b/src/libipsec/esp_context.c @@ -16,6 +16,7 @@ */ #include <limits.h> +#include <stdint.h> #include "esp_context.h" diff --git a/src/libipsec/ip_packet.c b/src/libipsec/ip_packet.c index 4593ba5c8..52aeaa209 100644 --- a/src/libipsec/ip_packet.c +++ b/src/libipsec/ip_packet.c @@ -130,19 +130,19 @@ ip_packet_t *ip_packet_create(chunk_t packet) { case 4: { - struct iphdr *ip; + struct ip *ip; - if (packet.len < sizeof(struct iphdr)) + if (packet.len < sizeof(struct ip)) { DBG1(DBG_ESP, "IPv4 packet too short"); goto failed; } - ip = (struct iphdr*)packet.ptr; + ip = (struct ip*)packet.ptr; src = host_create_from_chunk(AF_INET, - chunk_from_thing(ip->saddr), 0); + chunk_from_thing(ip->ip_src), 0); dst = host_create_from_chunk(AF_INET, - chunk_from_thing(ip->daddr), 0); - next_header = ip->protocol; + chunk_from_thing(ip->ip_dst), 0); + next_header = ip->ip_p; break; } #ifdef HAVE_NETINET_IP6_H diff --git a/src/libstrongswan/eap/eap.c b/src/libstrongswan/eap/eap.c index efd3ee981..1e4cf11bf 100644 --- a/src/libstrongswan/eap/eap.c +++ b/src/libstrongswan/eap/eap.c @@ -1,4 +1,5 @@ /* + * Copyright (C) 2012 Tobias Brunner * Copyright (C) 2006 Martin Willi * Hochschule fuer Technik Rapperswil * @@ -13,8 +14,13 @@ * for more details. */ +#include <stdlib.h> +#include <errno.h> + #include "eap.h" +#include <debug.h> + ENUM(eap_code_names, EAP_REQUEST, EAP_FAILURE, "EAP_REQUEST", "EAP_RESPONSE", @@ -51,12 +57,12 @@ ENUM_NEXT(eap_type_names, EAP_MSTLV, EAP_MSTLV, EAP_MSCHAPV2, "EAP_MSTLV"); ENUM_NEXT(eap_type_names, EAP_TNC, EAP_TNC, EAP_MSTLV, "EAP_TNC"); -ENUM_NEXT(eap_type_names, EAP_DYNAMIC, EAP_EXPERIMENTAL, EAP_TNC, - "EAP_DYNAMIC", - "EAP_RADIUS", +ENUM_NEXT(eap_type_names, EAP_EXPANDED, EAP_DYNAMIC, EAP_TNC, "EAP_EXPANDED", - "EAP_EXPERIMENTAL"); -ENUM_END(eap_type_names, EAP_EXPERIMENTAL); + "EAP_EXPERIMENTAL", + "EAP_RADIUS", + "EAP_DYNAMIC"); +ENUM_END(eap_type_names, EAP_DYNAMIC); ENUM_BEGIN(eap_type_short_names, EAP_IDENTITY, EAP_GTC, "ID", @@ -80,12 +86,12 @@ ENUM_NEXT(eap_type_short_names, EAP_MSTLV, EAP_MSTLV, EAP_MSCHAPV2, "MSTLV"); ENUM_NEXT(eap_type_short_names, EAP_TNC, EAP_TNC, EAP_MSTLV, "TNC"); -ENUM_NEXT(eap_type_short_names, EAP_DYNAMIC, EAP_EXPERIMENTAL, EAP_TNC, - "DYN", - "RAD", +ENUM_NEXT(eap_type_short_names, EAP_EXPANDED, EAP_DYNAMIC, EAP_TNC, "EXP", - "XP"); -ENUM_END(eap_type_short_names, EAP_EXPERIMENTAL); + "XP", + "RAD", + "DYN"); +ENUM_END(eap_type_short_names, EAP_DYNAMIC); /* * See header @@ -108,6 +114,7 @@ eap_type_t eap_type_from_string(char *name) {"peap", EAP_PEAP}, {"mschapv2", EAP_MSCHAPV2}, {"tnc", EAP_TNC}, + {"dynamic", EAP_DYNAMIC}, {"radius", EAP_RADIUS}, }; @@ -120,3 +127,56 @@ eap_type_t eap_type_from_string(char *name) } return 0; } + +/* + * See header + */ +eap_vendor_type_t *eap_vendor_type_from_string(char *str) +{ + enumerator_t *enumerator; + eap_vendor_type_t *result = NULL; + eap_type_t type = 0; + u_int32_t vendor = 0; + char *part, *end; + + /* parse EAP method string of the form: [eap-]type[-vendor] */ + enumerator = enumerator_create_token(str, "-", " "); + while (enumerator->enumerate(enumerator, &part)) + { + if (!type) + { + if (streq(part, "eap")) + { /* skip 'eap' at the beginning */ + continue; + } + type = eap_type_from_string(part); + if (!type) + { + type = strtoul(part, &end, 0); + if (*end != '\0' || errno) + { + DBG1(DBG_LIB, "unknown or invalid EAP method: %s", part); + break; + } + } + continue; + } + vendor = strtoul(part, &end, 0); + if (*end != '\0' || errno) + { + DBG1(DBG_LIB, "invalid EAP vendor: %s", part); + type = 0; + } + break; + } + enumerator->destroy(enumerator); + + if (type) + { + INIT(result, + .type = type, + .vendor = vendor, + ); + } + return result; +} diff --git a/src/libstrongswan/eap/eap.h b/src/libstrongswan/eap/eap.h index 143232e4b..0e144b123 100644 --- a/src/libstrongswan/eap/eap.h +++ b/src/libstrongswan/eap/eap.h @@ -1,6 +1,8 @@ /* + * Copyright (C) 2012 Tobias Brunner * Copyright (C) 2010 Martin Willi * Copyright (C) 2010 revosec AG + * 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 @@ -23,6 +25,7 @@ typedef enum eap_code_t eap_code_t; typedef enum eap_type_t eap_type_t; +typedef struct eap_vendor_type_t eap_vendor_type_t; #include <library.h> @@ -64,12 +67,12 @@ enum eap_type_t { EAP_MSCHAPV2 = 26, EAP_MSTLV = 33, EAP_TNC = 38, - /** select EAP method dynamically based on i.e. EAP-Identity */ - EAP_DYNAMIC = 252, - /** not a method, but an implementation providing different methods */ - EAP_RADIUS = 253, EAP_EXPANDED = 254, EAP_EXPERIMENTAL = 255, + /** not a method, but an implementation providing different methods */ + EAP_RADIUS = 256, + /** not a method, select method dynamically based on client selection */ + EAP_DYNAMIC = 257, }; /** @@ -83,6 +86,22 @@ extern enum_name_t *eap_type_names; extern enum_name_t *eap_type_short_names; /** + * Struct that stores EAP type and vendor ID + */ +struct eap_vendor_type_t { + + /** + * EAP type + */ + eap_type_t type; + + /** + * Vendor Id + */ + u_int32_t vendor; +}; + +/** * EAP packet format */ typedef struct __attribute__((packed)) { @@ -101,4 +120,12 @@ typedef struct __attribute__((packed)) { */ eap_type_t eap_type_from_string(char *name); +/** + * Parse a string of the form [eap-]type[-vendor]. + * + * @param str EAP method string + * @return parsed type (gets allocated), NULL if unknown or failed + */ +eap_vendor_type_t *eap_vendor_type_from_string(char *str); + #endif /** EAP_H_ @}*/ diff --git a/src/libstrongswan/utils/tun_device.c b/src/libstrongswan/utils/tun_device.c index 889fe6247..36f3359c0 100644 --- a/src/libstrongswan/utils/tun_device.c +++ b/src/libstrongswan/utils/tun_device.c @@ -3,6 +3,7 @@ * Copyright (C) 2012 Giuliano Grassi * Copyright (C) 2012 Ralf Sager * Hochschule fuer Technik Rapperswil + * Copyright (C) 2012 Martin Willi * * 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 @@ -24,8 +25,17 @@ #include <sys/socket.h> #include <sys/stat.h> #include <unistd.h> -#include <linux/if.h> +#include <net/if.h> + +#ifdef __APPLE__ +#include <net/if_utun.h> +#include <netinet/in_var.h> +#include <sys/kern_control.h> +#elif defined(__linux__) #include <linux/if_tun.h> +#else +#include <net/if_tun.h> +#endif #include "tun_device.h" @@ -127,6 +137,14 @@ METHOD(tun_device_t, set_address, bool, this->if_name, strerror(errno)); return FALSE; } +#ifdef __APPLE__ + if (ioctl(this->sock, SIOCSIFDSTADDR, &ifr) < 0) + { + DBG1(DBG_LIB, "failed to set dest address on %s: %s", + this->if_name, strerror(errno)); + return FALSE; + } +#endif /* __APPLE__ */ set_netmask(&ifr, family, netmask); @@ -176,6 +194,8 @@ METHOD(tun_device_t, set_mtu, bool, if (ioctl(this->sock, SIOCSIFMTU, &ifr) < 0) { + DBG1(DBG_LIB, "failed to set MTU on %s: %s", this->if_name, + strerror(errno)); return FALSE; } this->mtu = mtu; @@ -269,6 +289,20 @@ METHOD(tun_device_t, destroy, void, if (this->tunfd > 0) { close(this->tunfd); +#ifdef __FreeBSD__ + /* tun(4) says the following: "These network interfaces persist until + * the if_tun.ko module is unloaded, or until removed with the + * ifconfig(8) command." So simply closing the FD is not enough. */ + struct ifreq ifr; + + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, this->if_name, IFNAMSIZ); + if (ioctl(this->sock, SIOCIFDESTROY, &ifr) < 0) + { + DBG1(DBG_LIB, "failed to destroy %s: %s", this->if_name, + strerror(errno)); + } +#endif /* __FreeBSD__ */ } if (this->sock > 0) { @@ -278,18 +312,70 @@ METHOD(tun_device_t, destroy, void, } /** - * Allocate a TUN device + * Initialize the tun device */ -static int tun_alloc(char dev[IFNAMSIZ]) +static bool init_tun(private_tun_device_t *this, const char *name_tmpl) { +#ifdef __APPLE__ + + struct ctl_info info; + struct sockaddr_ctl addr; + socklen_t size = IFNAMSIZ; + + memset(&info, 0, sizeof(info)); + memset(&addr, 0, sizeof(addr)); + + this->tunfd = socket(PF_SYSTEM, SOCK_DGRAM, SYSPROTO_CONTROL); + if (this->tunfd < 0) + { + DBG1(DBG_LIB, "failed to open tundevice PF_SYSTEM socket: %s", + strerror(errno)); + return FALSE; + } + + /* get a control identifier for the utun kernel extension */ + strncpy(info.ctl_name, UTUN_CONTROL_NAME, strlen(UTUN_CONTROL_NAME)); + if (ioctl(this->tunfd, CTLIOCGINFO, &info) < 0) + { + DBG1(DBG_LIB, "failed to ioctl tundevice: %s", strerror(errno)); + close(this->tunfd); + return FALSE; + } + + addr.sc_id = info.ctl_id; + addr.sc_len = sizeof(addr); + addr.sc_family = AF_SYSTEM; + addr.ss_sysaddr = AF_SYS_CONTROL; + /* allocate identifier dynamically */ + addr.sc_unit = 0; + + if (connect(this->tunfd, (struct sockaddr*)&addr, sizeof(addr)) < 0) + { + DBG1(DBG_LIB, "failed to connect tundevice: %s", strerror(errno)); + close(this->tunfd); + return FALSE; + } + if (getsockopt(this->tunfd, SYSPROTO_CONTROL, UTUN_OPT_IFNAME, + this->if_name, &size) < 0) + { + DBG1(DBG_LIB, "getting tundevice name failed: %s", strerror(errno)); + close(this->tunfd); + return FALSE; + } + return TRUE; + +#elif defined(IFF_TUN) + struct ifreq ifr; - int fd; - fd = open("/dev/net/tun", O_RDWR); - if (fd < 0) + strncpy(this->if_name, name_tmpl ?: "tun%d", IFNAMSIZ); + this->if_name[IFNAMSIZ-1] = '\0'; + + this->tunfd = open("/dev/net/tun", O_RDWR); + if (this->tunfd < 0) { DBG1(DBG_LIB, "failed to open /dev/net/tun: %s", strerror(errno)); - return fd; + return FALSE; } memset(&ifr, 0, sizeof(ifr)); @@ -297,16 +383,42 @@ static int tun_alloc(char dev[IFNAMSIZ]) /* TUN device, no packet info */ ifr.ifr_flags = IFF_TUN | IFF_NO_PI; - strncpy(ifr.ifr_name, dev, IFNAMSIZ); - - if (ioctl(fd, TUNSETIFF, (void*)&ifr) < 0) + strncpy(ifr.ifr_name, this->if_name, IFNAMSIZ); + if (ioctl(this->tunfd, TUNSETIFF, (void*)&ifr) < 0) { DBG1(DBG_LIB, "failed to configure TUN device: %s", strerror(errno)); - close(fd); - return -1; + close(this->tunfd); + return FALSE; + } + strncpy(this->if_name, ifr.ifr_name, IFNAMSIZ); + return TRUE; + +#else /* !IFF_TUN */ + + /* this works on FreeBSD and might also work on Linux with older TUN + * driver versions (no IFF_TUN) */ + char devname[IFNAMSIZ]; + int i; + + if (name_tmpl) + { + DBG1(DBG_LIB, "arbitrary naming of TUN devices is not supported"); } - strncpy(dev, ifr.ifr_name, IFNAMSIZ); - return fd; + + for (i = 0; i < 256; i++) + { + snprintf(devname, IFNAMSIZ, "/dev/tun%d", i); + this->tunfd = open(devname, O_RDWR); + if (this->tunfd > 0) + { /* for ioctl(2) calls only the interface name is used */ + snprintf(this->if_name, IFNAMSIZ, "tun%d", i); + break; + } + DBG1(DBG_LIB, "failed to open %s: %s", this->if_name, strerror(errno)); + } + return this->tunfd > 0; + +#endif /* !__APPLE__ */ } /* @@ -331,13 +443,9 @@ tun_device_t *tun_device_create(const char *name_tmpl) .sock = -1, ); - strncpy(this->if_name, name_tmpl ?: "tun%d", IFNAMSIZ); - this->if_name[IFNAMSIZ-1] = '\0'; - - this->tunfd = tun_alloc(this->if_name); - if (this->tunfd < 0) + if (!init_tun(this, name_tmpl)) { - destroy(this); + free(this); return NULL; } DBG1(DBG_LIB, "created TUN device: %s", this->if_name); diff --git a/src/starter/args.c b/src/starter/args.c index 640cc43d6..eb25b2abb 100644 --- a/src/starter/args.c +++ b/src/starter/args.c @@ -186,7 +186,7 @@ static const token_info_t token_info[] = { ARG_STR, offsetof(starter_end_t, subnet), NULL }, { ARG_MISC, 0, NULL /* KW_PROTOPORT */ }, { ARG_STR, offsetof(starter_end_t, sourceip), NULL }, - { ARG_MISC, 0, NULL /* KW_NATIP */ }, + { ARG_STR, offsetof(starter_end_t, dns), NULL }, { ARG_ENUM, offsetof(starter_end_t, firewall), LST_bool }, { ARG_ENUM, offsetof(starter_end_t, hostaccess), LST_bool }, { ARG_ENUM, offsetof(starter_end_t, allow_any), LST_bool }, diff --git a/src/starter/cmp.c b/src/starter/cmp.c index 724dc7804..aaba7b11d 100644 --- a/src/starter/cmp.c +++ b/src/starter/cmp.c @@ -26,7 +26,6 @@ static bool starter_cmp_end(starter_end_t *c1, starter_end_t *c2) if ((c1 == NULL) || (c2 == NULL)) return FALSE; - VARCMP(has_natip); VARCMP(modecfg); VARCMP(port); VARCMP(protocol); diff --git a/src/starter/confread.c b/src/starter/confread.c index 642fc551b..f30f2188c 100644 --- a/src/starter/confread.c +++ b/src/starter/confread.c @@ -231,64 +231,6 @@ static void kw_end(starter_conn_t *conn, starter_end_t *end, kw_token_t token, end->host = strdupnull(value); break; case KW_SOURCEIP: - if (end->has_natip) - { - DBG1(DBG_APP, "# natip and sourceip cannot be defined at the same time"); - goto err; - } - if (value[0] == '%') - { - if (streq(value, "%modeconfig") || streq(value, "%modecfg") || - streq(value, "%config") || streq(value, "%cfg")) - { - /* request ip via config payload */ - free(end->sourceip); - end->sourceip = NULL; - end->sourceip_mask = 1; - } - else - { /* %poolname, strip %, serve ip requests */ - free(end->sourceip); - end->sourceip = strdupnull(value+1); - end->sourceip_mask = 0; - } - end->modecfg = TRUE; - } - else - { - host_t *host; - char *sep; - - sep = strchr(value, '/'); - if (sep) - { /* CIDR notation, address pool */ - *sep = '\0'; - host = host_create_from_string(value, 0); - if (!host) - { - DBG1(DBG_APP, "# bad subnet: %s=%s", name, value); - goto err; - } - host->destroy(host); - free(end->sourceip); - end->sourceip = strdupnull(value); - end->sourceip_mask = atoi(sep + 1); - /* restore the original text in case also= is used */ - *sep = '/'; - } - else - { /* fixed srcip */ - host = host_create_from_string(value, 0); - if (!host) - { - DBG1(DBG_APP, "# bad addr: %s=%s", name, value); - goto err; - } - end->sourceip_mask = (host->get_family(host) == AF_INET) ? - 32 : 128; - host->destroy(host); - } - } conn->mode = MODE_TUNNEL; conn->proxy_mode = FALSE; break; @@ -370,27 +312,6 @@ static void kw_end(starter_conn_t *conn, starter_end_t *end, kw_token_t token, } break; } - case KW_NATIP: - { - host_t *host; - if (end->sourceip) - { - DBG1(DBG_APP, "# natip and sourceip cannot be defined at the same time"); - goto err; - } - host = host_create_from_string(value, 0); - if (!host) - { - DBG1(DBG_APP, "# bad addr: %s=%s", name, value); - goto err; - } - host->destroy(host); - end->sourceip = strdupnull(value); - end->has_natip = TRUE; - conn->mode = MODE_TUNNEL; - conn->proxy_mode = FALSE; - break; - } default: break; } diff --git a/src/starter/confread.h b/src/starter/confread.h index 0c284229b..3f2079883 100644 --- a/src/starter/confread.h +++ b/src/starter/confread.h @@ -102,7 +102,6 @@ struct starter_end { char *host; u_int ikeport; char *subnet; - bool has_natip; bool modecfg; certpolicy_t sendcert; bool firewall; @@ -112,7 +111,7 @@ struct starter_end { u_int16_t port; u_int8_t protocol; char *sourceip; - int sourceip_mask; + char *dns; }; typedef struct also also_t; diff --git a/src/starter/keywords.h b/src/starter/keywords.h index bd9affe6c..537bceb07 100644 --- a/src/starter/keywords.h +++ b/src/starter/keywords.h @@ -97,7 +97,7 @@ typedef enum { KW_SUBNET, KW_PROTOPORT, KW_SOURCEIP, - KW_NATIP, + KW_DNS, KW_FIREWALL, KW_HOSTACCESS, KW_ALLOWANY, @@ -126,7 +126,7 @@ typedef enum { KW_LEFTSUBNET, KW_LEFTPROTOPORT, KW_LEFTSOURCEIP, - KW_LEFTNATIP, + KW_LEFTDNS, KW_LEFTFIREWALL, KW_LEFTHOSTACCESS, KW_LEFTALLOWANY, @@ -155,7 +155,7 @@ typedef enum { KW_RIGHTSUBNET, KW_RIGHTPROTOPORT, KW_RIGHTSOURCEIP, - KW_RIGHTNATIP, + KW_RIGHTDNS, KW_RIGHTFIREWALL, KW_RIGHTHOSTACCESS, KW_RIGHTALLOWANY, diff --git a/src/starter/keywords.txt b/src/starter/keywords.txt index 30d89f924..8366f5209 100644 --- a/src/starter/keywords.txt +++ b/src/starter/keywords.txt @@ -85,7 +85,7 @@ leftsubnet, KW_LEFTSUBNET leftsubnetwithin, KW_LEFTSUBNET leftprotoport, KW_LEFTPROTOPORT leftsourceip, KW_LEFTSOURCEIP -leftnatip, KW_LEFTNATIP +leftdns, KW_LEFTDNS leftfirewall, KW_LEFTFIREWALL lefthostaccess, KW_LEFTHOSTACCESS leftallowany, KW_LEFTALLOWANY @@ -109,7 +109,7 @@ rightsubnet, KW_RIGHTSUBNET rightsubnetwithin, KW_RIGHTSUBNET rightprotoport, KW_RIGHTPROTOPORT rightsourceip, KW_RIGHTSOURCEIP -rightnatip, KW_RIGHTNATIP +rightdns, KW_RIGHTDNS rightfirewall, KW_RIGHTFIREWALL righthostaccess, KW_RIGHTHOSTACCESS rightallowany, KW_RIGHTALLOWANY diff --git a/src/starter/starterstroke.c b/src/starter/starterstroke.c index 0fbc5d245..d6ad3eb89 100644 --- a/src/starter/starterstroke.c +++ b/src/starter/starterstroke.c @@ -140,7 +140,7 @@ static void starter_stroke_add_end(stroke_msg_t *msg, stroke_end_t *msg_end, sta msg_end->ikeport = conn_end->ikeport; msg_end->subnets = push_string(msg, conn_end->subnet); msg_end->sourceip = push_string(msg, conn_end->sourceip); - msg_end->sourceip_mask = conn_end->sourceip_mask; + msg_end->dns = push_string(msg, conn_end->dns); msg_end->sendcert = conn_end->sendcert; msg_end->hostaccess = conn_end->hostaccess; msg_end->tohost = !conn_end->subnet; diff --git a/src/stroke/stroke_msg.h b/src/stroke/stroke_msg.h index 95de7d69b..662feed69 100644 --- a/src/stroke/stroke_msg.h +++ b/src/stroke/stroke_msg.h @@ -158,7 +158,7 @@ struct stroke_end_t { char *address; u_int16_t ikeport; char *sourceip; - int sourceip_mask; + char *dns; char *subnets; int sendcert; int hostaccess; |