4a4ee0cbb858055b6ace09a8fde6f20f826fdfce
[strongswan.git] / src / libcharon / plugins / vici / vici_control.c
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>
22 #include <collections/array.h>
23
24 typedef struct private_vici_control_t private_vici_control_t;
25
26 /**
27 * Private data of an vici_control_t object.
28 */
29 struct 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 */
45 typedef 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;
52 } log_info_t;
53
54 /**
55 * Log using vici event messages
56 */
57 static bool log_vici(log_info_t *info, debug_t group, level_t level,
58 ike_sa_t *ike_sa, char *text)
59 {
60 if (level <= info->level)
61 {
62 vici_message_t *message;
63 vici_builder_t *builder;
64
65 builder = vici_builder_create();
66 builder->add_kv(builder, "group", "%N", debug_names, group);
67 builder->add_kv(builder, "level", "%d", level);
68 if (ike_sa)
69 {
70 builder->add_kv(builder, "ikesa-name", "%s",
71 ike_sa->get_name(ike_sa));
72 builder->add_kv(builder, "ikesa-uniqueid", "%u",
73 ike_sa->get_unique_id(ike_sa));
74 }
75 builder->add_kv(builder, "msg", "%s", text);
76
77 message = builder->finalize(builder);
78 if (message)
79 {
80 info->dispatcher->raise_event(info->dispatcher, "control-log",
81 info->id, message);
82 }
83 }
84 return TRUE;
85 }
86
87 /**
88 * Send a (error) reply message
89 */
90 static vici_message_t* send_reply(private_vici_control_t *this, char *fmt, ...)
91 {
92 vici_builder_t *builder;
93 va_list args;
94
95 builder = vici_builder_create();
96 builder->add_kv(builder, "success", fmt ? "no" : "yes");
97 if (fmt)
98 {
99 va_start(args, fmt);
100 builder->vadd_kv(builder, "errmsg", fmt, args);
101 va_end(args);
102 }
103 return builder->finalize(builder);
104 }
105
106 /**
107 * Get the child_cfg having name from peer_cfg
108 */
109 static child_cfg_t* get_child_from_peer(peer_cfg_t *peer_cfg, char *name)
110 {
111 child_cfg_t *current, *found = NULL;
112 enumerator_t *enumerator;
113
114 enumerator = peer_cfg->create_child_cfg_enumerator(peer_cfg);
115 while (enumerator->enumerate(enumerator, &current))
116 {
117 if (streq(current->get_name(current), name))
118 {
119 found = current;
120 found->get_ref(found);
121 break;
122 }
123 }
124 enumerator->destroy(enumerator);
125 return found;
126 }
127
128 CALLBACK(initiate, vici_message_t*,
129 private_vici_control_t *this, char *name, u_int id, vici_message_t *request)
130 {
131 child_cfg_t *child_cfg = NULL;
132 peer_cfg_t *peer_cfg;
133 enumerator_t *enumerator;
134 char *child;
135 u_int timeout;
136 log_info_t log = {
137 .dispatcher = this->dispatcher,
138 .id = id,
139 };
140
141 child = request->get_str(request, NULL, "child");
142 timeout = request->get_int(request, 0, "timeout");
143 log.level = request->get_int(request, 1, "loglevel");
144
145 if (!child)
146 {
147 return send_reply(this, "missing configuration name");
148 }
149 enumerator = charon->backends->create_peer_cfg_enumerator(charon->backends,
150 NULL, NULL, NULL, NULL, IKE_ANY);
151 while (enumerator->enumerate(enumerator, &peer_cfg))
152 {
153 child_cfg = get_child_from_peer(peer_cfg, child);
154 if (child_cfg)
155 {
156 peer_cfg->get_ref(peer_cfg);
157 break;
158 }
159 }
160 enumerator->destroy(enumerator);
161
162 if (!child_cfg)
163 {
164 return send_reply(this, "CHILD_SA config '%s' not found", child);
165 }
166 switch (charon->controller->initiate(charon->controller,
167 peer_cfg, child_cfg, (controller_cb_t)log_vici, &log, timeout))
168 {
169 case SUCCESS:
170 return send_reply(this, NULL);
171 case OUT_OF_RES:
172 return send_reply(this, "CHILD_SA '%s' not established after %dms",
173 child, timeout);
174 case FAILED:
175 default:
176 return send_reply(this, "establishing CHILD_SA '%s' failed", child);
177 }
178 }
179
180 CALLBACK(terminate, vici_message_t*,
181 private_vici_control_t *this, char *name, u_int id, vici_message_t *request)
182 {
183 enumerator_t *enumerator, *isas, *csas;
184 char *child, *ike;
185 u_int timeout, child_id, ike_id, current, *del, done = 0;
186 ike_sa_t *ike_sa;
187 child_sa_t *child_sa;
188 array_t *ids;
189 vici_message_t *reply;
190 log_info_t log = {
191 .dispatcher = this->dispatcher,
192 .id = id,
193 };
194
195 child = request->get_str(request, NULL, "child");
196 ike = request->get_str(request, NULL, "ike");
197 child_id = request->get_int(request, 0, "child-id");
198 ike_id = request->get_int(request, 0, "ike-id");
199 timeout = request->get_int(request, 0, "timeout");
200 log.level = request->get_int(request, 1, "loglevel");
201
202 if (!child && !ike && !ike_id && !child_id)
203 {
204 return send_reply(this, "missing terminate selector");
205 }
206
207 ids = array_create(sizeof(u_int), 0);
208
209 isas = charon->controller->create_ike_sa_enumerator(charon->controller, TRUE);
210 while (isas->enumerate(isas, &ike_sa))
211 {
212 if (child || child_id)
213 {
214 if (ike && !streq(ike, ike_sa->get_name(ike_sa)))
215 {
216 continue;
217 }
218 if (ike_id && ike_id != ike_sa->get_unique_id(ike_sa))
219 {
220 continue;
221 }
222 csas = ike_sa->create_child_sa_enumerator(ike_sa);
223 while (csas->enumerate(csas, &child_sa))
224 {
225 if (child && !streq(child, child_sa->get_name(child_sa)))
226 {
227 continue;
228 }
229 if (child_id && child_sa->get_reqid(child_sa) != child_id)
230 {
231 continue;
232 }
233 current = child_sa->get_reqid(child_sa);
234 array_insert(ids, ARRAY_TAIL, &current);
235 }
236 csas->destroy(csas);
237 }
238 else if (ike && streq(ike, ike_sa->get_name(ike_sa)))
239 {
240 current = ike_sa->get_unique_id(ike_sa);
241 array_insert(ids, ARRAY_TAIL, &current);
242 }
243 else if (ike_id && ike_id == ike_sa->get_unique_id(ike_sa))
244 {
245 array_insert(ids, ARRAY_TAIL, &ike_id);
246 }
247 }
248 isas->destroy(isas);
249
250 enumerator = array_create_enumerator(ids);
251 while (enumerator->enumerate(enumerator, &del))
252 {
253 if (child || child_id)
254 {
255 if (charon->controller->terminate_child(charon->controller, *del,
256 (controller_cb_t)log_vici, &log, timeout) == SUCCESS)
257 {
258 done++;
259 }
260 }
261 else
262 {
263 if (charon->controller->terminate_ike(charon->controller, *del,
264 (controller_cb_t)log_vici, &log, timeout) == SUCCESS)
265 {
266 done++;
267 }
268 }
269 }
270 enumerator->destroy(enumerator);
271
272 if (array_count(ids) == 0)
273 {
274 reply = send_reply(this, "no matching SAs to terminate found");
275 }
276 else if (done < array_count(ids))
277 {
278 if (array_count(ids) == 1)
279 {
280 reply = send_reply(this, "terminating SA failed");
281 }
282 else
283 {
284 reply = send_reply(this, "terminated %u of %u SAs",
285 done, array_count(ids));
286 }
287 }
288 else
289 {
290 reply = send_reply(this, NULL);
291 }
292 array_destroy(ids);
293 return reply;
294 }
295
296 static void manage_command(private_vici_control_t *this,
297 char *name, vici_command_cb_t cb, bool reg)
298 {
299 this->dispatcher->manage_command(this->dispatcher, name,
300 reg ? cb : NULL, this);
301 }
302
303 /**
304 * (Un-)register dispatcher functions
305 */
306 static void manage_commands(private_vici_control_t *this, bool reg)
307 {
308 manage_command(this, "initiate", initiate, reg);
309 manage_command(this, "terminate", terminate, reg);
310 this->dispatcher->manage_event(this->dispatcher, "control-log", reg);
311 }
312
313 METHOD(vici_control_t, destroy, void,
314 private_vici_control_t *this)
315 {
316 manage_commands(this, FALSE);
317 free(this);
318 }
319
320 /**
321 * See header
322 */
323 vici_control_t *vici_control_create(vici_dispatcher_t *dispatcher)
324 {
325 private_vici_control_t *this;
326
327 INIT(this,
328 .public = {
329 .destroy = _destroy,
330 },
331 .dispatcher = dispatcher,
332 );
333
334 manage_commands(this, TRUE);
335
336 return &this->public;
337 }