vici: Increased various string buffers to BUF_LEN (512 bytes)
[strongswan.git] / src / swanctl / commands / load_conns.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 #define _GNU_SOURCE
17 #include <stdio.h>
18 #include <errno.h>
19 #include <limits.h>
20
21 #include "command.h"
22 #include "swanctl.h"
23 #include "load_conns.h"
24
25 /**
26 * Check if we should handle a key as a list of comma separated values
27 */
28 static bool is_list_key(char *key)
29 {
30 char *keys[] = {
31 "local_addrs",
32 "remote_addrs",
33 "proposals",
34 "esp_proposals",
35 "ah_proposals",
36 "local_ts",
37 "remote_ts",
38 "vips",
39 "pools",
40 "groups",
41 };
42 int i;
43
44 for (i = 0; i < countof(keys); i++)
45 {
46 if (strcaseeq(keys[i], key))
47 {
48 return TRUE;
49 }
50 }
51 return FALSE;
52 }
53
54 /**
55 * Check if we should handle a key as a list of comma separated files
56 */
57 static bool is_file_list_key(char *key)
58 {
59 char *keys[] = {
60 "certs",
61 "cacerts",
62 "pubkeys"
63 };
64 int i;
65
66 for (i = 0; i < countof(keys); i++)
67 {
68 if (strcaseeq(keys[i], key))
69 {
70 return TRUE;
71 }
72 }
73 return FALSE;
74 }
75
76 /**
77 * Add a vici list from a comma separated string value
78 */
79 static void add_list_key(vici_req_t *req, char *key, char *value)
80 {
81 enumerator_t *enumerator;
82 char *token;
83
84 vici_begin_list(req, key);
85 enumerator = enumerator_create_token(value, ",", " ");
86 while (enumerator->enumerate(enumerator, &token))
87 {
88 vici_add_list_itemf(req, "%s", token);
89 }
90 enumerator->destroy(enumerator);
91 vici_end_list(req);
92 }
93
94 /**
95 * Add a vici list of blobs from a comma separated file list
96 */
97 static bool add_file_list_key(vici_req_t *req, char *key, char *value)
98 {
99 enumerator_t *enumerator;
100 chunk_t *map;
101 char *token, buf[PATH_MAX];
102 bool ret = TRUE;
103
104 vici_begin_list(req, key);
105 enumerator = enumerator_create_token(value, ",", " ");
106 while (enumerator->enumerate(enumerator, &token))
107 {
108 if (!path_absolute(token))
109 {
110 if (streq(key, "certs"))
111 {
112 snprintf(buf, sizeof(buf), "%s%s%s",
113 SWANCTL_X509DIR, DIRECTORY_SEPARATOR, token);
114 token = buf;
115 }
116 else if (streq(key, "cacerts"))
117 {
118 snprintf(buf, sizeof(buf), "%s%s%s",
119 SWANCTL_X509CADIR, DIRECTORY_SEPARATOR, token);
120 token = buf;
121 }
122 else if (streq(key, "pubkeys"))
123 {
124 snprintf(buf, sizeof(buf), "%s%s%s",
125 SWANCTL_PUBKEYDIR, DIRECTORY_SEPARATOR, token);
126 token = buf;
127 }
128 }
129
130 map = chunk_map(token, FALSE);
131 if (map)
132 {
133 vici_add_list_item(req, map->ptr, map->len);
134 chunk_unmap(map);
135 }
136 else
137 {
138 fprintf(stderr, "loading %s certificate '%s' failed: %s\n",
139 key, token, strerror(errno));
140 ret = FALSE;
141 break;
142 }
143 }
144 enumerator->destroy(enumerator);
145 vici_end_list(req);
146
147 return ret;
148 }
149
150 /**
151 * Translate setting key/values from a section into vici key-values/lists
152 */
153 static bool add_key_values(vici_req_t *req, settings_t *cfg, char *section)
154 {
155 enumerator_t *enumerator;
156 char *key, *value;
157 bool ret = TRUE;
158
159 enumerator = cfg->create_key_value_enumerator(cfg, section);
160 while (enumerator->enumerate(enumerator, &key, &value))
161 {
162 if (is_list_key(key))
163 {
164 add_list_key(req, key, value);
165 }
166 else if (is_file_list_key(key))
167 {
168 ret = add_file_list_key(req, key, value);
169 }
170 else
171 {
172 vici_add_key_valuef(req, key, "%s", value);
173 }
174 if (!ret)
175 {
176 break;
177 }
178 }
179 enumerator->destroy(enumerator);
180
181 return ret;
182 }
183
184 /**
185 * Translate a settings section to a vici section
186 */
187 static bool add_sections(vici_req_t *req, settings_t *cfg, char *section)
188 {
189 enumerator_t *enumerator;
190 char *name, buf[256];
191 bool ret = TRUE;
192
193 enumerator = cfg->create_section_enumerator(cfg, section);
194 while (enumerator->enumerate(enumerator, &name))
195 {
196 vici_begin_section(req, name);
197 snprintf(buf, sizeof(buf), "%s.%s", section, name);
198 ret = add_key_values(req, cfg, buf);
199 if (!ret)
200 {
201 break;
202 }
203 ret = add_sections(req, cfg, buf);
204 if (!ret)
205 {
206 break;
207 }
208 vici_end_section(req);
209 }
210 enumerator->destroy(enumerator);
211
212 return ret;
213 }
214
215 /**
216 * Load an IKE_SA config with CHILD_SA configs from a section
217 */
218 static bool load_conn(vici_conn_t *conn, settings_t *cfg,
219 char *section, command_format_options_t format)
220 {
221 vici_req_t *req;
222 vici_res_t *res;
223 bool ret = TRUE;
224 char buf[BUF_LEN];
225
226 snprintf(buf, sizeof(buf), "%s.%s", "connections", section);
227
228 req = vici_begin("load-conn");
229
230 vici_begin_section(req, section);
231 if (!add_key_values(req, cfg, buf) ||
232 !add_sections(req, cfg, buf))
233 {
234 vici_free_req(req);
235 return FALSE;
236 }
237 vici_end_section(req);
238
239 res = vici_submit(req, conn);
240 if (!res)
241 {
242 fprintf(stderr, "load-conn request failed: %s\n", strerror(errno));
243 return FALSE;
244 }
245 if (format & COMMAND_FORMAT_RAW)
246 {
247 vici_dump(res, "load-conn reply", format & COMMAND_FORMAT_PRETTY,
248 stdout);
249 }
250 else if (!streq(vici_find_str(res, "no", "success"), "yes"))
251 {
252 fprintf(stderr, "loading connection '%s' failed: %s\n",
253 section, vici_find_str(res, "", "errmsg"));
254 ret = FALSE;
255 }
256 else
257 {
258 printf("loaded connection '%s'\n", section);
259 }
260 vici_free_res(res);
261 return ret;
262 }
263
264 CALLBACK(list_conn, int,
265 linked_list_t *list, vici_res_t *res, char *name, void *value, int len)
266 {
267 if (streq(name, "conns"))
268 {
269 char *str;
270
271 if (asprintf(&str, "%.*s", len, value) != -1)
272 {
273 list->insert_last(list, str);
274 }
275 }
276 return 0;
277 }
278
279 /**
280 * Create a list of currently loaded connections
281 */
282 static linked_list_t* list_conns(vici_conn_t *conn,
283 command_format_options_t format)
284 {
285 linked_list_t *list;
286 vici_res_t *res;
287
288 list = linked_list_create();
289
290 res = vici_submit(vici_begin("get-conns"), conn);
291 if (res)
292 {
293 if (format & COMMAND_FORMAT_RAW)
294 {
295 vici_dump(res, "get-conns reply", format & COMMAND_FORMAT_PRETTY,
296 stdout);
297 }
298 vici_parse_cb(res, NULL, NULL, list_conn, list);
299 vici_free_res(res);
300 }
301 return list;
302 }
303
304 /**
305 * Remove and free a string from a list
306 */
307 static void remove_from_list(linked_list_t *list, char *str)
308 {
309 enumerator_t *enumerator;
310 char *current;
311
312 enumerator = list->create_enumerator(list);
313 while (enumerator->enumerate(enumerator, &current))
314 {
315 if (streq(current, str))
316 {
317 list->remove_at(list, enumerator);
318 free(current);
319 }
320 }
321 enumerator->destroy(enumerator);
322 }
323
324 /**
325 * Unload a connection by name
326 */
327 static bool unload_conn(vici_conn_t *conn, char *name,
328 command_format_options_t format)
329 {
330 vici_req_t *req;
331 vici_res_t *res;
332 bool ret = TRUE;
333
334 req = vici_begin("unload-conn");
335 vici_add_key_valuef(req, "name", "%s", name);
336 res = vici_submit(req, conn);
337 if (!res)
338 {
339 fprintf(stderr, "unload-conn request failed: %s\n", strerror(errno));
340 return FALSE;
341 }
342 if (format & COMMAND_FORMAT_RAW)
343 {
344 vici_dump(res, "unload-conn reply", format & COMMAND_FORMAT_PRETTY,
345 stdout);
346 }
347 else if (!streq(vici_find_str(res, "no", "success"), "yes"))
348 {
349 fprintf(stderr, "unloading connection '%s' failed: %s\n",
350 name, vici_find_str(res, "", "errmsg"));
351 ret = FALSE;
352 }
353 vici_free_res(res);
354 return ret;
355 }
356
357 /**
358 * See header.
359 */
360 int load_conns_cfg(vici_conn_t *conn, command_format_options_t format,
361 settings_t *cfg)
362 {
363 u_int found = 0, loaded = 0, unloaded = 0;
364 char *section;
365 enumerator_t *enumerator;
366 linked_list_t *conns;
367
368 conns = list_conns(conn, format);
369
370 enumerator = cfg->create_section_enumerator(cfg, "connections");
371 while (enumerator->enumerate(enumerator, &section))
372 {
373 remove_from_list(conns, section);
374 found++;
375 if (load_conn(conn, cfg, section, format))
376 {
377 loaded++;
378 }
379 }
380 enumerator->destroy(enumerator);
381
382 /* unload all connection in daemon, but not in file */
383 while (conns->remove_first(conns, (void**)&section) == SUCCESS)
384 {
385 if (unload_conn(conn, section, format))
386 {
387 unloaded++;
388 }
389 free(section);
390 }
391 conns->destroy(conns);
392
393 if (format & COMMAND_FORMAT_RAW)
394 {
395 return 0;
396 }
397 if (found == 0)
398 {
399 fprintf(stderr, "no connections found, %u unloaded\n", unloaded);
400 return 0;
401 }
402 if (loaded == found)
403 {
404 printf("successfully loaded %u connections, %u unloaded\n",
405 loaded, unloaded);
406 return 0;
407 }
408 fprintf(stderr, "loaded %u of %u connections, %u failed to load, "
409 "%u unloaded\n", loaded, found, found - loaded, unloaded);
410 return EINVAL;
411 }
412
413 static int load_conns(vici_conn_t *conn)
414 {
415 command_format_options_t format = COMMAND_FORMAT_NONE;
416 settings_t *cfg;
417 char *arg;
418 int ret;
419
420 while (TRUE)
421 {
422 switch (command_getopt(&arg))
423 {
424 case 'h':
425 return command_usage(NULL);
426 case 'P':
427 format |= COMMAND_FORMAT_PRETTY;
428 /* fall through to raw */
429 case 'r':
430 format |= COMMAND_FORMAT_RAW;
431 continue;
432 case EOF:
433 break;
434 default:
435 return command_usage("invalid --load-conns option");
436 }
437 break;
438 }
439
440 cfg = settings_create(SWANCTL_CONF);
441 if (!cfg)
442 {
443 fprintf(stderr, "parsing '%s' failed\n", SWANCTL_CONF);
444 return EINVAL;
445 }
446
447 ret = load_conns_cfg(conn, format, cfg);
448
449 cfg->destroy(cfg);
450
451 return ret;
452 }
453
454 /**
455 * Register the command.
456 */
457 static void __attribute__ ((constructor))reg()
458 {
459 command_register((command_t) {
460 load_conns, 'c', "load-conns", "(re-)load connection configuration",
461 {"[--raw|--pretty]"},
462 {
463 {"help", 'h', 0, "show usage information"},
464 {"raw", 'r', 0, "dump raw response message"},
465 {"pretty", 'P', 0, "dump raw response message in pretty print"},
466 }
467 });
468 }