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