swanctl: Add `token` secrets for keys on tokens/smartcards
[strongswan.git] / src / swanctl / commands / load_creds.c
1 /*
2 * Copyright (C) 2016 Tobias Brunner
3 * Copyright (C) 2015 Andreas Steffen
4 * HSR Hochschule fuer Technik Rapperswil
5 *
6 * Copyright (C) 2014 Martin Willi
7 * Copyright (C) 2014 revosec AG
8 *
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU General Public License as published by the
11 * Free Software Foundation; either version 2 of the License, or (at your
12 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
13 *
14 * This program is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
16 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
17 * for more details.
18 */
19
20 #define _GNU_SOURCE
21 #include <stdio.h>
22 #include <errno.h>
23 #include <unistd.h>
24 #include <sys/stat.h>
25
26 #include "command.h"
27 #include "swanctl.h"
28 #include "load_creds.h"
29
30 #include <credentials/sets/mem_cred.h>
31 #include <credentials/sets/callback_cred.h>
32 #include <credentials/containers/pkcs12.h>
33 #include <collections/hashtable.h>
34
35 #include <vici_cert_info.h>
36
37 #define HASH_SIZE_SHA1_HEX (2 * HASH_SIZE_SHA1)
38
39 /**
40 * Context used to track loaded secrets
41 */
42 typedef struct {
43 /** vici connection */
44 vici_conn_t *conn;
45 /** format options */
46 command_format_options_t format;
47 /** read setting */
48 settings_t *cfg;
49 /** don't prompt user for password */
50 bool noprompt;
51 /** list of key ids of loaded private keys */
52 hashtable_t *keys;
53 /** list of unique ids of loaded shared keys */
54 hashtable_t *shared;
55 } load_ctx_t;
56
57 /**
58 * Load a single certificate over vici
59 */
60 static bool load_cert(load_ctx_t *ctx, char *dir, certificate_type_t type,
61 x509_flag_t flag, chunk_t data)
62 {
63 vici_req_t *req;
64 vici_res_t *res;
65 bool ret = TRUE;
66
67 req = vici_begin("load-cert");
68
69 vici_add_key_valuef(req, "type", "%N", certificate_type_names, type);
70 if (type == CERT_X509)
71 {
72 vici_add_key_valuef(req, "flag", "%N", x509_flag_names, flag);
73 }
74 vici_add_key_value(req, "data", data.ptr, data.len);
75
76 res = vici_submit(req, ctx->conn);
77 if (!res)
78 {
79 fprintf(stderr, "load-cert request failed: %s\n", strerror(errno));
80 return FALSE;
81 }
82 if (ctx->format & COMMAND_FORMAT_RAW)
83 {
84 vici_dump(res, "load-cert reply", ctx->format & COMMAND_FORMAT_PRETTY,
85 stdout);
86 }
87 else if (!streq(vici_find_str(res, "no", "success"), "yes"))
88 {
89 fprintf(stderr, "loading '%s' failed: %s\n",
90 dir, vici_find_str(res, "", "errmsg"));
91 ret = FALSE;
92 }
93 else
94 {
95 printf("loaded certificate from '%s'\n", dir);
96 }
97 vici_free_res(res);
98 return ret;
99 }
100
101 /**
102 * Load certficiates from a directory
103 */
104 static void load_certs(load_ctx_t *ctx, char *type_str, char *dir)
105 {
106 enumerator_t *enumerator;
107 certificate_type_t type;
108 x509_flag_t flag;
109 struct stat st;
110 chunk_t *map;
111 char *path;
112
113 vici_cert_info_from_str(type_str, &type, &flag);
114
115 enumerator = enumerator_create_directory(dir);
116 if (enumerator)
117 {
118 while (enumerator->enumerate(enumerator, NULL, &path, &st))
119 {
120 if (S_ISREG(st.st_mode))
121 {
122 map = chunk_map(path, FALSE);
123 if (map)
124 {
125 load_cert(ctx, path, type, flag, *map);
126 chunk_unmap(map);
127 }
128 else
129 {
130 fprintf(stderr, "mapping '%s' failed: %s, skipped\n",
131 path, strerror(errno));
132 }
133 }
134 }
135 enumerator->destroy(enumerator);
136 }
137 }
138
139 /**
140 * Load a single private key over vici
141 */
142 static bool load_key(load_ctx_t *ctx, char *dir, char *type, chunk_t data)
143 {
144 vici_req_t *req;
145 vici_res_t *res;
146 bool ret = TRUE;
147
148 req = vici_begin("load-key");
149
150 if (streq(type, "private") ||
151 streq(type, "pkcs8"))
152 { /* as used by vici */
153 vici_add_key_valuef(req, "type", "any");
154 }
155 else
156 {
157 vici_add_key_valuef(req, "type", "%s", type);
158 }
159 vici_add_key_value(req, "data", data.ptr, data.len);
160
161 res = vici_submit(req, ctx->conn);
162 if (!res)
163 {
164 fprintf(stderr, "load-key request failed: %s\n", strerror(errno));
165 return FALSE;
166 }
167 if (ctx->format & COMMAND_FORMAT_RAW)
168 {
169 vici_dump(res, "load-key reply", ctx->format & COMMAND_FORMAT_PRETTY,
170 stdout);
171 }
172 else if (!streq(vici_find_str(res, "no", "success"), "yes"))
173 {
174 fprintf(stderr, "loading '%s' failed: %s\n",
175 dir, vici_find_str(res, "", "errmsg"));
176 ret = FALSE;
177 }
178 else
179 {
180 printf("loaded %s key from '%s'\n", type, dir);
181 }
182 vici_free_res(res);
183 return ret;
184 }
185
186 /**
187 * Load a private key of any type to vici
188 */
189 static bool load_key_anytype(load_ctx_t *ctx, char *path,
190 private_key_t *private)
191 {
192 bool loaded = FALSE;
193 chunk_t encoding, keyid;
194 char hex[HASH_SIZE_SHA1_HEX + 1];
195
196 if (!private->get_encoding(private, PRIVKEY_ASN1_DER, &encoding))
197 {
198 fprintf(stderr, "encoding private key from '%s' failed\n", path);
199 return FALSE;
200 }
201 switch (private->get_type(private))
202 {
203 case KEY_RSA:
204 loaded = load_key(ctx, path, "rsa", encoding);
205 break;
206 case KEY_ECDSA:
207 loaded = load_key(ctx, path, "ecdsa", encoding);
208 break;
209 case KEY_BLISS:
210 loaded = load_key(ctx, path, "bliss", encoding);
211 break;
212 default:
213 fprintf(stderr, "unsupported key type in '%s'\n", path);
214 break;
215 }
216
217 if (loaded &&
218 private->get_fingerprint(private, KEYID_PUBKEY_SHA1, &keyid) &&
219 snprintf(hex, sizeof(hex), "%+B", &keyid) == HASH_SIZE_SHA1_HEX)
220 {
221 free(ctx->keys->remove(ctx->keys, hex));
222 }
223 chunk_clear(&encoding);
224 return loaded;
225 }
226
227 /**
228 * Data passed to password callback
229 */
230 typedef struct {
231 char prompt[128];
232 mem_cred_t *cache;
233 } cb_data_t;
234
235 /**
236 * Callback function to prompt for private key passwords
237 */
238 CALLBACK(password_cb, shared_key_t*,
239 cb_data_t *data, shared_key_type_t type,
240 identification_t *me, identification_t *other,
241 id_match_t *match_me, id_match_t *match_other)
242 {
243 shared_key_t *shared;
244 char *pwd = NULL;
245
246 if (type != SHARED_PRIVATE_KEY_PASS)
247 {
248 return NULL;
249 }
250 #ifdef HAVE_GETPASS
251 pwd = getpass(data->prompt);
252 #endif
253 if (!pwd || strlen(pwd) == 0)
254 {
255 return NULL;
256 }
257 if (match_me)
258 {
259 *match_me = ID_MATCH_PERFECT;
260 }
261 if (match_other)
262 {
263 *match_other = ID_MATCH_PERFECT;
264 }
265 shared = shared_key_create(type, chunk_clone(chunk_from_str(pwd)));
266 /* cache secret if it is required more than once (PKCS#12) */
267 data->cache->add_shared(data->cache, shared, NULL);
268 return shared->get_ref(shared);
269 }
270
271 /**
272 * Determine credential type and subtype from a type string
273 */
274 static bool determine_credtype(char *type, credential_type_t *credtype,
275 int *subtype)
276 {
277 struct {
278 char *type;
279 credential_type_t credtype;
280 int subtype;
281 } map[] = {
282 { "private", CRED_PRIVATE_KEY, KEY_ANY, },
283 { "pkcs8", CRED_PRIVATE_KEY, KEY_ANY, },
284 { "rsa", CRED_PRIVATE_KEY, KEY_RSA, },
285 { "ecdsa", CRED_PRIVATE_KEY, KEY_ECDSA, },
286 { "bliss", CRED_PRIVATE_KEY, KEY_BLISS, },
287 { "pkcs12", CRED_CONTAINER, CONTAINER_PKCS12, },
288 };
289 int i;
290
291 for (i = 0; i < countof(map); i++)
292 {
293 if (streq(map[i].type, type))
294 {
295 *credtype = map[i].credtype;
296 *subtype = map[i].subtype;
297 return TRUE;
298 }
299 }
300 return FALSE;
301 }
302
303 /**
304 * Try to parse a potentially encrypted credential using password prompt
305 */
306 static void* decrypt(char *name, char *type, chunk_t encoding)
307 {
308 credential_type_t credtype;
309 int subtype;
310 void *cred;
311 callback_cred_t *cb;
312 cb_data_t data;
313
314 if (!determine_credtype(type, &credtype, &subtype))
315 {
316 return NULL;
317 }
318
319 snprintf(data.prompt, sizeof(data.prompt), "Password for %s file '%s': ",
320 type, name);
321
322 data.cache = mem_cred_create();
323 lib->credmgr->add_set(lib->credmgr, &data.cache->set);
324 cb = callback_cred_create_shared(password_cb, &data);
325 lib->credmgr->add_set(lib->credmgr, &cb->set);
326
327 cred = lib->creds->create(lib->creds, credtype, subtype,
328 BUILD_BLOB_PEM, encoding, BUILD_END);
329
330 lib->credmgr->remove_set(lib->credmgr, &data.cache->set);
331 data.cache->destroy(data.cache);
332 lib->credmgr->remove_set(lib->credmgr, &cb->set);
333 cb->destroy(cb);
334
335 return cred;
336 }
337
338 /**
339 * Try to parse a potentially encrypted credential using configured secret
340 */
341 static void* decrypt_with_config(load_ctx_t *ctx, char *name, char *type,
342 chunk_t encoding)
343 {
344 credential_type_t credtype;
345 int subtype;
346 enumerator_t *enumerator, *secrets;
347 char *section, *key, *value, *file, buf[128];
348 shared_key_t *shared;
349 void *cred = NULL;
350 mem_cred_t *mem = NULL;
351
352 if (!determine_credtype(type, &credtype, &subtype))
353 {
354 return NULL;
355 }
356
357 /* load all secrets for this key type */
358 enumerator = ctx->cfg->create_section_enumerator(ctx->cfg, "secrets");
359 while (enumerator->enumerate(enumerator, &section))
360 {
361 if (strpfx(section, type))
362 {
363 file = ctx->cfg->get_str(ctx->cfg, "secrets.%s.file", NULL, section);
364 if (file && strcaseeq(file, name))
365 {
366 snprintf(buf, sizeof(buf), "secrets.%s", section);
367 secrets = ctx->cfg->create_key_value_enumerator(ctx->cfg, buf);
368 while (secrets->enumerate(secrets, &key, &value))
369 {
370 if (strpfx(key, "secret"))
371 {
372 if (!mem)
373 {
374 mem = mem_cred_create();
375 }
376 shared = shared_key_create(SHARED_PRIVATE_KEY_PASS,
377 chunk_clone(chunk_from_str(value)));
378 mem->add_shared(mem, shared, NULL);
379 }
380 }
381 secrets->destroy(secrets);
382 }
383 }
384 }
385 enumerator->destroy(enumerator);
386
387 if (mem)
388 {
389 lib->credmgr->add_local_set(lib->credmgr, &mem->set, FALSE);
390
391 cred = lib->creds->create(lib->creds, credtype, subtype,
392 BUILD_BLOB_PEM, encoding, BUILD_END);
393
394 lib->credmgr->remove_local_set(lib->credmgr, &mem->set);
395
396 if (!cred)
397 {
398 fprintf(stderr, "configured decryption secret for '%s' invalid\n",
399 name);
400 }
401
402 mem->destroy(mem);
403 }
404
405 return cred;
406 }
407
408 /**
409 * Try to decrypt and load a private key
410 */
411 static bool load_encrypted_key(load_ctx_t *ctx, char *rel, char *path,
412 char *type, chunk_t data)
413 {
414 private_key_t *private;
415 bool loaded = FALSE;
416
417 private = decrypt_with_config(ctx, rel, type, data);
418 if (!private && !ctx->noprompt)
419 {
420 private = decrypt(rel, type, data);
421 }
422 if (private)
423 {
424 loaded = load_key_anytype(ctx, path, private);
425 private->destroy(private);
426 }
427 return loaded;
428 }
429
430 /**
431 * Load private keys from a directory
432 */
433 static void load_keys(load_ctx_t *ctx, char *type, char *dir)
434 {
435 enumerator_t *enumerator;
436 struct stat st;
437 chunk_t *map;
438 char *path, *rel;
439
440 enumerator = enumerator_create_directory(dir);
441 if (enumerator)
442 {
443 while (enumerator->enumerate(enumerator, &rel, &path, &st))
444 {
445 if (S_ISREG(st.st_mode))
446 {
447 map = chunk_map(path, FALSE);
448 if (map)
449 {
450 if (!load_encrypted_key(ctx, rel, path, type, *map))
451 {
452 load_key(ctx, path, type, *map);
453 }
454 chunk_unmap(map);
455 }
456 else
457 {
458 fprintf(stderr, "mapping '%s' failed: %s, skipped\n",
459 path, strerror(errno));
460 }
461 }
462 }
463 enumerator->destroy(enumerator);
464 }
465 }
466
467 /**
468 * Load credentials from a PKCS#12 container over vici
469 */
470 static bool load_pkcs12(load_ctx_t *ctx, char *path, pkcs12_t *p12)
471 {
472 enumerator_t *enumerator;
473 certificate_t *cert;
474 private_key_t *private;
475 chunk_t encoding;
476 bool loaded = TRUE;
477
478 enumerator = p12->create_cert_enumerator(p12);
479 while (loaded && enumerator->enumerate(enumerator, &cert))
480 {
481 loaded = FALSE;
482 if (cert->get_encoding(cert, CERT_ASN1_DER, &encoding))
483 {
484 loaded = load_cert(ctx, path, CERT_X509, X509_NONE, encoding);
485 if (loaded)
486 {
487 fprintf(stderr, " %Y\n", cert->get_subject(cert));
488 }
489 free(encoding.ptr);
490 }
491 else
492 {
493 fprintf(stderr, "encoding certificate from '%s' failed\n", path);
494 }
495 }
496 enumerator->destroy(enumerator);
497
498 enumerator = p12->create_key_enumerator(p12);
499 while (loaded && enumerator->enumerate(enumerator, &private))
500 {
501 loaded = load_key_anytype(ctx, path, private);
502 }
503 enumerator->destroy(enumerator);
504
505 return loaded;
506 }
507
508 /**
509 * Try to decrypt and load credentials from a container
510 */
511 static bool load_encrypted_container(load_ctx_t *ctx, char *rel, char *path,
512 char *type, chunk_t data)
513 {
514 container_t *container;
515 bool loaded = FALSE;
516
517 container = decrypt_with_config(ctx, rel, type, data);
518 if (!container && !ctx->noprompt)
519 {
520 container = decrypt(rel, type, data);
521 }
522 if (container)
523 {
524 switch (container->get_type(container))
525 {
526 case CONTAINER_PKCS12:
527 loaded = load_pkcs12(ctx, path, (pkcs12_t*)container);
528 break;
529 default:
530 break;
531 }
532 container->destroy(container);
533 }
534 return loaded;
535 }
536
537 /**
538 * Load credential containers from a directory
539 */
540 static void load_containers(load_ctx_t *ctx, char *type, char *dir)
541 {
542 enumerator_t *enumerator;
543 struct stat st;
544 chunk_t *map;
545 char *path, *rel;
546
547 enumerator = enumerator_create_directory(dir);
548 if (enumerator)
549 {
550 while (enumerator->enumerate(enumerator, &rel, &path, &st))
551 {
552 if (S_ISREG(st.st_mode))
553 {
554 map = chunk_map(path, FALSE);
555 if (map)
556 {
557 load_encrypted_container(ctx, rel, path, type, *map);
558 chunk_unmap(map);
559 }
560 else
561 {
562 fprintf(stderr, "mapping '%s' failed: %s, skipped\n",
563 path, strerror(errno));
564 }
565 }
566 }
567 enumerator->destroy(enumerator);
568 }
569 }
570
571 /**
572 * Load a single private key on a token over vici
573 */
574 static bool load_token(load_ctx_t *ctx, char *name, char *pin)
575 {
576 vici_req_t *req;
577 vici_res_t *res;
578 enumerator_t *enumerator;
579 char *key, *value, *id;
580 bool ret = TRUE;
581
582 req = vici_begin("load-token");
583
584 enumerator = ctx->cfg->create_key_value_enumerator(ctx->cfg, "secrets.%s",
585 name);
586 while (enumerator->enumerate(enumerator, &key, &value))
587 {
588 vici_add_key_valuef(req, key, "%s", value);
589 }
590 enumerator->destroy(enumerator);
591
592 if (pin)
593 {
594 vici_add_key_valuef(req, "pin", "%s", pin);
595 }
596 res = vici_submit(req, ctx->conn);
597 if (!res)
598 {
599 fprintf(stderr, "load-token request failed: %s\n", strerror(errno));
600 return FALSE;
601 }
602 if (ctx->format & COMMAND_FORMAT_RAW)
603 {
604 vici_dump(res, "load-token reply", ctx->format & COMMAND_FORMAT_PRETTY,
605 stdout);
606 }
607 else if (!streq(vici_find_str(res, "no", "success"), "yes"))
608 {
609 fprintf(stderr, "loading '%s' failed: %s\n",
610 name, vici_find_str(res, "", "errmsg"));
611 ret = FALSE;
612 }
613 else
614 {
615 id = vici_find_str(res, "", "id");
616 printf("loaded key %s from token [keyid: %s]\n", name, id);
617 free(ctx->keys->remove(ctx->keys, id));
618 }
619 vici_free_res(res);
620 return ret;
621 }
622
623 /**
624 * Load keys from tokens
625 */
626 static void load_tokens(load_ctx_t *ctx)
627 {
628 enumerator_t *enumerator;
629 char *section, *pin = NULL, prompt[128];
630
631 enumerator = ctx->cfg->create_section_enumerator(ctx->cfg, "secrets");
632 while (enumerator->enumerate(enumerator, &section))
633 {
634 if (strpfx(section, "token"))
635 {
636 if (!ctx->noprompt &&
637 !ctx->cfg->get_str(ctx->cfg, "secrets.%s.pin", NULL, section))
638 {
639 #ifdef HAVE_GETPASS
640 snprintf(prompt, sizeof(prompt), "PIN for %s: ", section);
641 pin = strdupnull(getpass(prompt));
642 #endif
643 }
644 load_token(ctx, section, pin);
645 if (pin)
646 {
647 memwipe(pin, strlen(pin));
648 free(pin);
649 pin = NULL;
650 }
651 }
652 }
653 enumerator->destroy(enumerator);
654 }
655
656
657
658 /**
659 * Load a single secret over VICI
660 */
661 static bool load_secret(load_ctx_t *ctx, char *section)
662 {
663 enumerator_t *enumerator;
664 vici_req_t *req;
665 vici_res_t *res;
666 chunk_t data;
667 char *key, *value, buf[128], *type = NULL;
668 bool ret = TRUE;
669 int i;
670 char *types[] = {
671 "eap",
672 "xauth",
673 "ntlm",
674 "ike",
675 "private",
676 "rsa",
677 "ecdsa",
678 "bliss",
679 "pkcs8",
680 "pkcs12",
681 "token",
682 };
683
684 for (i = 0; i < countof(types); i++)
685 {
686 if (strpfx(section, types[i]))
687 {
688 type = types[i];
689 break;
690 }
691 }
692 if (!type)
693 {
694 fprintf(stderr, "ignoring unsupported secret '%s'\n", section);
695 return FALSE;
696 }
697 if (!streq(type, "eap") && !streq(type, "xauth") && !streq(type, "ntlm") &&
698 !streq(type, "ike"))
699 { /* skip non-shared secrets */
700 return TRUE;
701 }
702
703 value = ctx->cfg->get_str(ctx->cfg, "secrets.%s.secret", NULL, section);
704 if (!value)
705 {
706 fprintf(stderr, "missing secret in '%s', ignored\n", section);
707 return FALSE;
708 }
709 if (strcasepfx(value, "0x"))
710 {
711 data = chunk_from_hex(chunk_from_str(value + 2), NULL);
712 }
713 else if (strcasepfx(value, "0s"))
714 {
715 data = chunk_from_base64(chunk_from_str(value + 2), NULL);
716 }
717 else
718 {
719 data = chunk_clone(chunk_from_str(value));
720 }
721
722 req = vici_begin("load-shared");
723
724 vici_add_key_valuef(req, "id", "%s", section);
725 vici_add_key_valuef(req, "type", "%s", type);
726 vici_add_key_value(req, "data", data.ptr, data.len);
727 chunk_clear(&data);
728
729 vici_begin_list(req, "owners");
730 snprintf(buf, sizeof(buf), "secrets.%s", section);
731 enumerator = ctx->cfg->create_key_value_enumerator(ctx->cfg, buf);
732 while (enumerator->enumerate(enumerator, &key, &value))
733 {
734 if (strpfx(key, "id"))
735 {
736 vici_add_list_itemf(req, "%s", value);
737 }
738 }
739 enumerator->destroy(enumerator);
740 vici_end_list(req);
741
742 res = vici_submit(req, ctx->conn);
743 if (!res)
744 {
745 fprintf(stderr, "load-shared request failed: %s\n", strerror(errno));
746 return FALSE;
747 }
748 if (ctx->format & COMMAND_FORMAT_RAW)
749 {
750 vici_dump(res, "load-shared reply", ctx->format & COMMAND_FORMAT_PRETTY,
751 stdout);
752 }
753 else if (!streq(vici_find_str(res, "no", "success"), "yes"))
754 {
755 fprintf(stderr, "loading shared secret failed: %s\n",
756 vici_find_str(res, "", "errmsg"));
757 ret = FALSE;
758 }
759 else
760 {
761 printf("loaded %s secret '%s'\n", type, section);
762 }
763 if (ret)
764 {
765 free(ctx->shared->remove(ctx->shared, section));
766 }
767 vici_free_res(res);
768 return ret;
769 }
770
771 CALLBACK(get_id, int,
772 hashtable_t *ht, vici_res_t *res, char *name, void *value, int len)
773 {
774 if (streq(name, "keys"))
775 {
776 char *str;
777
778 if (asprintf(&str, "%.*s", len, value) != -1)
779 {
780 free(ht->put(ht, str, str));
781 }
782 }
783 return 0;
784 }
785
786 /**
787 * Get a list of currently loaded private and shared keys
788 */
789 static void get_creds(load_ctx_t *ctx)
790 {
791 vici_res_t *res;
792
793 res = vici_submit(vici_begin("get-keys"), ctx->conn);
794 if (res)
795 {
796 if (ctx->format & COMMAND_FORMAT_RAW)
797 {
798 vici_dump(res, "get-keys reply", ctx->format & COMMAND_FORMAT_PRETTY,
799 stdout);
800 }
801 vici_parse_cb(res, NULL, NULL, get_id, ctx->keys);
802 vici_free_res(res);
803 }
804 res = vici_submit(vici_begin("get-shared"), ctx->conn);
805 if (res)
806 {
807 if (ctx->format & COMMAND_FORMAT_RAW)
808 {
809 vici_dump(res, "get-shared reply", ctx->format & COMMAND_FORMAT_PRETTY,
810 stdout);
811 }
812 vici_parse_cb(res, NULL, NULL, get_id, ctx->shared);
813 vici_free_res(res);
814 }
815 }
816
817 /**
818 * Remove a given key
819 */
820 static bool unload_key(load_ctx_t *ctx, char *command, char *id)
821 {
822 vici_req_t *req;
823 vici_res_t *res;
824 char buf[BUF_LEN];
825 bool ret = TRUE;
826
827 req = vici_begin(command);
828
829 vici_add_key_valuef(req, "id", "%s", id);
830
831 res = vici_submit(req, ctx->conn);
832 if (!res)
833 {
834 fprintf(stderr, "%s request failed: %s\n", command, strerror(errno));
835 return FALSE;
836 }
837 if (ctx->format & COMMAND_FORMAT_RAW)
838 {
839 snprintf(buf, sizeof(buf), "%s reply", command);
840 vici_dump(res, buf, ctx->format & COMMAND_FORMAT_PRETTY, stdout);
841 }
842 else if (!streq(vici_find_str(res, "no", "success"), "yes"))
843 {
844 fprintf(stderr, "unloading key '%s' failed: %s\n",
845 id, vici_find_str(res, "", "errmsg"));
846 ret = FALSE;
847 }
848 vici_free_res(res);
849 return ret;
850 }
851
852 /**
853 * Remove all keys in the given hashtable using the given command
854 */
855 static void unload_keys(load_ctx_t *ctx, hashtable_t *ht, char *command)
856 {
857 enumerator_t *enumerator;
858 char *id;
859
860 enumerator = ht->create_enumerator(ht);
861 while (enumerator->enumerate(enumerator, &id, NULL))
862 {
863 unload_key(ctx, command, id);
864 }
865 enumerator->destroy(enumerator);
866 }
867
868 /**
869 * Clear all currently loaded credentials
870 */
871 static bool clear_creds(vici_conn_t *conn, command_format_options_t format)
872 {
873 vici_res_t *res;
874
875 res = vici_submit(vici_begin("clear-creds"), conn);
876 if (!res)
877 {
878 fprintf(stderr, "clear-creds request failed: %s\n", strerror(errno));
879 return FALSE;
880 }
881 if (format & COMMAND_FORMAT_RAW)
882 {
883 vici_dump(res, "clear-creds reply", format & COMMAND_FORMAT_PRETTY,
884 stdout);
885 }
886 vici_free_res(res);
887 return TRUE;
888 }
889
890 /**
891 * See header.
892 */
893 int load_creds_cfg(vici_conn_t *conn, command_format_options_t format,
894 settings_t *cfg, bool clear, bool noprompt)
895 {
896 enumerator_t *enumerator;
897 char *section;
898 load_ctx_t ctx = {
899 .conn = conn,
900 .format = format,
901 .noprompt = noprompt,
902 .cfg = cfg,
903 .keys = hashtable_create(hashtable_hash_str, hashtable_equals_str, 8),
904 .shared = hashtable_create(hashtable_hash_str, hashtable_equals_str, 8),
905 };
906
907 if (clear)
908 {
909 if (!clear_creds(conn, format))
910 {
911 return ECONNREFUSED;
912 }
913 }
914
915 get_creds(&ctx);
916
917 load_certs(&ctx, "x509", SWANCTL_X509DIR);
918 load_certs(&ctx, "x509ca", SWANCTL_X509CADIR);
919 load_certs(&ctx, "x509ocsp", SWANCTL_X509OCSPDIR);
920 load_certs(&ctx, "x509aa", SWANCTL_X509AADIR);
921 load_certs(&ctx, "x509ac", SWANCTL_X509ACDIR);
922 load_certs(&ctx, "x509crl", SWANCTL_X509CRLDIR);
923 load_certs(&ctx, "pubkey", SWANCTL_PUBKEYDIR);
924
925 load_keys(&ctx, "private", SWANCTL_PRIVATEDIR);
926 load_keys(&ctx, "rsa", SWANCTL_RSADIR);
927 load_keys(&ctx, "ecdsa", SWANCTL_ECDSADIR);
928 load_keys(&ctx, "bliss", SWANCTL_BLISSDIR);
929 load_keys(&ctx, "pkcs8", SWANCTL_PKCS8DIR);
930
931 load_containers(&ctx, "pkcs12", SWANCTL_PKCS12DIR);
932
933 load_tokens(&ctx);
934
935 enumerator = cfg->create_section_enumerator(cfg, "secrets");
936 while (enumerator->enumerate(enumerator, &section))
937 {
938 load_secret(&ctx, section);
939 }
940 enumerator->destroy(enumerator);
941
942 unload_keys(&ctx, ctx.keys, "unload-key");
943 unload_keys(&ctx, ctx.shared, "unload-shared");
944
945 ctx.keys->destroy_function(ctx.keys, (void*)free);
946 ctx.shared->destroy_function(ctx.shared, (void*)free);
947 return 0;
948 }
949
950 static int load_creds(vici_conn_t *conn)
951 {
952 bool clear = FALSE, noprompt = FALSE;
953 command_format_options_t format = COMMAND_FORMAT_NONE;
954 settings_t *cfg;
955 char *arg;
956 int ret;
957
958 while (TRUE)
959 {
960 switch (command_getopt(&arg))
961 {
962 case 'h':
963 return command_usage(NULL);
964 case 'c':
965 clear = TRUE;
966 continue;
967 case 'n':
968 noprompt = TRUE;
969 continue;
970 case 'P':
971 format |= COMMAND_FORMAT_PRETTY;
972 /* fall through to raw */
973 case 'r':
974 format |= COMMAND_FORMAT_RAW;
975 continue;
976 case EOF:
977 break;
978 default:
979 return command_usage("invalid --load-creds option");
980 }
981 break;
982 }
983
984 cfg = settings_create(SWANCTL_CONF);
985 if (!cfg)
986 {
987 fprintf(stderr, "parsing '%s' failed\n", SWANCTL_CONF);
988 return EINVAL;
989 }
990
991 ret = load_creds_cfg(conn, format, cfg, clear, noprompt);
992
993 cfg->destroy(cfg);
994
995 return ret;
996 }
997
998 /**
999 * Register the command.
1000 */
1001 static void __attribute__ ((constructor))reg()
1002 {
1003 command_register((command_t) {
1004 load_creds, 's', "load-creds", "(re-)load credentials",
1005 {"[--raw|--pretty] [--clear] [--noprompt]"},
1006 {
1007 {"help", 'h', 0, "show usage information"},
1008 {"clear", 'c', 0, "clear previously loaded credentials"},
1009 {"noprompt", 'n', 0, "do not prompt for passwords"},
1010 {"raw", 'r', 0, "dump raw response message"},
1011 {"pretty", 'P', 0, "dump raw response message in pretty print"},
1012 }
1013 });
1014 }