]> git.ipfire.org Git - people/ms/strongswan.git/blame - src/libcharon/plugins/vici/vici_control.c
Merge branch 'vici-undo-on-unload'
[people/ms/strongswan.git] / src / libcharon / plugins / vici / vici_control.c
CommitLineData
5f95657c
MW
1/*
2 * Copyright (C) 2014 Martin Willi
3 * Copyright (C) 2014 revosec AG
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15
16#include "vici_control.h"
17#include "vici_builder.h"
18
19#include <inttypes.h>
20
21#include <daemon.h>
c7d4dad6 22#include <collections/array.h>
5f95657c
MW
23
24typedef struct private_vici_control_t private_vici_control_t;
25
26/**
27 * Private data of an vici_control_t object.
28 */
29struct private_vici_control_t {
30
31 /**
32 * Public vici_control_t interface.
33 */
34 vici_control_t public;
35
36 /**
37 * Dispatcher
38 */
39 vici_dispatcher_t *dispatcher;
40};
41
42/**
43 * Log callback helper data
44 */
45typedef struct {
46 /** dispatcher to send log messages over */
47 vici_dispatcher_t *dispatcher;
48 /** connection ID to send messages to */
49 u_int id;
50 /** loglevel */
51 level_t level;
3b5808a0
MW
52 /** prevent recursive log */
53 u_int recursive;
5f95657c
MW
54} log_info_t;
55
56/**
57 * Log using vici event messages
58 */
59static bool log_vici(log_info_t *info, debug_t group, level_t level,
60 ike_sa_t *ike_sa, char *text)
61{
62 if (level <= info->level)
63 {
3b5808a0 64 if (info->recursive++ == 0)
5f95657c 65 {
3b5808a0
MW
66 vici_message_t *message;
67 vici_builder_t *builder;
5f95657c 68
3b5808a0
MW
69 builder = vici_builder_create();
70 builder->add_kv(builder, "group", "%N", debug_names, group);
71 builder->add_kv(builder, "level", "%d", level);
72 if (ike_sa)
73 {
74 builder->add_kv(builder, "ikesa-name", "%s",
75 ike_sa->get_name(ike_sa));
76 builder->add_kv(builder, "ikesa-uniqueid", "%u",
77 ike_sa->get_unique_id(ike_sa));
78 }
79 builder->add_kv(builder, "msg", "%s", text);
80
81 message = builder->finalize(builder);
82 if (message)
83 {
84 info->dispatcher->raise_event(info->dispatcher, "control-log",
85 info->id, message);
86 }
5f95657c 87 }
3b5808a0 88 info->recursive--;
5f95657c
MW
89 }
90 return TRUE;
91}
92
93/**
94 * Send a (error) reply message
95 */
96static vici_message_t* send_reply(private_vici_control_t *this, char *fmt, ...)
97{
98 vici_builder_t *builder;
99 va_list args;
100
101 builder = vici_builder_create();
102 builder->add_kv(builder, "success", fmt ? "no" : "yes");
103 if (fmt)
104 {
105 va_start(args, fmt);
106 builder->vadd_kv(builder, "errmsg", fmt, args);
107 va_end(args);
108 }
109 return builder->finalize(builder);
110}
111
112/**
113 * Get the child_cfg having name from peer_cfg
114 */
115static child_cfg_t* get_child_from_peer(peer_cfg_t *peer_cfg, char *name)
116{
117 child_cfg_t *current, *found = NULL;
118 enumerator_t *enumerator;
119
120 enumerator = peer_cfg->create_child_cfg_enumerator(peer_cfg);
121 while (enumerator->enumerate(enumerator, &current))
122 {
123 if (streq(current->get_name(current), name))
124 {
125 found = current;
126 found->get_ref(found);
127 break;
128 }
129 }
130 enumerator->destroy(enumerator);
131 return found;
132}
133
550f3f56
MW
134/**
135 * Find a peer/child config from a child config name
136 */
eaca77d0 137static child_cfg_t* find_child_cfg(char *name, char *pname, peer_cfg_t **out)
550f3f56
MW
138{
139 enumerator_t *enumerator;
140 peer_cfg_t *peer_cfg;
69679482 141 child_cfg_t *child_cfg = NULL;
550f3f56
MW
142
143 enumerator = charon->backends->create_peer_cfg_enumerator(
144 charon->backends, NULL, NULL, NULL, NULL, IKE_ANY);
145 while (enumerator->enumerate(enumerator, &peer_cfg))
146 {
eaca77d0
MW
147 if (pname && !streq(pname, peer_cfg->get_name(peer_cfg)))
148 {
149 continue;
150 }
550f3f56
MW
151 child_cfg = get_child_from_peer(peer_cfg, name);
152 if (child_cfg)
153 {
154 *out = peer_cfg->get_ref(peer_cfg);
155 break;
156 }
157 }
158 enumerator->destroy(enumerator);
159
160 return child_cfg;
161}
162
5f95657c
MW
163CALLBACK(initiate, vici_message_t*,
164 private_vici_control_t *this, char *name, u_int id, vici_message_t *request)
165{
166 child_cfg_t *child_cfg = NULL;
167 peer_cfg_t *peer_cfg;
eaca77d0 168 char *child, *ike;
5e79ae2d 169 int timeout;
256e666d 170 bool limits;
5e79ae2d 171 controller_cb_t log_cb = NULL;
5f95657c
MW
172 log_info_t log = {
173 .dispatcher = this->dispatcher,
174 .id = id,
175 };
176
177 child = request->get_str(request, NULL, "child");
eaca77d0 178 ike = request->get_str(request, NULL, "ike");
5f95657c 179 timeout = request->get_int(request, 0, "timeout");
256e666d 180 limits = request->get_bool(request, FALSE, "init-limits");
5f95657c
MW
181 log.level = request->get_int(request, 1, "loglevel");
182
183 if (!child)
184 {
185 return send_reply(this, "missing configuration name");
186 }
5e79ae2d
MW
187 if (timeout >= 0)
188 {
189 log_cb = (controller_cb_t)log_vici;
190 }
2676ffdb
MW
191
192 DBG1(DBG_CFG, "vici initiate '%s'", child);
193
eaca77d0 194 child_cfg = find_child_cfg(child, ike, &peer_cfg);
5f95657c
MW
195 if (!child_cfg)
196 {
197 return send_reply(this, "CHILD_SA config '%s' not found", child);
198 }
ff0abde9 199 switch (charon->controller->initiate(charon->controller, peer_cfg,
5e79ae2d 200 child_cfg, log_cb, &log, timeout, limits))
5f95657c
MW
201 {
202 case SUCCESS:
203 return send_reply(this, NULL);
204 case OUT_OF_RES:
205 return send_reply(this, "CHILD_SA '%s' not established after %dms",
206 child, timeout);
256e666d
TB
207 case INVALID_STATE:
208 return send_reply(this, "establishing CHILD_SA '%s' not possible "
209 "at the moment due to limits", child);
5f95657c
MW
210 case FAILED:
211 default:
212 return send_reply(this, "establishing CHILD_SA '%s' failed", child);
213 }
214}
215
c7d4dad6
MW
216CALLBACK(terminate, vici_message_t*,
217 private_vici_control_t *this, char *name, u_int id, vici_message_t *request)
218{
219 enumerator_t *enumerator, *isas, *csas;
bc006ac1 220 char *child, *ike, *errmsg = NULL;
5e79ae2d
MW
221 u_int child_id, ike_id, current, *del, done = 0;
222 int timeout;
c7d4dad6
MW
223 ike_sa_t *ike_sa;
224 child_sa_t *child_sa;
225 array_t *ids;
bc006ac1 226 vici_builder_t *builder;
5e79ae2d 227 controller_cb_t log_cb = NULL;
c7d4dad6
MW
228 log_info_t log = {
229 .dispatcher = this->dispatcher,
230 .id = id,
231 };
232
233 child = request->get_str(request, NULL, "child");
234 ike = request->get_str(request, NULL, "ike");
235 child_id = request->get_int(request, 0, "child-id");
236 ike_id = request->get_int(request, 0, "ike-id");
237 timeout = request->get_int(request, 0, "timeout");
238 log.level = request->get_int(request, 1, "loglevel");
239
240 if (!child && !ike && !ike_id && !child_id)
241 {
242 return send_reply(this, "missing terminate selector");
243 }
244
2676ffdb
MW
245 if (ike_id)
246 {
247 DBG1(DBG_CFG, "vici terminate IKE_SA #%d", ike_id);
248 }
249 if (child_id)
250 {
251 DBG1(DBG_CFG, "vici terminate CHILD_SA #%d", child_id);
252 }
253 if (ike)
254 {
255 DBG1(DBG_CFG, "vici terminate IKE_SA '%s'", ike);
256 }
257 if (child)
258 {
259 DBG1(DBG_CFG, "vici terminate CHILD_SA '%s'", child);
260 }
261
5e79ae2d
MW
262 if (timeout >= 0)
263 {
264 log_cb = (controller_cb_t)log_vici;
265 }
266
c7d4dad6
MW
267 ids = array_create(sizeof(u_int), 0);
268
269 isas = charon->controller->create_ike_sa_enumerator(charon->controller, TRUE);
270 while (isas->enumerate(isas, &ike_sa))
271 {
272 if (child || child_id)
273 {
274 if (ike && !streq(ike, ike_sa->get_name(ike_sa)))
275 {
276 continue;
277 }
278 if (ike_id && ike_id != ike_sa->get_unique_id(ike_sa))
279 {
280 continue;
281 }
282 csas = ike_sa->create_child_sa_enumerator(ike_sa);
283 while (csas->enumerate(csas, &child_sa))
284 {
285 if (child && !streq(child, child_sa->get_name(child_sa)))
286 {
287 continue;
288 }
971a9168 289 if (child_id && child_sa->get_unique_id(child_sa) != child_id)
c7d4dad6
MW
290 {
291 continue;
292 }
971a9168 293 current = child_sa->get_unique_id(child_sa);
c7d4dad6
MW
294 array_insert(ids, ARRAY_TAIL, &current);
295 }
296 csas->destroy(csas);
297 }
298 else if (ike && streq(ike, ike_sa->get_name(ike_sa)))
299 {
300 current = ike_sa->get_unique_id(ike_sa);
301 array_insert(ids, ARRAY_TAIL, &current);
302 }
303 else if (ike_id && ike_id == ike_sa->get_unique_id(ike_sa))
304 {
305 array_insert(ids, ARRAY_TAIL, &ike_id);
306 }
307 }
308 isas->destroy(isas);
309
310 enumerator = array_create_enumerator(ids);
311 while (enumerator->enumerate(enumerator, &del))
312 {
313 if (child || child_id)
314 {
315 if (charon->controller->terminate_child(charon->controller, *del,
5e79ae2d 316 log_cb, &log, timeout) == SUCCESS)
c7d4dad6
MW
317 {
318 done++;
319 }
320 }
321 else
322 {
323 if (charon->controller->terminate_ike(charon->controller, *del,
5e79ae2d 324 log_cb, &log, timeout) == SUCCESS)
c7d4dad6
MW
325 {
326 done++;
327 }
328 }
329 }
330 enumerator->destroy(enumerator);
331
bc006ac1 332 builder = vici_builder_create();
c7d4dad6
MW
333 if (array_count(ids) == 0)
334 {
bc006ac1 335 errmsg = "no matching SAs to terminate found";
c7d4dad6
MW
336 }
337 else if (done < array_count(ids))
338 {
339 if (array_count(ids) == 1)
340 {
bc006ac1 341 errmsg = "terminating SA failed";
c7d4dad6
MW
342 }
343 else
344 {
bc006ac1 345 errmsg = "not all matching SAs could be terminated";
c7d4dad6
MW
346 }
347 }
bc006ac1
MW
348 builder->add_kv(builder, "success", errmsg ? "no" : "yes");
349 builder->add_kv(builder, "matches", "%u", array_count(ids));
350 builder->add_kv(builder, "terminated", "%u", done);
351 if (errmsg)
c7d4dad6 352 {
bc006ac1 353 builder->add_kv(builder, "errmsg", "%s", errmsg);
c7d4dad6
MW
354 }
355 array_destroy(ids);
bc006ac1 356 return builder->finalize(builder);
c7d4dad6
MW
357}
358
5c6e81dc
MW
359/**
360 * Find reqid of an existing CHILD_SA
361 */
362static u_int32_t find_reqid(child_cfg_t *cfg)
363{
364 enumerator_t *enumerator, *children;
365 child_sa_t *child_sa;
366 ike_sa_t *ike_sa;
367 u_int32_t reqid;
368
369 reqid = charon->traps->find_reqid(charon->traps, cfg);
370 if (reqid)
371 { /* already trapped */
372 return reqid;
373 }
374
375 enumerator = charon->controller->create_ike_sa_enumerator(
376 charon->controller, TRUE);
377 while (!reqid && enumerator->enumerate(enumerator, &ike_sa))
378 {
379 children = ike_sa->create_child_sa_enumerator(ike_sa);
380 while (children->enumerate(children, &child_sa))
381 {
382 if (streq(cfg->get_name(cfg), child_sa->get_name(child_sa)))
383 {
384 reqid = child_sa->get_reqid(child_sa);
385 break;
386 }
387 }
388 children->destroy(children);
389 }
390 enumerator->destroy(enumerator);
391 return reqid;
392}
393
394CALLBACK(install, vici_message_t*,
395 private_vici_control_t *this, char *name, u_int id, vici_message_t *request)
396{
397 child_cfg_t *child_cfg = NULL;
398 peer_cfg_t *peer_cfg;
eaca77d0 399 char *child, *ike;
5c6e81dc
MW
400 bool ok;
401
402 child = request->get_str(request, NULL, "child");
eaca77d0 403 ike = request->get_str(request, NULL, "ike");
5c6e81dc
MW
404 if (!child)
405 {
406 return send_reply(this, "missing configuration name");
407 }
2676ffdb
MW
408
409 DBG1(DBG_CFG, "vici install '%s'", child);
410
eaca77d0 411 child_cfg = find_child_cfg(child, ike, &peer_cfg);
5c6e81dc
MW
412 if (!child_cfg)
413 {
414 return send_reply(this, "configuration name not found");
415 }
416 switch (child_cfg->get_mode(child_cfg))
417 {
418 case MODE_PASS:
419 case MODE_DROP:
420 ok = charon->shunts->install(charon->shunts, child_cfg);
421 break;
422 default:
423 ok = charon->traps->install(charon->traps, peer_cfg, child_cfg,
424 find_reqid(child_cfg));
425 break;
426 }
427 peer_cfg->destroy(peer_cfg);
428 child_cfg->destroy(child_cfg);
429
430 return send_reply(this, ok ? NULL : "installing policy '%s' failed", child);
431}
432
433CALLBACK(uninstall, vici_message_t*,
434 private_vici_control_t *this, char *name, u_int id, vici_message_t *request)
435{
436 child_sa_t *child_sa;
437 enumerator_t *enumerator;
438 u_int32_t reqid = 0;
439 char *child;
440
441 child = request->get_str(request, NULL, "child");
442 if (!child)
443 {
444 return send_reply(this, "missing configuration name");
445 }
2676ffdb
MW
446
447 DBG1(DBG_CFG, "vici uninstall '%s'", child);
448
5c6e81dc
MW
449 if (charon->shunts->uninstall(charon->shunts, child))
450 {
451 return send_reply(this, NULL);
452 }
453
454 enumerator = charon->traps->create_enumerator(charon->traps);
455 while (enumerator->enumerate(enumerator, NULL, &child_sa))
456 {
457 if (streq(child, child_sa->get_name(child_sa)))
458 {
459 reqid = child_sa->get_reqid(child_sa);
460 break;
461 }
462 }
463 enumerator->destroy(enumerator);
464
465 if (reqid)
466 {
467 if (charon->traps->uninstall(charon->traps, reqid))
468 {
469 return send_reply(this, NULL);
470 }
471 return send_reply(this, "uninstalling policy '%s' failed", child);
472 }
473 return send_reply(this, "policy '%s' not found", child);
474}
475
455e213c
MW
476CALLBACK(reload_settings, vici_message_t*,
477 private_vici_control_t *this, char *name, u_int id, vici_message_t *request)
478{
479 if (lib->settings->load_files(lib->settings, lib->conf, FALSE))
480 {
481 lib->plugins->reload(lib->plugins, NULL);
482 return send_reply(this, NULL);
483 }
484 return send_reply(this, "reloading '%s' failed", lib->conf);
485}
486
5f95657c
MW
487static void manage_command(private_vici_control_t *this,
488 char *name, vici_command_cb_t cb, bool reg)
489{
490 this->dispatcher->manage_command(this->dispatcher, name,
491 reg ? cb : NULL, this);
492}
493
494/**
495 * (Un-)register dispatcher functions
496 */
497static void manage_commands(private_vici_control_t *this, bool reg)
498{
499 manage_command(this, "initiate", initiate, reg);
c7d4dad6 500 manage_command(this, "terminate", terminate, reg);
5c6e81dc
MW
501 manage_command(this, "install", install, reg);
502 manage_command(this, "uninstall", uninstall, reg);
455e213c 503 manage_command(this, "reload-settings", reload_settings, reg);
5f95657c
MW
504 this->dispatcher->manage_event(this->dispatcher, "control-log", reg);
505}
506
507METHOD(vici_control_t, destroy, void,
508 private_vici_control_t *this)
509{
510 manage_commands(this, FALSE);
511 free(this);
512}
513
514/**
515 * See header
516 */
517vici_control_t *vici_control_create(vici_dispatcher_t *dispatcher)
518{
519 private_vici_control_t *this;
520
521 INIT(this,
522 .public = {
523 .destroy = _destroy,
524 },
525 .dispatcher = dispatcher,
526 );
527
528 manage_commands(this, TRUE);
529
530 return &this->public;
531}