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