diff options
author | Martin Willi <martin@revosec.ch> | 2012-07-31 15:21:11 +0200 |
---|---|---|
committer | Martin Willi <martin@revosec.ch> | 2012-08-21 09:38:01 +0200 |
commit | e0d3014a17d28e543e69fd92250e98d3d87da952 (patch) | |
tree | 34b94fc578b18fecbc3aa9221e075ca02efb2967 | |
parent | c60f1da424b025fbc1b221c2b533e82a3bf623c7 (diff) | |
download | strongswan-e0d3014a17d28e543e69fd92250e98d3d87da952.tar.bz2 strongswan-e0d3014a17d28e543e69fd92250e98d3d87da952.tar.xz |
Add a DNS attribute handler to updown, passing servers to updown script
-rw-r--r-- | src/libcharon/plugins/updown/Makefile.am | 1 | ||||
-rw-r--r-- | src/libcharon/plugins/updown/updown_handler.c | 243 | ||||
-rw-r--r-- | src/libcharon/plugins/updown/updown_handler.h | 57 | ||||
-rw-r--r-- | src/libcharon/plugins/updown/updown_listener.c | 54 | ||||
-rw-r--r-- | src/libcharon/plugins/updown/updown_listener.h | 4 | ||||
-rw-r--r-- | src/libcharon/plugins/updown/updown_plugin.c | 22 |
6 files changed, 377 insertions, 4 deletions
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..944c46c6c 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,45 @@ 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); +} + METHOD(listener_t, child_updown, bool, private_updown_listener_t *this, ike_sa_t *ike_sa, child_sa_t *child_sa, bool up) @@ -117,7 +161,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; @@ -209,6 +253,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 +285,7 @@ METHOD(listener_t, child_updown, bool, "%s" "%s" "%s" + "%s" "%s", up ? "up" : "down", is_host ? "-host" : "-client", @@ -259,6 +306,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 +314,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 +364,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 +376,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; |