controller: Add option to force destruction of an IKE_SA
[strongswan.git] / src / libcharon / plugins / vici / vici_control.c
1 /*
2 * Copyright (C) 2015-2017 Tobias Brunner
3 * HSR Hochschule fuer Technik Rapperswil
4 *
5 * Copyright (C) 2014 Martin Willi
6 * Copyright (C) 2014 revosec AG
7 *
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the
10 * Free Software Foundation; either version 2 of the License, or (at your
11 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
12 *
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16 * for more details.
17 */
18
19 #include "vici_control.h"
20 #include "vici_builder.h"
21
22 #include <inttypes.h>
23
24 #include <daemon.h>
25 #include <collections/array.h>
26 #include <processing/jobs/rekey_ike_sa_job.h>
27 #include <processing/jobs/rekey_child_sa_job.h>
28 #include <processing/jobs/redirect_job.h>
29
30 typedef struct private_vici_control_t private_vici_control_t;
31
32 /**
33 * Private data of an vici_control_t object.
34 */
35 struct private_vici_control_t {
36
37 /**
38 * Public vici_control_t interface.
39 */
40 vici_control_t public;
41
42 /**
43 * Dispatcher
44 */
45 vici_dispatcher_t *dispatcher;
46 };
47
48 /**
49 * Log callback helper data
50 */
51 typedef struct {
52 /** dispatcher to send log messages over */
53 vici_dispatcher_t *dispatcher;
54 /** connection ID to send messages to */
55 u_int id;
56 /** loglevel */
57 level_t level;
58 /** prevent recursive log */
59 u_int recursive;
60 } log_info_t;
61
62 /**
63 * Log using vici event messages
64 */
65 static bool log_vici(log_info_t *info, debug_t group, level_t level,
66 ike_sa_t *ike_sa, char *text)
67 {
68 if (level <= info->level)
69 {
70 if (info->recursive++ == 0)
71 {
72 vici_message_t *message;
73 vici_builder_t *builder;
74
75 builder = vici_builder_create();
76 builder->add_kv(builder, "group", "%N", debug_names, group);
77 builder->add_kv(builder, "level", "%d", level);
78 if (ike_sa)
79 {
80 builder->add_kv(builder, "ikesa-name", "%s",
81 ike_sa->get_name(ike_sa));
82 builder->add_kv(builder, "ikesa-uniqueid", "%u",
83 ike_sa->get_unique_id(ike_sa));
84 }
85 builder->add_kv(builder, "msg", "%s", text);
86
87 message = builder->finalize(builder);
88 if (message)
89 {
90 info->dispatcher->raise_event(info->dispatcher, "control-log",
91 info->id, message);
92 }
93 }
94 info->recursive--;
95 }
96 return TRUE;
97 }
98
99 /**
100 * Send a (error) reply message
101 */
102 static vici_message_t* send_reply(private_vici_control_t *this, char *fmt, ...)
103 {
104 vici_builder_t *builder;
105 va_list args;
106
107 builder = vici_builder_create();
108 builder->add_kv(builder, "success", fmt ? "no" : "yes");
109 if (fmt)
110 {
111 va_start(args, fmt);
112 builder->vadd_kv(builder, "errmsg", fmt, args);
113 va_end(args);
114 }
115 return builder->finalize(builder);
116 }
117
118 /**
119 * Get the child_cfg having name from peer_cfg
120 */
121 static child_cfg_t* get_child_from_peer(peer_cfg_t *peer_cfg, char *name)
122 {
123 child_cfg_t *current, *found = NULL;
124 enumerator_t *enumerator;
125
126 enumerator = peer_cfg->create_child_cfg_enumerator(peer_cfg);
127 while (enumerator->enumerate(enumerator, &current))
128 {
129 if (streq(current->get_name(current), name))
130 {
131 found = current;
132 found->get_ref(found);
133 break;
134 }
135 }
136 enumerator->destroy(enumerator);
137 return found;
138 }
139
140 /**
141 * Find a peer/child config from a child config name
142 */
143 static child_cfg_t* find_child_cfg(char *name, char *pname, peer_cfg_t **out)
144 {
145 enumerator_t *enumerator;
146 peer_cfg_t *peer_cfg;
147 child_cfg_t *child_cfg = NULL;
148
149 enumerator = charon->backends->create_peer_cfg_enumerator(
150 charon->backends, NULL, NULL, NULL, NULL, IKE_ANY);
151 while (enumerator->enumerate(enumerator, &peer_cfg))
152 {
153 if (pname && !streq(pname, peer_cfg->get_name(peer_cfg)))
154 {
155 continue;
156 }
157 child_cfg = get_child_from_peer(peer_cfg, name);
158 if (child_cfg)
159 {
160 *out = peer_cfg->get_ref(peer_cfg);
161 break;
162 }
163 }
164 enumerator->destroy(enumerator);
165
166 return child_cfg;
167 }
168
169 CALLBACK(initiate, vici_message_t*,
170 private_vici_control_t *this, char *name, u_int id, vici_message_t *request)
171 {
172 child_cfg_t *child_cfg = NULL;
173 peer_cfg_t *peer_cfg;
174 char *child, *ike;
175 int timeout;
176 bool limits;
177 controller_cb_t log_cb = NULL;
178 log_info_t log = {
179 .dispatcher = this->dispatcher,
180 .id = id,
181 };
182
183 child = request->get_str(request, NULL, "child");
184 ike = request->get_str(request, NULL, "ike");
185 timeout = request->get_int(request, 0, "timeout");
186 limits = request->get_bool(request, FALSE, "init-limits");
187 log.level = request->get_int(request, 1, "loglevel");
188
189 if (!child)
190 {
191 return send_reply(this, "missing configuration name");
192 }
193 if (timeout >= 0)
194 {
195 log_cb = (controller_cb_t)log_vici;
196 }
197
198 DBG1(DBG_CFG, "vici initiate '%s'", child);
199
200 child_cfg = find_child_cfg(child, ike, &peer_cfg);
201 if (!child_cfg)
202 {
203 return send_reply(this, "CHILD_SA config '%s' not found", child);
204 }
205 switch (charon->controller->initiate(charon->controller, peer_cfg,
206 child_cfg, log_cb, &log, timeout, limits))
207 {
208 case SUCCESS:
209 return send_reply(this, NULL);
210 case OUT_OF_RES:
211 return send_reply(this, "CHILD_SA '%s' not established after %dms",
212 child, timeout);
213 case INVALID_STATE:
214 return send_reply(this, "establishing CHILD_SA '%s' not possible "
215 "at the moment due to limits", child);
216 case FAILED:
217 default:
218 return send_reply(this, "establishing CHILD_SA '%s' failed", child);
219 }
220 }
221
222 CALLBACK(terminate, vici_message_t*,
223 private_vici_control_t *this, char *name, u_int id, vici_message_t *request)
224 {
225 enumerator_t *enumerator, *isas, *csas;
226 char *child, *ike, *errmsg = NULL;
227 u_int child_id, ike_id, current, *del, done = 0;
228 int timeout;
229 ike_sa_t *ike_sa;
230 child_sa_t *child_sa;
231 array_t *ids;
232 vici_builder_t *builder;
233 controller_cb_t log_cb = NULL;
234 log_info_t log = {
235 .dispatcher = this->dispatcher,
236 .id = id,
237 };
238
239 child = request->get_str(request, NULL, "child");
240 ike = request->get_str(request, NULL, "ike");
241 child_id = request->get_int(request, 0, "child-id");
242 ike_id = request->get_int(request, 0, "ike-id");
243 timeout = request->get_int(request, 0, "timeout");
244 log.level = request->get_int(request, 1, "loglevel");
245
246 if (!child && !ike && !ike_id && !child_id)
247 {
248 return send_reply(this, "missing terminate selector");
249 }
250
251 if (ike_id)
252 {
253 DBG1(DBG_CFG, "vici terminate IKE_SA #%d", ike_id);
254 }
255 if (child_id)
256 {
257 DBG1(DBG_CFG, "vici terminate CHILD_SA #%d", child_id);
258 }
259 if (ike)
260 {
261 DBG1(DBG_CFG, "vici terminate IKE_SA '%s'", ike);
262 }
263 if (child)
264 {
265 DBG1(DBG_CFG, "vici terminate CHILD_SA '%s'", child);
266 }
267
268 if (timeout >= 0)
269 {
270 log_cb = (controller_cb_t)log_vici;
271 }
272
273 ids = array_create(sizeof(u_int), 0);
274
275 isas = charon->controller->create_ike_sa_enumerator(charon->controller, TRUE);
276 while (isas->enumerate(isas, &ike_sa))
277 {
278 if (child || child_id)
279 {
280 if (ike && !streq(ike, ike_sa->get_name(ike_sa)))
281 {
282 continue;
283 }
284 if (ike_id && ike_id != ike_sa->get_unique_id(ike_sa))
285 {
286 continue;
287 }
288 csas = ike_sa->create_child_sa_enumerator(ike_sa);
289 while (csas->enumerate(csas, &child_sa))
290 {
291 if (child && !streq(child, child_sa->get_name(child_sa)))
292 {
293 continue;
294 }
295 if (child_id && child_sa->get_unique_id(child_sa) != child_id)
296 {
297 continue;
298 }
299 current = child_sa->get_unique_id(child_sa);
300 array_insert(ids, ARRAY_TAIL, &current);
301 }
302 csas->destroy(csas);
303 }
304 else if (ike && streq(ike, ike_sa->get_name(ike_sa)))
305 {
306 current = ike_sa->get_unique_id(ike_sa);
307 array_insert(ids, ARRAY_TAIL, &current);
308 }
309 else if (ike_id && ike_id == ike_sa->get_unique_id(ike_sa))
310 {
311 array_insert(ids, ARRAY_TAIL, &ike_id);
312 }
313 }
314 isas->destroy(isas);
315
316 enumerator = array_create_enumerator(ids);
317 while (enumerator->enumerate(enumerator, &del))
318 {
319 if (child || child_id)
320 {
321 if (charon->controller->terminate_child(charon->controller, *del,
322 log_cb, &log, timeout) == SUCCESS)
323 {
324 done++;
325 }
326 }
327 else
328 {
329 if (charon->controller->terminate_ike(charon->controller, *del, FALSE,
330 log_cb, &log, timeout) == SUCCESS)
331 {
332 done++;
333 }
334 }
335 }
336 enumerator->destroy(enumerator);
337
338 builder = vici_builder_create();
339 if (array_count(ids) == 0)
340 {
341 errmsg = "no matching SAs to terminate found";
342 }
343 else if (done < array_count(ids))
344 {
345 if (array_count(ids) == 1)
346 {
347 errmsg = "terminating SA failed";
348 }
349 else
350 {
351 errmsg = "not all matching SAs could be terminated";
352 }
353 }
354 builder->add_kv(builder, "success", errmsg ? "no" : "yes");
355 builder->add_kv(builder, "matches", "%u", array_count(ids));
356 builder->add_kv(builder, "terminated", "%u", done);
357 if (errmsg)
358 {
359 builder->add_kv(builder, "errmsg", "%s", errmsg);
360 }
361 array_destroy(ids);
362 return builder->finalize(builder);
363 }
364
365 CALLBACK(rekey, vici_message_t*,
366 private_vici_control_t *this, char *name, u_int id, vici_message_t *request)
367 {
368 enumerator_t *isas, *csas;
369 char *child, *ike, *errmsg = NULL;
370 u_int child_id, ike_id, found = 0;
371 ike_sa_t *ike_sa;
372 child_sa_t *child_sa;
373 vici_builder_t *builder;
374
375 child = request->get_str(request, NULL, "child");
376 ike = request->get_str(request, NULL, "ike");
377 child_id = request->get_int(request, 0, "child-id");
378 ike_id = request->get_int(request, 0, "ike-id");
379
380 if (!child && !ike && !ike_id && !child_id)
381 {
382 return send_reply(this, "missing rekey selector");
383 }
384
385 if (ike_id)
386 {
387 DBG1(DBG_CFG, "vici rekey IKE_SA #%d", ike_id);
388 }
389 if (child_id)
390 {
391 DBG1(DBG_CFG, "vici rekey CHILD_SA #%d", child_id);
392 }
393 if (ike)
394 {
395 DBG1(DBG_CFG, "vici rekey IKE_SA '%s'", ike);
396 }
397 if (child)
398 {
399 DBG1(DBG_CFG, "vici rekey CHILD_SA '%s'", child);
400 }
401
402 isas = charon->controller->create_ike_sa_enumerator(charon->controller, TRUE);
403 while (isas->enumerate(isas, &ike_sa))
404 {
405 if (child || child_id)
406 {
407 if (ike && !streq(ike, ike_sa->get_name(ike_sa)))
408 {
409 continue;
410 }
411 if (ike_id && ike_id != ike_sa->get_unique_id(ike_sa))
412 {
413 continue;
414 }
415 csas = ike_sa->create_child_sa_enumerator(ike_sa);
416 while (csas->enumerate(csas, &child_sa))
417 {
418 if (child && !streq(child, child_sa->get_name(child_sa)))
419 {
420 continue;
421 }
422 if (child_id && child_sa->get_unique_id(child_sa) != child_id)
423 {
424 continue;
425 }
426 lib->processor->queue_job(lib->processor,
427 (job_t*)rekey_child_sa_job_create(
428 child_sa->get_protocol(child_sa),
429 child_sa->get_spi(child_sa, TRUE),
430 ike_sa->get_my_host(ike_sa)));
431 found++;
432 }
433 csas->destroy(csas);
434 }
435 else if ((ike && streq(ike, ike_sa->get_name(ike_sa))) ||
436 (ike_id && ike_id == ike_sa->get_unique_id(ike_sa)))
437 {
438 lib->processor->queue_job(lib->processor,
439 (job_t*)rekey_ike_sa_job_create(ike_sa->get_id(ike_sa), FALSE));
440 found++;
441 }
442 }
443 isas->destroy(isas);
444
445 builder = vici_builder_create();
446 if (!found)
447 {
448 errmsg = "no matching SAs to rekey found";
449 }
450 builder->add_kv(builder, "success", errmsg ? "no" : "yes");
451 builder->add_kv(builder, "matches", "%u", found);
452 if (errmsg)
453 {
454 builder->add_kv(builder, "errmsg", "%s", errmsg);
455 }
456 return builder->finalize(builder);
457 }
458
459 /**
460 * Parse a peer-ip specified, which can be a subnet in CIDR notation, a range
461 * or a single IP address.
462 */
463 static traffic_selector_t *parse_peer_ip(char *ip)
464 {
465 traffic_selector_t *ts;
466 host_t *from, *to;
467 ts_type_t type;
468
469 if (host_create_from_range(ip, &from, &to))
470 {
471 if (to->get_family(to) == AF_INET)
472 {
473 type = TS_IPV4_ADDR_RANGE;
474 }
475 else
476 {
477 type = TS_IPV6_ADDR_RANGE;
478 }
479 ts = traffic_selector_create_from_bytes(0, type,
480 from->get_address(from), 0,
481 to->get_address(to), 0xFFFF);
482 from->destroy(from);
483 to->destroy(to);
484 return ts;
485 }
486 return traffic_selector_create_from_cidr(ip, 0, 0, 0xFFFF);
487 }
488
489 CALLBACK(redirect, vici_message_t*,
490 private_vici_control_t *this, char *name, u_int id, vici_message_t *request)
491 {
492 enumerator_t *sas;
493 char *ike, *peer_ip, *peer_id, *gw, *errmsg = NULL;
494 u_int ike_id, current, found = 0;
495 identification_t *gateway, *identity = NULL, *other_id;
496 traffic_selector_t *ts = NULL;
497 ike_sa_t *ike_sa;
498 vici_builder_t *builder;
499
500 ike = request->get_str(request, NULL, "ike");
501 ike_id = request->get_int(request, 0, "ike-id");
502 peer_ip = request->get_str(request, NULL, "peer-ip");
503 peer_id = request->get_str(request, NULL, "peer-id");
504 gw = request->get_str(request, NULL, "gateway");
505
506 if (!gw || !(gateway = identification_create_from_string(gw)))
507 {
508 return send_reply(this, "missing target gateway");
509 }
510 switch (gateway->get_type(gateway))
511 {
512 case ID_IPV4_ADDR:
513 case ID_IPV6_ADDR:
514 case ID_FQDN:
515 break;
516 default:
517 return send_reply(this, "unsupported gateway identity");
518 }
519 if (peer_ip)
520 {
521 ts = parse_peer_ip(peer_ip);
522 if (!ts)
523 {
524 return send_reply(this, "invalid peer IP selector");
525 }
526 DBG1(DBG_CFG, "vici redirect IKE_SAs with src %R to %Y", ts,
527 gateway);
528 }
529 if (peer_id)
530 {
531 identity = identification_create_from_string(peer_id);
532 if (!identity)
533 {
534 DESTROY_IF(ts);
535 return send_reply(this, "invalid peer identity selector");
536 }
537 DBG1(DBG_CFG, "vici redirect IKE_SAs with ID '%Y' to %Y", identity,
538 gateway);
539 }
540 if (ike_id)
541 {
542 DBG1(DBG_CFG, "vici redirect IKE_SA #%d to '%Y'", ike_id, gateway);
543 }
544 if (ike)
545 {
546 DBG1(DBG_CFG, "vici redirect IKE_SA '%s' to '%Y'", ike, gateway);
547 }
548 if (!peer_ip && !peer_id && !ike && !ike_id)
549 {
550 return send_reply(this, "missing redirect selector");
551 }
552
553 sas = charon->controller->create_ike_sa_enumerator(charon->controller, TRUE);
554 while (sas->enumerate(sas, &ike_sa))
555 {
556 if (ike_sa->get_version(ike_sa) != IKEV2)
557 {
558 continue;
559 }
560 current = ike_sa->get_unique_id(ike_sa);
561 if (ike_id && ike_id != current)
562 {
563 continue;
564 }
565 if (ike && !streq(ike, ike_sa->get_name(ike_sa)))
566 {
567 continue;
568 }
569 if (ts && !ts->includes(ts, ike_sa->get_other_host(ike_sa)))
570 {
571 continue;
572 }
573 if (identity)
574 {
575 other_id = ike_sa->get_other_eap_id(ike_sa);
576 if (!other_id->matches(other_id, identity))
577 {
578 continue;
579 }
580 }
581 lib->processor->queue_job(lib->processor,
582 (job_t*)redirect_job_create(ike_sa->get_id(ike_sa), gateway));
583 found++;
584 }
585 sas->destroy(sas);
586
587 builder = vici_builder_create();
588 if (!found)
589 {
590 errmsg = "no matching SAs to redirect found";
591 }
592 builder->add_kv(builder, "success", errmsg ? "no" : "yes");
593 builder->add_kv(builder, "matches", "%u", found);
594 if (errmsg)
595 {
596 builder->add_kv(builder, "errmsg", "%s", errmsg);
597 }
598 gateway->destroy(gateway);
599 DESTROY_IF(identity);
600 DESTROY_IF(ts);
601 return builder->finalize(builder);
602 }
603
604 CALLBACK(install, vici_message_t*,
605 private_vici_control_t *this, char *name, u_int id, vici_message_t *request)
606 {
607 child_cfg_t *child_cfg = NULL;
608 peer_cfg_t *peer_cfg;
609 char *child, *ike;
610 bool ok;
611
612 child = request->get_str(request, NULL, "child");
613 ike = request->get_str(request, NULL, "ike");
614 if (!child)
615 {
616 return send_reply(this, "missing configuration name");
617 }
618
619 DBG1(DBG_CFG, "vici install '%s'", child);
620
621 child_cfg = find_child_cfg(child, ike, &peer_cfg);
622 if (!child_cfg)
623 {
624 return send_reply(this, "configuration name not found");
625 }
626 switch (child_cfg->get_mode(child_cfg))
627 {
628 case MODE_PASS:
629 case MODE_DROP:
630 ok = charon->shunts->install(charon->shunts,
631 peer_cfg->get_name(peer_cfg), child_cfg);
632 break;
633 default:
634 ok = charon->traps->install(charon->traps, peer_cfg, child_cfg);
635 break;
636 }
637 peer_cfg->destroy(peer_cfg);
638 child_cfg->destroy(child_cfg);
639
640 return send_reply(this, ok ? NULL : "installing policy '%s' failed", child);
641 }
642
643 CALLBACK(uninstall, vici_message_t*,
644 private_vici_control_t *this, char *name, u_int id, vici_message_t *request)
645 {
646 char *child, *ike;
647
648 child = request->get_str(request, NULL, "child");
649 ike = request->get_str(request, NULL, "ike");
650 if (!child)
651 {
652 return send_reply(this, "missing configuration name");
653 }
654
655 DBG1(DBG_CFG, "vici uninstall '%s'", child);
656
657 if (charon->shunts->uninstall(charon->shunts, ike, child))
658 {
659 return send_reply(this, NULL);
660 }
661 else if (charon->traps->uninstall(charon->traps, ike, child))
662 {
663 return send_reply(this, NULL);
664 }
665 return send_reply(this, "policy '%s' not found", child);
666 }
667
668 CALLBACK(reload_settings, vici_message_t*,
669 private_vici_control_t *this, char *name, u_int id, vici_message_t *request)
670 {
671 if (lib->settings->load_files(lib->settings, lib->conf, FALSE))
672 {
673 charon->load_loggers(charon);
674 lib->plugins->reload(lib->plugins, NULL);
675 return send_reply(this, NULL);
676 }
677 return send_reply(this, "reloading '%s' failed", lib->conf);
678 }
679
680 static void manage_command(private_vici_control_t *this,
681 char *name, vici_command_cb_t cb, bool reg)
682 {
683 this->dispatcher->manage_command(this->dispatcher, name,
684 reg ? cb : NULL, this);
685 }
686
687 /**
688 * (Un-)register dispatcher functions
689 */
690 static void manage_commands(private_vici_control_t *this, bool reg)
691 {
692 manage_command(this, "initiate", initiate, reg);
693 manage_command(this, "terminate", terminate, reg);
694 manage_command(this, "rekey", rekey, reg);
695 manage_command(this, "redirect", redirect, reg);
696 manage_command(this, "install", install, reg);
697 manage_command(this, "uninstall", uninstall, reg);
698 manage_command(this, "reload-settings", reload_settings, reg);
699 this->dispatcher->manage_event(this->dispatcher, "control-log", reg);
700 }
701
702 METHOD(vici_control_t, destroy, void,
703 private_vici_control_t *this)
704 {
705 manage_commands(this, FALSE);
706 free(this);
707 }
708
709 /**
710 * See header
711 */
712 vici_control_t *vici_control_create(vici_dispatcher_t *dispatcher)
713 {
714 private_vici_control_t *this;
715
716 INIT(this,
717 .public = {
718 .destroy = _destroy,
719 },
720 .dispatcher = dispatcher,
721 );
722
723 manage_commands(this, TRUE);
724
725 return &this->public;
726 }