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