2 * Copyright (C) 2014 Martin Willi
3 * Copyright (C) 2014 revosec AG
5 * Copyright (C) 2016 Tobias Brunner
6 * Copyright (C) 2015 Andreas Steffen
7 * HSR Hochschule fuer Technik Rapperswil
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>
34 #include <vici_cert_info.h>
37 * Load a single certificate over vici
39 static bool load_cert(vici_conn_t
*conn
, command_format_options_t format
,
40 char *dir
, certificate_type_t type
, x509_flag_t flag
,
47 req
= vici_begin("load-cert");
49 vici_add_key_valuef(req
, "type", "%N", certificate_type_names
, type
);
50 if (type
== CERT_X509
)
52 vici_add_key_valuef(req
, "flag", "%N", x509_flag_names
, flag
);
54 vici_add_key_value(req
, "data", data
.ptr
, data
.len
);
56 res
= vici_submit(req
, conn
);
59 fprintf(stderr
, "load-cert request failed: %s\n", strerror(errno
));
62 if (format
& COMMAND_FORMAT_RAW
)
64 vici_dump(res
, "load-cert reply", format
& COMMAND_FORMAT_PRETTY
,
67 else if (!streq(vici_find_str(res
, "no", "success"), "yes"))
69 fprintf(stderr
, "loading '%s' failed: %s\n",
70 dir
, vici_find_str(res
, "", "errmsg"));
75 printf("loaded certificate from '%s'\n", dir
);
82 * Load certficiates from a directory
84 static void load_certs(vici_conn_t
*conn
, command_format_options_t format
,
85 char *type_str
, char *dir
)
87 enumerator_t
*enumerator
;
88 certificate_type_t type
;
94 vici_cert_info_from_str(type_str
, &type
, &flag
);
96 enumerator
= enumerator_create_directory(dir
);
99 while (enumerator
->enumerate(enumerator
, NULL
, &path
, &st
))
101 if (S_ISREG(st
.st_mode
))
103 map
= chunk_map(path
, FALSE
);
106 load_cert(conn
, format
, path
, type
, flag
, *map
);
111 fprintf(stderr
, "mapping '%s' failed: %s, skipped\n",
112 path
, strerror(errno
));
116 enumerator
->destroy(enumerator
);
121 * Load a single private key over vici
123 static bool load_key(vici_conn_t
*conn
, command_format_options_t format
,
124 char *dir
, char *type
, chunk_t data
)
130 req
= vici_begin("load-key");
132 if (streq(type
, "private") ||
133 streq(type
, "pkcs8"))
134 { /* as used by vici */
135 vici_add_key_valuef(req
, "type", "any");
139 vici_add_key_valuef(req
, "type", "%s", type
);
141 vici_add_key_value(req
, "data", data
.ptr
, data
.len
);
143 res
= vici_submit(req
, conn
);
146 fprintf(stderr
, "load-key request failed: %s\n", strerror(errno
));
149 if (format
& COMMAND_FORMAT_RAW
)
151 vici_dump(res
, "load-key reply", format
& COMMAND_FORMAT_PRETTY
,
154 else if (!streq(vici_find_str(res
, "no", "success"), "yes"))
156 fprintf(stderr
, "loading '%s' failed: %s\n",
157 dir
, vici_find_str(res
, "", "errmsg"));
162 printf("loaded %s key from '%s'\n", type
, dir
);
169 * Load a private key of any type to vici
171 static bool load_key_anytype(vici_conn_t
*conn
, command_format_options_t format
,
172 char *path
, private_key_t
*private)
177 if (!private->get_encoding(private, PRIVKEY_ASN1_DER
, &encoding
))
179 fprintf(stderr
, "encoding private key from '%s' failed\n", path
);
182 switch (private->get_type(private))
185 loaded
= load_key(conn
, format
, path
, "rsa", encoding
);
188 loaded
= load_key(conn
, format
, path
, "ecdsa", encoding
);
191 loaded
= load_key(conn
, format
, path
, "bliss", encoding
);
194 fprintf(stderr
, "unsupported key type in '%s'\n", path
);
197 chunk_clear(&encoding
);
202 * Data passed to password callback
210 * Callback function to prompt for private key passwords
212 CALLBACK(password_cb
, shared_key_t
*,
213 cb_data_t
*data
, shared_key_type_t type
,
214 identification_t
*me
, identification_t
*other
,
215 id_match_t
*match_me
, id_match_t
*match_other
)
217 shared_key_t
*shared
;
220 if (type
!= SHARED_PRIVATE_KEY_PASS
)
225 pwd
= getpass(data
->prompt
);
227 if (!pwd
|| strlen(pwd
) == 0)
233 *match_me
= ID_MATCH_PERFECT
;
237 *match_other
= ID_MATCH_PERFECT
;
239 shared
= shared_key_create(type
, chunk_clone(chunk_from_str(pwd
)));
240 /* cache secret if it is required more than once (PKCS#12) */
241 data
->cache
->add_shared(data
->cache
, shared
, NULL
);
242 return shared
->get_ref(shared
);
246 * Determine credential type and subtype from a type string
248 static bool determine_credtype(char *type
, credential_type_t
*credtype
,
253 credential_type_t credtype
;
256 { "private", CRED_PRIVATE_KEY
, KEY_ANY
, },
257 { "pkcs8", CRED_PRIVATE_KEY
, KEY_ANY
, },
258 { "rsa", CRED_PRIVATE_KEY
, KEY_RSA
, },
259 { "ecdsa", CRED_PRIVATE_KEY
, KEY_ECDSA
, },
260 { "bliss", CRED_PRIVATE_KEY
, KEY_BLISS
, },
261 { "pkcs12", CRED_CONTAINER
, CONTAINER_PKCS12
, },
265 for (i
= 0; i
< countof(map
); i
++)
267 if (streq(map
[i
].type
, type
))
269 *credtype
= map
[i
].credtype
;
270 *subtype
= map
[i
].subtype
;
278 * Try to parse a potentially encrypted credential using password prompt
280 static void* decrypt(char *name
, char *type
, chunk_t encoding
)
282 credential_type_t credtype
;
288 if (!determine_credtype(type
, &credtype
, &subtype
))
293 snprintf(data
.prompt
, sizeof(data
.prompt
), "Password for %s file '%s': ",
296 data
.cache
= mem_cred_create();
297 lib
->credmgr
->add_set(lib
->credmgr
, &data
.cache
->set
);
298 cb
= callback_cred_create_shared(password_cb
, &data
);
299 lib
->credmgr
->add_set(lib
->credmgr
, &cb
->set
);
301 cred
= lib
->creds
->create(lib
->creds
, credtype
, subtype
,
302 BUILD_BLOB_PEM
, encoding
, BUILD_END
);
304 lib
->credmgr
->remove_set(lib
->credmgr
, &data
.cache
->set
);
305 data
.cache
->destroy(data
.cache
);
306 lib
->credmgr
->remove_set(lib
->credmgr
, &cb
->set
);
313 * Try to parse a potentially encrypted credential using configured secret
315 static void* decrypt_with_config(settings_t
*cfg
, char *name
, char *type
,
318 credential_type_t credtype
;
320 enumerator_t
*enumerator
, *secrets
;
321 char *section
, *key
, *value
, *file
, buf
[128];
322 shared_key_t
*shared
;
324 mem_cred_t
*mem
= NULL
;
326 if (!determine_credtype(type
, &credtype
, &subtype
))
331 /* load all secrets for this key type */
332 enumerator
= cfg
->create_section_enumerator(cfg
, "secrets");
333 while (enumerator
->enumerate(enumerator
, §ion
))
335 if (strpfx(section
, type
))
337 file
= cfg
->get_str(cfg
, "secrets.%s.file", NULL
, section
);
338 if (file
&& strcaseeq(file
, name
))
340 snprintf(buf
, sizeof(buf
), "secrets.%s", section
);
341 secrets
= cfg
->create_key_value_enumerator(cfg
, buf
);
342 while (secrets
->enumerate(secrets
, &key
, &value
))
344 if (strpfx(key
, "secret"))
348 mem
= mem_cred_create();
350 shared
= shared_key_create(SHARED_PRIVATE_KEY_PASS
,
351 chunk_clone(chunk_from_str(value
)));
352 mem
->add_shared(mem
, shared
, NULL
);
355 secrets
->destroy(secrets
);
359 enumerator
->destroy(enumerator
);
363 lib
->credmgr
->add_local_set(lib
->credmgr
, &mem
->set
, FALSE
);
365 cred
= lib
->creds
->create(lib
->creds
, credtype
, subtype
,
366 BUILD_BLOB_PEM
, encoding
, BUILD_END
);
368 lib
->credmgr
->remove_local_set(lib
->credmgr
, &mem
->set
);
372 fprintf(stderr
, "configured decryption secret for '%s' invalid\n",
383 * Try to decrypt and load a private key
385 static bool load_encrypted_key(vici_conn_t
*conn
,
386 command_format_options_t format
, settings_t
*cfg
,
387 char *rel
, char *path
, char *type
, bool noprompt
,
390 private_key_t
*private;
393 private = decrypt_with_config(cfg
, rel
, type
, data
);
394 if (!private && !noprompt
)
396 private = decrypt(rel
, type
, data
);
400 loaded
= load_key_anytype(conn
, format
, path
, private);
401 private->destroy(private);
407 * Load private keys from a directory
409 static void load_keys(vici_conn_t
*conn
, command_format_options_t format
,
410 bool noprompt
, settings_t
*cfg
, char *type
, char *dir
)
412 enumerator_t
*enumerator
;
417 enumerator
= enumerator_create_directory(dir
);
420 while (enumerator
->enumerate(enumerator
, &rel
, &path
, &st
))
422 if (S_ISREG(st
.st_mode
))
424 map
= chunk_map(path
, FALSE
);
427 if (!load_encrypted_key(conn
, format
, cfg
, rel
, path
, type
,
430 load_key(conn
, format
, path
, type
, *map
);
436 fprintf(stderr
, "mapping '%s' failed: %s, skipped\n",
437 path
, strerror(errno
));
441 enumerator
->destroy(enumerator
);
446 * Load credentials from a PKCS#12 container over vici
448 static bool load_pkcs12(vici_conn_t
*conn
, command_format_options_t format
,
449 char *path
, pkcs12_t
*p12
)
451 enumerator_t
*enumerator
;
453 private_key_t
*private;
457 enumerator
= p12
->create_cert_enumerator(p12
);
458 while (loaded
&& enumerator
->enumerate(enumerator
, &cert
))
461 if (cert
->get_encoding(cert
, CERT_ASN1_DER
, &encoding
))
463 loaded
= load_cert(conn
, format
, path
, CERT_X509
, X509_NONE
,
467 fprintf(stderr
, " %Y\n", cert
->get_subject(cert
));
473 fprintf(stderr
, "encoding certificate from '%s' failed\n", path
);
476 enumerator
->destroy(enumerator
);
478 enumerator
= p12
->create_key_enumerator(p12
);
479 while (loaded
&& enumerator
->enumerate(enumerator
, &private))
481 loaded
= load_key_anytype(conn
, format
, path
, private);
483 enumerator
->destroy(enumerator
);
489 * Try to decrypt and load credentials from a container
491 static bool load_encrypted_container(vici_conn_t
*conn
,
492 command_format_options_t format
, settings_t
*cfg
, char *rel
,
493 char *path
, char *type
, bool noprompt
, chunk_t data
)
495 container_t
*container
;
498 container
= decrypt_with_config(cfg
, rel
, type
, data
);
499 if (!container
&& !noprompt
)
501 container
= decrypt(rel
, type
, data
);
505 switch (container
->get_type(container
))
507 case CONTAINER_PKCS12
:
508 loaded
= load_pkcs12(conn
, format
, path
, (pkcs12_t
*)container
);
513 container
->destroy(container
);
519 * Load credential containers from a directory
521 static void load_containers(vici_conn_t
*conn
, command_format_options_t format
,
522 bool noprompt
, settings_t
*cfg
, char *type
, char *dir
)
524 enumerator_t
*enumerator
;
529 enumerator
= enumerator_create_directory(dir
);
532 while (enumerator
->enumerate(enumerator
, &rel
, &path
, &st
))
534 if (S_ISREG(st
.st_mode
))
536 map
= chunk_map(path
, FALSE
);
539 load_encrypted_container(conn
, format
, cfg
, rel
, path
,
540 type
, noprompt
, *map
);
545 fprintf(stderr
, "mapping '%s' failed: %s, skipped\n",
546 path
, strerror(errno
));
550 enumerator
->destroy(enumerator
);
555 * Load a single secret over VICI
557 static bool load_secret(vici_conn_t
*conn
, settings_t
*cfg
,
558 char *section
, command_format_options_t format
)
560 enumerator_t
*enumerator
;
564 char *key
, *value
, buf
[128], *type
= NULL
;
579 for (i
= 0; i
< countof(types
); i
++)
581 if (strpfx(section
, types
[i
]))
589 fprintf(stderr
, "ignoring unsupported secret '%s'\n", section
);
592 if (!streq(type
, "eap") && !streq(type
, "xauth") && !streq(type
, "ike"))
593 { /* skip non-shared secrets */
597 value
= cfg
->get_str(cfg
, "secrets.%s.secret", NULL
, section
);
600 fprintf(stderr
, "missing secret in '%s', ignored\n", section
);
603 if (strcasepfx(value
, "0x"))
605 data
= chunk_from_hex(chunk_from_str(value
+ 2), NULL
);
607 else if (strcasepfx(value
, "0s"))
609 data
= chunk_from_base64(chunk_from_str(value
+ 2), NULL
);
613 data
= chunk_clone(chunk_from_str(value
));
616 req
= vici_begin("load-shared");
618 vici_add_key_valuef(req
, "type", "%s", type
);
619 vici_add_key_value(req
, "data", data
.ptr
, data
.len
);
622 vici_begin_list(req
, "owners");
623 snprintf(buf
, sizeof(buf
), "secrets.%s", section
);
624 enumerator
= cfg
->create_key_value_enumerator(cfg
, buf
);
625 while (enumerator
->enumerate(enumerator
, &key
, &value
))
627 if (strpfx(key
, "id"))
629 vici_add_list_itemf(req
, "%s", value
);
632 enumerator
->destroy(enumerator
);
635 res
= vici_submit(req
, conn
);
638 fprintf(stderr
, "load-shared request failed: %s\n", strerror(errno
));
641 if (format
& COMMAND_FORMAT_RAW
)
643 vici_dump(res
, "load-shared reply", format
& COMMAND_FORMAT_PRETTY
,
646 else if (!streq(vici_find_str(res
, "no", "success"), "yes"))
648 fprintf(stderr
, "loading shared secret failed: %s\n",
649 vici_find_str(res
, "", "errmsg"));
654 printf("loaded %s secret '%s'\n", type
, section
);
661 * Clear all currently loaded credentials
663 static bool clear_creds(vici_conn_t
*conn
, command_format_options_t format
)
667 res
= vici_submit(vici_begin("clear-creds"), conn
);
670 fprintf(stderr
, "clear-creds request failed: %s\n", strerror(errno
));
673 if (format
& COMMAND_FORMAT_RAW
)
675 vici_dump(res
, "clear-creds reply", format
& COMMAND_FORMAT_PRETTY
,
685 int load_creds_cfg(vici_conn_t
*conn
, command_format_options_t format
,
686 settings_t
*cfg
, bool clear
, bool noprompt
)
688 enumerator_t
*enumerator
;
693 if (!clear_creds(conn
, format
))
699 load_certs(conn
, format
, "x509", SWANCTL_X509DIR
);
700 load_certs(conn
, format
, "x509ca", SWANCTL_X509CADIR
);
701 load_certs(conn
, format
, "x509ocsp", SWANCTL_X509OCSPDIR
);
702 load_certs(conn
, format
, "x509aa", SWANCTL_X509AADIR
);
703 load_certs(conn
, format
, "x509ac", SWANCTL_X509ACDIR
);
704 load_certs(conn
, format
, "x509crl", SWANCTL_X509CRLDIR
);
705 load_certs(conn
, format
, "pubkey", SWANCTL_PUBKEYDIR
);
707 load_keys(conn
, format
, noprompt
, cfg
, "private", SWANCTL_PRIVATEDIR
);
708 load_keys(conn
, format
, noprompt
, cfg
, "rsa", SWANCTL_RSADIR
);
709 load_keys(conn
, format
, noprompt
, cfg
, "ecdsa", SWANCTL_ECDSADIR
);
710 load_keys(conn
, format
, noprompt
, cfg
, "bliss", SWANCTL_BLISSDIR
);
711 load_keys(conn
, format
, noprompt
, cfg
, "pkcs8", SWANCTL_PKCS8DIR
);
713 load_containers(conn
, format
, noprompt
, cfg
, "pkcs12", SWANCTL_PKCS12DIR
);
715 enumerator
= cfg
->create_section_enumerator(cfg
, "secrets");
716 while (enumerator
->enumerate(enumerator
, §ion
))
718 load_secret(conn
, cfg
, section
, format
);
720 enumerator
->destroy(enumerator
);
725 static int load_creds(vici_conn_t
*conn
)
727 bool clear
= FALSE
, noprompt
= FALSE
;
728 command_format_options_t format
= COMMAND_FORMAT_NONE
;
735 switch (command_getopt(&arg
))
738 return command_usage(NULL
);
746 format
|= COMMAND_FORMAT_PRETTY
;
747 /* fall through to raw */
749 format
|= COMMAND_FORMAT_RAW
;
754 return command_usage("invalid --load-creds option");
759 cfg
= settings_create(SWANCTL_CONF
);
762 fprintf(stderr
, "parsing '%s' failed\n", SWANCTL_CONF
);
766 ret
= load_creds_cfg(conn
, format
, cfg
, clear
, noprompt
);
774 * Register the command.
776 static void __attribute__ ((constructor
))reg()
778 command_register((command_t
) {
779 load_creds
, 's', "load-creds", "(re-)load credentials",
780 {"[--raw|--pretty] [--clear] [--noprompt]"},
782 {"help", 'h', 0, "show usage information"},
783 {"clear", 'c', 0, "clear previously loaded credentials"},
784 {"noprompt", 'n', 0, "do not prompt for passwords"},
785 {"raw", 'r', 0, "dump raw response message"},
786 {"pretty", 'P', 0, "dump raw response message in pretty print"},