libhydra: Move kernel interface to libcharon
[strongswan.git] / src / libcharon / plugins / stroke / stroke_control.c
1 /*
2 * Copyright (C) 2013-2015 Tobias Brunner
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.
15 */
16
17 #include "stroke_control.h"
18
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 * Timeout for stroke commands, im ms
39 */
40 u_int timeout;
41 };
42
43
44 typedef struct stroke_log_info_t stroke_log_info_t;
45
46 /**
47 * helper struct to say what and where to log when using controller callback
48 */
49 struct stroke_log_info_t {
50
51 /**
52 * level to log up to
53 */
54 level_t level;
55
56 /**
57 * where to write log
58 */
59 FILE* out;
60 };
61
62 /**
63 * logging to the stroke interface
64 */
65 static bool stroke_log(stroke_log_info_t *info, debug_t group, level_t level,
66 ike_sa_t *ike_sa, char *message)
67 {
68 if (level <= info->level)
69 {
70 if (fprintf(info->out, "%s", message) < 0 ||
71 fprintf(info->out, "\n") < 0 ||
72 fflush(info->out) != 0)
73 {
74 return FALSE;
75 }
76 }
77 return TRUE;
78 }
79
80 /**
81 * get the child_cfg with the same name as the peer cfg
82 */
83 static child_cfg_t* get_child_from_peer(peer_cfg_t *peer_cfg, char *name)
84 {
85 child_cfg_t *current, *found = NULL;
86 enumerator_t *enumerator;
87
88 enumerator = peer_cfg->create_child_cfg_enumerator(peer_cfg);
89 while (enumerator->enumerate(enumerator, &current))
90 {
91 if (streq(current->get_name(current), name))
92 {
93 found = current;
94 found->get_ref(found);
95 break;
96 }
97 }
98 enumerator->destroy(enumerator);
99 return found;
100 }
101
102 /**
103 * call the charon controller to initiate the connection
104 */
105 static void charon_initiate(private_stroke_control_t *this, peer_cfg_t *peer_cfg,
106 child_cfg_t *child_cfg, stroke_msg_t *msg, FILE *out)
107 {
108 if (msg->output_verbosity < 0)
109 {
110 charon->controller->initiate(charon->controller, peer_cfg, child_cfg,
111 NULL, NULL, 0, FALSE);
112 }
113 else
114 {
115 stroke_log_info_t info = { msg->output_verbosity, out };
116 status_t status;
117
118 status = charon->controller->initiate(charon->controller,
119 peer_cfg, child_cfg, (controller_cb_t)stroke_log,
120 &info, this->timeout, FALSE);
121 switch (status)
122 {
123 case SUCCESS:
124 fprintf(out, "connection '%s' established successfully\n",
125 msg->initiate.name);
126 break;
127 case OUT_OF_RES:
128 fprintf(out, "connection '%s' not established after %dms, "
129 "detaching\n", msg->initiate.name, this->timeout);
130 break;
131 default:
132 case FAILED:
133 fprintf(out, "establishing connection '%s' failed\n",
134 msg->initiate.name);
135 break;
136 }
137 }
138 }
139
140 METHOD(stroke_control_t, initiate, void,
141 private_stroke_control_t *this, stroke_msg_t *msg, FILE *out)
142 {
143 child_cfg_t *child_cfg = NULL;
144 peer_cfg_t *peer_cfg;
145 enumerator_t *enumerator;
146 bool empty = TRUE;
147
148 peer_cfg = charon->backends->get_peer_cfg_by_name(charon->backends,
149 msg->initiate.name);
150 if (peer_cfg)
151 {
152 child_cfg = get_child_from_peer(peer_cfg, msg->initiate.name);
153 if (child_cfg == NULL)
154 {
155 enumerator = peer_cfg->create_child_cfg_enumerator(peer_cfg);
156 while (enumerator->enumerate(enumerator, &child_cfg))
157 {
158 empty = FALSE;
159 charon_initiate(this, peer_cfg->get_ref(peer_cfg),
160 child_cfg->get_ref(child_cfg), msg, out);
161 }
162 enumerator->destroy(enumerator);
163
164 if (empty)
165 {
166 DBG1(DBG_CFG, "no child config named '%s'", msg->initiate.name);
167 fprintf(out, "no child config named '%s'\n", msg->initiate.name);
168 }
169 peer_cfg->destroy(peer_cfg);
170 return;
171 }
172 }
173 else
174 {
175 enumerator = charon->backends->create_peer_cfg_enumerator(
176 charon->backends, NULL, NULL, NULL, NULL, IKE_ANY);
177 while (enumerator->enumerate(enumerator, &peer_cfg))
178 {
179 child_cfg = get_child_from_peer(peer_cfg, msg->initiate.name);
180 if (child_cfg)
181 {
182 peer_cfg->get_ref(peer_cfg);
183 break;
184 }
185 }
186 enumerator->destroy(enumerator);
187
188 if (child_cfg == NULL)
189 {
190 DBG1(DBG_CFG, "no config named '%s'", msg->initiate.name);
191 fprintf(out, "no config named '%s'\n", msg->initiate.name);
192 return;
193 }
194 }
195 charon_initiate(this, peer_cfg, child_cfg, msg, out);
196 }
197
198 /**
199 * Parse a terminate/rekey specifier
200 */
201 static bool parse_specifier(char *string, u_int32_t *id,
202 char **name, bool *child, bool *all)
203 {
204 int len;
205 char *pos = NULL;
206
207 *id = 0;
208 *name = NULL;
209 *all = FALSE;
210
211 len = strlen(string);
212 if (len < 1)
213 {
214 return FALSE;
215 }
216 switch (string[len-1])
217 {
218 case '}':
219 *child = TRUE;
220 pos = strchr(string, '{');
221 break;
222 case ']':
223 *child = FALSE;
224 pos = strchr(string, '[');
225 break;
226 default:
227 *name = string;
228 *child = FALSE;
229 break;
230 }
231
232 if (*name)
233 {
234 /* is a single name */
235 }
236 else if (pos == string + len - 2)
237 { /* is name[] or name{} */
238 string[len-2] = '\0';
239 *name = string;
240 }
241 else
242 {
243 if (!pos)
244 {
245 return FALSE;
246 }
247 if (*(pos + 1) == '*')
248 { /* is name[*] */
249 *all = TRUE;
250 *pos = '\0';
251 *name = string;
252 }
253 else
254 { /* is name[123] or name{23} */
255 *id = atoi(pos + 1);
256 if (*id == 0)
257 {
258 return FALSE;
259 }
260 }
261 }
262 return TRUE;
263 }
264
265 /**
266 * Report the result of a terminate() call to console
267 */
268 static void report_terminate_status(private_stroke_control_t *this,
269 status_t status, FILE *out, u_int32_t id, bool child)
270 {
271 char *prefix, *postfix;
272
273 if (child)
274 {
275 prefix = "CHILD_SA {";
276 postfix = "}";
277 }
278 else
279 {
280 prefix = "IKE_SA [";
281 postfix = "]";
282 }
283
284 switch (status)
285 {
286 case SUCCESS:
287 fprintf(out, "%s%d%s closed successfully\n", prefix, id, postfix);
288 break;
289 case OUT_OF_RES:
290 fprintf(out, "%s%d%s not closed after %dms, detaching\n",
291 prefix, id, postfix, this->timeout);
292 break;
293 default:
294 case FAILED:
295 fprintf(out, "closing %s%d%s failed\n", prefix, id, postfix);
296 break;
297 }
298 }
299
300 /**
301 * Call the charon controller to terminate a CHILD_SA
302 */
303 static void charon_terminate(private_stroke_control_t *this, u_int32_t id,
304 stroke_msg_t *msg, FILE *out, bool child)
305 {
306 if (msg->output_verbosity >= 0)
307 {
308 stroke_log_info_t info = { msg->output_verbosity, out };
309 status_t status;
310
311 if (child)
312 {
313 status = charon->controller->terminate_child(charon->controller, id,
314 (controller_cb_t)stroke_log, &info, this->timeout);
315 }
316 else
317 {
318 status = charon->controller->terminate_ike(charon->controller, id,
319 (controller_cb_t)stroke_log, &info, this->timeout);
320 }
321 report_terminate_status(this, status, out, id, child);
322 }
323 else if (child)
324 {
325 charon->controller->terminate_child(charon->controller, id,
326 NULL, NULL, 0);
327 }
328 else
329 {
330 charon->controller->terminate_ike(charon->controller, id,
331 NULL, NULL, 0);
332 }
333 }
334
335 METHOD(stroke_control_t, terminate, void,
336 private_stroke_control_t *this, stroke_msg_t *msg, FILE *out)
337 {
338 char *name;
339 u_int32_t id;
340 bool child, all;
341 ike_sa_t *ike_sa;
342 enumerator_t *enumerator;
343 linked_list_t *ike_list, *child_list;
344 uintptr_t del;
345
346 if (!parse_specifier(msg->terminate.name, &id, &name, &child, &all))
347 {
348 DBG1(DBG_CFG, "error parsing specifier string");
349 return;
350 }
351
352 if (id)
353 {
354 return charon_terminate(this, id, msg, out, child);
355 }
356
357 ike_list = linked_list_create();
358 child_list = linked_list_create();
359 enumerator = charon->controller->create_ike_sa_enumerator(
360 charon->controller, TRUE);
361 while (enumerator->enumerate(enumerator, &ike_sa))
362 {
363 child_sa_t *child_sa;
364 enumerator_t *children;
365
366 if (child)
367 {
368 children = ike_sa->create_child_sa_enumerator(ike_sa);
369 while (children->enumerate(children, (void**)&child_sa))
370 {
371 if (streq(name, child_sa->get_name(child_sa)))
372 {
373 child_list->insert_last(child_list,
374 (void*)(uintptr_t)child_sa->get_unique_id(child_sa));
375 if (!all)
376 {
377 break;
378 }
379 }
380 }
381 children->destroy(children);
382 if (child_list->get_count(child_list) && !all)
383 {
384 break;
385 }
386 }
387 else if (streq(name, ike_sa->get_name(ike_sa)))
388 {
389 ike_list->insert_last(ike_list,
390 (void*)(uintptr_t)ike_sa->get_unique_id(ike_sa));
391 if (!all)
392 {
393 break;
394 }
395 }
396 }
397 enumerator->destroy(enumerator);
398
399 enumerator = child_list->create_enumerator(child_list);
400 while (enumerator->enumerate(enumerator, &del))
401 {
402 charon_terminate(this, del, msg, out, TRUE);
403 }
404 enumerator->destroy(enumerator);
405
406 enumerator = ike_list->create_enumerator(ike_list);
407 while (enumerator->enumerate(enumerator, &del))
408 {
409 charon_terminate(this, del, msg, out, FALSE);
410 }
411 enumerator->destroy(enumerator);
412
413 if (child_list->get_count(child_list) == 0 &&
414 ike_list->get_count(ike_list) == 0)
415 {
416 DBG1(DBG_CFG, "no %s_SA named '%s' found",
417 child ? "CHILD" : "IKE", name);
418 }
419 ike_list->destroy(ike_list);
420 child_list->destroy(child_list);
421 }
422
423 METHOD(stroke_control_t, rekey, void,
424 private_stroke_control_t *this, stroke_msg_t *msg, FILE *out)
425 {
426 char *name;
427 u_int32_t id;
428 bool child, all, finished = FALSE;
429 ike_sa_t *ike_sa;
430 enumerator_t *enumerator;
431
432 if (!parse_specifier(msg->terminate.name, &id, &name, &child, &all))
433 {
434 DBG1(DBG_CFG, "error parsing specifier string");
435 return;
436 }
437 enumerator = charon->controller->create_ike_sa_enumerator(
438 charon->controller, TRUE);
439 while (enumerator->enumerate(enumerator, &ike_sa))
440 {
441 child_sa_t *child_sa;
442 enumerator_t *children;
443
444 if (child)
445 {
446 children = ike_sa->create_child_sa_enumerator(ike_sa);
447 while (children->enumerate(children, (void**)&child_sa))
448 {
449 if ((name && streq(name, child_sa->get_name(child_sa))) ||
450 (id && id == child_sa->get_unique_id(child_sa)))
451 {
452 lib->processor->queue_job(lib->processor,
453 (job_t*)rekey_child_sa_job_create(
454 child_sa->get_protocol(child_sa),
455 child_sa->get_spi(child_sa, TRUE),
456 ike_sa->get_my_host(ike_sa)));
457 if (!all)
458 {
459 finished = TRUE;
460 break;
461 }
462 }
463 }
464 children->destroy(children);
465 }
466 else if ((name && streq(name, ike_sa->get_name(ike_sa))) ||
467 (id && id == ike_sa->get_unique_id(ike_sa)))
468 {
469 lib->processor->queue_job(lib->processor,
470 (job_t*)rekey_ike_sa_job_create(ike_sa->get_id(ike_sa), FALSE));
471 if (!all)
472 {
473 finished = TRUE;
474 }
475 }
476 if (finished)
477 {
478 break;
479 }
480 }
481 enumerator->destroy(enumerator);
482 }
483
484 METHOD(stroke_control_t, terminate_srcip, void,
485 private_stroke_control_t *this, stroke_msg_t *msg, FILE *out)
486 {
487 enumerator_t *enumerator, *vips;
488 ike_sa_t *ike_sa;
489 host_t *start = NULL, *end = NULL, *vip;
490 chunk_t chunk_start, chunk_end = chunk_empty, chunk;
491
492 if (msg->terminate_srcip.start)
493 {
494 start = host_create_from_string(msg->terminate_srcip.start, 0);
495 }
496 if (!start)
497 {
498 DBG1(DBG_CFG, "invalid start address: %s", msg->terminate_srcip.start);
499 return;
500 }
501 chunk_start = start->get_address(start);
502 if (msg->terminate_srcip.end)
503 {
504 end = host_create_from_string(msg->terminate_srcip.end, 0);
505 if (!end)
506 {
507 DBG1(DBG_CFG, "invalid end address: %s", msg->terminate_srcip.end);
508 start->destroy(start);
509 return;
510 }
511 chunk_end = end->get_address(end);
512 }
513
514 enumerator = charon->controller->create_ike_sa_enumerator(
515 charon->controller, TRUE);
516 while (enumerator->enumerate(enumerator, &ike_sa))
517 {
518 bool match = FALSE;
519
520 vips = ike_sa->create_virtual_ip_enumerator(ike_sa, FALSE);
521 while (vips->enumerate(vips, &vip))
522 {
523 if (!end)
524 {
525 if (vip->ip_equals(vip, start))
526 {
527 match = TRUE;
528 break;
529 }
530 }
531 else
532 {
533 chunk = vip->get_address(vip);
534 if (chunk.len == chunk_start.len &&
535 chunk.len == chunk_end.len &&
536 memcmp(chunk.ptr, chunk_start.ptr, chunk.len) >= 0 &&
537 memcmp(chunk.ptr, chunk_end.ptr, chunk.len) <= 0)
538 {
539 match = TRUE;
540 break;
541 }
542 }
543 }
544 vips->destroy(vips);
545
546 if (match)
547 {
548 /* schedule delete asynchronously */
549 lib->processor->queue_job(lib->processor, (job_t*)
550 delete_ike_sa_job_create(ike_sa->get_id(ike_sa), TRUE));
551 }
552 }
553 enumerator->destroy(enumerator);
554 start->destroy(start);
555 DESTROY_IF(end);
556 }
557
558 METHOD(stroke_control_t, purge_ike, void,
559 private_stroke_control_t *this, stroke_msg_t *msg, FILE *out)
560 {
561 enumerator_t *enumerator, *children;
562 ike_sa_t *ike_sa;
563 child_sa_t *child_sa;
564 linked_list_t *list;
565 uintptr_t del;
566
567 list = linked_list_create();
568 enumerator = charon->controller->create_ike_sa_enumerator(
569 charon->controller, TRUE);
570 while (enumerator->enumerate(enumerator, &ike_sa))
571 {
572 children = ike_sa->create_child_sa_enumerator(ike_sa);
573 if (!children->enumerate(children, (void**)&child_sa))
574 {
575 list->insert_last(list,
576 (void*)(uintptr_t)ike_sa->get_unique_id(ike_sa));
577 }
578 children->destroy(children);
579 }
580 enumerator->destroy(enumerator);
581
582 enumerator = list->create_enumerator(list);
583 while (enumerator->enumerate(enumerator, &del))
584 {
585 charon_terminate(this, del, msg, out, FALSE);
586 }
587 enumerator->destroy(enumerator);
588 list->destroy(list);
589 }
590
591 /**
592 * Find an existing CHILD_SA/reqid
593 */
594 static u_int32_t find_reqid(child_cfg_t *child_cfg)
595 {
596 enumerator_t *enumerator, *children;
597 child_sa_t *child_sa;
598 ike_sa_t *ike_sa;
599 char *name;
600 u_int32_t reqid;
601
602 reqid = charon->traps->find_reqid(charon->traps, child_cfg);
603 if (reqid)
604 { /* already trapped */
605 return reqid;
606 }
607
608 name = child_cfg->get_name(child_cfg);
609 enumerator = charon->controller->create_ike_sa_enumerator(
610 charon->controller, TRUE);
611 while (enumerator->enumerate(enumerator, &ike_sa))
612 {
613 children = ike_sa->create_child_sa_enumerator(ike_sa);
614 while (children->enumerate(children, (void**)&child_sa))
615 {
616 if (streq(name, child_sa->get_name(child_sa)))
617 {
618 reqid = child_sa->get_reqid(child_sa);
619 break;
620 }
621 }
622 children->destroy(children);
623 if (reqid)
624 {
625 break;
626 }
627 }
628 enumerator->destroy(enumerator);
629 return reqid;
630 }
631
632 /**
633 * call charon to install a shunt or trap
634 */
635 static void charon_route(peer_cfg_t *peer_cfg, child_cfg_t *child_cfg,
636 char *name, FILE *out)
637 {
638 ipsec_mode_t mode;
639 u_int32_t reqid;
640
641 mode = child_cfg->get_mode(child_cfg);
642 if (mode == MODE_PASS || mode == MODE_DROP)
643 {
644 if (charon->shunts->install(charon->shunts, child_cfg))
645 {
646 fprintf(out, "'%s' shunt %N policy installed\n",
647 name, ipsec_mode_names, mode);
648 }
649 else
650 {
651 fprintf(out, "'%s' shunt %N policy installation failed\n",
652 name, ipsec_mode_names, mode);
653 }
654 }
655 else
656 {
657 reqid = find_reqid(child_cfg);
658 if (charon->traps->install(charon->traps, peer_cfg, child_cfg, reqid))
659 {
660 fprintf(out, "'%s' routed\n", name);
661 }
662 else
663 {
664 fprintf(out, "routing '%s' failed\n", name);
665 }
666 }
667 }
668
669 METHOD(stroke_control_t, route, void,
670 private_stroke_control_t *this, stroke_msg_t *msg, FILE *out)
671 {
672 child_cfg_t *child_cfg = NULL;
673 peer_cfg_t *peer_cfg;
674 enumerator_t *enumerator;
675 bool empty = TRUE;
676
677 peer_cfg = charon->backends->get_peer_cfg_by_name(charon->backends,
678 msg->route.name);
679 if (peer_cfg)
680 {
681 child_cfg = get_child_from_peer(peer_cfg, msg->route.name);
682 if (child_cfg == NULL)
683 {
684 enumerator = peer_cfg->create_child_cfg_enumerator(peer_cfg);
685 while (enumerator->enumerate(enumerator, &child_cfg))
686 {
687 empty = FALSE;
688 charon_route(peer_cfg, child_cfg, child_cfg->get_name(child_cfg),
689 out);
690 }
691 enumerator->destroy(enumerator);
692
693 if (empty)
694 {
695 DBG1(DBG_CFG, "no child config named '%s'", msg->route.name);
696 fprintf(out, "no child config named '%s'\n", msg->route.name);
697 }
698 peer_cfg->destroy(peer_cfg);
699 return;
700 }
701 }
702 else
703 {
704 enumerator = charon->backends->create_peer_cfg_enumerator(
705 charon->backends, NULL, NULL, NULL, NULL, IKE_ANY);
706 while (enumerator->enumerate(enumerator, &peer_cfg))
707 {
708 child_cfg = get_child_from_peer(peer_cfg, msg->route.name);
709 if (child_cfg)
710 {
711 peer_cfg->get_ref(peer_cfg);
712 break;
713 }
714 }
715 enumerator->destroy(enumerator);
716
717 if (child_cfg == NULL)
718 {
719 DBG1(DBG_CFG, "no config named '%s'", msg->route.name);
720 fprintf(out, "no config named '%s'\n", msg->route.name);
721 return;
722 }
723 }
724 charon_route(peer_cfg, child_cfg, msg->route.name, out);
725 peer_cfg->destroy(peer_cfg);
726 child_cfg->destroy(child_cfg);
727 }
728
729 METHOD(stroke_control_t, unroute, void,
730 private_stroke_control_t *this, stroke_msg_t *msg, FILE *out)
731 {
732 child_sa_t *child_sa;
733 enumerator_t *enumerator;
734 u_int32_t id = 0;
735
736 if (charon->shunts->uninstall(charon->shunts, msg->unroute.name))
737 {
738 fprintf(out, "shunt policy '%s' uninstalled\n", msg->unroute.name);
739 return;
740 }
741
742 enumerator = charon->traps->create_enumerator(charon->traps);
743 while (enumerator->enumerate(enumerator, NULL, &child_sa))
744 {
745 if (streq(msg->unroute.name, child_sa->get_name(child_sa)))
746 {
747 id = child_sa->get_reqid(child_sa);
748 break;
749 }
750 }
751 enumerator->destroy(enumerator);
752
753 if (id)
754 {
755 charon->traps->uninstall(charon->traps, id);
756 fprintf(out, "configuration '%s' unrouted\n", msg->unroute.name);
757 }
758 else
759 {
760 fprintf(out, "configuration '%s' not found\n", msg->unroute.name);
761 }
762 }
763
764 METHOD(stroke_control_t, destroy, void,
765 private_stroke_control_t *this)
766 {
767 free(this);
768 }
769
770 /*
771 * see header file
772 */
773 stroke_control_t *stroke_control_create()
774 {
775 private_stroke_control_t *this;
776
777 INIT(this,
778 .public = {
779 .initiate = _initiate,
780 .terminate = _terminate,
781 .terminate_srcip = _terminate_srcip,
782 .rekey = _rekey,
783 .purge_ike = _purge_ike,
784 .route = _route,
785 .unroute = _unroute,
786 .destroy = _destroy,
787 },
788 .timeout = lib->settings->get_int(lib->settings,
789 "%s.plugins.stroke.timeout", 0, lib->ns),
790 );
791
792 return &this->public;
793 }