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