swanctl: Stop logging with Ctrl+C on Windows as well
[strongswan.git] / src / swanctl / commands / load_creds.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 <unistd.h>
20 #include <sys/stat.h>
21
22 #include "command.h"
23 #include "swanctl.h"
24
25 #include <credentials/sets/callback_cred.h>
26
27 /**
28 * Load a single certificate over vici
29 */
30 static bool load_cert(vici_conn_t *conn, bool raw, char *dir,
31 char *type, chunk_t data)
32 {
33 vici_req_t *req;
34 vici_res_t *res;
35 bool ret = TRUE;
36
37 req = vici_begin("load-cert");
38
39 vici_add_key_valuef(req, "type", "%s", type);
40 vici_add_key_value(req, "data", data.ptr, data.len);
41
42 res = vici_submit(req, conn);
43 if (!res)
44 {
45 fprintf(stderr, "load-cert request failed: %s\n", strerror(errno));
46 return FALSE;
47 }
48 if (raw)
49 {
50 vici_dump(res, "load-cert reply", stdout);
51 }
52 else if (!streq(vici_find_str(res, "no", "success"), "yes"))
53 {
54 fprintf(stderr, "loading '%s' failed: %s\n",
55 dir, vici_find_str(res, "", "errmsg"));
56 ret = FALSE;
57 }
58 else
59 {
60 printf("loaded %s certificate '%s'\n", type, dir);
61 }
62 vici_free_res(res);
63 return ret;
64 }
65
66 /**
67 * Load certficiates from a directory
68 */
69 static void load_certs(vici_conn_t *conn, bool raw, char *type, char *dir)
70 {
71 enumerator_t *enumerator;
72 struct stat st;
73 chunk_t *map;
74 char *path;
75
76 enumerator = enumerator_create_directory(dir);
77 if (enumerator)
78 {
79 while (enumerator->enumerate(enumerator, NULL, &path, &st))
80 {
81 if (S_ISREG(st.st_mode))
82 {
83 map = chunk_map(path, FALSE);
84 if (map)
85 {
86 load_cert(conn, raw, path, type, *map);
87 chunk_unmap(map);
88 }
89 else
90 {
91 fprintf(stderr, "mapping '%s' failed: %s, skipped\n",
92 path, strerror(errno));
93 }
94 }
95 }
96 enumerator->destroy(enumerator);
97 }
98 }
99
100 /**
101 * Load a single private key over vici
102 */
103 static bool load_key(vici_conn_t *conn, bool raw, char *dir,
104 char *type, chunk_t data)
105 {
106 vici_req_t *req;
107 vici_res_t *res;
108 bool ret = TRUE;
109
110 req = vici_begin("load-key");
111
112 vici_add_key_valuef(req, "type", "%s", type);
113 vici_add_key_value(req, "data", data.ptr, data.len);
114
115 res = vici_submit(req, conn);
116 if (!res)
117 {
118 fprintf(stderr, "load-key request failed: %s\n", strerror(errno));
119 return FALSE;
120 }
121 if (raw)
122 {
123 vici_dump(res, "load-key reply", stdout);
124 }
125 else if (!streq(vici_find_str(res, "no", "success"), "yes"))
126 {
127 fprintf(stderr, "loading '%s' failed: %s\n",
128 dir, vici_find_str(res, "", "errmsg"));
129 ret = FALSE;
130 }
131 else
132 {
133 printf("loaded %s key '%s'\n", type, dir);
134 }
135 vici_free_res(res);
136 return ret;
137 }
138
139 /**
140 * Callback function to prompt for private key passwords
141 */
142 CALLBACK(password_cb, shared_key_t*,
143 char *prompt, shared_key_type_t type,
144 identification_t *me, identification_t *other,
145 id_match_t *match_me, id_match_t *match_other)
146 {
147 char *pwd = NULL;
148
149 if (type != SHARED_PRIVATE_KEY_PASS)
150 {
151 return NULL;
152 }
153 #ifdef HAVE_GETPASS
154 pwd = getpass(prompt);
155 #endif
156 if (!pwd || strlen(pwd) == 0)
157 {
158 return NULL;
159 }
160 if (match_me)
161 {
162 *match_me = ID_MATCH_PERFECT;
163 }
164 if (match_other)
165 {
166 *match_other = ID_MATCH_PERFECT;
167 }
168 return shared_key_create(type, chunk_clone(chunk_from_str(pwd)));
169 }
170
171 /**
172 * Try to parse a potentially encrypted private key
173 */
174 static private_key_t* decrypt_key(char *name, char *type, chunk_t encoding)
175 {
176 key_type_t kt = KEY_ANY;
177 private_key_t *private;
178 callback_cred_t *cb;
179 char buf[128];
180
181 if (streq(type, "rsa"))
182 {
183 kt = KEY_RSA;
184 }
185 else if (streq(type, "ecdsa"))
186 {
187 kt = KEY_ECDSA;
188 }
189
190 snprintf(buf, sizeof(buf), "Password for '%s': ", name);
191
192 cb = callback_cred_create_shared(password_cb, buf);
193 lib->credmgr->add_set(lib->credmgr, &cb->set);
194
195 private = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, kt,
196 BUILD_BLOB_PEM, encoding, BUILD_END);
197
198 lib->credmgr->remove_set(lib->credmgr, &cb->set);
199 cb->destroy(cb);
200
201 return private;
202 }
203
204 /**
205 * Try to decrypt and load a private key
206 */
207 static bool load_encrypted_key(vici_conn_t *conn, bool raw,
208 char *rel, char *path, char *type, chunk_t data)
209 {
210 private_key_t *private;
211 bool loaded = FALSE;
212 chunk_t encoding;
213
214 private = decrypt_key(rel, type, data);
215 if (private)
216 {
217 if (private->get_encoding(private, PRIVKEY_ASN1_DER,
218 &encoding))
219 {
220 switch (private->get_type(private))
221 {
222 case KEY_RSA:
223 loaded = load_key(conn, raw, path, "rsa", encoding);
224 break;
225 case KEY_ECDSA:
226 loaded = load_key(conn, raw, path, "ecdsa", encoding);
227 break;
228 default:
229 break;
230 }
231 chunk_clear(&encoding);
232 }
233 private->destroy(private);
234 }
235 return loaded;
236 }
237
238 /**
239 * Load private keys from a directory
240 */
241 static void load_keys(vici_conn_t *conn, bool raw, bool noprompt,
242 char *type, char *dir)
243 {
244 enumerator_t *enumerator;
245 struct stat st;
246 chunk_t *map;
247 char *path, *rel;
248
249 enumerator = enumerator_create_directory(dir);
250 if (enumerator)
251 {
252 while (enumerator->enumerate(enumerator, &rel, &path, &st))
253 {
254 if (S_ISREG(st.st_mode))
255 {
256 map = chunk_map(path, FALSE);
257 if (map)
258 {
259 if (noprompt ||
260 !load_encrypted_key(conn, raw, rel, path, type, *map))
261 {
262 load_key(conn, raw, path, type, *map);
263 }
264 chunk_unmap(map);
265 }
266 else
267 {
268 fprintf(stderr, "mapping '%s' failed: %s, skipped\n",
269 path, strerror(errno));
270 }
271 }
272 }
273 enumerator->destroy(enumerator);
274 }
275 }
276
277 /**
278 * Load a single secret over VICI
279 */
280 static bool load_secret(vici_conn_t *conn, settings_t *cfg,
281 char *section, bool raw)
282 {
283 enumerator_t *enumerator;
284 vici_req_t *req;
285 vici_res_t *res;
286 chunk_t data;
287 char *key, *value, buf[128], *type = NULL;
288 bool ret = TRUE;
289 int i;
290 char *types[] = {
291 "eap",
292 "xauth",
293 "ike",
294 };
295
296 for (i = 0; i < countof(types); i++)
297 {
298 if (strpfx(section, types[i]))
299 {
300 type = types[i];
301 break;
302 }
303 }
304 if (!type)
305 {
306 fprintf(stderr, "ignoring unsupported secret '%s'\n", section);
307 return FALSE;
308 }
309
310 value = cfg->get_str(cfg, "secrets.%s.secret", NULL, section);
311 if (!value)
312 {
313 fprintf(stderr, "missing secret in '%s', ignored\n", section);
314 return FALSE;
315 }
316 if (strcasepfx(value, "0x"))
317 {
318 data = chunk_from_hex(chunk_from_str(value + 2), NULL);
319 }
320 else if (strcasepfx(value, "0s"))
321 {
322 data = chunk_from_base64(chunk_from_str(value + 2), NULL);
323 }
324 else
325 {
326 data = chunk_clone(chunk_from_str(value));
327 }
328
329 req = vici_begin("load-shared");
330
331 vici_add_key_valuef(req, "type", "%s", type);
332 vici_add_key_value(req, "data", data.ptr, data.len);
333 chunk_clear(&data);
334
335 vici_begin_list(req, "owners");
336 snprintf(buf, sizeof(buf), "secrets.%s", section);
337 enumerator = cfg->create_key_value_enumerator(cfg, buf);
338 while (enumerator->enumerate(enumerator, &key, &value))
339 {
340 if (strpfx(key, "id"))
341 {
342 vici_add_list_itemf(req, "%s", value);
343 }
344 }
345 enumerator->destroy(enumerator);
346 vici_end_list(req);
347
348 res = vici_submit(req, conn);
349 if (!res)
350 {
351 fprintf(stderr, "load-shared request failed: %s\n", strerror(errno));
352 return FALSE;
353 }
354 if (raw)
355 {
356 vici_dump(res, "load-shared reply", stdout);
357 }
358 else if (!streq(vici_find_str(res, "no", "success"), "yes"))
359 {
360 fprintf(stderr, "loading shared secret failed: %s\n",
361 vici_find_str(res, "", "errmsg"));
362 ret = FALSE;
363 }
364 else
365 {
366 printf("loaded %s secret '%s'\n", type, section);
367 }
368 vici_free_res(res);
369 return ret;
370 }
371
372 /**
373 * Clear all currently loaded credentials
374 */
375 static bool clear_creds(vici_conn_t *conn, bool raw)
376 {
377 vici_res_t *res;
378
379 res = vici_submit(vici_begin("clear-creds"), conn);
380 if (!res)
381 {
382 fprintf(stderr, "clear-creds request failed: %s\n", strerror(errno));
383 return FALSE;
384 }
385 if (raw)
386 {
387 vici_dump(res, "clear-creds reply", stdout);
388 }
389 vici_free_res(res);
390 return TRUE;
391 }
392
393 static int load_creds(vici_conn_t *conn)
394 {
395 bool raw = FALSE, clear = FALSE, noprompt = FALSE;
396 enumerator_t *enumerator;
397 settings_t *cfg;
398 char *arg, *section;
399
400 while (TRUE)
401 {
402 switch (command_getopt(&arg))
403 {
404 case 'h':
405 return command_usage(NULL);
406 case 'c':
407 clear = TRUE;
408 continue;
409 case 'n':
410 noprompt = TRUE;
411 continue;
412 case 'r':
413 raw = TRUE;
414 continue;
415 case EOF:
416 break;
417 default:
418 return command_usage("invalid --load-creds option");
419 }
420 break;
421 }
422
423 if (clear)
424 {
425 if (!clear_creds(conn, raw))
426 {
427 return ECONNREFUSED;
428 }
429 }
430
431 load_certs(conn, raw, "x509", SWANCTL_X509DIR);
432 load_certs(conn, raw, "x509ca", SWANCTL_X509CADIR);
433 load_certs(conn, raw, "x509aa", SWANCTL_X509AADIR);
434 load_certs(conn, raw, "x509crl", SWANCTL_X509CRLDIR);
435 load_certs(conn, raw, "x509ac", SWANCTL_X509ACDIR);
436
437 load_keys(conn, raw, noprompt, "rsa", SWANCTL_RSADIR);
438 load_keys(conn, raw, noprompt, "ecdsa", SWANCTL_ECDSADIR);
439 load_keys(conn, raw, noprompt, "any", SWANCTL_PKCS8DIR);
440
441 cfg = settings_create(SWANCTL_CONF);
442 if (!cfg)
443 {
444 fprintf(stderr, "parsing '%s' failed\n", SWANCTL_CONF);
445 return EINVAL;
446 }
447
448 enumerator = cfg->create_section_enumerator(cfg, "secrets");
449 while (enumerator->enumerate(enumerator, &section))
450 {
451 load_secret(conn, cfg, section, raw);
452 }
453 enumerator->destroy(enumerator);
454
455 cfg->destroy(cfg);
456
457 return 0;
458 }
459
460 /**
461 * Register the command.
462 */
463 static void __attribute__ ((constructor))reg()
464 {
465 command_register((command_t) {
466 load_creds, 's', "load-creds", "(re-)load credentials",
467 {"[--raw]"},
468 {
469 {"help", 'h', 0, "show usage information"},
470 {"clear", 'c', 0, "clear previously loaded credentials"},
471 {"noprompt", 'n', 0, "do not prompt for passwords"},
472 {"raw", 'r', 0, "dump raw response message"},
473 }
474 });
475 }