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