]> git.ipfire.org Git - thirdparty/strongswan.git/blob - src/libcharon/plugins/stroke/stroke_control.c
Update copyright headers after acquisition by secunet
[thirdparty/strongswan.git] / src / libcharon / plugins / stroke / stroke_control.c
1 /*
2 * Copyright (C) 2013-2015 Tobias Brunner
3 * Copyright (C) 2008 Martin Willi
4 *
5 * Copyright (C) secunet Security Networks AG
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation; either version 2 of the License, or (at your
10 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 * for more details.
16 */
17
18 #include "stroke_control.h"
19
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, uint32_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, uint32_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, uint32_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 FALSE, (controller_cb_t)stroke_log, &info,
321 this->timeout);
322 }
323 report_terminate_status(this, status, out, id, child);
324 }
325 else if (child)
326 {
327 charon->controller->terminate_child(charon->controller, id,
328 NULL, NULL, 0);
329 }
330 else
331 {
332 charon->controller->terminate_ike(charon->controller, id, FALSE,
333 NULL, NULL, 0);
334 }
335 }
336
337 METHOD(stroke_control_t, terminate, void,
338 private_stroke_control_t *this, stroke_msg_t *msg, FILE *out)
339 {
340 char *name;
341 uint32_t id;
342 bool child, all;
343 ike_sa_t *ike_sa;
344 enumerator_t *enumerator;
345 linked_list_t *ike_list, *child_list;
346 uintptr_t del;
347
348 if (!parse_specifier(msg->terminate.name, &id, &name, &child, &all))
349 {
350 DBG1(DBG_CFG, "error parsing specifier string");
351 return;
352 }
353
354 if (id)
355 {
356 return charon_terminate(this, id, msg, out, child);
357 }
358
359 ike_list = linked_list_create();
360 child_list = linked_list_create();
361 enumerator = charon->controller->create_ike_sa_enumerator(
362 charon->controller, TRUE);
363 while (enumerator->enumerate(enumerator, &ike_sa))
364 {
365 child_sa_t *child_sa;
366 enumerator_t *children;
367
368 if (child)
369 {
370 children = ike_sa->create_child_sa_enumerator(ike_sa);
371 while (children->enumerate(children, (void**)&child_sa))
372 {
373 if (streq(name, child_sa->get_name(child_sa)))
374 {
375 child_list->insert_last(child_list,
376 (void*)(uintptr_t)child_sa->get_unique_id(child_sa));
377 if (!all)
378 {
379 break;
380 }
381 }
382 }
383 children->destroy(children);
384 if (child_list->get_count(child_list) && !all)
385 {
386 break;
387 }
388 }
389 else if (streq(name, ike_sa->get_name(ike_sa)))
390 {
391 ike_list->insert_last(ike_list,
392 (void*)(uintptr_t)ike_sa->get_unique_id(ike_sa));
393 if (!all)
394 {
395 break;
396 }
397 }
398 }
399 enumerator->destroy(enumerator);
400
401 enumerator = child_list->create_enumerator(child_list);
402 while (enumerator->enumerate(enumerator, &del))
403 {
404 charon_terminate(this, del, msg, out, TRUE);
405 }
406 enumerator->destroy(enumerator);
407
408 enumerator = ike_list->create_enumerator(ike_list);
409 while (enumerator->enumerate(enumerator, &del))
410 {
411 charon_terminate(this, del, msg, out, FALSE);
412 }
413 enumerator->destroy(enumerator);
414
415 if (child_list->get_count(child_list) == 0 &&
416 ike_list->get_count(ike_list) == 0)
417 {
418 DBG1(DBG_CFG, "no %s_SA named '%s' found",
419 child ? "CHILD" : "IKE", name);
420 }
421 ike_list->destroy(ike_list);
422 child_list->destroy(child_list);
423 }
424
425 METHOD(stroke_control_t, rekey, void,
426 private_stroke_control_t *this, stroke_msg_t *msg, FILE *out)
427 {
428 char *name;
429 uint32_t id;
430 bool child, all, finished = FALSE;
431 ike_sa_t *ike_sa;
432 enumerator_t *enumerator;
433
434 if (!parse_specifier(msg->terminate.name, &id, &name, &child, &all))
435 {
436 DBG1(DBG_CFG, "error parsing specifier string");
437 return;
438 }
439 enumerator = charon->controller->create_ike_sa_enumerator(
440 charon->controller, TRUE);
441 while (enumerator->enumerate(enumerator, &ike_sa))
442 {
443 child_sa_t *child_sa;
444 enumerator_t *children;
445
446 if (child)
447 {
448 children = ike_sa->create_child_sa_enumerator(ike_sa);
449 while (children->enumerate(children, (void**)&child_sa))
450 {
451 if ((name && streq(name, child_sa->get_name(child_sa))) ||
452 (id && id == child_sa->get_unique_id(child_sa)))
453 {
454 lib->processor->queue_job(lib->processor,
455 (job_t*)rekey_child_sa_job_create(
456 child_sa->get_protocol(child_sa),
457 child_sa->get_spi(child_sa, TRUE),
458 ike_sa->get_my_host(ike_sa)));
459 if (!all)
460 {
461 finished = TRUE;
462 break;
463 }
464 }
465 }
466 children->destroy(children);
467 }
468 else if ((name && streq(name, ike_sa->get_name(ike_sa))) ||
469 (id && id == ike_sa->get_unique_id(ike_sa)))
470 {
471 lib->processor->queue_job(lib->processor,
472 (job_t*)rekey_ike_sa_job_create(ike_sa->get_id(ike_sa), FALSE));
473 if (!all)
474 {
475 finished = TRUE;
476 }
477 }
478 if (finished)
479 {
480 break;
481 }
482 }
483 enumerator->destroy(enumerator);
484 }
485
486 METHOD(stroke_control_t, terminate_srcip, void,
487 private_stroke_control_t *this, stroke_msg_t *msg, FILE *out)
488 {
489 enumerator_t *enumerator, *vips;
490 ike_sa_t *ike_sa;
491 host_t *start = NULL, *end = NULL, *vip;
492 chunk_t chunk_start, chunk_end = chunk_empty, chunk;
493
494 if (msg->terminate_srcip.start)
495 {
496 start = host_create_from_string(msg->terminate_srcip.start, 0);
497 }
498 if (!start)
499 {
500 DBG1(DBG_CFG, "invalid start address: %s", msg->terminate_srcip.start);
501 return;
502 }
503 chunk_start = start->get_address(start);
504 if (msg->terminate_srcip.end)
505 {
506 end = host_create_from_string(msg->terminate_srcip.end, 0);
507 if (!end)
508 {
509 DBG1(DBG_CFG, "invalid end address: %s", msg->terminate_srcip.end);
510 start->destroy(start);
511 return;
512 }
513 chunk_end = end->get_address(end);
514 }
515
516 enumerator = charon->controller->create_ike_sa_enumerator(
517 charon->controller, TRUE);
518 while (enumerator->enumerate(enumerator, &ike_sa))
519 {
520 bool match = FALSE;
521
522 vips = ike_sa->create_virtual_ip_enumerator(ike_sa, FALSE);
523 while (vips->enumerate(vips, &vip))
524 {
525 if (!end)
526 {
527 if (vip->ip_equals(vip, start))
528 {
529 match = TRUE;
530 break;
531 }
532 }
533 else
534 {
535 chunk = vip->get_address(vip);
536 if (chunk.len == chunk_start.len &&
537 chunk.len == chunk_end.len &&
538 memcmp(chunk.ptr, chunk_start.ptr, chunk.len) >= 0 &&
539 memcmp(chunk.ptr, chunk_end.ptr, chunk.len) <= 0)
540 {
541 match = TRUE;
542 break;
543 }
544 }
545 }
546 vips->destroy(vips);
547
548 if (match)
549 {
550 /* schedule delete asynchronously */
551 lib->processor->queue_job(lib->processor, (job_t*)
552 delete_ike_sa_job_create(ike_sa->get_id(ike_sa), TRUE));
553 }
554 }
555 enumerator->destroy(enumerator);
556 start->destroy(start);
557 DESTROY_IF(end);
558 }
559
560 METHOD(stroke_control_t, purge_ike, void,
561 private_stroke_control_t *this, stroke_msg_t *msg, FILE *out)
562 {
563 enumerator_t *enumerator, *children;
564 ike_sa_t *ike_sa;
565 child_sa_t *child_sa;
566 linked_list_t *list;
567 uintptr_t del;
568
569 list = linked_list_create();
570 enumerator = charon->controller->create_ike_sa_enumerator(
571 charon->controller, TRUE);
572 while (enumerator->enumerate(enumerator, &ike_sa))
573 {
574 children = ike_sa->create_child_sa_enumerator(ike_sa);
575 if (!children->enumerate(children, (void**)&child_sa))
576 {
577 list->insert_last(list,
578 (void*)(uintptr_t)ike_sa->get_unique_id(ike_sa));
579 }
580 children->destroy(children);
581 }
582 enumerator->destroy(enumerator);
583
584 enumerator = list->create_enumerator(list);
585 while (enumerator->enumerate(enumerator, &del))
586 {
587 charon_terminate(this, del, msg, out, FALSE);
588 }
589 enumerator->destroy(enumerator);
590 list->destroy(list);
591 }
592
593 /**
594 * call charon to install a shunt or trap
595 */
596 static void charon_route(peer_cfg_t *peer_cfg, child_cfg_t *child_cfg,
597 char *name, FILE *out)
598 {
599 ipsec_mode_t mode;
600
601 mode = child_cfg->get_mode(child_cfg);
602 if (mode == MODE_PASS || mode == MODE_DROP)
603 {
604 if (charon->shunts->install(charon->shunts,
605 peer_cfg->get_name(peer_cfg), child_cfg))
606 {
607 fprintf(out, "'%s' shunt %N policy installed\n",
608 name, ipsec_mode_names, mode);
609 }
610 else
611 {
612 fprintf(out, "'%s' shunt %N policy installation failed\n",
613 name, ipsec_mode_names, mode);
614 }
615 }
616 else
617 {
618 if (charon->traps->install(charon->traps, peer_cfg, child_cfg))
619 {
620 fprintf(out, "'%s' routed\n", name);
621 }
622 else
623 {
624 fprintf(out, "routing '%s' failed\n", name);
625 }
626 }
627 }
628
629 METHOD(stroke_control_t, route, void,
630 private_stroke_control_t *this, stroke_msg_t *msg, FILE *out)
631 {
632 child_cfg_t *child_cfg = NULL;
633 peer_cfg_t *peer_cfg;
634 enumerator_t *enumerator;
635 bool empty = TRUE;
636
637 peer_cfg = charon->backends->get_peer_cfg_by_name(charon->backends,
638 msg->route.name);
639 if (peer_cfg)
640 {
641 child_cfg = get_child_from_peer(peer_cfg, msg->route.name);
642 if (child_cfg == NULL)
643 {
644 enumerator = peer_cfg->create_child_cfg_enumerator(peer_cfg);
645 while (enumerator->enumerate(enumerator, &child_cfg))
646 {
647 empty = FALSE;
648 charon_route(peer_cfg, child_cfg, child_cfg->get_name(child_cfg),
649 out);
650 }
651 enumerator->destroy(enumerator);
652
653 if (empty)
654 {
655 DBG1(DBG_CFG, "no child config named '%s'", msg->route.name);
656 fprintf(out, "no child config named '%s'\n", msg->route.name);
657 }
658 peer_cfg->destroy(peer_cfg);
659 return;
660 }
661 }
662 else
663 {
664 enumerator = charon->backends->create_peer_cfg_enumerator(
665 charon->backends, NULL, NULL, NULL, NULL, IKE_ANY);
666 while (enumerator->enumerate(enumerator, &peer_cfg))
667 {
668 child_cfg = get_child_from_peer(peer_cfg, msg->route.name);
669 if (child_cfg)
670 {
671 peer_cfg->get_ref(peer_cfg);
672 break;
673 }
674 }
675 enumerator->destroy(enumerator);
676
677 if (child_cfg == NULL)
678 {
679 DBG1(DBG_CFG, "no config named '%s'", msg->route.name);
680 fprintf(out, "no config named '%s'\n", msg->route.name);
681 return;
682 }
683 }
684 charon_route(peer_cfg, child_cfg, msg->route.name, out);
685 peer_cfg->destroy(peer_cfg);
686 child_cfg->destroy(child_cfg);
687 }
688
689 METHOD(stroke_control_t, unroute, void,
690 private_stroke_control_t *this, stroke_msg_t *msg, FILE *out)
691 {
692 if (charon->shunts->uninstall(charon->shunts, NULL, msg->unroute.name))
693 {
694 fprintf(out, "shunt policy '%s' uninstalled\n", msg->unroute.name);
695 }
696 else if (charon->traps->uninstall(charon->traps, NULL, msg->unroute.name))
697 {
698 fprintf(out, "trap policy '%s' unrouted\n", msg->unroute.name);
699 }
700 else
701 {
702 fprintf(out, "configuration '%s' not found\n", msg->unroute.name);
703 }
704 }
705
706 METHOD(stroke_control_t, destroy, void,
707 private_stroke_control_t *this)
708 {
709 free(this);
710 }
711
712 /*
713 * see header file
714 */
715 stroke_control_t *stroke_control_create()
716 {
717 private_stroke_control_t *this;
718
719 INIT(this,
720 .public = {
721 .initiate = _initiate,
722 .terminate = _terminate,
723 .terminate_srcip = _terminate_srcip,
724 .rekey = _rekey,
725 .purge_ike = _purge_ike,
726 .route = _route,
727 .unroute = _unroute,
728 .destroy = _destroy,
729 },
730 .timeout = lib->settings->get_int(lib->settings,
731 "%s.plugins.stroke.timeout", 0, lib->ns),
732 );
733
734 return &this->public;
735 }