]> git.ipfire.org Git - people/ms/strongswan.git/blame - src/libcharon/plugins/updown/updown_listener.c
updown: Increase buffer size for script and environment variables
[people/ms/strongswan.git] / src / libcharon / plugins / updown / updown_listener.c
CommitLineData
49653b6b 1/*
cf4a7395 2 * Copyright (C) 2013 Tobias Brunner
49653b6b
MW
3 * Copyright (C) 2008 Martin Willi
4 * Hochschule fuer Technik Rapperswil
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2 of the License, or (at your
9 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * for more details.
49653b6b
MW
15 */
16
17#define _GNU_SOURCE
18#include <stdio.h>
19
20#include "updown_listener.h"
21
f6659688 22#include <hydra.h>
49653b6b
MW
23#include <daemon.h>
24#include <config/child_cfg.h>
25
26typedef struct private_updown_listener_t private_updown_listener_t;
27
28/**
29 * Private data of an updown_listener_t object.
30 */
31struct private_updown_listener_t {
7daf5226 32
49653b6b
MW
33 /**
34 * Public updown_listener_t interface.
35 */
36 updown_listener_t public;
7daf5226 37
49653b6b
MW
38 /**
39 * List of cached interface names
40 */
41 linked_list_t *iface_cache;
e0d3014a
MW
42
43 /**
44 * DNS attribute handler
45 */
46 updown_handler_t *handler;
49653b6b
MW
47};
48
49typedef struct cache_entry_t cache_entry_t;
50
51/**
52 * Cache line in the interface name cache.
53 */
54struct cache_entry_t {
55 /** requid of the CHILD_SA */
56 u_int32_t reqid;
57 /** cached interface name */
58 char *iface;
59};
60
61/**
62 * Insert an interface name to the cache
63 */
64static void cache_iface(private_updown_listener_t *this, u_int32_t reqid,
65 char *iface)
66{
67 cache_entry_t *entry = malloc_thing(cache_entry_t);
7daf5226 68
49653b6b
MW
69 entry->reqid = reqid;
70 entry->iface = strdup(iface);
7daf5226 71
49653b6b
MW
72 this->iface_cache->insert_first(this->iface_cache, entry);
73}
74
75/**
76 * Remove a cached interface name and return it.
77 */
78static char* uncache_iface(private_updown_listener_t *this, u_int32_t reqid)
79{
80 enumerator_t *enumerator;
81 cache_entry_t *entry;
82 char *iface = NULL;
7daf5226 83
49653b6b
MW
84 enumerator = this->iface_cache->create_enumerator(this->iface_cache);
85 while (enumerator->enumerate(enumerator, &entry))
86 {
87 if (entry->reqid == reqid)
88 {
89 this->iface_cache->remove_at(this->iface_cache, enumerator);
90 iface = entry->iface;
91 free(entry);
92 break;
93 }
94 }
95 enumerator->destroy(enumerator);
96 return iface;
97}
98
e0d3014a
MW
99/**
100 * Create variables for handled DNS attributes
101 */
102static char *make_dns_vars(private_updown_listener_t *this, ike_sa_t *ike_sa)
103{
104 enumerator_t *enumerator;
105 host_t *host;
106 int v4 = 0, v6 = 0;
107 char total[512] = "", current[64];
108
109 if (!this->handler)
110 {
111 return strdup("");
112 }
113
114 enumerator = this->handler->create_dns_enumerator(this->handler,
115 ike_sa->get_unique_id(ike_sa));
116 while (enumerator->enumerate(enumerator, &host))
117 {
118 switch (host->get_family(host))
119 {
120 case AF_INET:
121 snprintf(current, sizeof(current),
122 "PLUTO_DNS4_%d='%H' ", ++v4, host);
123 break;
124 case AF_INET6:
125 snprintf(current, sizeof(current),
126 "PLUTO_DNS6_%d='%H' ", ++v6, host);
127 break;
128 default:
129 continue;
130 }
131 strncat(total, current, sizeof(total) - strlen(total) - 1);
132 }
133 enumerator->destroy(enumerator);
134
135 return strdup(total);
136}
137
101d26ba
MW
138/**
139 * Create variables for local virtual IPs
140 */
141static char *make_vip_vars(private_updown_listener_t *this, ike_sa_t *ike_sa)
142{
143 enumerator_t *enumerator;
144 host_t *host;
145 int v4 = 0, v6 = 0;
146 char total[512] = "", current[64];
147 bool first = TRUE;
148
149 enumerator = ike_sa->create_virtual_ip_enumerator(ike_sa, TRUE);
150 while (enumerator->enumerate(enumerator, &host))
151 {
152 if (first)
153 { /* legacy variable for first VIP */
154 snprintf(current, sizeof(current),
155 "PLUTO_MY_SOURCEIP='%H' ", host);
156 strncat(total, current, sizeof(total) - strlen(total) - 1);
157 }
158 switch (host->get_family(host))
159 {
160 case AF_INET:
161 snprintf(current, sizeof(current),
162 "PLUTO_MY_SOURCEIP4_%d='%H' ", ++v4, host);
163 break;
164 case AF_INET6:
165 snprintf(current, sizeof(current),
166 "PLUTO_MY_SOURCEIP6_%d='%H' ", ++v6, host);
167 break;
168 default:
169 continue;
170 }
171 strncat(total, current, sizeof(total) - strlen(total) - 1);
172 }
173 enumerator->destroy(enumerator);
174
175 return strdup(total);
176}
177
9739a0bf
TB
178/**
179 * Determine proper values for port env variable
180 */
181static u_int16_t get_port(traffic_selector_t *me,
182 traffic_selector_t *other, bool local)
183{
184 switch (max(me->get_protocol(me), other->get_protocol(other)))
185 {
186 case IPPROTO_ICMP:
187 case IPPROTO_ICMPV6:
188 {
189 u_int16_t port = me->get_from_port(me);
190
191 port = max(port, other->get_from_port(other));
192 return local ? traffic_selector_icmp_type(port)
193 : traffic_selector_icmp_code(port);
194 }
195 }
196 return local ? me->get_from_port(me) : other->get_from_port(other);
197}
198
7481f964
MW
199METHOD(listener_t, child_updown, bool,
200 private_updown_listener_t *this, ike_sa_t *ike_sa, child_sa_t *child_sa,
201 bool up)
49653b6b
MW
202{
203 traffic_selector_t *my_ts, *other_ts;
204 enumerator_t *enumerator;
205 child_cfg_t *config;
101d26ba 206 host_t *me, *other;
49653b6b 207 char *script;
7daf5226 208
49653b6b 209 config = child_sa->get_config(child_sa);
49653b6b
MW
210 script = config->get_updown(config);
211 me = ike_sa->get_my_host(ike_sa);
212 other = ike_sa->get_other_host(ike_sa);
7daf5226 213
49653b6b
MW
214 if (script == NULL)
215 {
7481f964 216 return TRUE;
49653b6b 217 }
7daf5226 218
49653b6b
MW
219 enumerator = child_sa->create_policy_enumerator(child_sa);
220 while (enumerator->enumerate(enumerator, &my_ts, &other_ts))
221 {
7e3bbcf7 222 char command[2048];
a8d0bd74
AS
223 host_t *my_client, *other_client;
224 u_int8_t my_client_mask, other_client_mask;
5b89e3b0 225 char *virtual_ip, *iface, *mark_in, *mark_out, *udp_enc, *dns, *xauth;
14665981 226 mark_t mark;
cf4a7395 227 bool is_host, is_ipv6, use_ipcomp;
49653b6b
MW
228 FILE *shell;
229
a8d0bd74
AS
230 my_ts->to_subnet(my_ts, &my_client, &my_client_mask);
231 other_ts->to_subnet(other_ts, &other_client, &other_client_mask);
49653b6b 232
101d26ba 233 virtual_ip = make_vip_vars(this, ike_sa);
7daf5226 234
14665981
AS
235 /* check for the presence of an inbound mark */
236 mark = config->get_mark(config, TRUE);
237 if (mark.value)
238 {
239 if (asprintf(&mark_in, "PLUTO_MARK_IN='%u/0x%08x' ",
240 mark.value, mark.mask ) < 0)
241 {
242 mark_in = NULL;
243 }
244 }
245 else
246 {
247 if (asprintf(&mark_in, "") < 0)
248 {
249 mark_in = NULL;
250 }
251 }
252
253 /* check for the presence of an outbound mark */
254 mark = config->get_mark(config, FALSE);
255 if (mark.value)
256 {
257 if (asprintf(&mark_out, "PLUTO_MARK_OUT='%u/0x%08x' ",
258 mark.value, mark.mask ) < 0)
259 {
260 mark_out = NULL;
261 }
262 }
263 else
264 {
265 if (asprintf(&mark_out, "") < 0)
266 {
267 mark_out = NULL;
268 }
269 }
270
ae0e3b03
AS
271 /* check for a NAT condition causing ESP_IN_UDP encapsulation */
272 if (ike_sa->has_condition(ike_sa, COND_NAT_ANY))
273 {
274 if (asprintf(&udp_enc, "PLUTO_UDP_ENC='%u' ",
275 other->get_port(other)) < 0)
276 {
277 udp_enc = NULL;
278 }
279
280 }
281 else
282 {
283 if (asprintf(&udp_enc, "") < 0)
284 {
285 udp_enc = NULL;
286 }
287
288 }
289
5b89e3b0
MW
290 if (ike_sa->has_condition(ike_sa, COND_EAP_AUTHENTICATED) ||
291 ike_sa->has_condition(ike_sa, COND_XAUTH_AUTHENTICATED))
292 {
293 if (asprintf(&xauth, "PLUTO_XAUTH_ID='%Y' ",
294 ike_sa->get_other_eap_id(ike_sa)) < 0)
295 {
296 xauth = NULL;
297 }
298 }
299 else
300 {
301 if (asprintf(&xauth, "") < 0)
302 {
303 xauth = NULL;
304 }
305 }
306
49653b6b
MW
307 if (up)
308 {
9ba36c0f
TB
309 if (hydra->kernel_interface->get_interface(hydra->kernel_interface,
310 me, &iface))
49653b6b
MW
311 {
312 cache_iface(this, child_sa->get_reqid(child_sa), iface);
313 }
43347356
AA
314 else
315 {
316 iface = NULL;
317 }
49653b6b
MW
318 }
319 else
320 {
321 iface = uncache_iface(this, child_sa->get_reqid(child_sa));
322 }
7daf5226 323
e0d3014a
MW
324 dns = make_dns_vars(this, ike_sa);
325
cf4a7395
TB
326 /* check for IPComp */
327 use_ipcomp = child_sa->get_ipcomp(child_sa) != IPCOMP_NONE;
328
9789d3a9
AS
329 /* determine IPv4/IPv6 and client/host situation */
330 is_host = my_ts->is_host(my_ts, me);
331 is_ipv6 = is_host ? (me->get_family(me) == AF_INET6) :
332 (my_ts->get_type(my_ts) == TS_IPV6_ADDR_RANGE);
333
49653b6b 334 /* build the command with all env variables.
49653b6b
MW
335 */
336 snprintf(command, sizeof(command),
337 "2>&1 "
338 "PLUTO_VERSION='1.1' "
339 "PLUTO_VERB='%s%s%s' "
340 "PLUTO_CONNECTION='%s' "
341 "PLUTO_INTERFACE='%s' "
342 "PLUTO_REQID='%u' "
c5d9b133 343 "PLUTO_PROTO='%s' "
2ce40343 344 "PLUTO_UNIQUEID='%u' "
49653b6b 345 "PLUTO_ME='%H' "
d24a74c5 346 "PLUTO_MY_ID='%Y' "
a8d0bd74 347 "PLUTO_MY_CLIENT='%H/%u' "
49653b6b
MW
348 "PLUTO_MY_PORT='%u' "
349 "PLUTO_MY_PROTOCOL='%u' "
350 "PLUTO_PEER='%H' "
d24a74c5 351 "PLUTO_PEER_ID='%Y' "
a8d0bd74 352 "PLUTO_PEER_CLIENT='%H/%u' "
49653b6b
MW
353 "PLUTO_PEER_PORT='%u' "
354 "PLUTO_PEER_PROTOCOL='%u' "
355 "%s"
356 "%s"
14665981
AS
357 "%s"
358 "%s"
ae0e3b03 359 "%s"
e0d3014a 360 "%s"
5b89e3b0 361 "%s"
cf4a7395 362 "%s"
49653b6b
MW
363 "%s",
364 up ? "up" : "down",
9789d3a9
AS
365 is_host ? "-host" : "-client",
366 is_ipv6 ? "-v6" : "",
49653b6b
MW
367 config->get_name(config),
368 iface ? iface : "unknown",
369 child_sa->get_reqid(child_sa),
c5d9b133 370 child_sa->get_protocol(child_sa) == PROTO_ESP ? "esp" : "ah",
2ce40343 371 ike_sa->get_unique_id(ike_sa),
49653b6b
MW
372 me, ike_sa->get_my_id(ike_sa),
373 my_client, my_client_mask,
9739a0bf 374 get_port(my_ts, other_ts, TRUE),
49653b6b
MW
375 my_ts->get_protocol(my_ts),
376 other, ike_sa->get_other_id(ike_sa),
377 other_client, other_client_mask,
9739a0bf 378 get_port(my_ts, other_ts, FALSE),
49653b6b 379 other_ts->get_protocol(other_ts),
5b89e3b0 380 xauth,
49653b6b 381 virtual_ip,
14665981
AS
382 mark_in,
383 mark_out,
ae0e3b03 384 udp_enc,
cf4a7395 385 use_ipcomp ? "PLUTO_IPCOMP='1' " : "",
49653b6b 386 config->get_hostaccess(config) ? "PLUTO_HOST_ACCESS='1' " : "",
e0d3014a 387 dns,
49653b6b 388 script);
145a8b04
AS
389 my_client->destroy(my_client);
390 other_client->destroy(other_client);
49653b6b 391 free(virtual_ip);
14665981
AS
392 free(mark_in);
393 free(mark_out);
ae0e3b03 394 free(udp_enc);
e0d3014a 395 free(dns);
49653b6b 396 free(iface);
5b89e3b0 397 free(xauth);
7daf5226 398
49653b6b
MW
399 DBG3(DBG_CHD, "running updown script: %s", command);
400 shell = popen(command, "r");
401
402 if (shell == NULL)
403 {
404 DBG1(DBG_CHD, "could not execute updown script '%s'", script);
7481f964 405 return TRUE;
49653b6b 406 }
7daf5226 407
49653b6b
MW
408 while (TRUE)
409 {
410 char resp[128];
7daf5226 411
49653b6b
MW
412 if (fgets(resp, sizeof(resp), shell) == NULL)
413 {
414 if (ferror(shell))
415 {
416 DBG1(DBG_CHD, "error reading output from updown script");
49653b6b 417 }
7481f964 418 break;
49653b6b
MW
419 }
420 else
421 {
422 char *e = resp + strlen(resp);
423 if (e > resp && e[-1] == '\n')
424 { /* trim trailing '\n' */
425 e[-1] = '\0';
426 }
427 DBG1(DBG_CHD, "updown: %s", resp);
428 }
429 }
430 pclose(shell);
431 }
432 enumerator->destroy(enumerator);
49653b6b
MW
433 return TRUE;
434}
435
7481f964
MW
436METHOD(updown_listener_t, destroy, void,
437 private_updown_listener_t *this)
49653b6b
MW
438{
439 this->iface_cache->destroy(this->iface_cache);
440 free(this);
441}
442
443/**
444 * See header
445 */
e0d3014a 446updown_listener_t *updown_listener_create(updown_handler_t *handler)
49653b6b 447{
7481f964
MW
448 private_updown_listener_t *this;
449
450 INIT(this,
451 .public = {
6f52d3b0
TB
452 .listener = {
453 .child_updown = _child_updown,
454 },
7481f964
MW
455 .destroy = _destroy,
456 },
457 .iface_cache = linked_list_create(),
e0d3014a 458 .handler = handler,
7481f964 459 );
7daf5226 460
49653b6b
MW
461 return &this->public;
462}