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