stroke: Allow specifying the ipsec.secrets location in strongswan.conf
[strongswan.git] / src / libcharon / plugins / stroke / stroke_control.c
1 /*
2 * Copyright (C) 2013 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);
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);
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 METHOD(stroke_control_t, terminate, void,
302 private_stroke_control_t *this, stroke_msg_t *msg, FILE *out)
303 {
304 char *name;
305 u_int32_t id;
306 bool child, all;
307 ike_sa_t *ike_sa;
308 enumerator_t *enumerator;
309 linked_list_t *ike_list, *child_list;
310 stroke_log_info_t info;
311 uintptr_t del;
312 status_t status;
313
314 if (!parse_specifier(msg->terminate.name, &id, &name, &child, &all))
315 {
316 DBG1(DBG_CFG, "error parsing specifier string");
317 return;
318 }
319
320 info.out = out;
321 info.level = msg->output_verbosity;
322
323 if (id)
324 {
325 if (child)
326 {
327 status = charon->controller->terminate_child(charon->controller, id,
328 (controller_cb_t)stroke_log, &info, this->timeout);
329 }
330 else
331 {
332 status = charon->controller->terminate_ike(charon->controller, id,
333 (controller_cb_t)stroke_log, &info, this->timeout);
334 }
335 return report_terminate_status(this, status, out, id, child);
336 }
337
338 ike_list = linked_list_create();
339 child_list = linked_list_create();
340 enumerator = charon->controller->create_ike_sa_enumerator(
341 charon->controller, TRUE);
342 while (enumerator->enumerate(enumerator, &ike_sa))
343 {
344 child_sa_t *child_sa;
345 enumerator_t *children;
346
347 if (child)
348 {
349 children = ike_sa->create_child_sa_enumerator(ike_sa);
350 while (children->enumerate(children, (void**)&child_sa))
351 {
352 if (streq(name, child_sa->get_name(child_sa)))
353 {
354 child_list->insert_last(child_list,
355 (void*)(uintptr_t)child_sa->get_reqid(child_sa));
356 if (!all)
357 {
358 break;
359 }
360 }
361 }
362 children->destroy(children);
363 if (child_list->get_count(child_list) && !all)
364 {
365 break;
366 }
367 }
368 else if (streq(name, ike_sa->get_name(ike_sa)))
369 {
370 ike_list->insert_last(ike_list,
371 (void*)(uintptr_t)ike_sa->get_unique_id(ike_sa));
372 if (!all)
373 {
374 break;
375 }
376 }
377 }
378 enumerator->destroy(enumerator);
379
380 enumerator = child_list->create_enumerator(child_list);
381 while (enumerator->enumerate(enumerator, &del))
382 {
383 status = charon->controller->terminate_child(charon->controller, del,
384 (controller_cb_t)stroke_log, &info, this->timeout);
385 report_terminate_status(this, status, out, del, TRUE);
386 }
387 enumerator->destroy(enumerator);
388
389 enumerator = ike_list->create_enumerator(ike_list);
390 while (enumerator->enumerate(enumerator, &del))
391 {
392 status = charon->controller->terminate_ike(charon->controller, del,
393 (controller_cb_t)stroke_log, &info, this->timeout);
394 report_terminate_status(this, status, out, del, FALSE);
395 }
396 enumerator->destroy(enumerator);
397
398 if (child_list->get_count(child_list) == 0 &&
399 ike_list->get_count(ike_list) == 0)
400 {
401 DBG1(DBG_CFG, "no %s_SA named '%s' found",
402 child ? "CHILD" : "IKE", name);
403 }
404 ike_list->destroy(ike_list);
405 child_list->destroy(child_list);
406 }
407
408 METHOD(stroke_control_t, rekey, void,
409 private_stroke_control_t *this, stroke_msg_t *msg, FILE *out)
410 {
411 char *name;
412 u_int32_t id;
413 bool child, all, finished = FALSE;
414 ike_sa_t *ike_sa;
415 enumerator_t *enumerator;
416
417 if (!parse_specifier(msg->terminate.name, &id, &name, &child, &all))
418 {
419 DBG1(DBG_CFG, "error parsing specifier string");
420 return;
421 }
422 enumerator = charon->controller->create_ike_sa_enumerator(
423 charon->controller, TRUE);
424 while (enumerator->enumerate(enumerator, &ike_sa))
425 {
426 child_sa_t *child_sa;
427 enumerator_t *children;
428
429 if (child)
430 {
431 children = ike_sa->create_child_sa_enumerator(ike_sa);
432 while (children->enumerate(children, (void**)&child_sa))
433 {
434 if ((name && streq(name, child_sa->get_name(child_sa))) ||
435 (id && id == child_sa->get_reqid(child_sa)))
436 {
437 lib->processor->queue_job(lib->processor,
438 (job_t*)rekey_child_sa_job_create(
439 child_sa->get_reqid(child_sa),
440 child_sa->get_protocol(child_sa),
441 child_sa->get_spi(child_sa, TRUE)));
442 if (!all)
443 {
444 finished = TRUE;
445 break;
446 }
447 }
448 }
449 children->destroy(children);
450 }
451 else if ((name && streq(name, ike_sa->get_name(ike_sa))) ||
452 (id && id == ike_sa->get_unique_id(ike_sa)))
453 {
454 lib->processor->queue_job(lib->processor,
455 (job_t*)rekey_ike_sa_job_create(ike_sa->get_id(ike_sa), FALSE));
456 if (!all)
457 {
458 finished = TRUE;
459 }
460 }
461 if (finished)
462 {
463 break;
464 }
465 }
466 enumerator->destroy(enumerator);
467 }
468
469 METHOD(stroke_control_t, terminate_srcip, void,
470 private_stroke_control_t *this, stroke_msg_t *msg, FILE *out)
471 {
472 enumerator_t *enumerator, *vips;
473 ike_sa_t *ike_sa;
474 host_t *start = NULL, *end = NULL, *vip;
475 chunk_t chunk_start, chunk_end = chunk_empty, chunk;
476
477 if (msg->terminate_srcip.start)
478 {
479 start = host_create_from_string(msg->terminate_srcip.start, 0);
480 }
481 if (!start)
482 {
483 DBG1(DBG_CFG, "invalid start address: %s", msg->terminate_srcip.start);
484 return;
485 }
486 chunk_start = start->get_address(start);
487 if (msg->terminate_srcip.end)
488 {
489 end = host_create_from_string(msg->terminate_srcip.end, 0);
490 if (!end)
491 {
492 DBG1(DBG_CFG, "invalid end address: %s", msg->terminate_srcip.end);
493 start->destroy(start);
494 return;
495 }
496 chunk_end = end->get_address(end);
497 }
498
499 enumerator = charon->controller->create_ike_sa_enumerator(
500 charon->controller, TRUE);
501 while (enumerator->enumerate(enumerator, &ike_sa))
502 {
503 bool match = FALSE;
504
505 vips = ike_sa->create_virtual_ip_enumerator(ike_sa, FALSE);
506 while (vips->enumerate(vips, &vip))
507 {
508 if (!end)
509 {
510 if (vip->ip_equals(vip, start))
511 {
512 match = TRUE;
513 break;
514 }
515 }
516 else
517 {
518 chunk = vip->get_address(vip);
519 if (chunk.len == chunk_start.len &&
520 chunk.len == chunk_end.len &&
521 memcmp(chunk.ptr, chunk_start.ptr, chunk.len) >= 0 &&
522 memcmp(chunk.ptr, chunk_end.ptr, chunk.len) <= 0)
523 {
524 match = TRUE;
525 break;
526 }
527 }
528 }
529 vips->destroy(vips);
530
531 if (match)
532 {
533 /* schedule delete asynchronously */
534 lib->processor->queue_job(lib->processor, (job_t*)
535 delete_ike_sa_job_create(ike_sa->get_id(ike_sa), TRUE));
536 }
537 }
538 enumerator->destroy(enumerator);
539 start->destroy(start);
540 DESTROY_IF(end);
541 }
542
543 METHOD(stroke_control_t, purge_ike, void,
544 private_stroke_control_t *this, stroke_msg_t *msg, FILE *out)
545 {
546 enumerator_t *enumerator, *children;
547 ike_sa_t *ike_sa;
548 child_sa_t *child_sa;
549 linked_list_t *list;
550 uintptr_t del;
551 stroke_log_info_t info;
552 status_t status;
553
554 info.out = out;
555 info.level = msg->output_verbosity;
556
557 list = linked_list_create();
558 enumerator = charon->controller->create_ike_sa_enumerator(
559 charon->controller, TRUE);
560 while (enumerator->enumerate(enumerator, &ike_sa))
561 {
562 children = ike_sa->create_child_sa_enumerator(ike_sa);
563 if (!children->enumerate(children, (void**)&child_sa))
564 {
565 list->insert_last(list,
566 (void*)(uintptr_t)ike_sa->get_unique_id(ike_sa));
567 }
568 children->destroy(children);
569 }
570 enumerator->destroy(enumerator);
571
572 enumerator = list->create_enumerator(list);
573 while (enumerator->enumerate(enumerator, &del))
574 {
575 status = charon->controller->terminate_ike(charon->controller, del,
576 (controller_cb_t)stroke_log, &info, this->timeout);
577 report_terminate_status(this, status, out, del, TRUE);
578 }
579 enumerator->destroy(enumerator);
580 list->destroy(list);
581 }
582
583 /**
584 * Find an existing CHILD_SA/reqid
585 */
586 static u_int32_t find_reqid(child_cfg_t *child_cfg)
587 {
588 enumerator_t *enumerator, *children;
589 child_sa_t *child_sa;
590 ike_sa_t *ike_sa;
591 char *name;
592 u_int32_t reqid;
593
594 reqid = charon->traps->find_reqid(charon->traps, child_cfg);
595 if (reqid)
596 { /* already trapped */
597 return reqid;
598 }
599
600 name = child_cfg->get_name(child_cfg);
601 enumerator = charon->controller->create_ike_sa_enumerator(
602 charon->controller, TRUE);
603 while (enumerator->enumerate(enumerator, &ike_sa))
604 {
605 children = ike_sa->create_child_sa_enumerator(ike_sa);
606 while (children->enumerate(children, (void**)&child_sa))
607 {
608 if (streq(name, child_sa->get_name(child_sa)))
609 {
610 reqid = child_sa->get_reqid(child_sa);
611 break;
612 }
613 }
614 children->destroy(children);
615 if (reqid)
616 {
617 break;
618 }
619 }
620 enumerator->destroy(enumerator);
621 return reqid;
622 }
623
624 /**
625 * call charon to install a shunt or trap
626 */
627 static void charon_route(peer_cfg_t *peer_cfg, child_cfg_t *child_cfg,
628 char *name, FILE *out)
629 {
630 ipsec_mode_t mode;
631 u_int32_t reqid;
632
633 mode = child_cfg->get_mode(child_cfg);
634 if (mode == MODE_PASS || mode == MODE_DROP)
635 {
636 if (charon->shunts->install(charon->shunts, child_cfg))
637 {
638 fprintf(out, "'%s' shunt %N policy installed\n",
639 name, ipsec_mode_names, mode);
640 }
641 else
642 {
643 fprintf(out, "'%s' shunt %N policy installation failed\n",
644 name, ipsec_mode_names, mode);
645 }
646 }
647 else
648 {
649 reqid = find_reqid(child_cfg);
650 if (charon->traps->install(charon->traps, peer_cfg, child_cfg, reqid))
651 {
652 fprintf(out, "'%s' routed\n", name);
653 }
654 else
655 {
656 fprintf(out, "routing '%s' failed\n", name);
657 }
658 }
659 }
660
661 METHOD(stroke_control_t, route, void,
662 private_stroke_control_t *this, stroke_msg_t *msg, FILE *out)
663 {
664 child_cfg_t *child_cfg = NULL;
665 peer_cfg_t *peer_cfg;
666 enumerator_t *enumerator;
667 bool empty = TRUE;
668
669 peer_cfg = charon->backends->get_peer_cfg_by_name(charon->backends,
670 msg->route.name);
671 if (peer_cfg)
672 {
673 child_cfg = get_child_from_peer(peer_cfg, msg->route.name);
674 if (child_cfg == NULL)
675 {
676 enumerator = peer_cfg->create_child_cfg_enumerator(peer_cfg);
677 while (enumerator->enumerate(enumerator, &child_cfg))
678 {
679 empty = FALSE;
680 charon_route(peer_cfg, child_cfg, child_cfg->get_name(child_cfg),
681 out);
682 }
683 enumerator->destroy(enumerator);
684
685 if (empty)
686 {
687 DBG1(DBG_CFG, "no child config named '%s'", msg->route.name);
688 fprintf(out, "no child config named '%s'\n", msg->route.name);
689 }
690 peer_cfg->destroy(peer_cfg);
691 return;
692 }
693 }
694 else
695 {
696 enumerator = charon->backends->create_peer_cfg_enumerator(
697 charon->backends, NULL, NULL, NULL, NULL, IKE_ANY);
698 while (enumerator->enumerate(enumerator, &peer_cfg))
699 {
700 child_cfg = get_child_from_peer(peer_cfg, msg->route.name);
701 if (child_cfg)
702 {
703 peer_cfg->get_ref(peer_cfg);
704 break;
705 }
706 }
707 enumerator->destroy(enumerator);
708
709 if (child_cfg == NULL)
710 {
711 DBG1(DBG_CFG, "no config named '%s'", msg->route.name);
712 fprintf(out, "no config named '%s'\n", msg->route.name);
713 return;
714 }
715 }
716 charon_route(peer_cfg, child_cfg, msg->route.name, out);
717 peer_cfg->destroy(peer_cfg);
718 child_cfg->destroy(child_cfg);
719 }
720
721 METHOD(stroke_control_t, unroute, void,
722 private_stroke_control_t *this, stroke_msg_t *msg, FILE *out)
723 {
724 child_sa_t *child_sa;
725 enumerator_t *enumerator;
726 u_int32_t id = 0;
727
728 if (charon->shunts->uninstall(charon->shunts, msg->unroute.name))
729 {
730 fprintf(out, "shunt policy '%s' uninstalled\n", msg->unroute.name);
731 return;
732 }
733
734 enumerator = charon->traps->create_enumerator(charon->traps);
735 while (enumerator->enumerate(enumerator, NULL, &child_sa))
736 {
737 if (streq(msg->unroute.name, child_sa->get_name(child_sa)))
738 {
739 id = child_sa->get_reqid(child_sa);
740 break;
741 }
742 }
743 enumerator->destroy(enumerator);
744
745 if (id)
746 {
747 charon->traps->uninstall(charon->traps, id);
748 fprintf(out, "configuration '%s' unrouted\n", msg->unroute.name);
749 }
750 else
751 {
752 fprintf(out, "configuration '%s' not found\n", msg->unroute.name);
753 }
754 }
755
756 METHOD(stroke_control_t, destroy, void,
757 private_stroke_control_t *this)
758 {
759 free(this);
760 }
761
762 /*
763 * see header file
764 */
765 stroke_control_t *stroke_control_create()
766 {
767 private_stroke_control_t *this;
768
769 INIT(this,
770 .public = {
771 .initiate = _initiate,
772 .terminate = _terminate,
773 .terminate_srcip = _terminate_srcip,
774 .rekey = _rekey,
775 .purge_ike = _purge_ike,
776 .route = _route,
777 .unroute = _unroute,
778 .destroy = _destroy,
779 },
780 .timeout = lib->settings->get_int(lib->settings,
781 "%s.plugins.stroke.timeout", 0, lib->ns),
782 );
783
784 return &this->public;
785 }