]>
Commit | Line | Data |
---|---|---|
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> | |
6890bdc7 | 19 | #include <unistd.h> |
49653b6b MW |
20 | |
21 | #include "updown_listener.h" | |
22 | ||
6890bdc7 | 23 | #include <utils/process.h> |
f6659688 | 24 | #include <hydra.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 */ | |
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); | |
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 | */ | |
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; | |
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 | /** |
6890bdc7 | 172 | * Push variables for local virtual IPs |
101d26ba | 173 | */ |
6890bdc7 MW |
174 | static void push_vip_env(private_updown_listener_t *this, ike_sa_t *ike_sa, |
175 | char *envp[], u_int count) | |
101d26ba MW |
176 | { |
177 | enumerator_t *enumerator; | |
178 | host_t *host; | |
179 | int v4 = 0, v6 = 0; | |
101d26ba MW |
180 | bool first = TRUE; |
181 | ||
182 | enumerator = ike_sa->create_virtual_ip_enumerator(ike_sa, TRUE); | |
183 | while (enumerator->enumerate(enumerator, &host)) | |
184 | { | |
185 | if (first) | |
186 | { /* legacy variable for first VIP */ | |
6890bdc7 MW |
187 | first = FALSE; |
188 | push_env(envp, count, "PLUTO_MY_SOURCEIP=%H", host); | |
101d26ba MW |
189 | } |
190 | switch (host->get_family(host)) | |
191 | { | |
192 | case AF_INET: | |
6890bdc7 | 193 | push_env(envp, count, "PLUTO_MY_SOURCEIP4_%d=%H", ++v4, host); |
101d26ba MW |
194 | break; |
195 | case AF_INET6: | |
6890bdc7 | 196 | push_env(envp, count, "PLUTO_MY_SOURCEIP6_%d=%H", ++v6, host); |
101d26ba MW |
197 | break; |
198 | default: | |
199 | continue; | |
200 | } | |
101d26ba MW |
201 | } |
202 | enumerator->destroy(enumerator); | |
101d26ba MW |
203 | } |
204 | ||
9739a0bf TB |
205 | /** |
206 | * Determine proper values for port env variable | |
207 | */ | |
208 | static u_int16_t get_port(traffic_selector_t *me, | |
209 | traffic_selector_t *other, bool local) | |
210 | { | |
211 | switch (max(me->get_protocol(me), other->get_protocol(other))) | |
212 | { | |
213 | case IPPROTO_ICMP: | |
214 | case IPPROTO_ICMPV6: | |
215 | { | |
216 | u_int16_t port = me->get_from_port(me); | |
217 | ||
218 | port = max(port, other->get_from_port(other)); | |
219 | return local ? traffic_selector_icmp_type(port) | |
220 | : traffic_selector_icmp_code(port); | |
221 | } | |
222 | } | |
223 | return local ? me->get_from_port(me) : other->get_from_port(other); | |
224 | } | |
225 | ||
6890bdc7 MW |
226 | /** |
227 | * Invoke the updown script once for given traffic selectors | |
228 | */ | |
229 | static void invoke_once(private_updown_listener_t *this, ike_sa_t *ike_sa, | |
230 | child_sa_t *child_sa, child_cfg_t *config, bool up, | |
231 | traffic_selector_t *my_ts, traffic_selector_t *other_ts) | |
49653b6b | 232 | { |
6890bdc7 MW |
233 | host_t *me, *other, *host; |
234 | char *iface; | |
235 | u_int8_t mask; | |
236 | mark_t mark; | |
237 | bool is_host, is_ipv6; | |
238 | int out; | |
239 | FILE *shell; | |
240 | process_t *process; | |
241 | char *envp[128] = {}; | |
7daf5226 | 242 | |
49653b6b MW |
243 | me = ike_sa->get_my_host(ike_sa); |
244 | other = ike_sa->get_other_host(ike_sa); | |
7daf5226 | 245 | |
6890bdc7 MW |
246 | push_env(envp, countof(envp), "PLUTO_VERSION=1.1"); |
247 | is_host = my_ts->is_host(my_ts, me); | |
248 | if (is_host) | |
49653b6b | 249 | { |
6890bdc7 | 250 | is_ipv6 = me->get_family(me) == AF_INET6; |
49653b6b | 251 | } |
6890bdc7 | 252 | else |
49653b6b | 253 | { |
6890bdc7 MW |
254 | is_ipv6 = my_ts->get_type(my_ts) == TS_IPV6_ADDR_RANGE; |
255 | } | |
256 | push_env(envp, countof(envp), "PLUTO_VERB=%s%s%s", | |
257 | up ? "up" : "down", | |
258 | is_host ? "-host" : "-client", | |
259 | is_ipv6 ? "-v6" : ""); | |
260 | push_env(envp, countof(envp), "PLUTO_CONNECTION=%s", | |
261 | config->get_name(config)); | |
262 | if (up) | |
263 | { | |
264 | if (hydra->kernel_interface->get_interface(hydra->kernel_interface, | |
265 | me, &iface)) | |
ae0e3b03 | 266 | { |
6890bdc7 | 267 | cache_iface(this, child_sa->get_reqid(child_sa), iface); |
ae0e3b03 AS |
268 | } |
269 | else | |
270 | { | |
6890bdc7 | 271 | iface = NULL; |
ae0e3b03 | 272 | } |
6890bdc7 MW |
273 | } |
274 | else | |
275 | { | |
276 | iface = uncache_iface(this, child_sa->get_reqid(child_sa)); | |
277 | } | |
278 | push_env(envp, countof(envp), "PLUTO_INTERFACE=%s", | |
279 | iface ? iface : "unknown"); | |
280 | push_env(envp, countof(envp), "PLUTO_REQID=%u", | |
281 | child_sa->get_reqid(child_sa)); | |
282 | push_env(envp, countof(envp), "PLUTO_PROTO=%s", | |
283 | child_sa->get_protocol(child_sa) == PROTO_ESP ? "esp" : "ah"); | |
284 | push_env(envp, countof(envp), "PLUTO_UNIQUEID=%u", | |
285 | ike_sa->get_unique_id(ike_sa)); | |
286 | push_env(envp, countof(envp), "PLUTO_ME=%H", me); | |
287 | push_env(envp, countof(envp), "PLUTO_MY_ID=%Y", ike_sa->get_my_id(ike_sa)); | |
288 | if (my_ts->to_subnet(my_ts, &host, &mask)) | |
289 | { | |
290 | push_env(envp, countof(envp), "PLUTO_MY_CLIENT=%+H/%u", host, mask); | |
291 | host->destroy(host); | |
292 | } | |
293 | push_env(envp, countof(envp), "PLUTO_MY_PORT=%u", | |
294 | get_port(my_ts, other_ts, TRUE)); | |
295 | push_env(envp, countof(envp), "PLUTO_MY_PROTOCOL=%u", | |
296 | my_ts->get_protocol(my_ts)); | |
297 | push_env(envp, countof(envp), "PLUTO_PEER=%H", other); | |
298 | push_env(envp, countof(envp), "PLUTO_PEER_ID=%Y", | |
299 | ike_sa->get_other_id(ike_sa)); | |
300 | if (other_ts->to_subnet(other_ts, &host, &mask)) | |
301 | { | |
302 | push_env(envp, countof(envp), "PLUTO_PEER_CLIENT=%+H/%u", host, mask); | |
303 | host->destroy(host); | |
304 | } | |
305 | push_env(envp, countof(envp), "PLUTO_PEER_PORT=%u", | |
306 | get_port(my_ts, other_ts, FALSE)); | |
307 | push_env(envp, countof(envp), "PLUTO_PEER_PROTOCOL=%u", | |
308 | other_ts->get_protocol(other_ts)); | |
309 | if (ike_sa->has_condition(ike_sa, COND_EAP_AUTHENTICATED) || | |
310 | ike_sa->has_condition(ike_sa, COND_XAUTH_AUTHENTICATED)) | |
311 | { | |
312 | push_env(envp, countof(envp), "PLUTO_XAUTH_ID=%Y", | |
313 | ike_sa->get_other_eap_id(ike_sa)); | |
314 | } | |
315 | push_vip_env(this, ike_sa, envp, countof(envp)); | |
316 | mark = config->get_mark(config, TRUE); | |
317 | if (mark.value) | |
318 | { | |
319 | push_env(envp, countof(envp), "PLUTO_MARK_IN=%u/0x%08x", | |
320 | mark.value, mark.mask); | |
321 | } | |
322 | mark = config->get_mark(config, FALSE); | |
323 | if (mark.value) | |
324 | { | |
325 | push_env(envp, countof(envp), "PLUTO_MARK_OUT=%u/0x%08x", | |
326 | mark.value, mark.mask); | |
327 | } | |
328 | if (ike_sa->has_condition(ike_sa, COND_NAT_ANY)) | |
329 | { | |
330 | push_env(envp, countof(envp), "PLUTO_UDP_ENC=%u", | |
331 | other->get_port(other)); | |
332 | } | |
333 | if (child_sa->get_ipcomp(child_sa) != IPCOMP_NONE) | |
334 | { | |
335 | push_env(envp, countof(envp), "PLUTO_IPCOMP=1"); | |
336 | } | |
337 | push_dns_env(this, ike_sa, envp, countof(envp)); | |
338 | if (config->get_hostaccess(config)) | |
339 | { | |
340 | push_env(envp, countof(envp), "PLUTO_HOST_ACCESS=1"); | |
341 | } | |
ae0e3b03 | 342 | |
6890bdc7 MW |
343 | process = process_start_shell(envp, NULL, &out, NULL, "2>&1 %s", |
344 | config->get_updown(config)); | |
345 | if (process) | |
346 | { | |
347 | shell = fdopen(out, "r"); | |
348 | if (shell) | |
5b89e3b0 | 349 | { |
6890bdc7 | 350 | while (TRUE) |
5b89e3b0 | 351 | { |
6890bdc7 | 352 | char resp[128]; |
5b89e3b0 | 353 | |
6890bdc7 MW |
354 | if (fgets(resp, sizeof(resp), shell) == NULL) |
355 | { | |
356 | if (ferror(shell)) | |
357 | { | |
358 | DBG1(DBG_CHD, "error reading from updown script"); | |
359 | } | |
360 | break; | |
361 | } | |
362 | else | |
363 | { | |
364 | char *e = resp + strlen(resp); | |
365 | if (e > resp && e[-1] == '\n') | |
366 | { | |
367 | e[-1] = '\0'; | |
368 | } | |
369 | DBG1(DBG_CHD, "updown: %s", resp); | |
370 | } | |
43347356 | 371 | } |
6890bdc7 | 372 | fclose(shell); |
49653b6b MW |
373 | } |
374 | else | |
375 | { | |
6890bdc7 | 376 | close(out); |
49653b6b | 377 | } |
6890bdc7 MW |
378 | process->wait(process, NULL); |
379 | } | |
380 | free(iface); | |
381 | free_env(envp); | |
382 | } | |
7daf5226 | 383 | |
6890bdc7 MW |
384 | METHOD(listener_t, child_updown, bool, |
385 | private_updown_listener_t *this, ike_sa_t *ike_sa, child_sa_t *child_sa, | |
386 | bool up) | |
387 | { | |
388 | traffic_selector_t *my_ts, *other_ts; | |
389 | enumerator_t *enumerator; | |
390 | child_cfg_t *config; | |
7daf5226 | 391 | |
6890bdc7 MW |
392 | config = child_sa->get_config(child_sa); |
393 | if (config->get_updown(config)) | |
394 | { | |
395 | enumerator = child_sa->create_policy_enumerator(child_sa); | |
396 | while (enumerator->enumerate(enumerator, &my_ts, &other_ts)) | |
49653b6b | 397 | { |
6890bdc7 | 398 | invoke_once(this, ike_sa, child_sa, config, up, my_ts, other_ts); |
49653b6b | 399 | } |
6890bdc7 | 400 | enumerator->destroy(enumerator); |
49653b6b | 401 | } |
49653b6b MW |
402 | return TRUE; |
403 | } | |
404 | ||
7481f964 MW |
405 | METHOD(updown_listener_t, destroy, void, |
406 | private_updown_listener_t *this) | |
49653b6b MW |
407 | { |
408 | this->iface_cache->destroy(this->iface_cache); | |
409 | free(this); | |
410 | } | |
411 | ||
412 | /** | |
413 | * See header | |
414 | */ | |
e0d3014a | 415 | updown_listener_t *updown_listener_create(updown_handler_t *handler) |
49653b6b | 416 | { |
7481f964 MW |
417 | private_updown_listener_t *this; |
418 | ||
419 | INIT(this, | |
420 | .public = { | |
6f52d3b0 TB |
421 | .listener = { |
422 | .child_updown = _child_updown, | |
423 | }, | |
7481f964 MW |
424 | .destroy = _destroy, |
425 | }, | |
426 | .iface_cache = linked_list_create(), | |
e0d3014a | 427 | .handler = handler, |
7481f964 | 428 | ); |
7daf5226 | 429 | |
49653b6b MW |
430 | return &this->public; |
431 | } |