]>
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> | |
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 | ||
26 | typedef struct private_updown_listener_t private_updown_listener_t; | |
27 | ||
28 | /** | |
29 | * Private data of an updown_listener_t object. | |
30 | */ | |
31 | struct 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 | ||
49 | typedef struct cache_entry_t cache_entry_t; | |
50 | ||
51 | /** | |
52 | * Cache line in the interface name cache. | |
53 | */ | |
54 | struct 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 | */ | |
64 | static 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 | */ | |
78 | static 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 | */ | |
102 | static 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 | */ | |
141 | static 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 | */ | |
181 | static 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 |
199 | METHOD(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 |
436 | METHOD(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 | 446 | updown_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 | } |