96282bee07eadf2624e25d07ef39e5a43bae06c9
[strongswan.git] / src / libcharon / plugins / updown / updown_listener.c
1 /*
2 * Copyright (C) 2013 Tobias Brunner
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.
15 */
16
17 #define _GNU_SOURCE
18 #include <stdio.h>
19 #include <unistd.h>
20
21 #include "updown_listener.h"
22
23 #include <utils/process.h>
24 #include <hydra.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 {
57 /** requid of the CHILD_SA */
58 u_int32_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, u_int32_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, u_int32_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 /**
209 * Determine proper values for port env variable
210 */
211 static u_int16_t get_port(traffic_selector_t *me,
212 traffic_selector_t *other, bool local)
213 {
214 switch (max(me->get_protocol(me), other->get_protocol(other)))
215 {
216 case IPPROTO_ICMP:
217 case IPPROTO_ICMPV6:
218 {
219 u_int16_t port = me->get_from_port(me);
220
221 port = max(port, other->get_from_port(other));
222 return local ? traffic_selector_icmp_type(port)
223 : traffic_selector_icmp_code(port);
224 }
225 }
226 return local ? me->get_from_port(me) : other->get_from_port(other);
227 }
228
229 /**
230 * Invoke the updown script once for given traffic selectors
231 */
232 static void invoke_once(private_updown_listener_t *this, ike_sa_t *ike_sa,
233 child_sa_t *child_sa, child_cfg_t *config, bool up,
234 traffic_selector_t *my_ts, traffic_selector_t *other_ts)
235 {
236 host_t *me, *other, *host;
237 char *iface;
238 u_int8_t mask;
239 mark_t mark;
240 bool is_host, is_ipv6;
241 int out;
242 FILE *shell;
243 process_t *process;
244 char *envp[128] = {};
245
246 me = ike_sa->get_my_host(ike_sa);
247 other = ike_sa->get_other_host(ike_sa);
248
249 push_env(envp, countof(envp), "PATH=%s", getenv("PATH"));
250 push_env(envp, countof(envp), "PLUTO_VERSION=1.1");
251 is_host = my_ts->is_host(my_ts, me);
252 if (is_host)
253 {
254 is_ipv6 = me->get_family(me) == AF_INET6;
255 }
256 else
257 {
258 is_ipv6 = my_ts->get_type(my_ts) == TS_IPV6_ADDR_RANGE;
259 }
260 push_env(envp, countof(envp), "PLUTO_VERB=%s%s%s",
261 up ? "up" : "down",
262 is_host ? "-host" : "-client",
263 is_ipv6 ? "-v6" : "");
264 push_env(envp, countof(envp), "PLUTO_CONNECTION=%s",
265 config->get_name(config));
266 if (up)
267 {
268 if (hydra->kernel_interface->get_interface(hydra->kernel_interface,
269 me, &iface))
270 {
271 cache_iface(this, child_sa->get_reqid(child_sa), iface);
272 }
273 else
274 {
275 iface = NULL;
276 }
277 }
278 else
279 {
280 iface = uncache_iface(this, child_sa->get_reqid(child_sa));
281 }
282 push_env(envp, countof(envp), "PLUTO_INTERFACE=%s",
283 iface ? iface : "unknown");
284 push_env(envp, countof(envp), "PLUTO_REQID=%u",
285 child_sa->get_reqid(child_sa));
286 push_env(envp, countof(envp), "PLUTO_PROTO=%s",
287 child_sa->get_protocol(child_sa) == PROTO_ESP ? "esp" : "ah");
288 push_env(envp, countof(envp), "PLUTO_UNIQUEID=%u",
289 ike_sa->get_unique_id(ike_sa));
290 push_env(envp, countof(envp), "PLUTO_ME=%H", me);
291 push_env(envp, countof(envp), "PLUTO_MY_ID=%Y", ike_sa->get_my_id(ike_sa));
292 if (my_ts->to_subnet(my_ts, &host, &mask))
293 {
294 push_env(envp, countof(envp), "PLUTO_MY_CLIENT=%+H/%u", host, mask);
295 host->destroy(host);
296 }
297 push_env(envp, countof(envp), "PLUTO_MY_PORT=%u",
298 get_port(my_ts, other_ts, TRUE));
299 push_env(envp, countof(envp), "PLUTO_MY_PROTOCOL=%u",
300 my_ts->get_protocol(my_ts));
301 push_env(envp, countof(envp), "PLUTO_PEER=%H", other);
302 push_env(envp, countof(envp), "PLUTO_PEER_ID=%Y",
303 ike_sa->get_other_id(ike_sa));
304 if (other_ts->to_subnet(other_ts, &host, &mask))
305 {
306 push_env(envp, countof(envp), "PLUTO_PEER_CLIENT=%+H/%u", host, mask);
307 host->destroy(host);
308 }
309 push_env(envp, countof(envp), "PLUTO_PEER_PORT=%u",
310 get_port(my_ts, other_ts, FALSE));
311 push_env(envp, countof(envp), "PLUTO_PEER_PROTOCOL=%u",
312 other_ts->get_protocol(other_ts));
313 if (ike_sa->has_condition(ike_sa, COND_EAP_AUTHENTICATED) ||
314 ike_sa->has_condition(ike_sa, COND_XAUTH_AUTHENTICATED))
315 {
316 push_env(envp, countof(envp), "PLUTO_XAUTH_ID=%Y",
317 ike_sa->get_other_eap_id(ike_sa));
318 }
319 push_vip_env(this, ike_sa, envp, countof(envp), TRUE);
320 push_vip_env(this, ike_sa, envp, countof(envp), FALSE);
321 mark = config->get_mark(config, TRUE);
322 if (mark.value)
323 {
324 push_env(envp, countof(envp), "PLUTO_MARK_IN=%u/0x%08x",
325 mark.value, mark.mask);
326 }
327 mark = config->get_mark(config, FALSE);
328 if (mark.value)
329 {
330 push_env(envp, countof(envp), "PLUTO_MARK_OUT=%u/0x%08x",
331 mark.value, mark.mask);
332 }
333 if (ike_sa->has_condition(ike_sa, COND_NAT_ANY))
334 {
335 push_env(envp, countof(envp), "PLUTO_UDP_ENC=%u",
336 other->get_port(other));
337 }
338 if (child_sa->get_ipcomp(child_sa) != IPCOMP_NONE)
339 {
340 push_env(envp, countof(envp), "PLUTO_IPCOMP=1");
341 }
342 push_dns_env(this, ike_sa, envp, countof(envp));
343 if (config->get_hostaccess(config))
344 {
345 push_env(envp, countof(envp), "PLUTO_HOST_ACCESS=1");
346 }
347
348 process = process_start_shell(envp, NULL, &out, NULL, "2>&1 %s",
349 config->get_updown(config));
350 if (process)
351 {
352 shell = fdopen(out, "r");
353 if (shell)
354 {
355 while (TRUE)
356 {
357 char resp[128];
358
359 if (fgets(resp, sizeof(resp), shell) == NULL)
360 {
361 if (ferror(shell))
362 {
363 DBG1(DBG_CHD, "error reading from updown script");
364 }
365 break;
366 }
367 else
368 {
369 char *e = resp + strlen(resp);
370 if (e > resp && e[-1] == '\n')
371 {
372 e[-1] = '\0';
373 }
374 DBG1(DBG_CHD, "updown: %s", resp);
375 }
376 }
377 fclose(shell);
378 }
379 else
380 {
381 close(out);
382 }
383 process->wait(process, NULL);
384 }
385 free(iface);
386 free_env(envp);
387 }
388
389 METHOD(listener_t, child_updown, bool,
390 private_updown_listener_t *this, ike_sa_t *ike_sa, child_sa_t *child_sa,
391 bool up)
392 {
393 traffic_selector_t *my_ts, *other_ts;
394 enumerator_t *enumerator;
395 child_cfg_t *config;
396
397 config = child_sa->get_config(child_sa);
398 if (config->get_updown(config))
399 {
400 enumerator = child_sa->create_policy_enumerator(child_sa);
401 while (enumerator->enumerate(enumerator, &my_ts, &other_ts))
402 {
403 invoke_once(this, ike_sa, child_sa, config, up, my_ts, other_ts);
404 }
405 enumerator->destroy(enumerator);
406 }
407 return TRUE;
408 }
409
410 METHOD(updown_listener_t, destroy, void,
411 private_updown_listener_t *this)
412 {
413 this->iface_cache->destroy(this->iface_cache);
414 free(this);
415 }
416
417 /**
418 * See header
419 */
420 updown_listener_t *updown_listener_create(updown_handler_t *handler)
421 {
422 private_updown_listener_t *this;
423
424 INIT(this,
425 .public = {
426 .listener = {
427 .child_updown = _child_updown,
428 },
429 .destroy = _destroy,
430 },
431 .iface_cache = linked_list_create(),
432 .handler = handler,
433 );
434
435 return &this->public;
436 }