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