bus->listen() and the controller wrappers accept a timeout to wait for callbacks
[strongswan.git] / src / libcharon / plugins / stroke / stroke_control.c
1 /*
2 * Copyright (C) 2008 Martin Willi
3 * Hochschule fuer Technik Rapperswil
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15
16 #include "stroke_control.h"
17
18 #include <hydra.h>
19 #include <daemon.h>
20
21 #include <processing/jobs/delete_ike_sa_job.h>
22 #include <processing/jobs/rekey_ike_sa_job.h>
23 #include <processing/jobs/rekey_child_sa_job.h>
24
25 typedef struct private_stroke_control_t private_stroke_control_t;
26
27 /**
28 * private data of stroke_control
29 */
30 struct private_stroke_control_t {
31
32 /**
33 * public functions
34 */
35 stroke_control_t public;
36 };
37
38
39 typedef struct stroke_log_info_t stroke_log_info_t;
40
41 /**
42 * helper struct to say what and where to log when using controller callback
43 */
44 struct stroke_log_info_t {
45
46 /**
47 * level to log up to
48 */
49 level_t level;
50
51 /**
52 * where to write log
53 */
54 FILE* out;
55 };
56
57 /**
58 * logging to the stroke interface
59 */
60 static bool stroke_log(stroke_log_info_t *info, debug_t group, level_t level,
61 ike_sa_t *ike_sa, char *format, va_list args)
62 {
63 if (level <= info->level)
64 {
65 if (vfprintf(info->out, format, args) < 0 ||
66 fprintf(info->out, "\n") < 0 ||
67 fflush(info->out) != 0)
68 {
69 return FALSE;
70 }
71 }
72 return TRUE;
73 }
74
75 /**
76 * get the child_cfg with the same name as the peer cfg
77 */
78 static child_cfg_t* get_child_from_peer(peer_cfg_t *peer_cfg, char *name)
79 {
80 child_cfg_t *current, *found = NULL;
81 enumerator_t *enumerator;
82
83 enumerator = peer_cfg->create_child_cfg_enumerator(peer_cfg);
84 while (enumerator->enumerate(enumerator, &current))
85 {
86 if (streq(current->get_name(current), name))
87 {
88 found = current;
89 found->get_ref(found);
90 break;
91 }
92 }
93 enumerator->destroy(enumerator);
94 return found;
95 }
96
97 /**
98 * call the charon controller to initiate the connection
99 */
100 static void charon_initiate(peer_cfg_t *peer_cfg, child_cfg_t *child_cfg,
101 stroke_msg_t *msg, FILE *out)
102 {
103 if (msg->output_verbosity < 0)
104 {
105 charon->controller->initiate(charon->controller, peer_cfg, child_cfg,
106 NULL, NULL, 0);
107 }
108 else
109 {
110 stroke_log_info_t info = { msg->output_verbosity, out };
111
112 charon->controller->initiate(charon->controller, peer_cfg, child_cfg,
113 (controller_cb_t)stroke_log, &info, 0);
114 }
115 }
116
117 METHOD(stroke_control_t, initiate, void,
118 private_stroke_control_t *this, stroke_msg_t *msg, FILE *out)
119 {
120 child_cfg_t *child_cfg = NULL;
121 peer_cfg_t *peer_cfg;
122 enumerator_t *enumerator;
123 bool empty = TRUE;
124
125 peer_cfg = charon->backends->get_peer_cfg_by_name(charon->backends,
126 msg->initiate.name);
127 if (peer_cfg)
128 {
129 if (peer_cfg->get_ike_version(peer_cfg) != 2)
130 {
131 DBG1(DBG_CFG, "ignoring initiation request for IKEv%d config",
132 peer_cfg->get_ike_version(peer_cfg));
133 peer_cfg->destroy(peer_cfg);
134 return;
135 }
136
137 child_cfg = get_child_from_peer(peer_cfg, msg->initiate.name);
138 if (child_cfg == NULL)
139 {
140 enumerator = peer_cfg->create_child_cfg_enumerator(peer_cfg);
141 while (enumerator->enumerate(enumerator, &child_cfg))
142 {
143 empty = FALSE;
144 charon_initiate(peer_cfg->get_ref(peer_cfg),
145 child_cfg->get_ref(child_cfg), msg, out);
146 }
147 enumerator->destroy(enumerator);
148
149 if (empty)
150 {
151 DBG1(DBG_CFG, "no child config named '%s'", msg->initiate.name);
152 fprintf(out, "no child config named '%s'\n", msg->initiate.name);
153 }
154 peer_cfg->destroy(peer_cfg);
155 return;
156 }
157 }
158 else
159 {
160 enumerator = charon->backends->create_peer_cfg_enumerator(charon->backends,
161 NULL, NULL, NULL, NULL);
162 while (enumerator->enumerate(enumerator, &peer_cfg))
163 {
164 if (peer_cfg->get_ike_version(peer_cfg) != 2)
165 {
166 continue;
167 }
168 child_cfg = get_child_from_peer(peer_cfg, msg->initiate.name);
169 if (child_cfg)
170 {
171 peer_cfg->get_ref(peer_cfg);
172 break;
173 }
174 }
175 enumerator->destroy(enumerator);
176
177 if (child_cfg == NULL)
178 {
179 DBG1(DBG_CFG, "no config named '%s'", msg->initiate.name);
180 fprintf(out, "no config named '%s'\n", msg->initiate.name);
181 return;
182 }
183 }
184 charon_initiate(peer_cfg, child_cfg, msg, out);
185 }
186
187 /**
188 * Parse a terminate/rekey specifier
189 */
190 static bool parse_specifier(char *string, u_int32_t *id,
191 char **name, bool *child, bool *all)
192 {
193 int len;
194 char *pos = NULL;
195
196 *id = 0;
197 *name = NULL;
198 *all = FALSE;
199
200 len = strlen(string);
201 if (len < 1)
202 {
203 return FALSE;
204 }
205 switch (string[len-1])
206 {
207 case '}':
208 *child = TRUE;
209 pos = strchr(string, '{');
210 break;
211 case ']':
212 *child = FALSE;
213 pos = strchr(string, '[');
214 break;
215 default:
216 *name = string;
217 *child = FALSE;
218 break;
219 }
220
221 if (*name)
222 {
223 /* is a single name */
224 }
225 else if (pos == string + len - 2)
226 { /* is name[] or name{} */
227 string[len-2] = '\0';
228 *name = string;
229 }
230 else
231 {
232 if (!pos)
233 {
234 return FALSE;
235 }
236 if (*(pos + 1) == '*')
237 { /* is name[*] */
238 *all = TRUE;
239 *pos = '\0';
240 *name = string;
241 }
242 else
243 { /* is name[123] or name{23} */
244 *id = atoi(pos + 1);
245 if (*id == 0)
246 {
247 return FALSE;
248 }
249 }
250 }
251 return TRUE;
252 }
253
254 METHOD(stroke_control_t, terminate, void,
255 private_stroke_control_t *this, stroke_msg_t *msg, FILE *out)
256 {
257 char *name;
258 u_int32_t id;
259 bool child, all;
260 ike_sa_t *ike_sa;
261 enumerator_t *enumerator;
262 linked_list_t *ike_list, *child_list;
263 stroke_log_info_t info;
264 uintptr_t del;
265
266 if (!parse_specifier(msg->terminate.name, &id, &name, &child, &all))
267 {
268 DBG1(DBG_CFG, "error parsing specifier string");
269 return;
270 }
271
272 info.out = out;
273 info.level = msg->output_verbosity;
274
275 if (id)
276 {
277 if (child)
278 {
279 charon->controller->terminate_child(charon->controller, id,
280 (controller_cb_t)stroke_log, &info, 0);
281 }
282 else
283 {
284 charon->controller->terminate_ike(charon->controller, id,
285 (controller_cb_t)stroke_log, &info, 0);
286 }
287 return;
288 }
289
290 ike_list = linked_list_create();
291 child_list = linked_list_create();
292 enumerator = charon->controller->create_ike_sa_enumerator(
293 charon->controller, TRUE);
294 while (enumerator->enumerate(enumerator, &ike_sa))
295 {
296 child_sa_t *child_sa;
297 enumerator_t *children;
298
299 if (child)
300 {
301 children = ike_sa->create_child_sa_enumerator(ike_sa);
302 while (children->enumerate(children, (void**)&child_sa))
303 {
304 if (streq(name, child_sa->get_name(child_sa)))
305 {
306 child_list->insert_last(child_list,
307 (void*)(uintptr_t)child_sa->get_reqid(child_sa));
308 if (!all)
309 {
310 break;
311 }
312 }
313 }
314 children->destroy(children);
315 if (child_list->get_count(child_list) && !all)
316 {
317 break;
318 }
319 }
320 else if (streq(name, ike_sa->get_name(ike_sa)))
321 {
322 ike_list->insert_last(ike_list,
323 (void*)(uintptr_t)ike_sa->get_unique_id(ike_sa));
324 if (!all)
325 {
326 break;
327 }
328 }
329 }
330 enumerator->destroy(enumerator);
331
332 enumerator = child_list->create_enumerator(child_list);
333 while (enumerator->enumerate(enumerator, &del))
334 {
335 charon->controller->terminate_child(charon->controller, del,
336 (controller_cb_t)stroke_log, &info, 0);
337 }
338 enumerator->destroy(enumerator);
339
340 enumerator = ike_list->create_enumerator(ike_list);
341 while (enumerator->enumerate(enumerator, &del))
342 {
343 charon->controller->terminate_ike(charon->controller, del,
344 (controller_cb_t)stroke_log, &info, 0);
345 }
346 enumerator->destroy(enumerator);
347
348 if (child_list->get_count(child_list) == 0 &&
349 ike_list->get_count(ike_list) == 0)
350 {
351 DBG1(DBG_CFG, "no %s_SA named '%s' found",
352 child ? "CHILD" : "IKE", name);
353 }
354 ike_list->destroy(ike_list);
355 child_list->destroy(child_list);
356 }
357
358 METHOD(stroke_control_t, rekey, void,
359 private_stroke_control_t *this, stroke_msg_t *msg, FILE *out)
360 {
361 char *name;
362 u_int32_t id;
363 bool child, all, finished = FALSE;
364 ike_sa_t *ike_sa;
365 enumerator_t *enumerator;
366
367 if (!parse_specifier(msg->terminate.name, &id, &name, &child, &all))
368 {
369 DBG1(DBG_CFG, "error parsing specifier string");
370 return;
371 }
372 enumerator = charon->controller->create_ike_sa_enumerator(
373 charon->controller, TRUE);
374 while (enumerator->enumerate(enumerator, &ike_sa))
375 {
376 child_sa_t *child_sa;
377 enumerator_t *children;
378
379 if (child)
380 {
381 children = ike_sa->create_child_sa_enumerator(ike_sa);
382 while (children->enumerate(children, (void**)&child_sa))
383 {
384 if ((name && streq(name, child_sa->get_name(child_sa))) ||
385 (id && id == child_sa->get_reqid(child_sa)))
386 {
387 lib->processor->queue_job(lib->processor,
388 (job_t*)rekey_child_sa_job_create(
389 child_sa->get_reqid(child_sa),
390 child_sa->get_protocol(child_sa),
391 child_sa->get_spi(child_sa, TRUE)));
392 if (!all)
393 {
394 finished = TRUE;
395 break;
396 }
397 }
398 }
399 children->destroy(children);
400 }
401 else if ((name && streq(name, ike_sa->get_name(ike_sa))) ||
402 (id && id == ike_sa->get_unique_id(ike_sa)))
403 {
404 lib->processor->queue_job(lib->processor,
405 (job_t*)rekey_ike_sa_job_create(ike_sa->get_id(ike_sa), FALSE));
406 if (!all)
407 {
408 finished = TRUE;
409 }
410 }
411 if (finished)
412 {
413 break;
414 }
415 }
416 enumerator->destroy(enumerator);
417 }
418
419 METHOD(stroke_control_t, terminate_srcip, void,
420 private_stroke_control_t *this, stroke_msg_t *msg, FILE *out)
421 {
422 enumerator_t *enumerator;
423 ike_sa_t *ike_sa;
424 host_t *start = NULL, *end = NULL, *vip;
425 chunk_t chunk_start, chunk_end = chunk_empty, chunk_vip;
426
427 if (msg->terminate_srcip.start)
428 {
429 start = host_create_from_string(msg->terminate_srcip.start, 0);
430 }
431 if (!start)
432 {
433 DBG1(DBG_CFG, "invalid start address: %s", msg->terminate_srcip.start);
434 return;
435 }
436 chunk_start = start->get_address(start);
437 if (msg->terminate_srcip.end)
438 {
439 end = host_create_from_string(msg->terminate_srcip.end, 0);
440 if (!end)
441 {
442 DBG1(DBG_CFG, "invalid end address: %s", msg->terminate_srcip.end);
443 start->destroy(start);
444 return;
445 }
446 chunk_end = end->get_address(end);
447 }
448
449 enumerator = charon->controller->create_ike_sa_enumerator(
450 charon->controller, TRUE);
451 while (enumerator->enumerate(enumerator, &ike_sa))
452 {
453 vip = ike_sa->get_virtual_ip(ike_sa, FALSE);
454 if (!vip)
455 {
456 continue;
457 }
458 if (!end)
459 {
460 if (!vip->ip_equals(vip, start))
461 {
462 continue;
463 }
464 }
465 else
466 {
467 chunk_vip = vip->get_address(vip);
468 if (chunk_vip.len != chunk_start.len ||
469 chunk_vip.len != chunk_end.len ||
470 memcmp(chunk_vip.ptr, chunk_start.ptr, chunk_vip.len) < 0 ||
471 memcmp(chunk_vip.ptr, chunk_end.ptr, chunk_vip.len) > 0)
472 {
473 continue;
474 }
475 }
476
477 /* schedule delete asynchronously */
478 lib->processor->queue_job(lib->processor, (job_t*)
479 delete_ike_sa_job_create(ike_sa->get_id(ike_sa), TRUE));
480 }
481 enumerator->destroy(enumerator);
482 start->destroy(start);
483 DESTROY_IF(end);
484 }
485
486 METHOD(stroke_control_t, purge_ike, void,
487 private_stroke_control_t *this, stroke_msg_t *msg, FILE *out)
488 {
489 enumerator_t *enumerator, *children;
490 ike_sa_t *ike_sa;
491 child_sa_t *child_sa;
492 linked_list_t *list;
493 uintptr_t del;
494 stroke_log_info_t info;
495
496 info.out = out;
497 info.level = msg->output_verbosity;
498
499 list = linked_list_create();
500 enumerator = charon->controller->create_ike_sa_enumerator(
501 charon->controller, TRUE);
502 while (enumerator->enumerate(enumerator, &ike_sa))
503 {
504 children = ike_sa->create_child_sa_enumerator(ike_sa);
505 if (!children->enumerate(children, (void**)&child_sa))
506 {
507 list->insert_last(list,
508 (void*)(uintptr_t)ike_sa->get_unique_id(ike_sa));
509 }
510 children->destroy(children);
511 }
512 enumerator->destroy(enumerator);
513
514 enumerator = list->create_enumerator(list);
515 while (enumerator->enumerate(enumerator, &del))
516 {
517 charon->controller->terminate_ike(charon->controller, del,
518 (controller_cb_t)stroke_log, &info, 0);
519 }
520 enumerator->destroy(enumerator);
521 list->destroy(list);
522 }
523
524 /**
525 * call charon to install a shunt or trap
526 */
527 static void charon_route(peer_cfg_t *peer_cfg, child_cfg_t *child_cfg,
528 char *name, FILE *out)
529 {
530 ipsec_mode_t mode;
531
532 mode = child_cfg->get_mode(child_cfg);
533 if (mode == MODE_PASS || mode == MODE_DROP)
534 {
535 if (charon->shunts->install(charon->shunts, child_cfg))
536 {
537 fprintf(out, "'%s' shunt %N policy installed\n",
538 name, ipsec_mode_names, mode);
539 }
540 else
541 {
542 fprintf(out, "'%s' shunt %N policy installation failed\n",
543 name, ipsec_mode_names, mode);
544 }
545 }
546 else
547 {
548 if (charon->traps->install(charon->traps, peer_cfg, child_cfg))
549 {
550 fprintf(out, "'%s' routed\n", name);
551 }
552 else
553 {
554 fprintf(out, "routing '%s' failed\n", name);
555 }
556 }
557 }
558
559 METHOD(stroke_control_t, route, void,
560 private_stroke_control_t *this, stroke_msg_t *msg, FILE *out)
561 {
562 child_cfg_t *child_cfg = NULL;
563 peer_cfg_t *peer_cfg;
564 enumerator_t *enumerator;
565 bool empty = TRUE;
566
567 peer_cfg = charon->backends->get_peer_cfg_by_name(charon->backends,
568 msg->route.name);
569 if (peer_cfg)
570 {
571 if (peer_cfg->get_ike_version(peer_cfg) != 2)
572 {
573 DBG1(DBG_CFG, "ignoring initiation request for IKEv%d config",
574 peer_cfg->get_ike_version(peer_cfg));
575 peer_cfg->destroy(peer_cfg);
576 return;
577 }
578
579 child_cfg = get_child_from_peer(peer_cfg, msg->route.name);
580 if (child_cfg == NULL)
581 {
582 enumerator = peer_cfg->create_child_cfg_enumerator(peer_cfg);
583 while (enumerator->enumerate(enumerator, &child_cfg))
584 {
585 empty = FALSE;
586 charon_route(peer_cfg, child_cfg, child_cfg->get_name(child_cfg),
587 out);
588 }
589 enumerator->destroy(enumerator);
590
591 if (empty)
592 {
593 DBG1(DBG_CFG, "no child config named '%s'", msg->route.name);
594 fprintf(out, "no child config named '%s'\n", msg->route.name);
595 }
596 peer_cfg->destroy(peer_cfg);
597 return;
598 }
599 }
600 else
601 {
602 enumerator = charon->backends->create_peer_cfg_enumerator(charon->backends,
603 NULL, NULL, NULL, NULL);
604 while (enumerator->enumerate(enumerator, &peer_cfg))
605 {
606 if (peer_cfg->get_ike_version(peer_cfg) != 2)
607 {
608 continue;
609 }
610 child_cfg = get_child_from_peer(peer_cfg, msg->route.name);
611 if (child_cfg)
612 {
613 peer_cfg->get_ref(peer_cfg);
614 break;
615 }
616 }
617 enumerator->destroy(enumerator);
618
619 if (child_cfg == NULL)
620 {
621 DBG1(DBG_CFG, "no config named '%s'", msg->route.name);
622 fprintf(out, "no config named '%s'\n", msg->route.name);
623 return;
624 }
625 }
626 charon_route(peer_cfg, child_cfg, msg->route.name, out);
627 peer_cfg->destroy(peer_cfg);
628 child_cfg->destroy(child_cfg);
629 }
630
631 METHOD(stroke_control_t, unroute, void,
632 private_stroke_control_t *this, stroke_msg_t *msg, FILE *out)
633 {
634 child_sa_t *child_sa;
635 enumerator_t *enumerator;
636 u_int32_t id;
637 bool found = FALSE;
638
639 if (charon->shunts->uninstall(charon->shunts, msg->unroute.name))
640 {
641 fprintf(out, "shunt policy '%s' uninstalled\n", msg->unroute.name);
642 return;
643 }
644
645 enumerator = charon->traps->create_enumerator(charon->traps);
646 while (enumerator->enumerate(enumerator, NULL, &child_sa))
647 {
648 if (streq(msg->unroute.name, child_sa->get_name(child_sa)))
649 {
650 id = child_sa->get_reqid(child_sa);
651 enumerator->destroy(enumerator);
652 charon->traps->uninstall(charon->traps, id);
653 fprintf(out, "configuration '%s' unrouted\n", msg->unroute.name);
654 found = TRUE;
655 }
656 }
657 enumerator->destroy(enumerator);
658
659 if (!found)
660 {
661 fprintf(out, "configuration '%s' not found\n", msg->unroute.name);
662 }
663 }
664
665 METHOD(stroke_control_t, destroy, void,
666 private_stroke_control_t *this)
667 {
668 free(this);
669 }
670
671 /*
672 * see header file
673 */
674 stroke_control_t *stroke_control_create()
675 {
676 private_stroke_control_t *this;
677
678 INIT(this,
679 .public = {
680 .initiate = _initiate,
681 .terminate = _terminate,
682 .terminate_srcip = _terminate_srcip,
683 .rekey = _rekey,
684 .purge_ike = _purge_ike,
685 .route = _route,
686 .unroute = _unroute,
687 .destroy = _destroy,
688 },
689 );
690
691 return &this->public;
692 }
693