2 * Copyright (C) 2016 Tobias Brunner
3 * Copyright (C) 2015 Andreas Steffen
4 * HSR Hochschule fuer Technik Rapperswil
6 * Copyright (C) 2014 Martin Willi
7 * Copyright (C) 2014 revosec AG
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>.
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
28 #include "load_creds.h"
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>
35 #include <vici_cert_info.h>
37 #define HASH_SIZE_SHA1_HEX (2 * HASH_SIZE_SHA1)
40 * Context used to track loaded secrets
43 /** vici connection */
46 command_format_options_t format
;
49 /** don't prompt user for password */
51 /** list of key ids of loaded private keys */
56 * Load a single certificate over vici
58 static bool load_cert(load_ctx_t
*ctx
, char *dir
, certificate_type_t type
,
59 x509_flag_t flag
, chunk_t data
)
65 req
= vici_begin("load-cert");
67 vici_add_key_valuef(req
, "type", "%N", certificate_type_names
, type
);
68 if (type
== CERT_X509
)
70 vici_add_key_valuef(req
, "flag", "%N", x509_flag_names
, flag
);
72 vici_add_key_value(req
, "data", data
.ptr
, data
.len
);
74 res
= vici_submit(req
, ctx
->conn
);
77 fprintf(stderr
, "load-cert request failed: %s\n", strerror(errno
));
80 if (ctx
->format
& COMMAND_FORMAT_RAW
)
82 vici_dump(res
, "load-cert reply", ctx
->format
& COMMAND_FORMAT_PRETTY
,
85 else if (!streq(vici_find_str(res
, "no", "success"), "yes"))
87 fprintf(stderr
, "loading '%s' failed: %s\n",
88 dir
, vici_find_str(res
, "", "errmsg"));
93 printf("loaded certificate from '%s'\n", dir
);
100 * Load certficiates from a directory
102 static void load_certs(load_ctx_t
*ctx
, char *type_str
, char *dir
)
104 enumerator_t
*enumerator
;
105 certificate_type_t type
;
111 vici_cert_info_from_str(type_str
, &type
, &flag
);
113 enumerator
= enumerator_create_directory(dir
);
116 while (enumerator
->enumerate(enumerator
, NULL
, &path
, &st
))
118 if (S_ISREG(st
.st_mode
))
120 map
= chunk_map(path
, FALSE
);
123 load_cert(ctx
, path
, type
, flag
, *map
);
128 fprintf(stderr
, "mapping '%s' failed: %s, skipped\n",
129 path
, strerror(errno
));
133 enumerator
->destroy(enumerator
);
138 * Load a single private key over vici
140 static bool load_key(load_ctx_t
*ctx
, char *dir
, char *type
, chunk_t data
)
146 req
= vici_begin("load-key");
148 if (streq(type
, "private") ||
149 streq(type
, "pkcs8"))
150 { /* as used by vici */
151 vici_add_key_valuef(req
, "type", "any");
155 vici_add_key_valuef(req
, "type", "%s", type
);
157 vici_add_key_value(req
, "data", data
.ptr
, data
.len
);
159 res
= vici_submit(req
, ctx
->conn
);
162 fprintf(stderr
, "load-key request failed: %s\n", strerror(errno
));
165 if (ctx
->format
& COMMAND_FORMAT_RAW
)
167 vici_dump(res
, "load-key reply", ctx
->format
& COMMAND_FORMAT_PRETTY
,
170 else if (!streq(vici_find_str(res
, "no", "success"), "yes"))
172 fprintf(stderr
, "loading '%s' failed: %s\n",
173 dir
, vici_find_str(res
, "", "errmsg"));
178 printf("loaded %s key from '%s'\n", type
, dir
);
185 * Load a private key of any type to vici
187 static bool load_key_anytype(load_ctx_t
*ctx
, char *path
,
188 private_key_t
*private)
191 chunk_t encoding
, keyid
;
192 char hex
[HASH_SIZE_SHA1_HEX
+ 1];
194 if (!private->get_encoding(private, PRIVKEY_ASN1_DER
, &encoding
))
196 fprintf(stderr
, "encoding private key from '%s' failed\n", path
);
199 switch (private->get_type(private))
202 loaded
= load_key(ctx
, path
, "rsa", encoding
);
205 loaded
= load_key(ctx
, path
, "ecdsa", encoding
);
208 loaded
= load_key(ctx
, path
, "bliss", encoding
);
211 fprintf(stderr
, "unsupported key type in '%s'\n", path
);
216 private->get_fingerprint(private, KEYID_PUBKEY_SHA1
, &keyid
) &&
217 snprintf(hex
, sizeof(hex
), "%+B", &keyid
) == HASH_SIZE_SHA1_HEX
)
219 free(ctx
->keys
->remove(ctx
->keys
, hex
));
221 chunk_clear(&encoding
);
226 * Data passed to password callback
234 * Callback function to prompt for private key passwords
236 CALLBACK(password_cb
, shared_key_t
*,
237 cb_data_t
*data
, shared_key_type_t type
,
238 identification_t
*me
, identification_t
*other
,
239 id_match_t
*match_me
, id_match_t
*match_other
)
241 shared_key_t
*shared
;
244 if (type
!= SHARED_PRIVATE_KEY_PASS
)
249 pwd
= getpass(data
->prompt
);
251 if (!pwd
|| strlen(pwd
) == 0)
257 *match_me
= ID_MATCH_PERFECT
;
261 *match_other
= ID_MATCH_PERFECT
;
263 shared
= shared_key_create(type
, chunk_clone(chunk_from_str(pwd
)));
264 /* cache secret if it is required more than once (PKCS#12) */
265 data
->cache
->add_shared(data
->cache
, shared
, NULL
);
266 return shared
->get_ref(shared
);
270 * Determine credential type and subtype from a type string
272 static bool determine_credtype(char *type
, credential_type_t
*credtype
,
277 credential_type_t credtype
;
280 { "private", CRED_PRIVATE_KEY
, KEY_ANY
, },
281 { "pkcs8", CRED_PRIVATE_KEY
, KEY_ANY
, },
282 { "rsa", CRED_PRIVATE_KEY
, KEY_RSA
, },
283 { "ecdsa", CRED_PRIVATE_KEY
, KEY_ECDSA
, },
284 { "bliss", CRED_PRIVATE_KEY
, KEY_BLISS
, },
285 { "pkcs12", CRED_CONTAINER
, CONTAINER_PKCS12
, },
289 for (i
= 0; i
< countof(map
); i
++)
291 if (streq(map
[i
].type
, type
))
293 *credtype
= map
[i
].credtype
;
294 *subtype
= map
[i
].subtype
;
302 * Try to parse a potentially encrypted credential using password prompt
304 static void* decrypt(char *name
, char *type
, chunk_t encoding
)
306 credential_type_t credtype
;
312 if (!determine_credtype(type
, &credtype
, &subtype
))
317 snprintf(data
.prompt
, sizeof(data
.prompt
), "Password for %s file '%s': ",
320 data
.cache
= mem_cred_create();
321 lib
->credmgr
->add_set(lib
->credmgr
, &data
.cache
->set
);
322 cb
= callback_cred_create_shared(password_cb
, &data
);
323 lib
->credmgr
->add_set(lib
->credmgr
, &cb
->set
);
325 cred
= lib
->creds
->create(lib
->creds
, credtype
, subtype
,
326 BUILD_BLOB_PEM
, encoding
, BUILD_END
);
328 lib
->credmgr
->remove_set(lib
->credmgr
, &data
.cache
->set
);
329 data
.cache
->destroy(data
.cache
);
330 lib
->credmgr
->remove_set(lib
->credmgr
, &cb
->set
);
337 * Try to parse a potentially encrypted credential using configured secret
339 static void* decrypt_with_config(load_ctx_t
*ctx
, char *name
, char *type
,
342 credential_type_t credtype
;
344 enumerator_t
*enumerator
, *secrets
;
345 char *section
, *key
, *value
, *file
, buf
[128];
346 shared_key_t
*shared
;
348 mem_cred_t
*mem
= NULL
;
350 if (!determine_credtype(type
, &credtype
, &subtype
))
355 /* load all secrets for this key type */
356 enumerator
= ctx
->cfg
->create_section_enumerator(ctx
->cfg
, "secrets");
357 while (enumerator
->enumerate(enumerator
, §ion
))
359 if (strpfx(section
, type
))
361 file
= ctx
->cfg
->get_str(ctx
->cfg
, "secrets.%s.file", NULL
, section
);
362 if (file
&& strcaseeq(file
, name
))
364 snprintf(buf
, sizeof(buf
), "secrets.%s", section
);
365 secrets
= ctx
->cfg
->create_key_value_enumerator(ctx
->cfg
, buf
);
366 while (secrets
->enumerate(secrets
, &key
, &value
))
368 if (strpfx(key
, "secret"))
372 mem
= mem_cred_create();
374 shared
= shared_key_create(SHARED_PRIVATE_KEY_PASS
,
375 chunk_clone(chunk_from_str(value
)));
376 mem
->add_shared(mem
, shared
, NULL
);
379 secrets
->destroy(secrets
);
383 enumerator
->destroy(enumerator
);
387 lib
->credmgr
->add_local_set(lib
->credmgr
, &mem
->set
, FALSE
);
389 cred
= lib
->creds
->create(lib
->creds
, credtype
, subtype
,
390 BUILD_BLOB_PEM
, encoding
, BUILD_END
);
392 lib
->credmgr
->remove_local_set(lib
->credmgr
, &mem
->set
);
396 fprintf(stderr
, "configured decryption secret for '%s' invalid\n",
407 * Try to decrypt and load a private key
409 static bool load_encrypted_key(load_ctx_t
*ctx
, char *rel
, char *path
,
410 char *type
, chunk_t data
)
412 private_key_t
*private;
415 private = decrypt_with_config(ctx
, rel
, type
, data
);
416 if (!private && !ctx
->noprompt
)
418 private = decrypt(rel
, type
, data
);
422 loaded
= load_key_anytype(ctx
, path
, private);
423 private->destroy(private);
429 * Load private keys from a directory
431 static void load_keys(load_ctx_t
*ctx
, char *type
, char *dir
)
433 enumerator_t
*enumerator
;
438 enumerator
= enumerator_create_directory(dir
);
441 while (enumerator
->enumerate(enumerator
, &rel
, &path
, &st
))
443 if (S_ISREG(st
.st_mode
))
445 map
= chunk_map(path
, FALSE
);
448 if (!load_encrypted_key(ctx
, rel
, path
, type
, *map
))
450 load_key(ctx
, path
, type
, *map
);
456 fprintf(stderr
, "mapping '%s' failed: %s, skipped\n",
457 path
, strerror(errno
));
461 enumerator
->destroy(enumerator
);
466 * Load credentials from a PKCS#12 container over vici
468 static bool load_pkcs12(load_ctx_t
*ctx
, char *path
, pkcs12_t
*p12
)
470 enumerator_t
*enumerator
;
472 private_key_t
*private;
476 enumerator
= p12
->create_cert_enumerator(p12
);
477 while (loaded
&& enumerator
->enumerate(enumerator
, &cert
))
480 if (cert
->get_encoding(cert
, CERT_ASN1_DER
, &encoding
))
482 loaded
= load_cert(ctx
, path
, CERT_X509
, X509_NONE
, encoding
);
485 fprintf(stderr
, " %Y\n", cert
->get_subject(cert
));
491 fprintf(stderr
, "encoding certificate from '%s' failed\n", path
);
494 enumerator
->destroy(enumerator
);
496 enumerator
= p12
->create_key_enumerator(p12
);
497 while (loaded
&& enumerator
->enumerate(enumerator
, &private))
499 loaded
= load_key_anytype(ctx
, path
, private);
501 enumerator
->destroy(enumerator
);
507 * Try to decrypt and load credentials from a container
509 static bool load_encrypted_container(load_ctx_t
*ctx
, char *rel
, char *path
,
510 char *type
, chunk_t data
)
512 container_t
*container
;
515 container
= decrypt_with_config(ctx
, rel
, type
, data
);
516 if (!container
&& !ctx
->noprompt
)
518 container
= decrypt(rel
, type
, data
);
522 switch (container
->get_type(container
))
524 case CONTAINER_PKCS12
:
525 loaded
= load_pkcs12(ctx
, path
, (pkcs12_t
*)container
);
530 container
->destroy(container
);
536 * Load credential containers from a directory
538 static void load_containers(load_ctx_t
*ctx
, char *type
, char *dir
)
540 enumerator_t
*enumerator
;
545 enumerator
= enumerator_create_directory(dir
);
548 while (enumerator
->enumerate(enumerator
, &rel
, &path
, &st
))
550 if (S_ISREG(st
.st_mode
))
552 map
= chunk_map(path
, FALSE
);
555 load_encrypted_container(ctx
, rel
, path
, type
, *map
);
560 fprintf(stderr
, "mapping '%s' failed: %s, skipped\n",
561 path
, strerror(errno
));
565 enumerator
->destroy(enumerator
);
570 * Load a single secret over VICI
572 static bool load_secret(load_ctx_t
*ctx
, char *section
)
574 enumerator_t
*enumerator
;
578 char *key
, *value
, buf
[128], *type
= NULL
;
593 for (i
= 0; i
< countof(types
); i
++)
595 if (strpfx(section
, types
[i
]))
603 fprintf(stderr
, "ignoring unsupported secret '%s'\n", section
);
606 if (!streq(type
, "eap") && !streq(type
, "xauth") && !streq(type
, "ike"))
607 { /* skip non-shared secrets */
611 value
= ctx
->cfg
->get_str(ctx
->cfg
, "secrets.%s.secret", NULL
, section
);
614 fprintf(stderr
, "missing secret in '%s', ignored\n", section
);
617 if (strcasepfx(value
, "0x"))
619 data
= chunk_from_hex(chunk_from_str(value
+ 2), NULL
);
621 else if (strcasepfx(value
, "0s"))
623 data
= chunk_from_base64(chunk_from_str(value
+ 2), NULL
);
627 data
= chunk_clone(chunk_from_str(value
));
630 req
= vici_begin("load-shared");
632 vici_add_key_valuef(req
, "type", "%s", type
);
633 vici_add_key_value(req
, "data", data
.ptr
, data
.len
);
636 vici_begin_list(req
, "owners");
637 snprintf(buf
, sizeof(buf
), "secrets.%s", section
);
638 enumerator
= ctx
->cfg
->create_key_value_enumerator(ctx
->cfg
, buf
);
639 while (enumerator
->enumerate(enumerator
, &key
, &value
))
641 if (strpfx(key
, "id"))
643 vici_add_list_itemf(req
, "%s", value
);
646 enumerator
->destroy(enumerator
);
649 res
= vici_submit(req
, ctx
->conn
);
652 fprintf(stderr
, "load-shared request failed: %s\n", strerror(errno
));
655 if (ctx
->format
& COMMAND_FORMAT_RAW
)
657 vici_dump(res
, "load-shared reply", ctx
->format
& COMMAND_FORMAT_PRETTY
,
660 else if (!streq(vici_find_str(res
, "no", "success"), "yes"))
662 fprintf(stderr
, "loading shared secret failed: %s\n",
663 vici_find_str(res
, "", "errmsg"));
668 printf("loaded %s secret '%s'\n", type
, section
);
674 CALLBACK(get_id
, int,
675 hashtable_t
*ht
, vici_res_t
*res
, char *name
, void *value
, int len
)
677 if (streq(name
, "keys"))
681 if (asprintf(&str
, "%.*s", len
, value
) != -1)
683 free(ht
->put(ht
, str
, str
));
690 * Get a list of currently loaded private keys
692 static void get_keys(load_ctx_t
*ctx
)
696 res
= vici_submit(vici_begin("get-keys"), ctx
->conn
);
699 if (ctx
->format
& COMMAND_FORMAT_RAW
)
701 vici_dump(res
, "get-keys reply", ctx
->format
& COMMAND_FORMAT_PRETTY
,
704 vici_parse_cb(res
, NULL
, NULL
, get_id
, ctx
->keys
);
712 static bool unload_key(load_ctx_t
*ctx
, char *id
)
718 req
= vici_begin("unload-key");
720 vici_add_key_valuef(req
, "id", "%s", id
);
722 res
= vici_submit(req
, ctx
->conn
);
725 fprintf(stderr
, "unload-key request failed: %s\n", strerror(errno
));
728 if (ctx
->format
& COMMAND_FORMAT_RAW
)
730 vici_dump(res
, "unload-key reply", ctx
->format
& COMMAND_FORMAT_PRETTY
,
733 else if (!streq(vici_find_str(res
, "no", "success"), "yes"))
735 fprintf(stderr
, "unloading key '%s' failed: %s\n",
736 id
, vici_find_str(res
, "", "errmsg"));
744 * Clear all currently loaded credentials
746 static bool clear_creds(vici_conn_t
*conn
, command_format_options_t format
)
750 res
= vici_submit(vici_begin("clear-creds"), conn
);
753 fprintf(stderr
, "clear-creds request failed: %s\n", strerror(errno
));
756 if (format
& COMMAND_FORMAT_RAW
)
758 vici_dump(res
, "clear-creds reply", format
& COMMAND_FORMAT_PRETTY
,
768 int load_creds_cfg(vici_conn_t
*conn
, command_format_options_t format
,
769 settings_t
*cfg
, bool clear
, bool noprompt
)
771 enumerator_t
*enumerator
;
776 .noprompt
= noprompt
,
778 .keys
= hashtable_create(hashtable_hash_str
, hashtable_equals_str
, 8),
783 if (!clear_creds(conn
, format
))
791 load_certs(&ctx
, "x509", SWANCTL_X509DIR
);
792 load_certs(&ctx
, "x509ca", SWANCTL_X509CADIR
);
793 load_certs(&ctx
, "x509ocsp", SWANCTL_X509OCSPDIR
);
794 load_certs(&ctx
, "x509aa", SWANCTL_X509AADIR
);
795 load_certs(&ctx
, "x509ac", SWANCTL_X509ACDIR
);
796 load_certs(&ctx
, "x509crl", SWANCTL_X509CRLDIR
);
797 load_certs(&ctx
, "pubkey", SWANCTL_PUBKEYDIR
);
799 load_keys(&ctx
, "private", SWANCTL_PRIVATEDIR
);
800 load_keys(&ctx
, "rsa", SWANCTL_RSADIR
);
801 load_keys(&ctx
, "ecdsa", SWANCTL_ECDSADIR
);
802 load_keys(&ctx
, "bliss", SWANCTL_BLISSDIR
);
803 load_keys(&ctx
, "pkcs8", SWANCTL_PKCS8DIR
);
805 load_containers(&ctx
, "pkcs12", SWANCTL_PKCS12DIR
);
807 enumerator
= cfg
->create_section_enumerator(cfg
, "secrets");
808 while (enumerator
->enumerate(enumerator
, §ion
))
810 load_secret(&ctx
, section
);
812 enumerator
->destroy(enumerator
);
814 enumerator
= ctx
.keys
->create_enumerator(ctx
.keys
);
815 while (enumerator
->enumerate(enumerator
, &id
, NULL
))
817 unload_key(&ctx
, id
);
819 enumerator
->destroy(enumerator
);
820 ctx
.keys
->destroy_function(ctx
.keys
, (void*)free
);
824 static int load_creds(vici_conn_t
*conn
)
826 bool clear
= FALSE
, noprompt
= FALSE
;
827 command_format_options_t format
= COMMAND_FORMAT_NONE
;
834 switch (command_getopt(&arg
))
837 return command_usage(NULL
);
845 format
|= COMMAND_FORMAT_PRETTY
;
846 /* fall through to raw */
848 format
|= COMMAND_FORMAT_RAW
;
853 return command_usage("invalid --load-creds option");
858 cfg
= settings_create(SWANCTL_CONF
);
861 fprintf(stderr
, "parsing '%s' failed\n", SWANCTL_CONF
);
865 ret
= load_creds_cfg(conn
, format
, cfg
, clear
, noprompt
);
873 * Register the command.
875 static void __attribute__ ((constructor
))reg()
877 command_register((command_t
) {
878 load_creds
, 's', "load-creds", "(re-)load credentials",
879 {"[--raw|--pretty] [--clear] [--noprompt]"},
881 {"help", 'h', 0, "show usage information"},
882 {"clear", 'c', 0, "clear previously loaded credentials"},
883 {"noprompt", 'n', 0, "do not prompt for passwords"},
884 {"raw", 'r', 0, "dump raw response message"},
885 {"pretty", 'P', 0, "dump raw response message in pretty print"},