Merge branch 'childless'
[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 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 if (!name)
158 {
159 *out = peer_cfg->get_ref(peer_cfg);
160 break;
161 }
162 child_cfg = get_child_from_peer(peer_cfg, name);
163 if (child_cfg)
164 {
165 *out = peer_cfg->get_ref(peer_cfg);
166 break;
167 }
168 }
169 enumerator->destroy(enumerator);
170
171 return child_cfg;
172 }
173
174 CALLBACK(initiate, vici_message_t*,
175 private_vici_control_t *this, char *name, u_int id, vici_message_t *request)
176 {
177 peer_cfg_t *peer_cfg = NULL;
178 child_cfg_t *child_cfg;
179 char *child, *ike, *type, *sa;
180 int timeout;
181 bool limits;
182 controller_cb_t log_cb = NULL;
183 log_info_t log = {
184 .dispatcher = this->dispatcher,
185 .id = id,
186 };
187
188 child = request->get_str(request, NULL, "child");
189 ike = request->get_str(request, NULL, "ike");
190 timeout = request->get_int(request, 0, "timeout");
191 limits = request->get_bool(request, FALSE, "init-limits");
192 log.level = request->get_int(request, 1, "loglevel");
193
194 if (!child && !ike)
195 {
196 return send_reply(this, "missing configuration name");
197 }
198 if (timeout >= 0)
199 {
200 log_cb = (controller_cb_t)log_vici;
201 }
202
203 type = child ? "CHILD_SA" : "IKE_SA";
204 sa = child ?: ike;
205
206 child_cfg = find_child_cfg(child, ike, &peer_cfg);
207
208 DBG1(DBG_CFG, "vici initiate %s '%s'", type, sa);
209 if (!peer_cfg)
210 {
211 return send_reply(this, "%s config '%s' not found", type, sa);
212 }
213 switch (charon->controller->initiate(charon->controller, peer_cfg,
214 child_cfg, log_cb, &log, timeout, limits))
215 {
216 case SUCCESS:
217 return send_reply(this, NULL);
218 case OUT_OF_RES:
219 return send_reply(this, "%s '%s' not established after %dms", type,
220 sa, timeout);
221 case INVALID_STATE:
222 return send_reply(this, "establishing %s '%s' not possible at the "
223 "moment due to limits", type, sa);
224 case FAILED:
225 default:
226 return send_reply(this, "establishing %s '%s' failed", type, sa);
227 }
228 }
229
230 CALLBACK(terminate, vici_message_t*,
231 private_vici_control_t *this, char *name, u_int id, vici_message_t *request)
232 {
233 enumerator_t *enumerator, *isas, *csas;
234 char *child, *ike, *errmsg = NULL;
235 u_int child_id, ike_id, current, *del, done = 0;
236 bool force;
237 int timeout;
238 ike_sa_t *ike_sa;
239 child_sa_t *child_sa;
240 array_t *ids;
241 vici_builder_t *builder;
242 controller_cb_t log_cb = NULL;
243 log_info_t log = {
244 .dispatcher = this->dispatcher,
245 .id = id,
246 };
247
248 child = request->get_str(request, NULL, "child");
249 ike = request->get_str(request, NULL, "ike");
250 child_id = request->get_int(request, 0, "child-id");
251 ike_id = request->get_int(request, 0, "ike-id");
252 force = request->get_bool(request, FALSE, "force");
253 timeout = request->get_int(request, 0, "timeout");
254 log.level = request->get_int(request, 1, "loglevel");
255
256 if (!child && !ike && !ike_id && !child_id)
257 {
258 return send_reply(this, "missing terminate selector");
259 }
260
261 if (ike_id)
262 {
263 DBG1(DBG_CFG, "vici terminate IKE_SA #%d", ike_id);
264 }
265 if (child_id)
266 {
267 DBG1(DBG_CFG, "vici terminate CHILD_SA #%d", child_id);
268 }
269 if (ike)
270 {
271 DBG1(DBG_CFG, "vici terminate IKE_SA '%s'", ike);
272 }
273 if (child)
274 {
275 DBG1(DBG_CFG, "vici terminate CHILD_SA '%s'", child);
276 }
277
278 if (timeout >= 0)
279 {
280 log_cb = (controller_cb_t)log_vici;
281 }
282
283 ids = array_create(sizeof(u_int), 0);
284
285 isas = charon->controller->create_ike_sa_enumerator(charon->controller, TRUE);
286 while (isas->enumerate(isas, &ike_sa))
287 {
288 if (child || child_id)
289 {
290 if (ike && !streq(ike, ike_sa->get_name(ike_sa)))
291 {
292 continue;
293 }
294 if (ike_id && ike_id != ike_sa->get_unique_id(ike_sa))
295 {
296 continue;
297 }
298 csas = ike_sa->create_child_sa_enumerator(ike_sa);
299 while (csas->enumerate(csas, &child_sa))
300 {
301 if (child && !streq(child, child_sa->get_name(child_sa)))
302 {
303 continue;
304 }
305 if (child_id && child_sa->get_unique_id(child_sa) != child_id)
306 {
307 continue;
308 }
309 current = child_sa->get_unique_id(child_sa);
310 array_insert(ids, ARRAY_TAIL, &current);
311 }
312 csas->destroy(csas);
313 }
314 else if (ike && streq(ike, ike_sa->get_name(ike_sa)))
315 {
316 current = ike_sa->get_unique_id(ike_sa);
317 array_insert(ids, ARRAY_TAIL, &current);
318 }
319 else if (ike_id && ike_id == ike_sa->get_unique_id(ike_sa))
320 {
321 array_insert(ids, ARRAY_TAIL, &ike_id);
322 }
323 }
324 isas->destroy(isas);
325
326 enumerator = array_create_enumerator(ids);
327 while (enumerator->enumerate(enumerator, &del))
328 {
329 if (child || child_id)
330 {
331 if (charon->controller->terminate_child(charon->controller, *del,
332 log_cb, &log, timeout) == SUCCESS)
333 {
334 done++;
335 }
336 }
337 else
338 {
339 if (charon->controller->terminate_ike(charon->controller, *del, force,
340 log_cb, &log, timeout) == SUCCESS)
341 {
342 done++;
343 }
344 }
345 }
346 enumerator->destroy(enumerator);
347
348 builder = vici_builder_create();
349 if (array_count(ids) == 0)
350 {
351 errmsg = "no matching SAs to terminate found";
352 }
353 else if (done < array_count(ids))
354 {
355 if (array_count(ids) == 1)
356 {
357 errmsg = "terminating SA failed";
358 }
359 else
360 {
361 errmsg = "not all matching SAs could be terminated";
362 }
363 }
364 builder->add_kv(builder, "success", errmsg ? "no" : "yes");
365 builder->add_kv(builder, "matches", "%u", array_count(ids));
366 builder->add_kv(builder, "terminated", "%u", done);
367 if (errmsg)
368 {
369 builder->add_kv(builder, "errmsg", "%s", errmsg);
370 }
371 array_destroy(ids);
372 return builder->finalize(builder);
373 }
374
375 CALLBACK(rekey, vici_message_t*,
376 private_vici_control_t *this, char *name, u_int id, vici_message_t *request)
377 {
378 enumerator_t *isas, *csas;
379 char *child, *ike, *errmsg = NULL;
380 u_int child_id, ike_id, found = 0;
381 ike_sa_t *ike_sa;
382 child_sa_t *child_sa;
383 vici_builder_t *builder;
384 bool reauth;
385
386 child = request->get_str(request, NULL, "child");
387 ike = request->get_str(request, NULL, "ike");
388 child_id = request->get_int(request, 0, "child-id");
389 ike_id = request->get_int(request, 0, "ike-id");
390 reauth = request->get_bool(request, FALSE, "reauth");
391
392 if (!child && !ike && !ike_id && !child_id)
393 {
394 return send_reply(this, "missing rekey selector");
395 }
396
397 if (ike_id)
398 {
399 DBG1(DBG_CFG, "vici rekey IKE_SA #%d", ike_id);
400 }
401 if (child_id)
402 {
403 DBG1(DBG_CFG, "vici rekey CHILD_SA #%d", child_id);
404 }
405 if (ike)
406 {
407 DBG1(DBG_CFG, "vici rekey IKE_SA '%s'", ike);
408 }
409 if (child)
410 {
411 DBG1(DBG_CFG, "vici rekey CHILD_SA '%s'", child);
412 }
413
414 isas = charon->controller->create_ike_sa_enumerator(charon->controller, TRUE);
415 while (isas->enumerate(isas, &ike_sa))
416 {
417 if (child || child_id)
418 {
419 if (ike && !streq(ike, ike_sa->get_name(ike_sa)))
420 {
421 continue;
422 }
423 if (ike_id && ike_id != ike_sa->get_unique_id(ike_sa))
424 {
425 continue;
426 }
427 csas = ike_sa->create_child_sa_enumerator(ike_sa);
428 while (csas->enumerate(csas, &child_sa))
429 {
430 if (child && !streq(child, child_sa->get_name(child_sa)))
431 {
432 continue;
433 }
434 if (child_id && child_sa->get_unique_id(child_sa) != child_id)
435 {
436 continue;
437 }
438 lib->processor->queue_job(lib->processor,
439 (job_t*)rekey_child_sa_job_create(
440 child_sa->get_protocol(child_sa),
441 child_sa->get_spi(child_sa, TRUE),
442 ike_sa->get_my_host(ike_sa)));
443 found++;
444 }
445 csas->destroy(csas);
446 }
447 else if ((ike && streq(ike, ike_sa->get_name(ike_sa))) ||
448 (ike_id && ike_id == ike_sa->get_unique_id(ike_sa)))
449 {
450 lib->processor->queue_job(lib->processor,
451 (job_t*)rekey_ike_sa_job_create(ike_sa->get_id(ike_sa), reauth));
452 found++;
453 }
454 }
455 isas->destroy(isas);
456
457 builder = vici_builder_create();
458 if (!found)
459 {
460 errmsg = "no matching SAs to rekey found";
461 }
462 builder->add_kv(builder, "success", errmsg ? "no" : "yes");
463 builder->add_kv(builder, "matches", "%u", found);
464 if (errmsg)
465 {
466 builder->add_kv(builder, "errmsg", "%s", errmsg);
467 }
468 return builder->finalize(builder);
469 }
470
471 /**
472 * Parse a peer-ip specified, which can be a subnet in CIDR notation, a range
473 * or a single IP address.
474 */
475 static traffic_selector_t *parse_peer_ip(char *ip)
476 {
477 traffic_selector_t *ts;
478 host_t *from, *to;
479 ts_type_t type;
480
481 if (host_create_from_range(ip, &from, &to))
482 {
483 if (to->get_family(to) == AF_INET)
484 {
485 type = TS_IPV4_ADDR_RANGE;
486 }
487 else
488 {
489 type = TS_IPV6_ADDR_RANGE;
490 }
491 ts = traffic_selector_create_from_bytes(0, type,
492 from->get_address(from), 0,
493 to->get_address(to), 0xFFFF);
494 from->destroy(from);
495 to->destroy(to);
496 return ts;
497 }
498 return traffic_selector_create_from_cidr(ip, 0, 0, 0xFFFF);
499 }
500
501 CALLBACK(redirect, vici_message_t*,
502 private_vici_control_t *this, char *name, u_int id, vici_message_t *request)
503 {
504 enumerator_t *sas;
505 char *ike, *peer_ip, *peer_id, *gw, *errmsg = NULL;
506 u_int ike_id, current, found = 0;
507 identification_t *gateway, *identity = NULL, *other_id;
508 traffic_selector_t *ts = NULL;
509 ike_sa_t *ike_sa;
510 vici_builder_t *builder;
511
512 ike = request->get_str(request, NULL, "ike");
513 ike_id = request->get_int(request, 0, "ike-id");
514 peer_ip = request->get_str(request, NULL, "peer-ip");
515 peer_id = request->get_str(request, NULL, "peer-id");
516 gw = request->get_str(request, NULL, "gateway");
517
518 if (!gw || !(gateway = identification_create_from_string(gw)))
519 {
520 return send_reply(this, "missing target gateway");
521 }
522 switch (gateway->get_type(gateway))
523 {
524 case ID_IPV4_ADDR:
525 case ID_IPV6_ADDR:
526 case ID_FQDN:
527 break;
528 default:
529 return send_reply(this, "unsupported gateway identity");
530 }
531 if (peer_ip)
532 {
533 ts = parse_peer_ip(peer_ip);
534 if (!ts)
535 {
536 return send_reply(this, "invalid peer IP selector");
537 }
538 DBG1(DBG_CFG, "vici redirect IKE_SAs with src %R to %Y", ts,
539 gateway);
540 }
541 if (peer_id)
542 {
543 identity = identification_create_from_string(peer_id);
544 if (!identity)
545 {
546 DESTROY_IF(ts);
547 return send_reply(this, "invalid peer identity selector");
548 }
549 DBG1(DBG_CFG, "vici redirect IKE_SAs with ID '%Y' to %Y", identity,
550 gateway);
551 }
552 if (ike_id)
553 {
554 DBG1(DBG_CFG, "vici redirect IKE_SA #%d to '%Y'", ike_id, gateway);
555 }
556 if (ike)
557 {
558 DBG1(DBG_CFG, "vici redirect IKE_SA '%s' to '%Y'", ike, gateway);
559 }
560 if (!peer_ip && !peer_id && !ike && !ike_id)
561 {
562 return send_reply(this, "missing redirect selector");
563 }
564
565 sas = charon->controller->create_ike_sa_enumerator(charon->controller, TRUE);
566 while (sas->enumerate(sas, &ike_sa))
567 {
568 if (ike_sa->get_version(ike_sa) != IKEV2)
569 {
570 continue;
571 }
572 current = ike_sa->get_unique_id(ike_sa);
573 if (ike_id && ike_id != current)
574 {
575 continue;
576 }
577 if (ike && !streq(ike, ike_sa->get_name(ike_sa)))
578 {
579 continue;
580 }
581 if (ts && !ts->includes(ts, ike_sa->get_other_host(ike_sa)))
582 {
583 continue;
584 }
585 if (identity)
586 {
587 other_id = ike_sa->get_other_eap_id(ike_sa);
588 if (!other_id->matches(other_id, identity))
589 {
590 continue;
591 }
592 }
593 lib->processor->queue_job(lib->processor,
594 (job_t*)redirect_job_create(ike_sa->get_id(ike_sa), gateway));
595 found++;
596 }
597 sas->destroy(sas);
598
599 builder = vici_builder_create();
600 if (!found)
601 {
602 errmsg = "no matching SAs to redirect found";
603 }
604 builder->add_kv(builder, "success", errmsg ? "no" : "yes");
605 builder->add_kv(builder, "matches", "%u", found);
606 if (errmsg)
607 {
608 builder->add_kv(builder, "errmsg", "%s", errmsg);
609 }
610 gateway->destroy(gateway);
611 DESTROY_IF(identity);
612 DESTROY_IF(ts);
613 return builder->finalize(builder);
614 }
615
616 CALLBACK(install, vici_message_t*,
617 private_vici_control_t *this, char *name, u_int id, vici_message_t *request)
618 {
619 child_cfg_t *child_cfg = NULL;
620 peer_cfg_t *peer_cfg;
621 char *child, *ike;
622 bool ok;
623
624 child = request->get_str(request, NULL, "child");
625 ike = request->get_str(request, NULL, "ike");
626 if (!child)
627 {
628 return send_reply(this, "missing configuration name");
629 }
630
631 DBG1(DBG_CFG, "vici install '%s'", child);
632
633 child_cfg = find_child_cfg(child, ike, &peer_cfg);
634 if (!child_cfg)
635 {
636 return send_reply(this, "configuration name not found");
637 }
638 switch (child_cfg->get_mode(child_cfg))
639 {
640 case MODE_PASS:
641 case MODE_DROP:
642 ok = charon->shunts->install(charon->shunts,
643 peer_cfg->get_name(peer_cfg), child_cfg);
644 break;
645 default:
646 ok = charon->traps->install(charon->traps, peer_cfg, child_cfg);
647 break;
648 }
649 peer_cfg->destroy(peer_cfg);
650 child_cfg->destroy(child_cfg);
651
652 return send_reply(this, ok ? NULL : "installing policy '%s' failed", child);
653 }
654
655 CALLBACK(uninstall, vici_message_t*,
656 private_vici_control_t *this, char *name, u_int id, vici_message_t *request)
657 {
658 char *child, *ike;
659
660 child = request->get_str(request, NULL, "child");
661 ike = request->get_str(request, NULL, "ike");
662 if (!child)
663 {
664 return send_reply(this, "missing configuration name");
665 }
666
667 DBG1(DBG_CFG, "vici uninstall '%s'", child);
668
669 if (charon->shunts->uninstall(charon->shunts, ike, child))
670 {
671 return send_reply(this, NULL);
672 }
673 else if (charon->traps->uninstall(charon->traps, ike, child))
674 {
675 return send_reply(this, NULL);
676 }
677 return send_reply(this, "policy '%s' not found", child);
678 }
679
680 CALLBACK(reload_settings, vici_message_t*,
681 private_vici_control_t *this, char *name, u_int id, vici_message_t *request)
682 {
683 if (lib->settings->load_files(lib->settings, lib->conf, FALSE))
684 {
685 charon->load_loggers(charon);
686 lib->plugins->reload(lib->plugins, NULL);
687 return send_reply(this, NULL);
688 }
689 return send_reply(this, "reloading '%s' failed", lib->conf);
690 }
691
692 static void manage_command(private_vici_control_t *this,
693 char *name, vici_command_cb_t cb, bool reg)
694 {
695 this->dispatcher->manage_command(this->dispatcher, name,
696 reg ? cb : NULL, this);
697 }
698
699 /**
700 * (Un-)register dispatcher functions
701 */
702 static void manage_commands(private_vici_control_t *this, bool reg)
703 {
704 manage_command(this, "initiate", initiate, reg);
705 manage_command(this, "terminate", terminate, reg);
706 manage_command(this, "rekey", rekey, reg);
707 manage_command(this, "redirect", redirect, reg);
708 manage_command(this, "install", install, reg);
709 manage_command(this, "uninstall", uninstall, reg);
710 manage_command(this, "reload-settings", reload_settings, reg);
711 this->dispatcher->manage_event(this->dispatcher, "control-log", reg);
712 }
713
714 METHOD(vici_control_t, destroy, void,
715 private_vici_control_t *this)
716 {
717 manage_commands(this, FALSE);
718 free(this);
719 }
720
721 /**
722 * See header
723 */
724 vici_control_t *vici_control_create(vici_dispatcher_t *dispatcher)
725 {
726 private_vici_control_t *this;
727
728 INIT(this,
729 .public = {
730 .destroy = _destroy,
731 },
732 .dispatcher = dispatcher,
733 );
734
735 manage_commands(this, TRUE);
736
737 return &this->public;
738 }