]>
Commit | Line | Data |
---|---|---|
49653b6b | 1 | /* |
cf4a7395 | 2 | * Copyright (C) 2013 Tobias Brunner |
49653b6b | 3 | * Copyright (C) 2008 Martin Willi |
0d7202c7 AS |
4 | * Copyright (C) 2016 Andreas Steffen |
5 | * HSR Hochschule fuer Technik Rapperswil | |
49653b6b MW |
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. | |
49653b6b MW |
16 | */ |
17 | ||
18 | #define _GNU_SOURCE | |
19 | #include <stdio.h> | |
6890bdc7 | 20 | #include <unistd.h> |
49653b6b MW |
21 | |
22 | #include "updown_listener.h" | |
23 | ||
6890bdc7 | 24 | #include <utils/process.h> |
49653b6b MW |
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 { | |
7daf5226 | 34 | |
49653b6b MW |
35 | /** |
36 | * Public updown_listener_t interface. | |
37 | */ | |
38 | updown_listener_t public; | |
7daf5226 | 39 | |
49653b6b MW |
40 | /** |
41 | * List of cached interface names | |
42 | */ | |
43 | linked_list_t *iface_cache; | |
e0d3014a MW |
44 | |
45 | /** | |
46 | * DNS attribute handler | |
47 | */ | |
48 | updown_handler_t *handler; | |
49653b6b MW |
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 */ | |
b12c53ce | 58 | uint32_t reqid; |
49653b6b MW |
59 | /** cached interface name */ |
60 | char *iface; | |
61 | }; | |
62 | ||
63 | /** | |
64 | * Insert an interface name to the cache | |
65 | */ | |
b12c53ce | 66 | static void cache_iface(private_updown_listener_t *this, uint32_t reqid, |
49653b6b MW |
67 | char *iface) |
68 | { | |
69 | cache_entry_t *entry = malloc_thing(cache_entry_t); | |
7daf5226 | 70 | |
49653b6b MW |
71 | entry->reqid = reqid; |
72 | entry->iface = strdup(iface); | |
7daf5226 | 73 | |
49653b6b MW |
74 | this->iface_cache->insert_first(this->iface_cache, entry); |
75 | } | |
76 | ||
77 | /** | |
78 | * Remove a cached interface name and return it. | |
79 | */ | |
b12c53ce | 80 | static char* uncache_iface(private_updown_listener_t *this, uint32_t reqid) |
49653b6b MW |
81 | { |
82 | enumerator_t *enumerator; | |
83 | cache_entry_t *entry; | |
84 | char *iface = NULL; | |
7daf5226 | 85 | |
49653b6b MW |
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 | ||
e0d3014a | 101 | /** |
6890bdc7 | 102 | * Allocate and push a format string to the environment |
e0d3014a | 103 | */ |
6890bdc7 | 104 | static bool push_env(char *envp[], u_int count, char *fmt, ...) |
e0d3014a | 105 | { |
6890bdc7 MW |
106 | int i = 0; |
107 | char *str; | |
108 | va_list args; | |
e0d3014a | 109 | |
6890bdc7 | 110 | while (envp[i]) |
e0d3014a | 111 | { |
6890bdc7 MW |
112 | if (++i + 1 >= count) |
113 | { | |
114 | return FALSE; | |
115 | } | |
e0d3014a | 116 | } |
6890bdc7 MW |
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 | } | |
e0d3014a | 125 | |
6890bdc7 MW |
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++) | |
e0d3014a | 134 | { |
6890bdc7 MW |
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)) | |
e0d3014a | 154 | { |
6890bdc7 MW |
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 | } | |
e0d3014a | 166 | } |
6890bdc7 | 167 | enumerator->destroy(enumerator); |
e0d3014a | 168 | } |
e0d3014a MW |
169 | } |
170 | ||
101d26ba | 171 | /** |
1de31bcc | 172 | * Push variables for local/remote virtual IPs |
101d26ba | 173 | */ |
6890bdc7 | 174 | static void push_vip_env(private_updown_listener_t *this, ike_sa_t *ike_sa, |
1de31bcc | 175 | char *envp[], u_int count, bool local) |
101d26ba MW |
176 | { |
177 | enumerator_t *enumerator; | |
178 | host_t *host; | |
179 | int v4 = 0, v6 = 0; | |
101d26ba MW |
180 | bool first = TRUE; |
181 | ||
1de31bcc | 182 | enumerator = ike_sa->create_virtual_ip_enumerator(ike_sa, local); |
101d26ba MW |
183 | while (enumerator->enumerate(enumerator, &host)) |
184 | { | |
185 | if (first) | |
186 | { /* legacy variable for first VIP */ | |
6890bdc7 | 187 | first = FALSE; |
1de31bcc TB |
188 | push_env(envp, count, "PLUTO_%s_SOURCEIP=%H", |
189 | local ? "MY" : "PEER", host); | |
101d26ba MW |
190 | } |
191 | switch (host->get_family(host)) | |
192 | { | |
193 | case AF_INET: | |
1de31bcc TB |
194 | push_env(envp, count, "PLUTO_%s_SOURCEIP4_%d=%H", |
195 | local ? "MY" : "PEER", ++v4, host); | |
101d26ba MW |
196 | break; |
197 | case AF_INET6: | |
1de31bcc TB |
198 | push_env(envp, count, "PLUTO_%s_SOURCEIP6_%d=%H", |
199 | local ? "MY" : "PEER", ++v6, host); | |
101d26ba MW |
200 | break; |
201 | default: | |
202 | continue; | |
203 | } | |
101d26ba MW |
204 | } |
205 | enumerator->destroy(enumerator); | |
101d26ba MW |
206 | } |
207 | ||
0d7202c7 AS |
208 | #define PORT_BUF_LEN 12 |
209 | ||
9739a0bf TB |
210 | /** |
211 | * Determine proper values for port env variable | |
212 | */ | |
0d7202c7 AS |
213 | static char* get_port(traffic_selector_t *me, traffic_selector_t *other, |
214 | char *port_buf, bool local) | |
9739a0bf | 215 | { |
0d7202c7 AS |
216 | uint16_t port, to, from; |
217 | ||
9739a0bf TB |
218 | switch (max(me->get_protocol(me), other->get_protocol(other))) |
219 | { | |
220 | case IPPROTO_ICMP: | |
221 | case IPPROTO_ICMPV6: | |
222 | { | |
0d7202c7 AS |
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; | |
9739a0bf TB |
228 | } |
229 | } | |
0d7202c7 AS |
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 | } | |
ad82c95f | 240 | if (from == to || (from == 0 && to == 65535)) |
0d7202c7 AS |
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; | |
9739a0bf TB |
249 | } |
250 | ||
6890bdc7 MW |
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) | |
49653b6b | 257 | { |
6890bdc7 MW |
258 | host_t *me, *other, *host; |
259 | char *iface; | |
b12c53ce | 260 | uint8_t mask; |
3fa8c3e5 | 261 | uint32_t if_id; |
6890bdc7 MW |
262 | mark_t mark; |
263 | bool is_host, is_ipv6; | |
264 | int out; | |
265 | FILE *shell; | |
266 | process_t *process; | |
0d7202c7 | 267 | char port_buf[PORT_BUF_LEN]; |
6890bdc7 | 268 | char *envp[128] = {}; |
7daf5226 | 269 | |
49653b6b MW |
270 | me = ike_sa->get_my_host(ike_sa); |
271 | other = ike_sa->get_other_host(ike_sa); | |
7daf5226 | 272 | |
4736ba06 | 273 | push_env(envp, countof(envp), "PATH=%s", getenv("PATH")); |
6890bdc7 MW |
274 | push_env(envp, countof(envp), "PLUTO_VERSION=1.1"); |
275 | is_host = my_ts->is_host(my_ts, me); | |
276 | if (is_host) | |
49653b6b | 277 | { |
6890bdc7 | 278 | is_ipv6 = me->get_family(me) == AF_INET6; |
49653b6b | 279 | } |
6890bdc7 | 280 | else |
49653b6b | 281 | { |
6890bdc7 MW |
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 | { | |
8394ea2a | 292 | if (charon->kernel->get_interface(charon->kernel, me, &iface)) |
ae0e3b03 | 293 | { |
6890bdc7 | 294 | cache_iface(this, child_sa->get_reqid(child_sa), iface); |
ae0e3b03 AS |
295 | } |
296 | else | |
297 | { | |
6890bdc7 | 298 | iface = NULL; |
ae0e3b03 | 299 | } |
6890bdc7 MW |
300 | } |
301 | else | |
302 | { | |
303 | iface = uncache_iface(this, child_sa->get_reqid(child_sa)); | |
304 | } | |
305 | push_env(envp, countof(envp), "PLUTO_INTERFACE=%s", | |
306 | iface ? iface : "unknown"); | |
307 | push_env(envp, countof(envp), "PLUTO_REQID=%u", | |
308 | child_sa->get_reqid(child_sa)); | |
309 | push_env(envp, countof(envp), "PLUTO_PROTO=%s", | |
310 | child_sa->get_protocol(child_sa) == PROTO_ESP ? "esp" : "ah"); | |
311 | push_env(envp, countof(envp), "PLUTO_UNIQUEID=%u", | |
312 | ike_sa->get_unique_id(ike_sa)); | |
313 | push_env(envp, countof(envp), "PLUTO_ME=%H", me); | |
314 | push_env(envp, countof(envp), "PLUTO_MY_ID=%Y", ike_sa->get_my_id(ike_sa)); | |
3f1de986 | 315 | if (!my_ts->to_subnet(my_ts, &host, &mask)) |
6890bdc7 | 316 | { |
3f1de986 AS |
317 | DBG1(DBG_CHD, "updown approximates local TS %R " |
318 | "by next larger subnet", my_ts); | |
6890bdc7 | 319 | } |
3f1de986 AS |
320 | push_env(envp, countof(envp), "PLUTO_MY_CLIENT=%+H/%u", host, mask); |
321 | host->destroy(host); | |
0d7202c7 AS |
322 | push_env(envp, countof(envp), "PLUTO_MY_PORT=%s", |
323 | get_port(my_ts, other_ts, port_buf, TRUE)); | |
6890bdc7 MW |
324 | push_env(envp, countof(envp), "PLUTO_MY_PROTOCOL=%u", |
325 | my_ts->get_protocol(my_ts)); | |
326 | push_env(envp, countof(envp), "PLUTO_PEER=%H", other); | |
327 | push_env(envp, countof(envp), "PLUTO_PEER_ID=%Y", | |
328 | ike_sa->get_other_id(ike_sa)); | |
3f1de986 | 329 | if (!other_ts->to_subnet(other_ts, &host, &mask)) |
6890bdc7 | 330 | { |
3f1de986 AS |
331 | DBG1(DBG_CHD, "updown approximates remote TS %R " |
332 | "by next larger subnet", other_ts); | |
6890bdc7 | 333 | } |
3f1de986 AS |
334 | push_env(envp, countof(envp), "PLUTO_PEER_CLIENT=%+H/%u", host, mask); |
335 | host->destroy(host); | |
0d7202c7 AS |
336 | push_env(envp, countof(envp), "PLUTO_PEER_PORT=%s", |
337 | get_port(my_ts, other_ts, port_buf, FALSE)); | |
6890bdc7 MW |
338 | push_env(envp, countof(envp), "PLUTO_PEER_PROTOCOL=%u", |
339 | other_ts->get_protocol(other_ts)); | |
340 | if (ike_sa->has_condition(ike_sa, COND_EAP_AUTHENTICATED) || | |
341 | ike_sa->has_condition(ike_sa, COND_XAUTH_AUTHENTICATED)) | |
342 | { | |
343 | push_env(envp, countof(envp), "PLUTO_XAUTH_ID=%Y", | |
344 | ike_sa->get_other_eap_id(ike_sa)); | |
345 | } | |
1de31bcc TB |
346 | push_vip_env(this, ike_sa, envp, countof(envp), TRUE); |
347 | push_vip_env(this, ike_sa, envp, countof(envp), FALSE); | |
b2103693 | 348 | mark = child_sa->get_mark(child_sa, TRUE); |
6890bdc7 MW |
349 | if (mark.value) |
350 | { | |
351 | push_env(envp, countof(envp), "PLUTO_MARK_IN=%u/0x%08x", | |
352 | mark.value, mark.mask); | |
353 | } | |
b2103693 | 354 | mark = child_sa->get_mark(child_sa, FALSE); |
6890bdc7 MW |
355 | if (mark.value) |
356 | { | |
357 | push_env(envp, countof(envp), "PLUTO_MARK_OUT=%u/0x%08x", | |
358 | mark.value, mark.mask); | |
359 | } | |
3fa8c3e5 TB |
360 | if_id = child_sa->get_if_id(child_sa, TRUE); |
361 | if (if_id) | |
362 | { | |
363 | push_env(envp, countof(envp), "PLUTO_IF_ID_IN=%u", if_id); | |
364 | } | |
365 | if_id = child_sa->get_if_id(child_sa, FALSE); | |
366 | if (if_id) | |
367 | { | |
368 | push_env(envp, countof(envp), "PLUTO_IF_ID_OUT=%u", if_id); | |
369 | } | |
6890bdc7 MW |
370 | if (ike_sa->has_condition(ike_sa, COND_NAT_ANY)) |
371 | { | |
372 | push_env(envp, countof(envp), "PLUTO_UDP_ENC=%u", | |
373 | other->get_port(other)); | |
374 | } | |
375 | if (child_sa->get_ipcomp(child_sa) != IPCOMP_NONE) | |
376 | { | |
377 | push_env(envp, countof(envp), "PLUTO_IPCOMP=1"); | |
378 | } | |
379 | push_dns_env(this, ike_sa, envp, countof(envp)); | |
749ac175 | 380 | if (config->has_option(config, OPT_HOSTACCESS)) |
6890bdc7 MW |
381 | { |
382 | push_env(envp, countof(envp), "PLUTO_HOST_ACCESS=1"); | |
383 | } | |
ae0e3b03 | 384 | |
6890bdc7 MW |
385 | process = process_start_shell(envp, NULL, &out, NULL, "2>&1 %s", |
386 | config->get_updown(config)); | |
387 | if (process) | |
388 | { | |
389 | shell = fdopen(out, "r"); | |
390 | if (shell) | |
5b89e3b0 | 391 | { |
6890bdc7 | 392 | while (TRUE) |
5b89e3b0 | 393 | { |
6890bdc7 | 394 | char resp[128]; |
5b89e3b0 | 395 | |
6890bdc7 MW |
396 | if (fgets(resp, sizeof(resp), shell) == NULL) |
397 | { | |
398 | if (ferror(shell)) | |
399 | { | |
400 | DBG1(DBG_CHD, "error reading from updown script"); | |
401 | } | |
402 | break; | |
403 | } | |
404 | else | |
405 | { | |
406 | char *e = resp + strlen(resp); | |
407 | if (e > resp && e[-1] == '\n') | |
408 | { | |
409 | e[-1] = '\0'; | |
410 | } | |
411 | DBG1(DBG_CHD, "updown: %s", resp); | |
412 | } | |
43347356 | 413 | } |
6890bdc7 | 414 | fclose(shell); |
49653b6b MW |
415 | } |
416 | else | |
417 | { | |
6890bdc7 | 418 | close(out); |
49653b6b | 419 | } |
6890bdc7 MW |
420 | process->wait(process, NULL); |
421 | } | |
422 | free(iface); | |
423 | free_env(envp); | |
424 | } | |
7daf5226 | 425 | |
6890bdc7 MW |
426 | METHOD(listener_t, child_updown, bool, |
427 | private_updown_listener_t *this, ike_sa_t *ike_sa, child_sa_t *child_sa, | |
428 | bool up) | |
429 | { | |
430 | traffic_selector_t *my_ts, *other_ts; | |
431 | enumerator_t *enumerator; | |
432 | child_cfg_t *config; | |
7daf5226 | 433 | |
6890bdc7 MW |
434 | config = child_sa->get_config(child_sa); |
435 | if (config->get_updown(config)) | |
436 | { | |
437 | enumerator = child_sa->create_policy_enumerator(child_sa); | |
438 | while (enumerator->enumerate(enumerator, &my_ts, &other_ts)) | |
49653b6b | 439 | { |
6890bdc7 | 440 | invoke_once(this, ike_sa, child_sa, config, up, my_ts, other_ts); |
49653b6b | 441 | } |
6890bdc7 | 442 | enumerator->destroy(enumerator); |
49653b6b | 443 | } |
49653b6b MW |
444 | return TRUE; |
445 | } | |
446 | ||
7481f964 MW |
447 | METHOD(updown_listener_t, destroy, void, |
448 | private_updown_listener_t *this) | |
49653b6b MW |
449 | { |
450 | this->iface_cache->destroy(this->iface_cache); | |
451 | free(this); | |
452 | } | |
453 | ||
454 | /** | |
455 | * See header | |
456 | */ | |
e0d3014a | 457 | updown_listener_t *updown_listener_create(updown_handler_t *handler) |
49653b6b | 458 | { |
7481f964 MW |
459 | private_updown_listener_t *this; |
460 | ||
461 | INIT(this, | |
462 | .public = { | |
6f52d3b0 TB |
463 | .listener = { |
464 | .child_updown = _child_updown, | |
465 | }, | |
7481f964 MW |
466 | .destroy = _destroy, |
467 | }, | |
468 | .iface_cache = linked_list_create(), | |
e0d3014a | 469 | .handler = handler, |
7481f964 | 470 | ); |
7daf5226 | 471 | |
49653b6b MW |
472 | return &this->public; |
473 | } |