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