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