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