aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/libcharon/plugins/updown/updown_listener.c461
1 files changed, 215 insertions, 246 deletions
diff --git a/src/libcharon/plugins/updown/updown_listener.c b/src/libcharon/plugins/updown/updown_listener.c
index 200f298a1..1d15cc55e 100644
--- a/src/libcharon/plugins/updown/updown_listener.c
+++ b/src/libcharon/plugins/updown/updown_listener.c
@@ -16,9 +16,11 @@
#define _GNU_SOURCE
#include <stdio.h>
+#include <unistd.h>
#include "updown_listener.h"
+#include <utils/process.h>
#include <hydra.h>
#include <daemon.h>
#include <config/child_cfg.h>
@@ -97,53 +99,84 @@ static char* uncache_iface(private_updown_listener_t *this, u_int32_t reqid)
}
/**
- * Create variables for handled DNS attributes
+ * Allocate and push a format string to the environment
*/
-static char *make_dns_vars(private_updown_listener_t *this, ike_sa_t *ike_sa)
+static bool push_env(char *envp[], u_int count, char *fmt, ...)
{
- enumerator_t *enumerator;
- host_t *host;
- int v4 = 0, v6 = 0;
- char total[512] = "", current[64];
+ int i = 0;
+ char *str;
+ va_list args;
- if (!this->handler)
+ while (envp[i])
{
- return strdup("");
+ if (++i + 1 >= count)
+ {
+ return FALSE;
+ }
}
+ va_start(args, fmt);
+ if (vasprintf(&str, fmt, args) >= 0)
+ {
+ envp[i] = str;
+ }
+ va_end(args);
+ return envp[i] != NULL;
+}
- enumerator = this->handler->create_dns_enumerator(this->handler,
- ike_sa->get_unique_id(ike_sa));
- while (enumerator->enumerate(enumerator, &host))
+/**
+ * Free all allocated environment strings
+ */
+static void free_env(char *envp[])
+{
+ int i;
+
+ for (i = 0; envp[i]; i++)
{
- switch (host->get_family(host))
+ free(envp[i]);
+ }
+}
+
+/**
+ * Push variables for handled DNS attributes
+ */
+static void push_dns_env(private_updown_listener_t *this, ike_sa_t *ike_sa,
+ char *envp[], u_int count)
+{
+ enumerator_t *enumerator;
+ host_t *host;
+ int v4 = 0, v6 = 0;
+
+ if (this->handler)
+ {
+ enumerator = this->handler->create_dns_enumerator(this->handler,
+ ike_sa->get_unique_id(ike_sa));
+ while (enumerator->enumerate(enumerator, &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;
+ switch (host->get_family(host))
+ {
+ case AF_INET:
+ push_env(envp, count, "PLUTO_DNS4_%d=%H", ++v4, host);
+ break;
+ case AF_INET6:
+ push_env(envp, count, "PLUTO_DNS6_%d=%H", ++v6, host);
+ break;
+ default:
+ continue;
+ }
}
- strncat(total, current, sizeof(total) - strlen(total) - 1);
+ enumerator->destroy(enumerator);
}
- enumerator->destroy(enumerator);
-
- return strdup(total);
}
/**
- * Create variables for local virtual IPs
+ * Push variables for local virtual IPs
*/
-static char *make_vip_vars(private_updown_listener_t *this, ike_sa_t *ike_sa)
+static void push_vip_env(private_updown_listener_t *this, ike_sa_t *ike_sa,
+ char *envp[], u_int count)
{
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);
@@ -151,28 +184,22 @@ static char *make_vip_vars(private_updown_listener_t *this, ike_sa_t *ike_sa)
{
if (first)
{ /* legacy variable for first VIP */
- snprintf(current, sizeof(current),
- "PLUTO_MY_SOURCEIP='%H' ", host);
- strncat(total, current, sizeof(total) - strlen(total) - 1);
+ first = FALSE;
+ push_env(envp, count, "PLUTO_MY_SOURCEIP=%H", host);
}
switch (host->get_family(host))
{
case AF_INET:
- snprintf(current, sizeof(current),
- "PLUTO_MY_SOURCEIP4_%d='%H' ", ++v4, host);
+ push_env(envp, count, "PLUTO_MY_SOURCEIP4_%d=%H", ++v4, host);
break;
case AF_INET6:
- snprintf(current, sizeof(current),
- "PLUTO_MY_SOURCEIP6_%d='%H' ", ++v6, host);
+ push_env(envp, count, "PLUTO_MY_SOURCEIP6_%d=%H", ++v6, host);
break;
default:
continue;
}
- strncat(total, current, sizeof(total) - strlen(total) - 1);
}
enumerator->destroy(enumerator);
-
- return strdup(total);
}
/**
@@ -196,240 +223,182 @@ static u_int16_t get_port(traffic_selector_t *me,
return local ? me->get_from_port(me) : other->get_from_port(other);
}
-METHOD(listener_t, child_updown, bool,
- private_updown_listener_t *this, ike_sa_t *ike_sa, child_sa_t *child_sa,
- bool up)
+/**
+ * Invoke the updown script once for given traffic selectors
+ */
+static void invoke_once(private_updown_listener_t *this, ike_sa_t *ike_sa,
+ child_sa_t *child_sa, child_cfg_t *config, bool up,
+ traffic_selector_t *my_ts, traffic_selector_t *other_ts)
{
- traffic_selector_t *my_ts, *other_ts;
- enumerator_t *enumerator;
- child_cfg_t *config;
- host_t *me, *other;
- char *script;
+ host_t *me, *other, *host;
+ char *iface;
+ u_int8_t mask;
+ mark_t mark;
+ bool is_host, is_ipv6;
+ int out;
+ FILE *shell;
+ process_t *process;
+ char *envp[128] = {};
- config = child_sa->get_config(child_sa);
- script = config->get_updown(config);
me = ike_sa->get_my_host(ike_sa);
other = ike_sa->get_other_host(ike_sa);
- if (script == NULL)
+ push_env(envp, countof(envp), "PLUTO_VERSION=1.1");
+ is_host = my_ts->is_host(my_ts, me);
+ if (is_host)
{
- return TRUE;
+ is_ipv6 = me->get_family(me) == AF_INET6;
}
-
- enumerator = child_sa->create_policy_enumerator(child_sa);
- while (enumerator->enumerate(enumerator, &my_ts, &other_ts))
+ else
{
- char command[2048];
- 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, *dns, *xauth;
- mark_t mark;
- bool is_host, is_ipv6, use_ipcomp;
- FILE *shell;
-
- my_ts->to_subnet(my_ts, &my_client, &my_client_mask);
- other_ts->to_subnet(other_ts, &other_client, &other_client_mask);
-
- virtual_ip = make_vip_vars(this, ike_sa);
-
- /* check for the presence of an inbound mark */
- mark = config->get_mark(config, TRUE);
- if (mark.value)
- {
- if (asprintf(&mark_in, "PLUTO_MARK_IN='%u/0x%08x' ",
- mark.value, mark.mask ) < 0)
- {
- mark_in = NULL;
- }
- }
- else
- {
- if (asprintf(&mark_in, "") < 0)
- {
- mark_in = NULL;
- }
- }
-
- /* check for the presence of an outbound mark */
- mark = config->get_mark(config, FALSE);
- if (mark.value)
- {
- if (asprintf(&mark_out, "PLUTO_MARK_OUT='%u/0x%08x' ",
- mark.value, mark.mask ) < 0)
- {
- mark_out = NULL;
- }
- }
- else
- {
- if (asprintf(&mark_out, "") < 0)
- {
- mark_out = NULL;
- }
- }
-
- /* check for a NAT condition causing ESP_IN_UDP encapsulation */
- if (ike_sa->has_condition(ike_sa, COND_NAT_ANY))
+ is_ipv6 = my_ts->get_type(my_ts) == TS_IPV6_ADDR_RANGE;
+ }
+ push_env(envp, countof(envp), "PLUTO_VERB=%s%s%s",
+ up ? "up" : "down",
+ is_host ? "-host" : "-client",
+ is_ipv6 ? "-v6" : "");
+ push_env(envp, countof(envp), "PLUTO_CONNECTION=%s",
+ config->get_name(config));
+ if (up)
+ {
+ if (hydra->kernel_interface->get_interface(hydra->kernel_interface,
+ me, &iface))
{
- if (asprintf(&udp_enc, "PLUTO_UDP_ENC='%u' ",
- other->get_port(other)) < 0)
- {
- udp_enc = NULL;
- }
-
+ cache_iface(this, child_sa->get_reqid(child_sa), iface);
}
else
{
- if (asprintf(&udp_enc, "") < 0)
- {
- udp_enc = NULL;
- }
-
+ iface = NULL;
}
+ }
+ else
+ {
+ iface = uncache_iface(this, child_sa->get_reqid(child_sa));
+ }
+ push_env(envp, countof(envp), "PLUTO_INTERFACE=%s",
+ iface ? iface : "unknown");
+ push_env(envp, countof(envp), "PLUTO_REQID=%u",
+ child_sa->get_reqid(child_sa));
+ push_env(envp, countof(envp), "PLUTO_PROTO=%s",
+ child_sa->get_protocol(child_sa) == PROTO_ESP ? "esp" : "ah");
+ push_env(envp, countof(envp), "PLUTO_UNIQUEID=%u",
+ ike_sa->get_unique_id(ike_sa));
+ push_env(envp, countof(envp), "PLUTO_ME=%H", me);
+ push_env(envp, countof(envp), "PLUTO_MY_ID=%Y", ike_sa->get_my_id(ike_sa));
+ if (my_ts->to_subnet(my_ts, &host, &mask))
+ {
+ push_env(envp, countof(envp), "PLUTO_MY_CLIENT=%+H/%u", host, mask);
+ host->destroy(host);
+ }
+ push_env(envp, countof(envp), "PLUTO_MY_PORT=%u",
+ get_port(my_ts, other_ts, TRUE));
+ push_env(envp, countof(envp), "PLUTO_MY_PROTOCOL=%u",
+ my_ts->get_protocol(my_ts));
+ push_env(envp, countof(envp), "PLUTO_PEER=%H", other);
+ push_env(envp, countof(envp), "PLUTO_PEER_ID=%Y",
+ ike_sa->get_other_id(ike_sa));
+ if (other_ts->to_subnet(other_ts, &host, &mask))
+ {
+ push_env(envp, countof(envp), "PLUTO_PEER_CLIENT=%+H/%u", host, mask);
+ host->destroy(host);
+ }
+ push_env(envp, countof(envp), "PLUTO_PEER_PORT=%u",
+ get_port(my_ts, other_ts, FALSE));
+ push_env(envp, countof(envp), "PLUTO_PEER_PROTOCOL=%u",
+ other_ts->get_protocol(other_ts));
+ if (ike_sa->has_condition(ike_sa, COND_EAP_AUTHENTICATED) ||
+ ike_sa->has_condition(ike_sa, COND_XAUTH_AUTHENTICATED))
+ {
+ push_env(envp, countof(envp), "PLUTO_XAUTH_ID=%Y",
+ ike_sa->get_other_eap_id(ike_sa));
+ }
+ push_vip_env(this, ike_sa, envp, countof(envp));
+ mark = config->get_mark(config, TRUE);
+ if (mark.value)
+ {
+ push_env(envp, countof(envp), "PLUTO_MARK_IN=%u/0x%08x",
+ mark.value, mark.mask);
+ }
+ mark = config->get_mark(config, FALSE);
+ if (mark.value)
+ {
+ push_env(envp, countof(envp), "PLUTO_MARK_OUT=%u/0x%08x",
+ mark.value, mark.mask);
+ }
+ if (ike_sa->has_condition(ike_sa, COND_NAT_ANY))
+ {
+ push_env(envp, countof(envp), "PLUTO_UDP_ENC=%u",
+ other->get_port(other));
+ }
+ if (child_sa->get_ipcomp(child_sa) != IPCOMP_NONE)
+ {
+ push_env(envp, countof(envp), "PLUTO_IPCOMP=1");
+ }
+ push_dns_env(this, ike_sa, envp, countof(envp));
+ if (config->get_hostaccess(config))
+ {
+ push_env(envp, countof(envp), "PLUTO_HOST_ACCESS=1");
+ }
- if (ike_sa->has_condition(ike_sa, COND_EAP_AUTHENTICATED) ||
- ike_sa->has_condition(ike_sa, COND_XAUTH_AUTHENTICATED))
- {
- if (asprintf(&xauth, "PLUTO_XAUTH_ID='%Y' ",
- ike_sa->get_other_eap_id(ike_sa)) < 0)
- {
- xauth = NULL;
- }
- }
- else
+ process = process_start_shell(envp, NULL, &out, NULL, "2>&1 %s",
+ config->get_updown(config));
+ if (process)
+ {
+ shell = fdopen(out, "r");
+ if (shell)
{
- if (asprintf(&xauth, "") < 0)
+ while (TRUE)
{
- xauth = NULL;
- }
- }
+ char resp[128];
- if (up)
- {
- if (hydra->kernel_interface->get_interface(hydra->kernel_interface,
- me, &iface))
- {
- cache_iface(this, child_sa->get_reqid(child_sa), iface);
- }
- else
- {
- iface = NULL;
+ if (fgets(resp, sizeof(resp), shell) == NULL)
+ {
+ if (ferror(shell))
+ {
+ DBG1(DBG_CHD, "error reading from updown script");
+ }
+ break;
+ }
+ else
+ {
+ char *e = resp + strlen(resp);
+ if (e > resp && e[-1] == '\n')
+ {
+ e[-1] = '\0';
+ }
+ DBG1(DBG_CHD, "updown: %s", resp);
+ }
}
+ fclose(shell);
}
else
{
- iface = uncache_iface(this, child_sa->get_reqid(child_sa));
+ close(out);
}
+ process->wait(process, NULL);
+ }
+ free(iface);
+ free_env(envp);
+}
- dns = make_dns_vars(this, ike_sa);
-
- /* check for IPComp */
- use_ipcomp = child_sa->get_ipcomp(child_sa) != IPCOMP_NONE;
-
- /* 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) :
- (my_ts->get_type(my_ts) == TS_IPV6_ADDR_RANGE);
-
- /* build the command with all env variables.
- */
- snprintf(command, sizeof(command),
- "2>&1 "
- "PLUTO_VERSION='1.1' "
- "PLUTO_VERB='%s%s%s' "
- "PLUTO_CONNECTION='%s' "
- "PLUTO_INTERFACE='%s' "
- "PLUTO_REQID='%u' "
- "PLUTO_PROTO='%s' "
- "PLUTO_UNIQUEID='%u' "
- "PLUTO_ME='%H' "
- "PLUTO_MY_ID='%Y' "
- "PLUTO_MY_CLIENT='%+H/%u' "
- "PLUTO_MY_PORT='%u' "
- "PLUTO_MY_PROTOCOL='%u' "
- "PLUTO_PEER='%H' "
- "PLUTO_PEER_ID='%Y' "
- "PLUTO_PEER_CLIENT='%+H/%u' "
- "PLUTO_PEER_PORT='%u' "
- "PLUTO_PEER_PROTOCOL='%u' "
- "%s"
- "%s"
- "%s"
- "%s"
- "%s"
- "%s"
- "%s"
- "%s"
- "%s",
- up ? "up" : "down",
- is_host ? "-host" : "-client",
- is_ipv6 ? "-v6" : "",
- config->get_name(config),
- iface ? iface : "unknown",
- child_sa->get_reqid(child_sa),
- child_sa->get_protocol(child_sa) == PROTO_ESP ? "esp" : "ah",
- ike_sa->get_unique_id(ike_sa),
- me, ike_sa->get_my_id(ike_sa),
- my_client, my_client_mask,
- get_port(my_ts, other_ts, TRUE),
- my_ts->get_protocol(my_ts),
- other, ike_sa->get_other_id(ike_sa),
- other_client, other_client_mask,
- get_port(my_ts, other_ts, FALSE),
- other_ts->get_protocol(other_ts),
- xauth,
- virtual_ip,
- mark_in,
- mark_out,
- udp_enc,
- use_ipcomp ? "PLUTO_IPCOMP='1' " : "",
- config->get_hostaccess(config) ? "PLUTO_HOST_ACCESS='1' " : "",
- dns,
- script);
- my_client->destroy(my_client);
- other_client->destroy(other_client);
- free(virtual_ip);
- free(mark_in);
- free(mark_out);
- free(udp_enc);
- free(dns);
- free(iface);
- free(xauth);
-
- DBG3(DBG_CHD, "running updown script: %s", command);
- shell = popen(command, "r");
-
- if (shell == NULL)
- {
- DBG1(DBG_CHD, "could not execute updown script '%s'", script);
- return TRUE;
- }
+METHOD(listener_t, child_updown, bool,
+ private_updown_listener_t *this, ike_sa_t *ike_sa, child_sa_t *child_sa,
+ bool up)
+{
+ traffic_selector_t *my_ts, *other_ts;
+ enumerator_t *enumerator;
+ child_cfg_t *config;
- while (TRUE)
+ config = child_sa->get_config(child_sa);
+ if (config->get_updown(config))
+ {
+ enumerator = child_sa->create_policy_enumerator(child_sa);
+ while (enumerator->enumerate(enumerator, &my_ts, &other_ts))
{
- char resp[128];
-
- if (fgets(resp, sizeof(resp), shell) == NULL)
- {
- if (ferror(shell))
- {
- DBG1(DBG_CHD, "error reading output from updown script");
- }
- break;
- }
- else
- {
- char *e = resp + strlen(resp);
- if (e > resp && e[-1] == '\n')
- { /* trim trailing '\n' */
- e[-1] = '\0';
- }
- DBG1(DBG_CHD, "updown: %s", resp);
- }
+ invoke_once(this, ike_sa, child_sa, config, up, my_ts, other_ts);
}
- pclose(shell);
+ enumerator->destroy(enumerator);
}
- enumerator->destroy(enumerator);
return TRUE;
}