d27b0fbf392dbc45f1acdc94174570a0a5ede6c8
[strongswan.git] / src / swanctl / commands / load_creds.c
1 /*
2 * Copyright (C) 2014 Martin Willi
3 * Copyright (C) 2014 revosec AG
4 *
5 * Copyright (C) 2015 Andreas Steffen
6 * HSR Hochschule fuer Technik Rapperswil
7 *
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the
10 * Free Software Foundation; either version 2 of the License, or (at your
11 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
12 *
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16 * for more details.
17 */
18
19 #define _GNU_SOURCE
20 #include <stdio.h>
21 #include <errno.h>
22 #include <unistd.h>
23 #include <sys/stat.h>
24
25 #include "command.h"
26 #include "swanctl.h"
27 #include "load_creds.h"
28
29 #include <credentials/sets/mem_cred.h>
30 #include <credentials/sets/callback_cred.h>
31 #include <credentials/containers/pkcs12.h>
32
33 /**
34 * Load a single certificate over vici
35 */
36 static bool load_cert(vici_conn_t *conn, command_format_options_t format,
37 char *dir, char *type, chunk_t data)
38 {
39 vici_req_t *req;
40 vici_res_t *res;
41 bool ret = TRUE;
42
43 req = vici_begin("load-cert");
44
45 vici_add_key_valuef(req, "type", "%s", type);
46 vici_add_key_value(req, "data", data.ptr, data.len);
47
48 res = vici_submit(req, conn);
49 if (!res)
50 {
51 fprintf(stderr, "load-cert request failed: %s\n", strerror(errno));
52 return FALSE;
53 }
54 if (format & COMMAND_FORMAT_RAW)
55 {
56 vici_dump(res, "load-cert reply", format & COMMAND_FORMAT_PRETTY,
57 stdout);
58 }
59 else if (!streq(vici_find_str(res, "no", "success"), "yes"))
60 {
61 fprintf(stderr, "loading '%s' failed: %s\n",
62 dir, vici_find_str(res, "", "errmsg"));
63 ret = FALSE;
64 }
65 else
66 {
67 printf("loaded %s certificate from '%s'\n", type, dir);
68 }
69 vici_free_res(res);
70 return ret;
71 }
72
73 /**
74 * Load certficiates from a directory
75 */
76 static void load_certs(vici_conn_t *conn, command_format_options_t format,
77 char *type, char *dir)
78 {
79 enumerator_t *enumerator;
80 struct stat st;
81 chunk_t *map;
82 char *path;
83
84 enumerator = enumerator_create_directory(dir);
85 if (enumerator)
86 {
87 while (enumerator->enumerate(enumerator, NULL, &path, &st))
88 {
89 if (S_ISREG(st.st_mode))
90 {
91 map = chunk_map(path, FALSE);
92 if (map)
93 {
94 load_cert(conn, format, path, type, *map);
95 chunk_unmap(map);
96 }
97 else
98 {
99 fprintf(stderr, "mapping '%s' failed: %s, skipped\n",
100 path, strerror(errno));
101 }
102 }
103 }
104 enumerator->destroy(enumerator);
105 }
106 }
107
108 /**
109 * Load a single private key over vici
110 */
111 static bool load_key(vici_conn_t *conn, command_format_options_t format,
112 char *dir, char *type, chunk_t data)
113 {
114 vici_req_t *req;
115 vici_res_t *res;
116 bool ret = TRUE;
117
118 req = vici_begin("load-key");
119
120 if (streq(type, "pkcs8"))
121 { /* as used by vici */
122 vici_add_key_valuef(req, "type", "any");
123 }
124 else
125 {
126 vici_add_key_valuef(req, "type", "%s", type);
127 }
128 vici_add_key_value(req, "data", data.ptr, data.len);
129
130 res = vici_submit(req, conn);
131 if (!res)
132 {
133 fprintf(stderr, "load-key request failed: %s\n", strerror(errno));
134 return FALSE;
135 }
136 if (format & COMMAND_FORMAT_RAW)
137 {
138 vici_dump(res, "load-key reply", format & COMMAND_FORMAT_PRETTY,
139 stdout);
140 }
141 else if (!streq(vici_find_str(res, "no", "success"), "yes"))
142 {
143 fprintf(stderr, "loading '%s' failed: %s\n",
144 dir, vici_find_str(res, "", "errmsg"));
145 ret = FALSE;
146 }
147 else
148 {
149 printf("loaded %s key from '%s'\n", type, dir);
150 }
151 vici_free_res(res);
152 return ret;
153 }
154
155 /**
156 * Load a private key of any type to vici
157 */
158 static bool load_key_anytype(vici_conn_t *conn, command_format_options_t format,
159 char *path, private_key_t *private)
160 {
161 bool loaded = FALSE;
162 chunk_t encoding;
163
164 if (!private->get_encoding(private, PRIVKEY_ASN1_DER, &encoding))
165 {
166 fprintf(stderr, "encoding private key from '%s' failed\n", path);
167 return FALSE;
168 }
169 switch (private->get_type(private))
170 {
171 case KEY_RSA:
172 loaded = load_key(conn, format, path, "rsa", encoding);
173 break;
174 case KEY_ECDSA:
175 loaded = load_key(conn, format, path, "ecdsa", encoding);
176 break;
177 case KEY_BLISS:
178 loaded = load_key(conn, format, path, "bliss", encoding);
179 break;
180 default:
181 fprintf(stderr, "unsupported key type in '%s'\n", path);
182 break;
183 }
184 chunk_clear(&encoding);
185 return loaded;
186 }
187
188 /**
189 * Data passed to password callback
190 */
191 typedef struct {
192 char prompt[128];
193 mem_cred_t *cache;
194 } cb_data_t;
195
196 /**
197 * Callback function to prompt for private key passwords
198 */
199 CALLBACK(password_cb, shared_key_t*,
200 cb_data_t *data, shared_key_type_t type,
201 identification_t *me, identification_t *other,
202 id_match_t *match_me, id_match_t *match_other)
203 {
204 shared_key_t *shared;
205 char *pwd = NULL;
206
207 if (type != SHARED_PRIVATE_KEY_PASS)
208 {
209 return NULL;
210 }
211 #ifdef HAVE_GETPASS
212 pwd = getpass(data->prompt);
213 #endif
214 if (!pwd || strlen(pwd) == 0)
215 {
216 return NULL;
217 }
218 if (match_me)
219 {
220 *match_me = ID_MATCH_PERFECT;
221 }
222 if (match_other)
223 {
224 *match_other = ID_MATCH_PERFECT;
225 }
226 shared = shared_key_create(type, chunk_clone(chunk_from_str(pwd)));
227 /* cache secret if it is required more than once (PKCS#12) */
228 data->cache->add_shared(data->cache, shared, NULL);
229 return shared->get_ref(shared);
230 }
231
232 /**
233 * Determine credential type and subtype from a type string
234 */
235 static bool determine_credtype(char *type, credential_type_t *credtype,
236 int *subtype)
237 {
238 struct {
239 char *type;
240 credential_type_t credtype;
241 int subtype;
242 } map[] = {
243 { "pkcs8", CRED_PRIVATE_KEY, KEY_ANY, },
244 { "rsa", CRED_PRIVATE_KEY, KEY_RSA, },
245 { "ecdsa", CRED_PRIVATE_KEY, KEY_ECDSA, },
246 { "bliss", CRED_PRIVATE_KEY, KEY_BLISS, },
247 { "pkcs12", CRED_CONTAINER, CONTAINER_PKCS12, },
248 };
249 int i;
250
251 for (i = 0; i < countof(map); i++)
252 {
253 if (streq(map[i].type, type))
254 {
255 *credtype = map[i].credtype;
256 *subtype = map[i].subtype;
257 return TRUE;
258 }
259 }
260 return FALSE;
261 }
262
263 /**
264 * Try to parse a potentially encrypted credential using password prompt
265 */
266 static void* decrypt(char *name, char *type, chunk_t encoding)
267 {
268 credential_type_t credtype;
269 int subtype;
270 void *cred;
271 callback_cred_t *cb;
272 cb_data_t data;
273
274 if (!determine_credtype(type, &credtype, &subtype))
275 {
276 return NULL;
277 }
278
279 snprintf(data.prompt, sizeof(data.prompt), "Password for %s file '%s': ",
280 type, name);
281
282 data.cache = mem_cred_create();
283 lib->credmgr->add_set(lib->credmgr, &data.cache->set);
284 cb = callback_cred_create_shared(password_cb, &data);
285 lib->credmgr->add_set(lib->credmgr, &cb->set);
286
287 cred = lib->creds->create(lib->creds, credtype, subtype,
288 BUILD_BLOB_PEM, encoding, BUILD_END);
289
290 lib->credmgr->remove_set(lib->credmgr, &data.cache->set);
291 data.cache->destroy(data.cache);
292 lib->credmgr->remove_set(lib->credmgr, &cb->set);
293 cb->destroy(cb);
294
295 return cred;
296 }
297
298 /**
299 * Try to parse a potentially encrypted credential using configured secret
300 */
301 static void* decrypt_with_config(settings_t *cfg, char *name, char *type,
302 chunk_t encoding)
303 {
304 credential_type_t credtype;
305 int subtype;
306 enumerator_t *enumerator, *secrets;
307 char *section, *key, *value, *file, buf[128];
308 shared_key_t *shared;
309 void *cred = NULL;
310 mem_cred_t *mem = NULL;
311
312 if (!determine_credtype(type, &credtype, &subtype))
313 {
314 return NULL;
315 }
316
317 /* load all secrets for this key type */
318 enumerator = cfg->create_section_enumerator(cfg, "secrets");
319 while (enumerator->enumerate(enumerator, &section))
320 {
321 if (strpfx(section, type))
322 {
323 file = cfg->get_str(cfg, "secrets.%s.file", NULL, section);
324 if (file && strcaseeq(file, name))
325 {
326 snprintf(buf, sizeof(buf), "secrets.%s", section);
327 secrets = cfg->create_key_value_enumerator(cfg, buf);
328 while (secrets->enumerate(secrets, &key, &value))
329 {
330 if (strpfx(key, "secret"))
331 {
332 if (!mem)
333 {
334 mem = mem_cred_create();
335 }
336 shared = shared_key_create(SHARED_PRIVATE_KEY_PASS,
337 chunk_clone(chunk_from_str(value)));
338 mem->add_shared(mem, shared, NULL);
339 }
340 }
341 secrets->destroy(secrets);
342 }
343 }
344 }
345 enumerator->destroy(enumerator);
346
347 if (mem)
348 {
349 lib->credmgr->add_local_set(lib->credmgr, &mem->set, FALSE);
350
351 cred = lib->creds->create(lib->creds, credtype, subtype,
352 BUILD_BLOB_PEM, encoding, BUILD_END);
353
354 lib->credmgr->remove_local_set(lib->credmgr, &mem->set);
355
356 if (!cred)
357 {
358 fprintf(stderr, "configured decryption secret for '%s' invalid\n",
359 name);
360 }
361
362 mem->destroy(mem);
363 }
364
365 return cred;
366 }
367
368 /**
369 * Try to decrypt and load a private key
370 */
371 static bool load_encrypted_key(vici_conn_t *conn,
372 command_format_options_t format, settings_t *cfg,
373 char *rel, char *path, char *type, bool noprompt,
374 chunk_t data)
375 {
376 private_key_t *private;
377 bool loaded = FALSE;
378
379 private = decrypt_with_config(cfg, rel, type, data);
380 if (!private && !noprompt)
381 {
382 private = decrypt(rel, type, data);
383 }
384 if (private)
385 {
386 loaded = load_key_anytype(conn, format, path, private);
387 private->destroy(private);
388 }
389 return loaded;
390 }
391
392 /**
393 * Load private keys from a directory
394 */
395 static void load_keys(vici_conn_t *conn, command_format_options_t format,
396 bool noprompt, settings_t *cfg, char *type, char *dir)
397 {
398 enumerator_t *enumerator;
399 struct stat st;
400 chunk_t *map;
401 char *path, *rel;
402
403 enumerator = enumerator_create_directory(dir);
404 if (enumerator)
405 {
406 while (enumerator->enumerate(enumerator, &rel, &path, &st))
407 {
408 if (S_ISREG(st.st_mode))
409 {
410 map = chunk_map(path, FALSE);
411 if (map)
412 {
413 if (!load_encrypted_key(conn, format, cfg, rel, path, type,
414 noprompt, *map))
415 {
416 load_key(conn, format, path, type, *map);
417 }
418 chunk_unmap(map);
419 }
420 else
421 {
422 fprintf(stderr, "mapping '%s' failed: %s, skipped\n",
423 path, strerror(errno));
424 }
425 }
426 }
427 enumerator->destroy(enumerator);
428 }
429 }
430
431 /**
432 * Load credentials from a PKCS#12 container over vici
433 */
434 static bool load_pkcs12(vici_conn_t *conn, command_format_options_t format,
435 char *path, pkcs12_t *p12)
436 {
437 enumerator_t *enumerator;
438 certificate_t *cert;
439 private_key_t *private;
440 chunk_t encoding;
441 bool loaded = TRUE;
442
443 enumerator = p12->create_cert_enumerator(p12);
444 while (loaded && enumerator->enumerate(enumerator, &cert))
445 {
446 loaded = FALSE;
447 if (cert->get_encoding(cert, CERT_ASN1_DER, &encoding))
448 {
449 loaded = load_cert(conn, format, path, "x509", encoding);
450 if (loaded)
451 {
452 fprintf(stderr, " %Y\n", cert->get_subject(cert));
453 }
454 free(encoding.ptr);
455 }
456 else
457 {
458 fprintf(stderr, "encoding certificate from '%s' failed\n", path);
459 }
460 }
461 enumerator->destroy(enumerator);
462
463 enumerator = p12->create_key_enumerator(p12);
464 while (loaded && enumerator->enumerate(enumerator, &private))
465 {
466 loaded = load_key_anytype(conn, format, path, private);
467 }
468 enumerator->destroy(enumerator);
469
470 return loaded;
471 }
472
473 /**
474 * Try to decrypt and load credentials from a container
475 */
476 static bool load_encrypted_container(vici_conn_t *conn,
477 command_format_options_t format, settings_t *cfg, char *rel,
478 char *path, char *type, bool noprompt, chunk_t data)
479 {
480 container_t *container;
481 bool loaded = FALSE;
482
483 container = decrypt_with_config(cfg, rel, type, data);
484 if (!container && !noprompt)
485 {
486 container = decrypt(rel, type, data);
487 }
488 if (container)
489 {
490 switch (container->get_type(container))
491 {
492 case CONTAINER_PKCS12:
493 loaded = load_pkcs12(conn, format, path, (pkcs12_t*)container);
494 break;
495 default:
496 break;
497 }
498 container->destroy(container);
499 }
500 return loaded;
501 }
502
503 /**
504 * Load credential containers from a directory
505 */
506 static void load_containers(vici_conn_t *conn, command_format_options_t format,
507 bool noprompt, settings_t *cfg, char *type, char *dir)
508 {
509 enumerator_t *enumerator;
510 struct stat st;
511 chunk_t *map;
512 char *path, *rel;
513
514 enumerator = enumerator_create_directory(dir);
515 if (enumerator)
516 {
517 while (enumerator->enumerate(enumerator, &rel, &path, &st))
518 {
519 if (S_ISREG(st.st_mode))
520 {
521 map = chunk_map(path, FALSE);
522 if (map)
523 {
524 load_encrypted_container(conn, format, cfg, rel, path,
525 type, noprompt, *map);
526 chunk_unmap(map);
527 }
528 else
529 {
530 fprintf(stderr, "mapping '%s' failed: %s, skipped\n",
531 path, strerror(errno));
532 }
533 }
534 }
535 enumerator->destroy(enumerator);
536 }
537 }
538
539 /**
540 * Load a single secret over VICI
541 */
542 static bool load_secret(vici_conn_t *conn, settings_t *cfg,
543 char *section, command_format_options_t format)
544 {
545 enumerator_t *enumerator;
546 vici_req_t *req;
547 vici_res_t *res;
548 chunk_t data;
549 char *key, *value, buf[128], *type = NULL;
550 bool ret = TRUE;
551 int i;
552 char *types[] = {
553 "eap",
554 "xauth",
555 "ike",
556 "rsa",
557 "ecdsa",
558 "bliss",
559 "pkcs8",
560 "pkcs12",
561 };
562
563 for (i = 0; i < countof(types); i++)
564 {
565 if (strpfx(section, types[i]))
566 {
567 type = types[i];
568 break;
569 }
570 }
571 if (!type)
572 {
573 fprintf(stderr, "ignoring unsupported secret '%s'\n", section);
574 return FALSE;
575 }
576 if (!streq(type, "eap") && !streq(type, "xauth") && !streq(type, "ike"))
577 { /* skip non-shared secrets */
578 return TRUE;
579 }
580
581 value = cfg->get_str(cfg, "secrets.%s.secret", NULL, section);
582 if (!value)
583 {
584 fprintf(stderr, "missing secret in '%s', ignored\n", section);
585 return FALSE;
586 }
587 if (strcasepfx(value, "0x"))
588 {
589 data = chunk_from_hex(chunk_from_str(value + 2), NULL);
590 }
591 else if (strcasepfx(value, "0s"))
592 {
593 data = chunk_from_base64(chunk_from_str(value + 2), NULL);
594 }
595 else
596 {
597 data = chunk_clone(chunk_from_str(value));
598 }
599
600 req = vici_begin("load-shared");
601
602 vici_add_key_valuef(req, "type", "%s", type);
603 vici_add_key_value(req, "data", data.ptr, data.len);
604 chunk_clear(&data);
605
606 vici_begin_list(req, "owners");
607 snprintf(buf, sizeof(buf), "secrets.%s", section);
608 enumerator = cfg->create_key_value_enumerator(cfg, buf);
609 while (enumerator->enumerate(enumerator, &key, &value))
610 {
611 if (strpfx(key, "id"))
612 {
613 vici_add_list_itemf(req, "%s", value);
614 }
615 }
616 enumerator->destroy(enumerator);
617 vici_end_list(req);
618
619 res = vici_submit(req, conn);
620 if (!res)
621 {
622 fprintf(stderr, "load-shared request failed: %s\n", strerror(errno));
623 return FALSE;
624 }
625 if (format & COMMAND_FORMAT_RAW)
626 {
627 vici_dump(res, "load-shared reply", format & COMMAND_FORMAT_PRETTY,
628 stdout);
629 }
630 else if (!streq(vici_find_str(res, "no", "success"), "yes"))
631 {
632 fprintf(stderr, "loading shared secret failed: %s\n",
633 vici_find_str(res, "", "errmsg"));
634 ret = FALSE;
635 }
636 else
637 {
638 printf("loaded %s secret '%s'\n", type, section);
639 }
640 vici_free_res(res);
641 return ret;
642 }
643
644 /**
645 * Clear all currently loaded credentials
646 */
647 static bool clear_creds(vici_conn_t *conn, command_format_options_t format)
648 {
649 vici_res_t *res;
650
651 res = vici_submit(vici_begin("clear-creds"), conn);
652 if (!res)
653 {
654 fprintf(stderr, "clear-creds request failed: %s\n", strerror(errno));
655 return FALSE;
656 }
657 if (format & COMMAND_FORMAT_RAW)
658 {
659 vici_dump(res, "clear-creds reply", format & COMMAND_FORMAT_PRETTY,
660 stdout);
661 }
662 vici_free_res(res);
663 return TRUE;
664 }
665
666 /**
667 * See header.
668 */
669 int load_creds_cfg(vici_conn_t *conn, command_format_options_t format,
670 settings_t *cfg, bool clear, bool noprompt)
671 {
672 enumerator_t *enumerator;
673 char *section;
674
675 if (clear)
676 {
677 if (!clear_creds(conn, format))
678 {
679 return ECONNREFUSED;
680 }
681 }
682
683 load_certs(conn, format, "x509", SWANCTL_X509DIR);
684 load_certs(conn, format, "x509ca", SWANCTL_X509CADIR);
685 load_certs(conn, format, "x509aa", SWANCTL_X509AADIR);
686 load_certs(conn, format, "x509crl", SWANCTL_X509CRLDIR);
687 load_certs(conn, format, "x509ac", SWANCTL_X509ACDIR);
688 load_certs(conn, format, "x509ocsp", SWANCTL_X509OCSPDIR);
689 load_certs(conn, format, "pubkey", SWANCTL_PUBKEYDIR);
690
691 load_keys(conn, format, noprompt, cfg, "rsa", SWANCTL_RSADIR);
692 load_keys(conn, format, noprompt, cfg, "ecdsa", SWANCTL_ECDSADIR);
693 load_keys(conn, format, noprompt, cfg, "bliss", SWANCTL_BLISSDIR);
694 load_keys(conn, format, noprompt, cfg, "pkcs8", SWANCTL_PKCS8DIR);
695
696 load_containers(conn, format, noprompt, cfg, "pkcs12", SWANCTL_PKCS12DIR);
697
698 enumerator = cfg->create_section_enumerator(cfg, "secrets");
699 while (enumerator->enumerate(enumerator, &section))
700 {
701 load_secret(conn, cfg, section, format);
702 }
703 enumerator->destroy(enumerator);
704
705 return 0;
706 }
707
708 static int load_creds(vici_conn_t *conn)
709 {
710 bool clear = FALSE, noprompt = FALSE;
711 command_format_options_t format = COMMAND_FORMAT_NONE;
712 settings_t *cfg;
713 char *arg;
714 int ret;
715
716 while (TRUE)
717 {
718 switch (command_getopt(&arg))
719 {
720 case 'h':
721 return command_usage(NULL);
722 case 'c':
723 clear = TRUE;
724 continue;
725 case 'n':
726 noprompt = TRUE;
727 continue;
728 case 'P':
729 format |= COMMAND_FORMAT_PRETTY;
730 /* fall through to raw */
731 case 'r':
732 format |= COMMAND_FORMAT_RAW;
733 continue;
734 case EOF:
735 break;
736 default:
737 return command_usage("invalid --load-creds option");
738 }
739 break;
740 }
741
742 cfg = settings_create(SWANCTL_CONF);
743 if (!cfg)
744 {
745 fprintf(stderr, "parsing '%s' failed\n", SWANCTL_CONF);
746 return EINVAL;
747 }
748
749 ret = load_creds_cfg(conn, format, cfg, clear, noprompt);
750
751 cfg->destroy(cfg);
752
753 return ret;
754 }
755
756 /**
757 * Register the command.
758 */
759 static void __attribute__ ((constructor))reg()
760 {
761 command_register((command_t) {
762 load_creds, 's', "load-creds", "(re-)load credentials",
763 {"[--raw|--pretty] [--clear] [--noprompt]"},
764 {
765 {"help", 'h', 0, "show usage information"},
766 {"clear", 'c', 0, "clear previously loaded credentials"},
767 {"noprompt", 'n', 0, "do not prompt for passwords"},
768 {"raw", 'r', 0, "dump raw response message"},
769 {"pretty", 'P', 0, "dump raw response message in pretty print"},
770 }
771 });
772 }