]> git.ipfire.org Git - thirdparty/strongswan.git/blob - src/libcharon/plugins/stroke/stroke_control.c
implemented PASS and DROP shunt policies
[thirdparty/strongswan.git] / src / libcharon / plugins / stroke / stroke_control.c
1 /*
2 * Copyright (C) 2008 Martin Willi
3 * Hochschule fuer Technik Rapperswil
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15
16 #include "stroke_control.h"
17
18 #include <hydra.h>
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
39 typedef struct stroke_log_info_t stroke_log_info_t;
40
41 /**
42 * helper struct to say what and where to log when using controller callback
43 */
44 struct stroke_log_info_t {
45
46 /**
47 * level to log up to
48 */
49 level_t level;
50
51 /**
52 * where to write log
53 */
54 FILE* out;
55 };
56
57 /**
58 * logging to the stroke interface
59 */
60 static bool stroke_log(stroke_log_info_t *info, debug_t group, level_t level,
61 ike_sa_t *ike_sa, char *format, va_list args)
62 {
63 if (level <= info->level)
64 {
65 if (vfprintf(info->out, format, args) < 0 ||
66 fprintf(info->out, "\n") < 0 ||
67 fflush(info->out) != 0)
68 {
69 return FALSE;
70 }
71 }
72 return TRUE;
73 }
74
75 /**
76 * get the child_cfg with the same name as the peer cfg
77 */
78 static child_cfg_t* get_child_from_peer(peer_cfg_t *peer_cfg, char *name)
79 {
80 child_cfg_t *current, *found = NULL;
81 enumerator_t *enumerator;
82
83 enumerator = peer_cfg->create_child_cfg_enumerator(peer_cfg);
84 while (enumerator->enumerate(enumerator, &current))
85 {
86 if (streq(current->get_name(current), name))
87 {
88 found = current;
89 found->get_ref(found);
90 break;
91 }
92 }
93 enumerator->destroy(enumerator);
94 return found;
95 }
96
97 /**
98 * call the charon controller to initiate the connection
99 */
100 static void charon_initiate(peer_cfg_t *peer_cfg, child_cfg_t *child_cfg,
101 stroke_msg_t *msg, FILE *out)
102 {
103 if (msg->output_verbosity < 0)
104 {
105 charon->controller->initiate(charon->controller, peer_cfg, child_cfg,
106 NULL, NULL);
107 }
108 else
109 {
110 stroke_log_info_t info = { msg->output_verbosity, out };
111
112 charon->controller->initiate(charon->controller, peer_cfg, child_cfg,
113 (controller_cb_t)stroke_log, &info);
114 }
115 }
116
117 METHOD(stroke_control_t, initiate, void,
118 private_stroke_control_t *this, stroke_msg_t *msg, FILE *out)
119 {
120 child_cfg_t *child_cfg = NULL;
121 peer_cfg_t *peer_cfg;
122 enumerator_t *enumerator;
123 bool empty = TRUE;
124
125 peer_cfg = charon->backends->get_peer_cfg_by_name(charon->backends,
126 msg->initiate.name);
127 if (peer_cfg)
128 {
129 if (peer_cfg->get_ike_version(peer_cfg) != 2)
130 {
131 DBG1(DBG_CFG, "ignoring initiation request for IKEv%d config",
132 peer_cfg->get_ike_version(peer_cfg));
133 peer_cfg->destroy(peer_cfg);
134 return;
135 }
136
137 child_cfg = get_child_from_peer(peer_cfg, msg->initiate.name);
138 if (child_cfg == NULL)
139 {
140 enumerator = peer_cfg->create_child_cfg_enumerator(peer_cfg);
141 while (enumerator->enumerate(enumerator, &child_cfg))
142 {
143 empty = FALSE;
144 charon_initiate(peer_cfg->get_ref(peer_cfg),
145 child_cfg->get_ref(child_cfg), msg, out);
146 }
147 enumerator->destroy(enumerator);
148
149 if (empty)
150 {
151 DBG1(DBG_CFG, "no child config named '%s'", msg->initiate.name);
152 fprintf(out, "no child config named '%s'\n", msg->initiate.name);
153 }
154 peer_cfg->destroy(peer_cfg);
155 return;
156 }
157 }
158 else
159 {
160 enumerator = charon->backends->create_peer_cfg_enumerator(charon->backends,
161 NULL, NULL, NULL, NULL);
162 while (enumerator->enumerate(enumerator, &peer_cfg))
163 {
164 if (peer_cfg->get_ike_version(peer_cfg) != 2)
165 {
166 continue;
167 }
168 child_cfg = get_child_from_peer(peer_cfg, msg->initiate.name);
169 if (child_cfg)
170 {
171 peer_cfg->get_ref(peer_cfg);
172 break;
173 }
174 }
175 enumerator->destroy(enumerator);
176
177 if (child_cfg == NULL)
178 {
179 DBG1(DBG_CFG, "no config named '%s'", msg->initiate.name);
180 fprintf(out, "no config named '%s'\n", msg->initiate.name);
181 return;
182 }
183 }
184 charon_initiate(peer_cfg, child_cfg, msg, out);
185 }
186
187 /**
188 * Parse a terminate/rekey specifier
189 */
190 static bool parse_specifier(char *string, u_int32_t *id,
191 char **name, bool *child, bool *all)
192 {
193 int len;
194 char *pos = NULL;
195
196 *id = 0;
197 *name = NULL;
198 *all = FALSE;
199
200 len = strlen(string);
201 if (len < 1)
202 {
203 return FALSE;
204 }
205 switch (string[len-1])
206 {
207 case '}':
208 *child = TRUE;
209 pos = strchr(string, '{');
210 break;
211 case ']':
212 *child = FALSE;
213 pos = strchr(string, '[');
214 break;
215 default:
216 *name = string;
217 *child = FALSE;
218 break;
219 }
220
221 if (*name)
222 {
223 /* is a single name */
224 }
225 else if (pos == string + len - 2)
226 { /* is name[] or name{} */
227 string[len-2] = '\0';
228 *name = string;
229 }
230 else
231 {
232 if (!pos)
233 {
234 return FALSE;
235 }
236 if (*(pos + 1) == '*')
237 { /* is name[*] */
238 *all = TRUE;
239 *pos = '\0';
240 *name = string;
241 }
242 else
243 { /* is name[123] or name{23} */
244 *id = atoi(pos + 1);
245 if (*id == 0)
246 {
247 return FALSE;
248 }
249 }
250 }
251 return TRUE;
252 }
253
254 METHOD(stroke_control_t, terminate, void,
255 private_stroke_control_t *this, stroke_msg_t *msg, FILE *out)
256 {
257 char *name;
258 u_int32_t id;
259 bool child, all;
260 ike_sa_t *ike_sa;
261 enumerator_t *enumerator;
262 linked_list_t *ike_list, *child_list;
263 stroke_log_info_t info;
264 uintptr_t del;
265
266 if (!parse_specifier(msg->terminate.name, &id, &name, &child, &all))
267 {
268 DBG1(DBG_CFG, "error parsing specifier string");
269 return;
270 }
271
272 info.out = out;
273 info.level = msg->output_verbosity;
274
275 if (id)
276 {
277 if (child)
278 {
279 charon->controller->terminate_child(charon->controller, id,
280 (controller_cb_t)stroke_log, &info);
281 }
282 else
283 {
284 charon->controller->terminate_ike(charon->controller, id,
285 (controller_cb_t)stroke_log, &info);
286 }
287 return;
288 }
289
290 ike_list = linked_list_create();
291 child_list = linked_list_create();
292 enumerator = charon->controller->create_ike_sa_enumerator(
293 charon->controller, TRUE);
294 while (enumerator->enumerate(enumerator, &ike_sa))
295 {
296 child_sa_t *child_sa;
297 iterator_t *children;
298
299 if (child)
300 {
301 children = ike_sa->create_child_sa_iterator(ike_sa);
302 while (children->iterate(children, (void**)&child_sa))
303 {
304 if (streq(name, child_sa->get_name(child_sa)))
305 {
306 child_list->insert_last(child_list,
307 (void*)(uintptr_t)child_sa->get_reqid(child_sa));
308 if (!all)
309 {
310 break;
311 }
312 }
313 }
314 children->destroy(children);
315 if (child_list->get_count(child_list) && !all)
316 {
317 break;
318 }
319 }
320 else if (streq(name, ike_sa->get_name(ike_sa)))
321 {
322 ike_list->insert_last(ike_list,
323 (void*)(uintptr_t)ike_sa->get_unique_id(ike_sa));
324 if (!all)
325 {
326 break;
327 }
328 }
329 }
330 enumerator->destroy(enumerator);
331
332 enumerator = child_list->create_enumerator(child_list);
333 while (enumerator->enumerate(enumerator, &del))
334 {
335 charon->controller->terminate_child(charon->controller, del,
336 (controller_cb_t)stroke_log, &info);
337 }
338 enumerator->destroy(enumerator);
339
340 enumerator = ike_list->create_enumerator(ike_list);
341 while (enumerator->enumerate(enumerator, &del))
342 {
343 charon->controller->terminate_ike(charon->controller, del,
344 (controller_cb_t)stroke_log, &info);
345 }
346 enumerator->destroy(enumerator);
347
348 if (child_list->get_count(child_list) == 0 &&
349 ike_list->get_count(ike_list) == 0)
350 {
351 DBG1(DBG_CFG, "no %s_SA named '%s' found",
352 child ? "CHILD" : "IKE", name);
353 }
354 ike_list->destroy(ike_list);
355 child_list->destroy(child_list);
356 }
357
358 METHOD(stroke_control_t, rekey, void,
359 private_stroke_control_t *this, stroke_msg_t *msg, FILE *out)
360 {
361 char *name;
362 u_int32_t id;
363 bool child, all, finished = FALSE;
364 ike_sa_t *ike_sa;
365 enumerator_t *enumerator;
366
367 if (!parse_specifier(msg->terminate.name, &id, &name, &child, &all))
368 {
369 DBG1(DBG_CFG, "error parsing specifier string");
370 return;
371 }
372 enumerator = charon->controller->create_ike_sa_enumerator(
373 charon->controller, TRUE);
374 while (enumerator->enumerate(enumerator, &ike_sa))
375 {
376 child_sa_t *child_sa;
377 iterator_t *children;
378
379 if (child)
380 {
381 children = ike_sa->create_child_sa_iterator(ike_sa);
382 while (children->iterate(children, (void**)&child_sa))
383 {
384 if ((name && streq(name, child_sa->get_name(child_sa))) ||
385 (id && id == child_sa->get_reqid(child_sa)))
386 {
387 lib->processor->queue_job(lib->processor,
388 (job_t*)rekey_child_sa_job_create(
389 child_sa->get_reqid(child_sa),
390 child_sa->get_protocol(child_sa),
391 child_sa->get_spi(child_sa, TRUE)));
392 if (!all)
393 {
394 finished = TRUE;
395 break;
396 }
397 }
398 }
399 children->destroy(children);
400 }
401 else if ((name && streq(name, ike_sa->get_name(ike_sa))) ||
402 (id && id == ike_sa->get_unique_id(ike_sa)))
403 {
404 lib->processor->queue_job(lib->processor,
405 (job_t*)rekey_ike_sa_job_create(ike_sa->get_id(ike_sa), FALSE));
406 if (!all)
407 {
408 finished = TRUE;
409 }
410 }
411 if (finished)
412 {
413 break;
414 }
415 }
416 enumerator->destroy(enumerator);
417 }
418
419 METHOD(stroke_control_t, terminate_srcip, void,
420 private_stroke_control_t *this, stroke_msg_t *msg, FILE *out)
421 {
422 enumerator_t *enumerator;
423 ike_sa_t *ike_sa;
424 host_t *start = NULL, *end = NULL, *vip;
425 chunk_t chunk_start, chunk_end = chunk_empty, chunk_vip;
426
427 if (msg->terminate_srcip.start)
428 {
429 start = host_create_from_string(msg->terminate_srcip.start, 0);
430 }
431 if (!start)
432 {
433 DBG1(DBG_CFG, "invalid start address: %s", msg->terminate_srcip.start);
434 return;
435 }
436 chunk_start = start->get_address(start);
437 if (msg->terminate_srcip.end)
438 {
439 end = host_create_from_string(msg->terminate_srcip.end, 0);
440 if (!end)
441 {
442 DBG1(DBG_CFG, "invalid end address: %s", msg->terminate_srcip.end);
443 start->destroy(start);
444 return;
445 }
446 chunk_end = end->get_address(end);
447 }
448
449 enumerator = charon->controller->create_ike_sa_enumerator(
450 charon->controller, TRUE);
451 while (enumerator->enumerate(enumerator, &ike_sa))
452 {
453 vip = ike_sa->get_virtual_ip(ike_sa, FALSE);
454 if (!vip)
455 {
456 continue;
457 }
458 if (!end)
459 {
460 if (!vip->ip_equals(vip, start))
461 {
462 continue;
463 }
464 }
465 else
466 {
467 chunk_vip = vip->get_address(vip);
468 if (chunk_vip.len != chunk_start.len ||
469 chunk_vip.len != chunk_end.len ||
470 memcmp(chunk_vip.ptr, chunk_start.ptr, chunk_vip.len) < 0 ||
471 memcmp(chunk_vip.ptr, chunk_end.ptr, chunk_vip.len) > 0)
472 {
473 continue;
474 }
475 }
476
477 /* schedule delete asynchronously */
478 lib->processor->queue_job(lib->processor, (job_t*)
479 delete_ike_sa_job_create(ike_sa->get_id(ike_sa), TRUE));
480 }
481 enumerator->destroy(enumerator);
482 start->destroy(start);
483 DESTROY_IF(end);
484 }
485
486 METHOD(stroke_control_t, purge_ike, void,
487 private_stroke_control_t *this, stroke_msg_t *msg, FILE *out)
488 {
489 enumerator_t *enumerator;
490 iterator_t *iterator;
491 ike_sa_t *ike_sa;
492 child_sa_t *child_sa;
493 linked_list_t *list;
494 uintptr_t del;
495 stroke_log_info_t info;
496
497 info.out = out;
498 info.level = msg->output_verbosity;
499
500 list = linked_list_create();
501 enumerator = charon->controller->create_ike_sa_enumerator(
502 charon->controller, TRUE);
503 while (enumerator->enumerate(enumerator, &ike_sa))
504 {
505 iterator = ike_sa->create_child_sa_iterator(ike_sa);
506 if (!iterator->iterate(iterator, (void**)&child_sa))
507 {
508 list->insert_last(list,
509 (void*)(uintptr_t)ike_sa->get_unique_id(ike_sa));
510 }
511 iterator->destroy(iterator);
512 }
513 enumerator->destroy(enumerator);
514
515 enumerator = list->create_enumerator(list);
516 while (enumerator->enumerate(enumerator, &del))
517 {
518 charon->controller->terminate_ike(charon->controller, del,
519 (controller_cb_t)stroke_log, &info);
520 }
521 enumerator->destroy(enumerator);
522 list->destroy(list);
523 }
524
525 /**
526 * call charon to install a shunt or trap
527 */
528 static void charon_route(peer_cfg_t *peer_cfg, child_cfg_t *child_cfg,
529 char *name, FILE *out)
530 {
531 ipsec_mode_t mode;
532
533 mode = child_cfg->get_mode(child_cfg);
534 if (mode == MODE_PASS || mode == MODE_DROP)
535 {
536 if (charon->shunts->install(charon->shunts, child_cfg))
537 {
538 fprintf(out, "'%s' shunt %N policy installed\n",
539 name, ipsec_mode_names, mode);
540 }
541 else
542 {
543 fprintf(out, "'%s' shunt %N policy installation failed\n",
544 name, ipsec_mode_names, mode);
545 }
546 }
547 else
548 {
549 if (charon->traps->install(charon->traps, peer_cfg, child_cfg))
550 {
551 fprintf(out, "'%s' routed\n", name);
552 }
553 else
554 {
555 fprintf(out, "routing '%s' failed\n", name);
556 }
557 }
558 }
559
560 METHOD(stroke_control_t, route, void,
561 private_stroke_control_t *this, stroke_msg_t *msg, FILE *out)
562 {
563 child_cfg_t *child_cfg = NULL;
564 peer_cfg_t *peer_cfg;
565 enumerator_t *enumerator;
566 bool empty = TRUE;
567
568 peer_cfg = charon->backends->get_peer_cfg_by_name(charon->backends,
569 msg->route.name);
570 if (peer_cfg)
571 {
572 if (peer_cfg->get_ike_version(peer_cfg) != 2)
573 {
574 DBG1(DBG_CFG, "ignoring initiation request for IKEv%d config",
575 peer_cfg->get_ike_version(peer_cfg));
576 peer_cfg->destroy(peer_cfg);
577 return;
578 }
579
580 child_cfg = get_child_from_peer(peer_cfg, msg->route.name);
581 if (child_cfg == NULL)
582 {
583 enumerator = peer_cfg->create_child_cfg_enumerator(peer_cfg);
584 while (enumerator->enumerate(enumerator, &child_cfg))
585 {
586 empty = FALSE;
587 charon_route(peer_cfg, child_cfg, child_cfg->get_name(child_cfg),
588 out);
589 }
590 enumerator->destroy(enumerator);
591
592 if (empty)
593 {
594 DBG1(DBG_CFG, "no child config named '%s'", msg->route.name);
595 fprintf(out, "no child config named '%s'\n", msg->route.name);
596 }
597 peer_cfg->destroy(peer_cfg);
598 return;
599 }
600 }
601 else
602 {
603 enumerator = charon->backends->create_peer_cfg_enumerator(charon->backends,
604 NULL, NULL, NULL, NULL);
605 while (enumerator->enumerate(enumerator, &peer_cfg))
606 {
607 if (peer_cfg->get_ike_version(peer_cfg) != 2)
608 {
609 continue;
610 }
611 child_cfg = get_child_from_peer(peer_cfg, msg->route.name);
612 if (child_cfg)
613 {
614 peer_cfg->get_ref(peer_cfg);
615 break;
616 }
617 }
618 enumerator->destroy(enumerator);
619
620 if (child_cfg == NULL)
621 {
622 DBG1(DBG_CFG, "no config named '%s'", msg->route.name);
623 fprintf(out, "no config named '%s'\n", msg->route.name);
624 return;
625 }
626 }
627 charon_route(peer_cfg, child_cfg, msg->route.name, out);
628 peer_cfg->destroy(peer_cfg);
629 child_cfg->destroy(child_cfg);
630 }
631
632 METHOD(stroke_control_t, unroute, void,
633 private_stroke_control_t *this, stroke_msg_t *msg, FILE *out)
634 {
635 child_sa_t *child_sa;
636 enumerator_t *enumerator;
637 u_int32_t id;
638 bool found = FALSE;
639
640 if (charon->shunts->uninstall(charon->shunts, msg->unroute.name))
641 {
642 fprintf(out, "shunt policy '%s' uninstalled\n", msg->unroute.name);
643 return;
644 }
645
646 enumerator = charon->traps->create_enumerator(charon->traps);
647 while (enumerator->enumerate(enumerator, NULL, &child_sa))
648 {
649 if (streq(msg->unroute.name, child_sa->get_name(child_sa)))
650 {
651 id = child_sa->get_reqid(child_sa);
652 enumerator->destroy(enumerator);
653 charon->traps->uninstall(charon->traps, id);
654 fprintf(out, "configuration '%s' unrouted\n", msg->unroute.name);
655 found = TRUE;
656 }
657 }
658 enumerator->destroy(enumerator);
659
660 if (!found)
661 {
662 fprintf(out, "configuration '%s' not found\n", msg->unroute.name);
663 }
664 }
665
666 METHOD(stroke_control_t, destroy, void,
667 private_stroke_control_t *this)
668 {
669 free(this);
670 }
671
672 /*
673 * see header file
674 */
675 stroke_control_t *stroke_control_create()
676 {
677 private_stroke_control_t *this;
678
679 INIT(this,
680 .public = {
681 .initiate = _initiate,
682 .terminate = _terminate,
683 .terminate_srcip = _terminate_srcip,
684 .rekey = _rekey,
685 .purge_ike = _purge_ike,
686 .route = _route,
687 .unroute = _unroute,
688 .destroy = _destroy,
689 },
690 );
691
692 return &this->public;
693 }
694