163a2a750ac540bc83f58f656309beff189cdeb8
[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 *message)
62 {
63 if (level <= info->level)
64 {
65 if (fprintf(info->out, "%s", message) < 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, 0);
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, 0);
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 child_cfg = get_child_from_peer(peer_cfg, msg->initiate.name);
130 if (child_cfg == NULL)
131 {
132 enumerator = peer_cfg->create_child_cfg_enumerator(peer_cfg);
133 while (enumerator->enumerate(enumerator, &child_cfg))
134 {
135 empty = FALSE;
136 charon_initiate(peer_cfg->get_ref(peer_cfg),
137 child_cfg->get_ref(child_cfg), msg, out);
138 }
139 enumerator->destroy(enumerator);
140
141 if (empty)
142 {
143 DBG1(DBG_CFG, "no child config named '%s'", msg->initiate.name);
144 fprintf(out, "no child config named '%s'\n", msg->initiate.name);
145 }
146 peer_cfg->destroy(peer_cfg);
147 return;
148 }
149 }
150 else
151 {
152 enumerator = charon->backends->create_peer_cfg_enumerator(
153 charon->backends, NULL, NULL, NULL, NULL, IKE_ANY);
154 while (enumerator->enumerate(enumerator, &peer_cfg))
155 {
156 child_cfg = get_child_from_peer(peer_cfg, msg->initiate.name);
157 if (child_cfg)
158 {
159 peer_cfg->get_ref(peer_cfg);
160 break;
161 }
162 }
163 enumerator->destroy(enumerator);
164
165 if (child_cfg == NULL)
166 {
167 DBG1(DBG_CFG, "no config named '%s'", msg->initiate.name);
168 fprintf(out, "no config named '%s'\n", msg->initiate.name);
169 return;
170 }
171 }
172 charon_initiate(peer_cfg, child_cfg, msg, out);
173 }
174
175 /**
176 * Parse a terminate/rekey specifier
177 */
178 static bool parse_specifier(char *string, u_int32_t *id,
179 char **name, bool *child, bool *all)
180 {
181 int len;
182 char *pos = NULL;
183
184 *id = 0;
185 *name = NULL;
186 *all = FALSE;
187
188 len = strlen(string);
189 if (len < 1)
190 {
191 return FALSE;
192 }
193 switch (string[len-1])
194 {
195 case '}':
196 *child = TRUE;
197 pos = strchr(string, '{');
198 break;
199 case ']':
200 *child = FALSE;
201 pos = strchr(string, '[');
202 break;
203 default:
204 *name = string;
205 *child = FALSE;
206 break;
207 }
208
209 if (*name)
210 {
211 /* is a single name */
212 }
213 else if (pos == string + len - 2)
214 { /* is name[] or name{} */
215 string[len-2] = '\0';
216 *name = string;
217 }
218 else
219 {
220 if (!pos)
221 {
222 return FALSE;
223 }
224 if (*(pos + 1) == '*')
225 { /* is name[*] */
226 *all = TRUE;
227 *pos = '\0';
228 *name = string;
229 }
230 else
231 { /* is name[123] or name{23} */
232 *id = atoi(pos + 1);
233 if (*id == 0)
234 {
235 return FALSE;
236 }
237 }
238 }
239 return TRUE;
240 }
241
242 METHOD(stroke_control_t, terminate, void,
243 private_stroke_control_t *this, stroke_msg_t *msg, FILE *out)
244 {
245 char *name;
246 u_int32_t id;
247 bool child, all;
248 ike_sa_t *ike_sa;
249 enumerator_t *enumerator;
250 linked_list_t *ike_list, *child_list;
251 stroke_log_info_t info;
252 uintptr_t del;
253
254 if (!parse_specifier(msg->terminate.name, &id, &name, &child, &all))
255 {
256 DBG1(DBG_CFG, "error parsing specifier string");
257 return;
258 }
259
260 info.out = out;
261 info.level = msg->output_verbosity;
262
263 if (id)
264 {
265 if (child)
266 {
267 charon->controller->terminate_child(charon->controller, id,
268 (controller_cb_t)stroke_log, &info, 0);
269 }
270 else
271 {
272 charon->controller->terminate_ike(charon->controller, id,
273 (controller_cb_t)stroke_log, &info, 0);
274 }
275 return;
276 }
277
278 ike_list = linked_list_create();
279 child_list = linked_list_create();
280 enumerator = charon->controller->create_ike_sa_enumerator(
281 charon->controller, TRUE);
282 while (enumerator->enumerate(enumerator, &ike_sa))
283 {
284 child_sa_t *child_sa;
285 enumerator_t *children;
286
287 if (child)
288 {
289 children = ike_sa->create_child_sa_enumerator(ike_sa);
290 while (children->enumerate(children, (void**)&child_sa))
291 {
292 if (streq(name, child_sa->get_name(child_sa)))
293 {
294 child_list->insert_last(child_list,
295 (void*)(uintptr_t)child_sa->get_reqid(child_sa));
296 if (!all)
297 {
298 break;
299 }
300 }
301 }
302 children->destroy(children);
303 if (child_list->get_count(child_list) && !all)
304 {
305 break;
306 }
307 }
308 else if (streq(name, ike_sa->get_name(ike_sa)))
309 {
310 ike_list->insert_last(ike_list,
311 (void*)(uintptr_t)ike_sa->get_unique_id(ike_sa));
312 if (!all)
313 {
314 break;
315 }
316 }
317 }
318 enumerator->destroy(enumerator);
319
320 enumerator = child_list->create_enumerator(child_list);
321 while (enumerator->enumerate(enumerator, &del))
322 {
323 charon->controller->terminate_child(charon->controller, del,
324 (controller_cb_t)stroke_log, &info, 0);
325 }
326 enumerator->destroy(enumerator);
327
328 enumerator = ike_list->create_enumerator(ike_list);
329 while (enumerator->enumerate(enumerator, &del))
330 {
331 charon->controller->terminate_ike(charon->controller, del,
332 (controller_cb_t)stroke_log, &info, 0);
333 }
334 enumerator->destroy(enumerator);
335
336 if (child_list->get_count(child_list) == 0 &&
337 ike_list->get_count(ike_list) == 0)
338 {
339 DBG1(DBG_CFG, "no %s_SA named '%s' found",
340 child ? "CHILD" : "IKE", name);
341 }
342 ike_list->destroy(ike_list);
343 child_list->destroy(child_list);
344 }
345
346 METHOD(stroke_control_t, rekey, void,
347 private_stroke_control_t *this, stroke_msg_t *msg, FILE *out)
348 {
349 char *name;
350 u_int32_t id;
351 bool child, all, finished = FALSE;
352 ike_sa_t *ike_sa;
353 enumerator_t *enumerator;
354
355 if (!parse_specifier(msg->terminate.name, &id, &name, &child, &all))
356 {
357 DBG1(DBG_CFG, "error parsing specifier string");
358 return;
359 }
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 ((name && streq(name, child_sa->get_name(child_sa))) ||
373 (id && id == child_sa->get_reqid(child_sa)))
374 {
375 lib->processor->queue_job(lib->processor,
376 (job_t*)rekey_child_sa_job_create(
377 child_sa->get_reqid(child_sa),
378 child_sa->get_protocol(child_sa),
379 child_sa->get_spi(child_sa, TRUE)));
380 if (!all)
381 {
382 finished = TRUE;
383 break;
384 }
385 }
386 }
387 children->destroy(children);
388 }
389 else if ((name && streq(name, ike_sa->get_name(ike_sa))) ||
390 (id && id == ike_sa->get_unique_id(ike_sa)))
391 {
392 lib->processor->queue_job(lib->processor,
393 (job_t*)rekey_ike_sa_job_create(ike_sa->get_id(ike_sa), FALSE));
394 if (!all)
395 {
396 finished = TRUE;
397 }
398 }
399 if (finished)
400 {
401 break;
402 }
403 }
404 enumerator->destroy(enumerator);
405 }
406
407 METHOD(stroke_control_t, terminate_srcip, void,
408 private_stroke_control_t *this, stroke_msg_t *msg, FILE *out)
409 {
410 enumerator_t *enumerator;
411 ike_sa_t *ike_sa;
412 host_t *start = NULL, *end = NULL, *vip;
413 chunk_t chunk_start, chunk_end = chunk_empty, chunk_vip;
414
415 if (msg->terminate_srcip.start)
416 {
417 start = host_create_from_string(msg->terminate_srcip.start, 0);
418 }
419 if (!start)
420 {
421 DBG1(DBG_CFG, "invalid start address: %s", msg->terminate_srcip.start);
422 return;
423 }
424 chunk_start = start->get_address(start);
425 if (msg->terminate_srcip.end)
426 {
427 end = host_create_from_string(msg->terminate_srcip.end, 0);
428 if (!end)
429 {
430 DBG1(DBG_CFG, "invalid end address: %s", msg->terminate_srcip.end);
431 start->destroy(start);
432 return;
433 }
434 chunk_end = end->get_address(end);
435 }
436
437 enumerator = charon->controller->create_ike_sa_enumerator(
438 charon->controller, TRUE);
439 while (enumerator->enumerate(enumerator, &ike_sa))
440 {
441 vip = ike_sa->get_virtual_ip(ike_sa, FALSE);
442 if (!vip)
443 {
444 continue;
445 }
446 if (!end)
447 {
448 if (!vip->ip_equals(vip, start))
449 {
450 continue;
451 }
452 }
453 else
454 {
455 chunk_vip = vip->get_address(vip);
456 if (chunk_vip.len != chunk_start.len ||
457 chunk_vip.len != chunk_end.len ||
458 memcmp(chunk_vip.ptr, chunk_start.ptr, chunk_vip.len) < 0 ||
459 memcmp(chunk_vip.ptr, chunk_end.ptr, chunk_vip.len) > 0)
460 {
461 continue;
462 }
463 }
464
465 /* schedule delete asynchronously */
466 lib->processor->queue_job(lib->processor, (job_t*)
467 delete_ike_sa_job_create(ike_sa->get_id(ike_sa), TRUE));
468 }
469 enumerator->destroy(enumerator);
470 start->destroy(start);
471 DESTROY_IF(end);
472 }
473
474 METHOD(stroke_control_t, purge_ike, void,
475 private_stroke_control_t *this, stroke_msg_t *msg, FILE *out)
476 {
477 enumerator_t *enumerator, *children;
478 ike_sa_t *ike_sa;
479 child_sa_t *child_sa;
480 linked_list_t *list;
481 uintptr_t del;
482 stroke_log_info_t info;
483
484 info.out = out;
485 info.level = msg->output_verbosity;
486
487 list = linked_list_create();
488 enumerator = charon->controller->create_ike_sa_enumerator(
489 charon->controller, TRUE);
490 while (enumerator->enumerate(enumerator, &ike_sa))
491 {
492 children = ike_sa->create_child_sa_enumerator(ike_sa);
493 if (!children->enumerate(children, (void**)&child_sa))
494 {
495 list->insert_last(list,
496 (void*)(uintptr_t)ike_sa->get_unique_id(ike_sa));
497 }
498 children->destroy(children);
499 }
500 enumerator->destroy(enumerator);
501
502 enumerator = list->create_enumerator(list);
503 while (enumerator->enumerate(enumerator, &del))
504 {
505 charon->controller->terminate_ike(charon->controller, del,
506 (controller_cb_t)stroke_log, &info, 0);
507 }
508 enumerator->destroy(enumerator);
509 list->destroy(list);
510 }
511
512 /**
513 * call charon to install a shunt or trap
514 */
515 static void charon_route(peer_cfg_t *peer_cfg, child_cfg_t *child_cfg,
516 char *name, FILE *out)
517 {
518 ipsec_mode_t mode;
519
520 mode = child_cfg->get_mode(child_cfg);
521 if (mode == MODE_PASS || mode == MODE_DROP)
522 {
523 if (charon->shunts->install(charon->shunts, child_cfg))
524 {
525 fprintf(out, "'%s' shunt %N policy installed\n",
526 name, ipsec_mode_names, mode);
527 }
528 else
529 {
530 fprintf(out, "'%s' shunt %N policy installation failed\n",
531 name, ipsec_mode_names, mode);
532 }
533 }
534 else
535 {
536 if (charon->traps->install(charon->traps, peer_cfg, child_cfg))
537 {
538 fprintf(out, "'%s' routed\n", name);
539 }
540 else
541 {
542 fprintf(out, "routing '%s' failed\n", name);
543 }
544 }
545 }
546
547 METHOD(stroke_control_t, route, void,
548 private_stroke_control_t *this, stroke_msg_t *msg, FILE *out)
549 {
550 child_cfg_t *child_cfg = NULL;
551 peer_cfg_t *peer_cfg;
552 enumerator_t *enumerator;
553 bool empty = TRUE;
554
555 peer_cfg = charon->backends->get_peer_cfg_by_name(charon->backends,
556 msg->route.name);
557 if (peer_cfg)
558 {
559 child_cfg = get_child_from_peer(peer_cfg, msg->route.name);
560 if (child_cfg == NULL)
561 {
562 enumerator = peer_cfg->create_child_cfg_enumerator(peer_cfg);
563 while (enumerator->enumerate(enumerator, &child_cfg))
564 {
565 empty = FALSE;
566 charon_route(peer_cfg, child_cfg, child_cfg->get_name(child_cfg),
567 out);
568 }
569 enumerator->destroy(enumerator);
570
571 if (empty)
572 {
573 DBG1(DBG_CFG, "no child config named '%s'", msg->route.name);
574 fprintf(out, "no child config named '%s'\n", msg->route.name);
575 }
576 peer_cfg->destroy(peer_cfg);
577 return;
578 }
579 }
580 else
581 {
582 enumerator = charon->backends->create_peer_cfg_enumerator(
583 charon->backends, NULL, NULL, NULL, NULL, IKE_ANY);
584 while (enumerator->enumerate(enumerator, &peer_cfg))
585 {
586 child_cfg = get_child_from_peer(peer_cfg, msg->route.name);
587 if (child_cfg)
588 {
589 peer_cfg->get_ref(peer_cfg);
590 break;
591 }
592 }
593 enumerator->destroy(enumerator);
594
595 if (child_cfg == NULL)
596 {
597 DBG1(DBG_CFG, "no config named '%s'", msg->route.name);
598 fprintf(out, "no config named '%s'\n", msg->route.name);
599 return;
600 }
601 }
602 charon_route(peer_cfg, child_cfg, msg->route.name, out);
603 peer_cfg->destroy(peer_cfg);
604 child_cfg->destroy(child_cfg);
605 }
606
607 METHOD(stroke_control_t, unroute, void,
608 private_stroke_control_t *this, stroke_msg_t *msg, FILE *out)
609 {
610 child_sa_t *child_sa;
611 enumerator_t *enumerator;
612 u_int32_t id = 0;
613
614 if (charon->shunts->uninstall(charon->shunts, msg->unroute.name))
615 {
616 fprintf(out, "shunt policy '%s' uninstalled\n", msg->unroute.name);
617 return;
618 }
619
620 enumerator = charon->traps->create_enumerator(charon->traps);
621 while (enumerator->enumerate(enumerator, NULL, &child_sa))
622 {
623 if (streq(msg->unroute.name, child_sa->get_name(child_sa)))
624 {
625 id = child_sa->get_reqid(child_sa);
626 break;
627 }
628 }
629 enumerator->destroy(enumerator);
630
631 if (id)
632 {
633 charon->traps->uninstall(charon->traps, id);
634 fprintf(out, "configuration '%s' unrouted\n", msg->unroute.name);
635 }
636 else
637 {
638 fprintf(out, "configuration '%s' not found\n", msg->unroute.name);
639 }
640 }
641
642 METHOD(stroke_control_t, destroy, void,
643 private_stroke_control_t *this)
644 {
645 free(this);
646 }
647
648 /*
649 * see header file
650 */
651 stroke_control_t *stroke_control_create()
652 {
653 private_stroke_control_t *this;
654
655 INIT(this,
656 .public = {
657 .initiate = _initiate,
658 .terminate = _terminate,
659 .terminate_srcip = _terminate_srcip,
660 .rekey = _rekey,
661 .purge_ike = _purge_ike,
662 .route = _route,
663 .unroute = _unroute,
664 .destroy = _destroy,
665 },
666 );
667
668 return &this->public;
669 }
670