016f0e71bb03354ca1fff0112623ae40a05cdf42
[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, uint32_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, uint32_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, uint32_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 FALSE, (controller_cb_t)stroke_log, &info,
320 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, FALSE,
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 uint32_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 uint32_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 * call charon to install a shunt or trap
594 */
595 static void charon_route(peer_cfg_t *peer_cfg, child_cfg_t *child_cfg,
596 char *name, FILE *out)
597 {
598 ipsec_mode_t mode;
599
600 mode = child_cfg->get_mode(child_cfg);
601 if (mode == MODE_PASS || mode == MODE_DROP)
602 {
603 if (charon->shunts->install(charon->shunts,
604 peer_cfg->get_name(peer_cfg), child_cfg))
605 {
606 fprintf(out, "'%s' shunt %N policy installed\n",
607 name, ipsec_mode_names, mode);
608 }
609 else
610 {
611 fprintf(out, "'%s' shunt %N policy installation failed\n",
612 name, ipsec_mode_names, mode);
613 }
614 }
615 else
616 {
617 if (charon->traps->install(charon->traps, peer_cfg, child_cfg))
618 {
619 fprintf(out, "'%s' routed\n", name);
620 }
621 else
622 {
623 fprintf(out, "routing '%s' failed\n", name);
624 }
625 }
626 }
627
628 METHOD(stroke_control_t, route, void,
629 private_stroke_control_t *this, stroke_msg_t *msg, FILE *out)
630 {
631 child_cfg_t *child_cfg = NULL;
632 peer_cfg_t *peer_cfg;
633 enumerator_t *enumerator;
634 bool empty = TRUE;
635
636 peer_cfg = charon->backends->get_peer_cfg_by_name(charon->backends,
637 msg->route.name);
638 if (peer_cfg)
639 {
640 child_cfg = get_child_from_peer(peer_cfg, msg->route.name);
641 if (child_cfg == NULL)
642 {
643 enumerator = peer_cfg->create_child_cfg_enumerator(peer_cfg);
644 while (enumerator->enumerate(enumerator, &child_cfg))
645 {
646 empty = FALSE;
647 charon_route(peer_cfg, child_cfg, child_cfg->get_name(child_cfg),
648 out);
649 }
650 enumerator->destroy(enumerator);
651
652 if (empty)
653 {
654 DBG1(DBG_CFG, "no child config named '%s'", msg->route.name);
655 fprintf(out, "no child config named '%s'\n", msg->route.name);
656 }
657 peer_cfg->destroy(peer_cfg);
658 return;
659 }
660 }
661 else
662 {
663 enumerator = charon->backends->create_peer_cfg_enumerator(
664 charon->backends, NULL, NULL, NULL, NULL, IKE_ANY);
665 while (enumerator->enumerate(enumerator, &peer_cfg))
666 {
667 child_cfg = get_child_from_peer(peer_cfg, msg->route.name);
668 if (child_cfg)
669 {
670 peer_cfg->get_ref(peer_cfg);
671 break;
672 }
673 }
674 enumerator->destroy(enumerator);
675
676 if (child_cfg == NULL)
677 {
678 DBG1(DBG_CFG, "no config named '%s'", msg->route.name);
679 fprintf(out, "no config named '%s'\n", msg->route.name);
680 return;
681 }
682 }
683 charon_route(peer_cfg, child_cfg, msg->route.name, out);
684 peer_cfg->destroy(peer_cfg);
685 child_cfg->destroy(child_cfg);
686 }
687
688 METHOD(stroke_control_t, unroute, void,
689 private_stroke_control_t *this, stroke_msg_t *msg, FILE *out)
690 {
691 if (charon->shunts->uninstall(charon->shunts, NULL, msg->unroute.name))
692 {
693 fprintf(out, "shunt policy '%s' uninstalled\n", msg->unroute.name);
694 }
695 else if (charon->traps->uninstall(charon->traps, NULL, msg->unroute.name))
696 {
697 fprintf(out, "trap policy '%s' unrouted\n", msg->unroute.name);
698 }
699 else
700 {
701 fprintf(out, "configuration '%s' not found\n", msg->unroute.name);
702 }
703 }
704
705 METHOD(stroke_control_t, destroy, void,
706 private_stroke_control_t *this)
707 {
708 free(this);
709 }
710
711 /*
712 * see header file
713 */
714 stroke_control_t *stroke_control_create()
715 {
716 private_stroke_control_t *this;
717
718 INIT(this,
719 .public = {
720 .initiate = _initiate,
721 .terminate = _terminate,
722 .terminate_srcip = _terminate_srcip,
723 .rekey = _rekey,
724 .purge_ike = _purge_ike,
725 .route = _route,
726 .unroute = _unroute,
727 .destroy = _destroy,
728 },
729 .timeout = lib->settings->get_int(lib->settings,
730 "%s.plugins.stroke.timeout", 0, lib->ns),
731 );
732
733 return &this->public;
734 }