Annotation of embedaddon/strongswan/src/libcharon/plugins/vici/vici_control.c, revision 1.1.1.1
1.1 misho 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, ¤t))
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, ¤t);
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, ¤t);
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: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>