]>
Commit | Line | Data |
---|---|---|
1 | /** | |
2 | * @file child_sa.c | |
3 | * | |
4 | * @brief Implementation of child_sa_t. | |
5 | * | |
6 | */ | |
7 | ||
8 | /* | |
9 | * Copyright (C) 2005-2007 Martin Willi | |
10 | * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger | |
11 | * Copyright (C) 2005 Jan Hutter | |
12 | * Hochschule fuer Technik Rapperswil | |
13 | * | |
14 | * This program is free software; you can redistribute it and/or modify it | |
15 | * under the terms of the GNU General Public License as published by the | |
16 | * Free Software Foundation; either version 2 of the License, or (at your | |
17 | * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. | |
18 | * | |
19 | * This program is distributed in the hope that it will be useful, but | |
20 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | |
21 | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
22 | * for more details. | |
23 | */ | |
24 | ||
25 | #define _GNU_SOURCE | |
26 | #include "child_sa.h" | |
27 | ||
28 | #include <stdio.h> | |
29 | #include <string.h> | |
30 | #include <printf.h> | |
31 | ||
32 | #include <daemon.h> | |
33 | ||
34 | ENUM(child_sa_state_names, CHILD_CREATED, CHILD_DELETING, | |
35 | "CREATED", | |
36 | "ROUTED", | |
37 | "INSTALLED", | |
38 | "REKEYING", | |
39 | "DELETING", | |
40 | ); | |
41 | ||
42 | typedef struct sa_policy_t sa_policy_t; | |
43 | ||
44 | /** | |
45 | * Struct used to store information for a policy. This | |
46 | * is needed since we must provide all this information | |
47 | * for deleting a policy... | |
48 | */ | |
49 | struct sa_policy_t { | |
50 | /** | |
51 | * Traffic selector for us | |
52 | */ | |
53 | traffic_selector_t *my_ts; | |
54 | ||
55 | /** | |
56 | * Traffic selector for other | |
57 | */ | |
58 | traffic_selector_t *other_ts; | |
59 | }; | |
60 | ||
61 | typedef struct private_child_sa_t private_child_sa_t; | |
62 | ||
63 | /** | |
64 | * Private data of a child_sa_t \ 1bject. | |
65 | */ | |
66 | struct private_child_sa_t { | |
67 | /** | |
68 | * Public interface of child_sa_t. | |
69 | */ | |
70 | child_sa_t public; | |
71 | ||
72 | struct { | |
73 | /** address of peer */ | |
74 | host_t *addr; | |
75 | /** id of peer */ | |
76 | identification_t *id; | |
77 | /** actual used SPI, 0 if unused */ | |
78 | u_int32_t spi; | |
79 | } me, other; | |
80 | ||
81 | /** | |
82 | * Allocated SPI for a ESP proposal candidates | |
83 | */ | |
84 | u_int32_t alloc_esp_spi; | |
85 | ||
86 | /** | |
87 | * Allocated SPI for a AH proposal candidates | |
88 | */ | |
89 | u_int32_t alloc_ah_spi; | |
90 | ||
91 | /** | |
92 | * Protocol used to protect this SA, ESP|AH | |
93 | */ | |
94 | protocol_id_t protocol; | |
95 | ||
96 | /** | |
97 | * List containing sa_policy_t objects | |
98 | */ | |
99 | linked_list_t *policies; | |
100 | ||
101 | /** | |
102 | * Seperate list for local traffic selectors | |
103 | */ | |
104 | linked_list_t *my_ts; | |
105 | ||
106 | /** | |
107 | * Seperate list for remote traffic selectors | |
108 | */ | |
109 | linked_list_t *other_ts; | |
110 | ||
111 | /** | |
112 | * reqid used for this child_sa | |
113 | */ | |
114 | u_int32_t reqid; | |
115 | ||
116 | /** | |
117 | * encryption algorithm used for this SA | |
118 | */ | |
119 | algorithm_t encryption; | |
120 | ||
121 | /** | |
122 | * integrity protection algorithm used for this SA | |
123 | */ | |
124 | algorithm_t integrity; | |
125 | ||
126 | /** | |
127 | * time, on which SA was installed | |
128 | */ | |
129 | time_t install_time; | |
130 | ||
131 | /** | |
132 | * absolute time when rekeying is sceduled | |
133 | */ | |
134 | time_t rekey_time; | |
135 | ||
136 | /** | |
137 | * state of the CHILD_SA | |
138 | */ | |
139 | child_sa_state_t state; | |
140 | ||
141 | /** | |
142 | * Specifies if NAT traversal is used | |
143 | */ | |
144 | bool use_natt; | |
145 | ||
146 | /** | |
147 | * mode this SA uses, tunnel/transport | |
148 | */ | |
149 | mode_t mode; | |
150 | ||
151 | /** | |
152 | * virtual IP assinged to local host | |
153 | */ | |
154 | host_t *virtual_ip; | |
155 | ||
156 | /** | |
157 | * config used to create this child | |
158 | */ | |
159 | child_cfg_t *config; | |
160 | }; | |
161 | ||
162 | /** | |
163 | * Implementation of child_sa_t.get_name. | |
164 | */ | |
165 | static char *get_name(private_child_sa_t *this) | |
166 | { | |
167 | return this->config->get_name(this->config); | |
168 | } | |
169 | ||
170 | /** | |
171 | * Implements child_sa_t.get_reqid | |
172 | */ | |
173 | static u_int32_t get_reqid(private_child_sa_t *this) | |
174 | { | |
175 | return this->reqid; | |
176 | } | |
177 | ||
178 | /** | |
179 | * Implements child_sa_t.get_spi | |
180 | */ | |
181 | u_int32_t get_spi(private_child_sa_t *this, bool inbound) | |
182 | { | |
183 | if (inbound) | |
184 | { | |
185 | return this->me.spi; | |
186 | } | |
187 | return this->other.spi; | |
188 | } | |
189 | ||
190 | /** | |
191 | * Implements child_sa_t.get_protocol | |
192 | */ | |
193 | protocol_id_t get_protocol(private_child_sa_t *this) | |
194 | { | |
195 | return this->protocol; | |
196 | } | |
197 | ||
198 | /** | |
199 | * Implements child_sa_t.get_state | |
200 | */ | |
201 | static child_sa_state_t get_state(private_child_sa_t *this) | |
202 | { | |
203 | return this->state; | |
204 | } | |
205 | ||
206 | /** | |
207 | * Implements child_sa_t.get_config | |
208 | */ | |
209 | static child_cfg_t* get_config(private_child_sa_t *this) | |
210 | { | |
211 | return this->config; | |
212 | } | |
213 | ||
214 | /** | |
215 | * Run the up/down script | |
216 | */ | |
217 | static void updown(private_child_sa_t *this, bool up) | |
218 | { | |
219 | sa_policy_t *policy; | |
220 | iterator_t *iterator; | |
221 | char *script; | |
222 | ||
223 | script = this->config->get_updown(this->config); | |
224 | ||
225 | if (script == NULL) | |
226 | { | |
227 | return; | |
228 | } | |
229 | ||
230 | iterator = this->policies->create_iterator(this->policies, TRUE); | |
231 | while (iterator->iterate(iterator, (void**)&policy)) | |
232 | { | |
233 | char command[1024]; | |
234 | char *ifname = NULL; | |
235 | char *my_client, *other_client, *my_client_mask, *other_client_mask; | |
236 | char *pos, *virtual_ip; | |
237 | FILE *shell; | |
238 | ||
239 | /* get subnet/bits from string */ | |
240 | asprintf(&my_client, "%R", policy->my_ts); | |
241 | pos = strchr(my_client, '/'); | |
242 | *pos = '\0'; | |
243 | my_client_mask = pos + 1; | |
244 | pos = strchr(my_client_mask, '['); | |
245 | if (pos) | |
246 | { | |
247 | *pos = '\0'; | |
248 | } | |
249 | asprintf(&other_client, "%R", policy->other_ts); | |
250 | pos = strchr(other_client, '/'); | |
251 | *pos = '\0'; | |
252 | other_client_mask = pos + 1; | |
253 | pos = strchr(other_client_mask, '['); | |
254 | if (pos) | |
255 | { | |
256 | *pos = '\0'; | |
257 | } | |
258 | ||
259 | if (this->virtual_ip) | |
260 | { | |
261 | asprintf(&virtual_ip, "PLUTO_MY_SOURCEIP='%H' ", | |
262 | this->virtual_ip); | |
263 | } | |
264 | else | |
265 | { | |
266 | asprintf(&virtual_ip, ""); | |
267 | } | |
268 | ||
269 | ifname = charon->kernel_interface->get_interface(charon->kernel_interface, | |
270 | this->me.addr); | |
271 | ||
272 | /* build the command with all env variables. | |
273 | * TODO: PLUTO_PEER_CA and PLUTO_NEXT_HOP are currently missing | |
274 | */ | |
275 | snprintf(command, sizeof(command), | |
276 | "2>&1 " | |
277 | "PLUTO_VERSION='1.1' " | |
278 | "PLUTO_VERB='%s%s%s' " | |
279 | "PLUTO_CONNECTION='%s' " | |
280 | "PLUTO_INTERFACE='%s' " | |
281 | "PLUTO_REQID='%u' " | |
282 | "PLUTO_ME='%H' " | |
283 | "PLUTO_MY_ID='%D' " | |
284 | "PLUTO_MY_CLIENT='%s/%s' " | |
285 | "PLUTO_MY_CLIENT_NET='%s' " | |
286 | "PLUTO_MY_CLIENT_MASK='%s' " | |
287 | "PLUTO_MY_PORT='%u' " | |
288 | "PLUTO_MY_PROTOCOL='%u' " | |
289 | "PLUTO_PEER='%H' " | |
290 | "PLUTO_PEER_ID='%D' " | |
291 | "PLUTO_PEER_CLIENT='%s/%s' " | |
292 | "PLUTO_PEER_CLIENT_NET='%s' " | |
293 | "PLUTO_PEER_CLIENT_MASK='%s' " | |
294 | "PLUTO_PEER_PORT='%u' " | |
295 | "PLUTO_PEER_PROTOCOL='%u' " | |
296 | "%s" | |
297 | "%s" | |
298 | "%s", | |
299 | up ? "up" : "down", | |
300 | policy->my_ts->is_host(policy->my_ts, | |
301 | this->me.addr) ? "-host" : "-client", | |
302 | this->me.addr->get_family(this->me.addr) == AF_INET ? "" : "-ipv6", | |
303 | this->config->get_name(this->config), | |
304 | ifname ? ifname : "(unknown)", | |
305 | this->reqid, | |
306 | this->me.addr, | |
307 | this->me.id, | |
308 | my_client, my_client_mask, | |
309 | my_client, my_client_mask, | |
310 | policy->my_ts->get_from_port(policy->my_ts), | |
311 | policy->my_ts->get_protocol(policy->my_ts), | |
312 | this->other.addr, | |
313 | this->other.id, | |
314 | other_client, other_client_mask, | |
315 | other_client, other_client_mask, | |
316 | policy->other_ts->get_from_port(policy->other_ts), | |
317 | policy->other_ts->get_protocol(policy->other_ts), | |
318 | virtual_ip, | |
319 | this->config->get_hostaccess(this->config) ? | |
320 | "PLUTO_HOST_ACCESS='1' " : "", | |
321 | script); | |
322 | free(ifname); | |
323 | free(my_client); | |
324 | free(other_client); | |
325 | free(virtual_ip); | |
326 | ||
327 | shell = popen(command, "r"); | |
328 | ||
329 | if (shell == NULL) | |
330 | { | |
331 | DBG1(DBG_CHD, "could not execute updown script '%s'", script); | |
332 | return; | |
333 | } | |
334 | ||
335 | while (TRUE) | |
336 | { | |
337 | char resp[128]; | |
338 | ||
339 | if (fgets(resp, sizeof(resp), shell) == NULL) | |
340 | { | |
341 | if (ferror(shell)) | |
342 | { | |
343 | DBG1(DBG_CHD, "error reading output from updown script"); | |
344 | return; | |
345 | } | |
346 | else | |
347 | { | |
348 | break; | |
349 | } | |
350 | } | |
351 | else | |
352 | { | |
353 | char *e = resp + strlen(resp); | |
354 | if (e > resp && e[-1] == '\n') | |
355 | { /* trim trailing '\n' */ | |
356 | e[-1] = '\0'; | |
357 | } | |
358 | DBG1(DBG_CHD, "updown: %s", resp); | |
359 | } | |
360 | } | |
361 | pclose(shell); | |
362 | } | |
363 | iterator->destroy(iterator); | |
364 | } | |
365 | ||
366 | /** | |
367 | * Implements child_sa_t.set_state | |
368 | */ | |
369 | static void set_state(private_child_sa_t *this, child_sa_state_t state) | |
370 | { | |
371 | this->state = state; | |
372 | if (state == CHILD_INSTALLED) | |
373 | { | |
374 | updown(this, TRUE); | |
375 | } | |
376 | } | |
377 | ||
378 | /** | |
379 | * Allocate SPI for a single proposal | |
380 | */ | |
381 | static status_t alloc_proposal(private_child_sa_t *this, proposal_t *proposal) | |
382 | { | |
383 | protocol_id_t protocol = proposal->get_protocol(proposal); | |
384 | ||
385 | if (protocol == PROTO_AH) | |
386 | { | |
387 | /* get a new spi for AH, if not already done */ | |
388 | if (this->alloc_ah_spi == 0) | |
389 | { | |
390 | if (charon->kernel_interface->get_spi( | |
391 | charon->kernel_interface, | |
392 | this->other.addr, this->me.addr, | |
393 | PROTO_AH, this->reqid, | |
394 | &this->alloc_ah_spi) != SUCCESS) | |
395 | { | |
396 | return FAILED; | |
397 | } | |
398 | } | |
399 | proposal->set_spi(proposal, this->alloc_ah_spi); | |
400 | } | |
401 | if (protocol == PROTO_ESP) | |
402 | { | |
403 | /* get a new spi for ESP, if not already done */ | |
404 | if (this->alloc_esp_spi == 0) | |
405 | { | |
406 | if (charon->kernel_interface->get_spi( | |
407 | charon->kernel_interface, | |
408 | this->other.addr, this->me.addr, | |
409 | PROTO_ESP, this->reqid, | |
410 | &this->alloc_esp_spi) != SUCCESS) | |
411 | { | |
412 | return FAILED; | |
413 | } | |
414 | } | |
415 | proposal->set_spi(proposal, this->alloc_esp_spi); | |
416 | } | |
417 | return SUCCESS; | |
418 | } | |
419 | ||
420 | ||
421 | /** | |
422 | * Implements child_sa_t.alloc | |
423 | */ | |
424 | static status_t alloc(private_child_sa_t *this, linked_list_t *proposals) | |
425 | { | |
426 | iterator_t *iterator; | |
427 | proposal_t *proposal; | |
428 | ||
429 | /* iterator through proposals to update spis */ | |
430 | iterator = proposals->create_iterator(proposals, TRUE); | |
431 | while(iterator->iterate(iterator, (void**)&proposal)) | |
432 | { | |
433 | if (alloc_proposal(this, proposal) != SUCCESS) | |
434 | { | |
435 | iterator->destroy(iterator); | |
436 | return FAILED; | |
437 | } | |
438 | } | |
439 | iterator->destroy(iterator); | |
440 | return SUCCESS; | |
441 | } | |
442 | ||
443 | static status_t install(private_child_sa_t *this, proposal_t *proposal, | |
444 | mode_t mode, prf_plus_t *prf_plus, bool mine) | |
445 | { | |
446 | u_int32_t spi, soft, hard;; | |
447 | algorithm_t *enc_algo, *int_algo; | |
448 | algorithm_t enc_algo_none = {ENCR_UNDEFINED, 0}; | |
449 | algorithm_t int_algo_none = {AUTH_UNDEFINED, 0}; | |
450 | host_t *src; | |
451 | host_t *dst; | |
452 | natt_conf_t *natt; | |
453 | status_t status; | |
454 | ||
455 | this->protocol = proposal->get_protocol(proposal); | |
456 | ||
457 | /* now we have to decide which spi to use. Use self allocated, if "mine", | |
458 | * or the one in the proposal, if not "mine" (others). Additionally, | |
459 | * source and dest host switch depending on the role */ | |
460 | if (mine) | |
461 | { | |
462 | /* if we have allocated SPIs for AH and ESP, we must delete the unused | |
463 | * one. */ | |
464 | if (this->protocol == PROTO_ESP) | |
465 | { | |
466 | this->me.spi = this->alloc_esp_spi; | |
467 | if (this->alloc_ah_spi) | |
468 | { | |
469 | charon->kernel_interface->del_sa(charon->kernel_interface, this->me.addr, | |
470 | this->alloc_ah_spi, PROTO_AH); | |
471 | } | |
472 | } | |
473 | else | |
474 | { | |
475 | this->me.spi = this->alloc_ah_spi; | |
476 | if (this->alloc_esp_spi) | |
477 | { | |
478 | charon->kernel_interface->del_sa(charon->kernel_interface, this->me.addr, | |
479 | this->alloc_esp_spi, PROTO_ESP); | |
480 | } | |
481 | } | |
482 | spi = this->me.spi; | |
483 | dst = this->me.addr; | |
484 | src = this->other.addr; | |
485 | } | |
486 | else | |
487 | { | |
488 | this->other.spi = proposal->get_spi(proposal); | |
489 | spi = this->other.spi; | |
490 | src = this->me.addr; | |
491 | dst = this->other.addr; | |
492 | } | |
493 | ||
494 | DBG2(DBG_CHD, "adding %s %N SA", mine ? "inbound" : "outbound", | |
495 | protocol_id_names, this->protocol); | |
496 | ||
497 | /* select encryption algo */ | |
498 | if (proposal->get_algorithm(proposal, ENCRYPTION_ALGORITHM, &enc_algo)) | |
499 | { | |
500 | DBG2(DBG_CHD, " using %N for encryption", | |
501 | encryption_algorithm_names, enc_algo->algorithm); | |
502 | } | |
503 | else | |
504 | { | |
505 | enc_algo = &enc_algo_none; | |
506 | } | |
507 | ||
508 | /* select integrity algo */ | |
509 | if (proposal->get_algorithm(proposal, INTEGRITY_ALGORITHM, &int_algo)) | |
510 | { | |
511 | DBG2(DBG_CHD, " using %N for integrity", | |
512 | integrity_algorithm_names, int_algo->algorithm); | |
513 | } | |
514 | else | |
515 | { | |
516 | int_algo = &int_algo_none; | |
517 | } | |
518 | ||
519 | /* setup nat-t */ | |
520 | if (this->use_natt) | |
521 | { | |
522 | natt = alloca(sizeof(natt_conf_t)); | |
523 | natt->sport = src->get_port(src); | |
524 | natt->dport = dst->get_port(dst); | |
525 | } | |
526 | else | |
527 | { | |
528 | natt = NULL; | |
529 | } | |
530 | ||
531 | soft = this->config->get_lifetime(this->config, TRUE); | |
532 | hard = this->config->get_lifetime(this->config, FALSE); | |
533 | ||
534 | /* send SA down to the kernel */ | |
535 | DBG2(DBG_CHD, " SPI 0x%.8x, src %H dst %H", ntohl(spi), src, dst); | |
536 | status = charon->kernel_interface->add_sa(charon->kernel_interface, | |
537 | src, dst, spi, this->protocol, | |
538 | this->reqid, mine ? soft : 0, | |
539 | hard, enc_algo, int_algo, | |
540 | prf_plus, natt, mode, mine); | |
541 | ||
542 | this->encryption = *enc_algo; | |
543 | this->integrity = *int_algo; | |
544 | this->install_time = time(NULL); | |
545 | this->rekey_time = soft; | |
546 | ||
547 | return status; | |
548 | } | |
549 | ||
550 | static status_t add(private_child_sa_t *this, proposal_t *proposal, | |
551 | mode_t mode, prf_plus_t *prf_plus) | |
552 | { | |
553 | u_int32_t outbound_spi, inbound_spi; | |
554 | ||
555 | /* backup outbound spi, as alloc overwrites it */ | |
556 | outbound_spi = proposal->get_spi(proposal); | |
557 | ||
558 | /* get SPIs inbound SAs */ | |
559 | if (alloc_proposal(this, proposal) != SUCCESS) | |
560 | { | |
561 | return FAILED; | |
562 | } | |
563 | inbound_spi = proposal->get_spi(proposal); | |
564 | ||
565 | /* install inbound SAs */ | |
566 | if (install(this, proposal, mode, prf_plus, TRUE) != SUCCESS) | |
567 | { | |
568 | return FAILED; | |
569 | } | |
570 | ||
571 | /* install outbound SAs, restore spi*/ | |
572 | proposal->set_spi(proposal, outbound_spi); | |
573 | if (install(this, proposal, mode, prf_plus, FALSE) != SUCCESS) | |
574 | { | |
575 | return FAILED; | |
576 | } | |
577 | proposal->set_spi(proposal, inbound_spi); | |
578 | ||
579 | return SUCCESS; | |
580 | } | |
581 | ||
582 | static status_t update(private_child_sa_t *this, proposal_t *proposal, | |
583 | mode_t mode, prf_plus_t *prf_plus) | |
584 | { | |
585 | u_int32_t inbound_spi; | |
586 | ||
587 | /* backup received spi, as install() overwrites it */ | |
588 | inbound_spi = proposal->get_spi(proposal); | |
589 | ||
590 | /* install outbound SAs */ | |
591 | if (install(this, proposal, mode, prf_plus, FALSE) != SUCCESS) | |
592 | { | |
593 | return FAILED; | |
594 | } | |
595 | ||
596 | /* restore spi */ | |
597 | proposal->set_spi(proposal, inbound_spi); | |
598 | /* install inbound SAs */ | |
599 | if (install(this, proposal, mode, prf_plus, TRUE) != SUCCESS) | |
600 | { | |
601 | return FAILED; | |
602 | } | |
603 | ||
604 | return SUCCESS; | |
605 | } | |
606 | ||
607 | static status_t add_policies(private_child_sa_t *this, | |
608 | linked_list_t *my_ts_list, | |
609 | linked_list_t *other_ts_list, mode_t mode) | |
610 | { | |
611 | iterator_t *my_iter, *other_iter; | |
612 | traffic_selector_t *my_ts, *other_ts; | |
613 | /* use low prio for ROUTED policies */ | |
614 | bool high_prio = (this->state != CHILD_CREATED); | |
615 | ||
616 | /* iterate over both lists */ | |
617 | my_iter = my_ts_list->create_iterator(my_ts_list, TRUE); | |
618 | other_iter = other_ts_list->create_iterator(other_ts_list, TRUE); | |
619 | while (my_iter->iterate(my_iter, (void**)&my_ts)) | |
620 | { | |
621 | other_iter->reset(other_iter); | |
622 | while (other_iter->iterate(other_iter, (void**)&other_ts)) | |
623 | { | |
624 | /* set up policies for every entry in my_ts_list to every entry in other_ts_list */ | |
625 | status_t status; | |
626 | sa_policy_t *policy; | |
627 | ||
628 | if (my_ts->get_type(my_ts) != other_ts->get_type(other_ts)) | |
629 | { | |
630 | DBG2(DBG_CHD, | |
631 | "CHILD_SA policy uses two different IP families, ignored"); | |
632 | continue; | |
633 | } | |
634 | ||
635 | /* only set up policies if protocol matches, or if one is zero (any) */ | |
636 | if (my_ts->get_protocol(my_ts) != other_ts->get_protocol(other_ts) && | |
637 | my_ts->get_protocol(my_ts) && other_ts->get_protocol(other_ts)) | |
638 | { | |
639 | DBG2(DBG_CHD, | |
640 | "CHILD_SA policy uses two different protocols, ignored"); | |
641 | continue; | |
642 | } | |
643 | ||
644 | /* install 3 policies: out, in and forward */ | |
645 | status = charon->kernel_interface->add_policy(charon->kernel_interface, | |
646 | this->me.addr, this->other.addr, my_ts, other_ts, POLICY_OUT, | |
647 | this->protocol, this->reqid, high_prio, mode, FALSE); | |
648 | ||
649 | status |= charon->kernel_interface->add_policy(charon->kernel_interface, | |
650 | this->other.addr, this->me.addr, other_ts, my_ts, POLICY_IN, | |
651 | this->protocol, this->reqid, high_prio, mode, FALSE); | |
652 | ||
653 | status |= charon->kernel_interface->add_policy(charon->kernel_interface, | |
654 | this->other.addr, this->me.addr, other_ts, my_ts, POLICY_FWD, | |
655 | this->protocol, this->reqid, high_prio, mode, FALSE); | |
656 | ||
657 | if (status != SUCCESS) | |
658 | { | |
659 | my_iter->destroy(my_iter); | |
660 | other_iter->destroy(other_iter); | |
661 | return status; | |
662 | } | |
663 | ||
664 | /* store policy to delete/update them later */ | |
665 | policy = malloc_thing(sa_policy_t); | |
666 | policy->my_ts = my_ts->clone(my_ts); | |
667 | policy->other_ts = other_ts->clone(other_ts); | |
668 | this->policies->insert_last(this->policies, policy); | |
669 | /* add to separate list to query them via get_*_traffic_selectors() */ | |
670 | this->my_ts->insert_last(this->my_ts, policy->my_ts); | |
671 | this->other_ts->insert_last(this->other_ts, policy->other_ts); | |
672 | } | |
673 | } | |
674 | my_iter->destroy(my_iter); | |
675 | other_iter->destroy(other_iter); | |
676 | ||
677 | /* switch to routed state if no SAD entry set up */ | |
678 | if (this->state == CHILD_CREATED) | |
679 | { | |
680 | this->state = CHILD_ROUTED; | |
681 | } | |
682 | /* needed to update hosts */ | |
683 | this->mode = mode; | |
684 | return SUCCESS; | |
685 | } | |
686 | ||
687 | /** | |
688 | * Implementation of child_sa_t.get_traffic_selectors. | |
689 | */ | |
690 | static linked_list_t *get_traffic_selectors(private_child_sa_t *this, bool local) | |
691 | { | |
692 | if (local) | |
693 | { | |
694 | return this->my_ts; | |
695 | } | |
696 | return this->other_ts; | |
697 | } | |
698 | ||
699 | /** | |
700 | * Implementation of child_sa_t.get_use_time | |
701 | */ | |
702 | static status_t get_use_time(private_child_sa_t *this, bool inbound, time_t *use_time) | |
703 | { | |
704 | iterator_t *iterator; | |
705 | sa_policy_t *policy; | |
706 | status_t status = FAILED; | |
707 | ||
708 | *use_time = UNDEFINED_TIME; | |
709 | ||
710 | iterator = this->policies->create_iterator(this->policies, TRUE); | |
711 | while (iterator->iterate(iterator, (void**)&policy)) | |
712 | { | |
713 | if (inbound) | |
714 | { | |
715 | time_t in = UNDEFINED_TIME, fwd = UNDEFINED_TIME; | |
716 | ||
717 | status = charon->kernel_interface->query_policy( | |
718 | charon->kernel_interface, | |
719 | policy->other_ts, policy->my_ts, | |
720 | POLICY_IN, (u_int32_t*)&in); | |
721 | status |= charon->kernel_interface->query_policy( | |
722 | charon->kernel_interface, | |
723 | policy->other_ts, policy->my_ts, | |
724 | POLICY_FWD, (u_int32_t*)&fwd); | |
725 | *use_time = max(in, fwd); | |
726 | } | |
727 | else | |
728 | { | |
729 | status = charon->kernel_interface->query_policy( | |
730 | charon->kernel_interface, | |
731 | policy->my_ts, policy->other_ts, | |
732 | POLICY_OUT, (u_int32_t*)use_time); | |
733 | } | |
734 | } | |
735 | iterator->destroy(iterator); | |
736 | return status; | |
737 | } | |
738 | ||
739 | /** | |
740 | * output handler in printf() | |
741 | */ | |
742 | static int print(FILE *stream, const struct printf_info *info, | |
743 | const void *const *args) | |
744 | { | |
745 | private_child_sa_t *this = *((private_child_sa_t**)(args[0])); | |
746 | iterator_t *iterator; | |
747 | sa_policy_t *policy; | |
748 | u_int32_t now, rekeying; | |
749 | u_int32_t use, use_in, use_fwd; | |
750 | status_t status; | |
751 | size_t written = 0; | |
752 | ||
753 | if (this == NULL) | |
754 | { | |
755 | return fprintf(stream, "(null)"); | |
756 | } | |
757 | ||
758 | now = time(NULL); | |
759 | ||
760 | written += fprintf(stream, "%12s{%d}: %N, %N", | |
761 | this->config->get_name(this->config), this->reqid, | |
762 | child_sa_state_names, this->state, | |
763 | mode_names, this->mode); | |
764 | ||
765 | if (this->state == CHILD_INSTALLED) | |
766 | { | |
767 | written += fprintf(stream, ", %N SPIs: 0x%0x_i 0x%0x_o", | |
768 | protocol_id_names, this->protocol, | |
769 | htonl(this->me.spi), htonl(this->other.spi)); | |
770 | ||
771 | if (info->alt) | |
772 | { | |
773 | written += fprintf(stream, "\n%12s{%d}: ", | |
774 | this->config->get_name(this->config), | |
775 | this->reqid); | |
776 | ||
777 | if (this->protocol == PROTO_ESP) | |
778 | { | |
779 | written += fprintf(stream, "%N", encryption_algorithm_names, | |
780 | this->encryption.algorithm); | |
781 | ||
782 | if (this->encryption.key_size) | |
783 | { | |
784 | written += fprintf(stream, "-%d", this->encryption.key_size); | |
785 | } | |
786 | written += fprintf(stream, "/"); | |
787 | } | |
788 | ||
789 | written += fprintf(stream, "%N", integrity_algorithm_names, | |
790 | this->integrity.algorithm); | |
791 | if (this->integrity.key_size) | |
792 | { | |
793 | written += fprintf(stream, "-%d", this->integrity.key_size); | |
794 | } | |
795 | written += fprintf(stream, ", rekeying "); | |
796 | ||
797 | /* calculate rekey times */ | |
798 | if (this->rekey_time) | |
799 | { | |
800 | rekeying = this->install_time + this->rekey_time - now; | |
801 | written += fprintf(stream, "in %ds", rekeying); | |
802 | } | |
803 | else | |
804 | { | |
805 | written += fprintf(stream, "disabled"); | |
806 | } | |
807 | } | |
808 | } | |
809 | iterator = this->policies->create_iterator(this->policies, TRUE); | |
810 | while (iterator->iterate(iterator, (void**)&policy)) | |
811 | { | |
812 | written += fprintf(stream, "\n%12s{%d}: %R===%R, last use: ", | |
813 | this->config->get_name(this->config), this->reqid, | |
814 | policy->my_ts, policy->other_ts); | |
815 | ||
816 | /* query time of last policy use */ | |
817 | ||
818 | /* inbound: POLICY_IN or POLICY_FWD */ | |
819 | status = charon->kernel_interface->query_policy(charon->kernel_interface, | |
820 | policy->other_ts, policy->my_ts, POLICY_IN, &use_in); | |
821 | use_in = (status == SUCCESS)? use_in : 0; | |
822 | status = charon->kernel_interface->query_policy(charon->kernel_interface, | |
823 | policy->other_ts, policy->my_ts, POLICY_FWD, &use_fwd); | |
824 | use_fwd = (status == SUCCESS)? use_fwd : 0; | |
825 | use = max(use_in, use_fwd); | |
826 | if (use) | |
827 | { | |
828 | written += fprintf(stream, "%ds_i ", now - use); | |
829 | } | |
830 | else | |
831 | { | |
832 | written += fprintf(stream, "no_i "); | |
833 | } | |
834 | ||
835 | /* outbound: POLICY_OUT */ | |
836 | status = charon->kernel_interface->query_policy(charon->kernel_interface, | |
837 | policy->my_ts, policy->other_ts, POLICY_OUT, &use); | |
838 | if (status == SUCCESS && use) | |
839 | { | |
840 | written += fprintf(stream, "%ds_o ", now - use); | |
841 | } | |
842 | else | |
843 | { | |
844 | written += fprintf(stream, "no_o "); | |
845 | } | |
846 | } | |
847 | iterator->destroy(iterator); | |
848 | return written; | |
849 | } | |
850 | ||
851 | /** | |
852 | * register printf() handlers | |
853 | */ | |
854 | static void __attribute__ ((constructor))print_register() | |
855 | { | |
856 | register_printf_function(PRINTF_CHILD_SA, print, arginfo_ptr); | |
857 | } | |
858 | ||
859 | /** | |
860 | * Update the host adress/port of a SA | |
861 | */ | |
862 | static status_t update_sa_hosts(private_child_sa_t *this, host_t *new_me, host_t *new_other, | |
863 | int my_changes, int other_changes, bool mine) | |
864 | { | |
865 | host_t *src, *dst, *new_src, *new_dst; | |
866 | int src_changes, dst_changes; | |
867 | status_t status; | |
868 | u_int32_t spi; | |
869 | ||
870 | if (mine) | |
871 | { | |
872 | src = this->other.addr; | |
873 | dst = this->me.addr; | |
874 | new_src = new_other; | |
875 | new_dst = new_me; | |
876 | src_changes = other_changes; | |
877 | dst_changes = my_changes; | |
878 | spi = this->other.spi; | |
879 | } | |
880 | else | |
881 | { | |
882 | src = this->me.addr; | |
883 | dst = this->other.addr; | |
884 | new_src = new_me; | |
885 | new_dst = new_other; | |
886 | src_changes = my_changes; | |
887 | dst_changes = other_changes; | |
888 | spi = this->me.spi; | |
889 | } | |
890 | ||
891 | DBG2(DBG_CHD, "updating %N SA 0x%x, from %#H..#H to %#H..%#H", | |
892 | protocol_id_names, this->protocol, ntohl(spi), src, dst, new_src, new_dst); | |
893 | ||
894 | status = charon->kernel_interface->update_sa(charon->kernel_interface, | |
895 | dst, spi, this->protocol, | |
896 | new_src, new_dst, | |
897 | src_changes, dst_changes); | |
898 | ||
899 | if (status != SUCCESS) | |
900 | { | |
901 | return FAILED; | |
902 | } | |
903 | return SUCCESS; | |
904 | } | |
905 | ||
906 | /** | |
907 | * Update the host adress/port of a policy | |
908 | */ | |
909 | static status_t update_policy_hosts(private_child_sa_t *this, host_t *new_me, host_t *new_other) | |
910 | { | |
911 | iterator_t *iterator; | |
912 | sa_policy_t *policy; | |
913 | status_t status; | |
914 | /* we always use high priorities, as hosts getting updated are INSTALLED */ | |
915 | ||
916 | iterator = this->policies->create_iterator(this->policies, TRUE); | |
917 | while (iterator->iterate(iterator, (void**)&policy)) | |
918 | { | |
919 | status = charon->kernel_interface->add_policy( | |
920 | charon->kernel_interface, | |
921 | new_me, new_other, | |
922 | policy->my_ts, policy->other_ts, | |
923 | POLICY_OUT, this->protocol, this->reqid, TRUE, this->mode, TRUE); | |
924 | ||
925 | status |= charon->kernel_interface->add_policy( | |
926 | charon->kernel_interface, | |
927 | new_other, new_me, | |
928 | policy->other_ts, policy->my_ts, | |
929 | POLICY_IN, this->protocol, this->reqid, TRUE, this->mode, TRUE); | |
930 | ||
931 | status |= charon->kernel_interface->add_policy( | |
932 | charon->kernel_interface, | |
933 | new_other, new_me, | |
934 | policy->other_ts, policy->my_ts, | |
935 | POLICY_FWD, this->protocol, this->reqid, TRUE, this->mode, TRUE); | |
936 | ||
937 | if (status != SUCCESS) | |
938 | { | |
939 | iterator->destroy(iterator); | |
940 | return FAILED; | |
941 | } | |
942 | } | |
943 | iterator->destroy(iterator); | |
944 | ||
945 | return SUCCESS; | |
946 | } | |
947 | ||
948 | /** | |
949 | * Implementation of child_sa_t.update_hosts. | |
950 | */ | |
951 | static status_t update_hosts(private_child_sa_t *this, host_t *new_me, host_t *new_other, | |
952 | host_diff_t my_changes, host_diff_t other_changes) | |
953 | { | |
954 | if (!my_changes && !other_changes) | |
955 | { | |
956 | return SUCCESS; | |
957 | } | |
958 | ||
959 | /* update our (initator) SAs */ | |
960 | if (update_sa_hosts(this, new_me, new_other, my_changes, other_changes, TRUE) != SUCCESS) | |
961 | { | |
962 | return FAILED; | |
963 | } | |
964 | ||
965 | /* update his (responder) SAs */ | |
966 | if (update_sa_hosts(this, new_me, new_other, my_changes, other_changes, FALSE) != SUCCESS) | |
967 | { | |
968 | return FAILED; | |
969 | } | |
970 | ||
971 | /* update policies */ | |
972 | if (my_changes & HOST_DIFF_ADDR || other_changes & HOST_DIFF_ADDR) | |
973 | { | |
974 | if (update_policy_hosts(this, new_me, new_other) != SUCCESS) | |
975 | { | |
976 | return FAILED; | |
977 | } | |
978 | } | |
979 | ||
980 | /* update hosts */ | |
981 | if (my_changes) | |
982 | { | |
983 | this->me.addr->destroy(this->me.addr); | |
984 | this->me.addr = new_me->clone(new_me); | |
985 | } | |
986 | ||
987 | if (other_changes) | |
988 | { | |
989 | this->other.addr->destroy(this->other.addr); | |
990 | this->other.addr = new_other->clone(new_other); | |
991 | } | |
992 | ||
993 | return SUCCESS; | |
994 | } | |
995 | ||
996 | /** | |
997 | * Implementation of child_sa_t.set_virtual_ip. | |
998 | */ | |
999 | static void set_virtual_ip(private_child_sa_t *this, host_t *ip) | |
1000 | { | |
1001 | this->virtual_ip = ip->clone(ip); | |
1002 | } | |
1003 | ||
1004 | /** | |
1005 | * Implementation of child_sa_t.destroy. | |
1006 | */ | |
1007 | static void destroy(private_child_sa_t *this) | |
1008 | { | |
1009 | sa_policy_t *policy; | |
1010 | ||
1011 | if (this->state == CHILD_DELETING || this->state == CHILD_INSTALLED) | |
1012 | { | |
1013 | updown(this, FALSE); | |
1014 | } | |
1015 | ||
1016 | /* delete SAs in the kernel, if they are set up */ | |
1017 | if (this->me.spi) | |
1018 | { | |
1019 | charon->kernel_interface->del_sa(charon->kernel_interface, | |
1020 | this->me.addr, this->me.spi, this->protocol); | |
1021 | } | |
1022 | if (this->alloc_esp_spi && this->alloc_esp_spi != this->me.spi) | |
1023 | { | |
1024 | charon->kernel_interface->del_sa(charon->kernel_interface, | |
1025 | this->me.addr, this->alloc_esp_spi, PROTO_ESP); | |
1026 | } | |
1027 | if (this->alloc_ah_spi && this->alloc_ah_spi != this->me.spi) | |
1028 | { | |
1029 | charon->kernel_interface->del_sa(charon->kernel_interface, | |
1030 | this->me.addr, this->alloc_ah_spi, PROTO_AH); | |
1031 | } | |
1032 | if (this->other.spi) | |
1033 | { | |
1034 | charon->kernel_interface->del_sa(charon->kernel_interface, | |
1035 | this->other.addr, this->other.spi, this->protocol); | |
1036 | } | |
1037 | ||
1038 | /* delete all policies in the kernel */ | |
1039 | while (this->policies->remove_last(this->policies, (void**)&policy) == SUCCESS) | |
1040 | { | |
1041 | /* let rekeyed policies, as they are used by another child_sa */ | |
1042 | charon->kernel_interface->del_policy(charon->kernel_interface, | |
1043 | policy->my_ts, policy->other_ts, | |
1044 | POLICY_OUT); | |
1045 | ||
1046 | charon->kernel_interface->del_policy(charon->kernel_interface, | |
1047 | policy->other_ts, policy->my_ts, | |
1048 | POLICY_IN); | |
1049 | ||
1050 | charon->kernel_interface->del_policy(charon->kernel_interface, | |
1051 | policy->other_ts, policy->my_ts, | |
1052 | POLICY_FWD); | |
1053 | policy->my_ts->destroy(policy->my_ts); | |
1054 | policy->other_ts->destroy(policy->other_ts); | |
1055 | free(policy); | |
1056 | } | |
1057 | this->policies->destroy(this->policies); | |
1058 | ||
1059 | this->my_ts->destroy(this->my_ts); | |
1060 | this->other_ts->destroy(this->other_ts); | |
1061 | this->me.addr->destroy(this->me.addr); | |
1062 | this->other.addr->destroy(this->other.addr); | |
1063 | this->me.id->destroy(this->me.id); | |
1064 | this->other.id->destroy(this->other.id); | |
1065 | this->config->destroy(this->config); | |
1066 | DESTROY_IF(this->virtual_ip); | |
1067 | free(this); | |
1068 | } | |
1069 | ||
1070 | /* | |
1071 | * Described in header. | |
1072 | */ | |
1073 | child_sa_t * child_sa_create(host_t *me, host_t* other, | |
1074 | identification_t *my_id, identification_t *other_id, | |
1075 | child_cfg_t *config, u_int32_t rekey, bool use_natt) | |
1076 | { | |
1077 | static u_int32_t reqid = 0; | |
1078 | private_child_sa_t *this = malloc_thing(private_child_sa_t); | |
1079 | ||
1080 | /* public functions */ | |
1081 | this->public.get_name = (char*(*)(child_sa_t*))get_name; | |
1082 | this->public.get_reqid = (u_int32_t(*)(child_sa_t*))get_reqid; | |
1083 | this->public.get_spi = (u_int32_t(*)(child_sa_t*, bool))get_spi; | |
1084 | this->public.get_protocol = (protocol_id_t(*)(child_sa_t*))get_protocol; | |
1085 | this->public.alloc = (status_t(*)(child_sa_t*,linked_list_t*))alloc; | |
1086 | this->public.add = (status_t(*)(child_sa_t*,proposal_t*,mode_t,prf_plus_t*))add; | |
1087 | this->public.update = (status_t(*)(child_sa_t*,proposal_t*,mode_t,prf_plus_t*))update; | |
1088 | this->public.update_hosts = (status_t (*)(child_sa_t*,host_t*,host_t*,host_diff_t,host_diff_t))update_hosts; | |
1089 | this->public.add_policies = (status_t (*)(child_sa_t*, linked_list_t*,linked_list_t*,mode_t))add_policies; | |
1090 | this->public.get_traffic_selectors = (linked_list_t*(*)(child_sa_t*,bool))get_traffic_selectors; | |
1091 | this->public.get_use_time = (status_t (*)(child_sa_t*,bool,time_t*))get_use_time; | |
1092 | this->public.set_state = (void(*)(child_sa_t*,child_sa_state_t))set_state; | |
1093 | this->public.get_state = (child_sa_state_t(*)(child_sa_t*))get_state; | |
1094 | this->public.get_config = (child_cfg_t*(*)(child_sa_t*))get_config; | |
1095 | this->public.set_virtual_ip = (void(*)(child_sa_t*,host_t*))set_virtual_ip; | |
1096 | this->public.destroy = (void(*)(child_sa_t*))destroy; | |
1097 | ||
1098 | /* private data */ | |
1099 | this->me.addr = me->clone(me); | |
1100 | this->other.addr = other->clone(other); | |
1101 | this->me.id = my_id->clone(my_id); | |
1102 | this->other.id = other_id->clone(other_id); | |
1103 | this->me.spi = 0; | |
1104 | this->other.spi = 0; | |
1105 | this->alloc_ah_spi = 0; | |
1106 | this->alloc_esp_spi = 0; | |
1107 | this->use_natt = use_natt; | |
1108 | this->state = CHILD_CREATED; | |
1109 | /* reuse old reqid if we are rekeying an existing CHILD_SA */ | |
1110 | this->reqid = rekey ? rekey : ++reqid; | |
1111 | this->encryption.algorithm = ENCR_UNDEFINED; | |
1112 | this->encryption.key_size = 0; | |
1113 | this->integrity.algorithm = AUTH_UNDEFINED; | |
1114 | this->encryption.key_size = 0; | |
1115 | this->policies = linked_list_create(); | |
1116 | this->my_ts = linked_list_create(); | |
1117 | this->other_ts = linked_list_create(); | |
1118 | this->protocol = PROTO_NONE; | |
1119 | this->mode = MODE_TUNNEL; | |
1120 | this->virtual_ip = NULL; | |
1121 | this->config = config; | |
1122 | config->get_ref(config); | |
1123 | ||
1124 | return &this->public; | |
1125 | } |