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