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