diff options
Diffstat (limited to 'ssh-add.c')
-rw-r--r-- | ssh-add.c | 142 |
1 files changed, 106 insertions, 36 deletions
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: ssh-add.c,v 1.141 2019/09/06 05:23:55 djm Exp $ */ | 1 | /* $OpenBSD: ssh-add.c,v 1.152 2020/02/06 22:30:54 naddy Exp $ */ |
2 | /* | 2 | /* |
3 | * Author: Tatu Ylonen <ylo@cs.hut.fi> | 3 | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
4 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 4 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
@@ -66,6 +66,7 @@ | |||
66 | #include "misc.h" | 66 | #include "misc.h" |
67 | #include "ssherr.h" | 67 | #include "ssherr.h" |
68 | #include "digest.h" | 68 | #include "digest.h" |
69 | #include "ssh-sk.h" | ||
69 | 70 | ||
70 | /* argv0 */ | 71 | /* argv0 */ |
71 | extern char *__progname; | 72 | extern char *__progname; |
@@ -77,9 +78,11 @@ static char *default_files[] = { | |||
77 | _PATH_SSH_CLIENT_ID_DSA, | 78 | _PATH_SSH_CLIENT_ID_DSA, |
78 | #ifdef OPENSSL_HAS_ECC | 79 | #ifdef OPENSSL_HAS_ECC |
79 | _PATH_SSH_CLIENT_ID_ECDSA, | 80 | _PATH_SSH_CLIENT_ID_ECDSA, |
81 | _PATH_SSH_CLIENT_ID_ECDSA_SK, | ||
80 | #endif | 82 | #endif |
81 | #endif /* WITH_OPENSSL */ | 83 | #endif /* WITH_OPENSSL */ |
82 | _PATH_SSH_CLIENT_ID_ED25519, | 84 | _PATH_SSH_CLIENT_ID_ED25519, |
85 | _PATH_SSH_CLIENT_ID_ED25519_SK, | ||
83 | _PATH_SSH_CLIENT_ID_XMSS, | 86 | _PATH_SSH_CLIENT_ID_XMSS, |
84 | NULL | 87 | NULL |
85 | }; | 88 | }; |
@@ -191,7 +194,8 @@ delete_all(int agent_fd, int qflag) | |||
191 | } | 194 | } |
192 | 195 | ||
193 | static int | 196 | static int |
194 | add_file(int agent_fd, const char *filename, int key_only, int qflag) | 197 | add_file(int agent_fd, const char *filename, int key_only, int qflag, |
198 | const char *skprovider) | ||
195 | { | 199 | { |
196 | struct sshkey *private, *cert; | 200 | struct sshkey *private, *cert; |
197 | char *comment = NULL; | 201 | char *comment = NULL; |
@@ -220,9 +224,7 @@ add_file(int agent_fd, const char *filename, int key_only, int qflag) | |||
220 | return -1; | 224 | return -1; |
221 | } | 225 | } |
222 | } | 226 | } |
223 | if ((keyblob = sshbuf_new()) == NULL) | 227 | if ((r = sshbuf_load_fd(fd, &keyblob)) != 0) { |
224 | fatal("%s: sshbuf_new failed", __func__); | ||
225 | if ((r = sshkey_load_file(fd, keyblob)) != 0) { | ||
226 | fprintf(stderr, "Error loading key \"%s\": %s\n", | 228 | fprintf(stderr, "Error loading key \"%s\": %s\n", |
227 | filename, ssh_err(r)); | 229 | filename, ssh_err(r)); |
228 | sshbuf_free(keyblob); | 230 | sshbuf_free(keyblob); |
@@ -310,8 +312,16 @@ add_file(int agent_fd, const char *filename, int key_only, int qflag) | |||
310 | ssh_free_identitylist(idlist); | 312 | ssh_free_identitylist(idlist); |
311 | } | 313 | } |
312 | 314 | ||
315 | if (!sshkey_is_sk(private)) | ||
316 | skprovider = NULL; /* Don't send constraint for other keys */ | ||
317 | else if (skprovider == NULL) { | ||
318 | fprintf(stderr, "Cannot load authenticator-hosted key %s " | ||
319 | "without provider\n", filename); | ||
320 | goto out; | ||
321 | } | ||
322 | |||
313 | if ((r = ssh_add_identity_constrained(agent_fd, private, comment, | 323 | if ((r = ssh_add_identity_constrained(agent_fd, private, comment, |
314 | lifetime, confirm, maxsign)) == 0) { | 324 | lifetime, confirm, maxsign, skprovider)) == 0) { |
315 | ret = 0; | 325 | ret = 0; |
316 | if (!qflag) { | 326 | if (!qflag) { |
317 | fprintf(stderr, "Identity added: %s (%s)\n", | 327 | fprintf(stderr, "Identity added: %s (%s)\n", |
@@ -364,7 +374,7 @@ add_file(int agent_fd, const char *filename, int key_only, int qflag) | |||
364 | sshkey_free(cert); | 374 | sshkey_free(cert); |
365 | 375 | ||
366 | if ((r = ssh_add_identity_constrained(agent_fd, private, comment, | 376 | if ((r = ssh_add_identity_constrained(agent_fd, private, comment, |
367 | lifetime, confirm, maxsign)) != 0) { | 377 | lifetime, confirm, maxsign, skprovider)) != 0) { |
368 | error("Certificate %s (%s) add failed: %s", certpath, | 378 | error("Certificate %s (%s) add failed: %s", certpath, |
369 | private->cert->key_id, ssh_err(r)); | 379 | private->cert->key_id, ssh_err(r)); |
370 | goto out; | 380 | goto out; |
@@ -440,7 +450,7 @@ test_key(int agent_fd, const char *filename) | |||
440 | goto done; | 450 | goto done; |
441 | } | 451 | } |
442 | if ((r = sshkey_verify(key, sig, slen, data, sizeof(data), | 452 | if ((r = sshkey_verify(key, sig, slen, data, sizeof(data), |
443 | NULL, 0)) != 0) { | 453 | NULL, 0, NULL)) != 0) { |
444 | error("Signature verification failed for %s: %s", | 454 | error("Signature verification failed for %s: %s", |
445 | filename, ssh_err(r)); | 455 | filename, ssh_err(r)); |
446 | goto done; | 456 | goto done; |
@@ -529,13 +539,63 @@ lock_agent(int agent_fd, int lock) | |||
529 | } | 539 | } |
530 | 540 | ||
531 | static int | 541 | static int |
532 | do_file(int agent_fd, int deleting, int key_only, char *file, int qflag) | 542 | load_resident_keys(int agent_fd, const char *skprovider, int qflag) |
543 | { | ||
544 | struct sshkey **keys; | ||
545 | size_t nkeys, i; | ||
546 | int r, ok = 0; | ||
547 | char *fp; | ||
548 | |||
549 | pass = read_passphrase("Enter PIN for authenticator: ", RP_ALLOW_STDIN); | ||
550 | if ((r = sshsk_load_resident(skprovider, NULL, pass, | ||
551 | &keys, &nkeys)) != 0) { | ||
552 | error("Unable to load resident keys: %s", ssh_err(r)); | ||
553 | return r; | ||
554 | } | ||
555 | for (i = 0; i < nkeys; i++) { | ||
556 | if ((fp = sshkey_fingerprint(keys[i], | ||
557 | fingerprint_hash, SSH_FP_DEFAULT)) == NULL) | ||
558 | fatal("%s: sshkey_fingerprint failed", __func__); | ||
559 | if ((r = ssh_add_identity_constrained(agent_fd, keys[i], "", | ||
560 | lifetime, confirm, maxsign, skprovider)) != 0) { | ||
561 | error("Unable to add key %s %s", | ||
562 | sshkey_type(keys[i]), fp); | ||
563 | free(fp); | ||
564 | ok = r; | ||
565 | continue; | ||
566 | } | ||
567 | if (ok == 0) | ||
568 | ok = 1; | ||
569 | if (!qflag) { | ||
570 | fprintf(stderr, "Resident identity added: %s %s\n", | ||
571 | sshkey_type(keys[i]), fp); | ||
572 | if (lifetime != 0) { | ||
573 | fprintf(stderr, | ||
574 | "Lifetime set to %d seconds\n", lifetime); | ||
575 | } | ||
576 | if (confirm != 0) { | ||
577 | fprintf(stderr, "The user must confirm " | ||
578 | "each use of the key\n"); | ||
579 | } | ||
580 | } | ||
581 | free(fp); | ||
582 | sshkey_free(keys[i]); | ||
583 | } | ||
584 | free(keys); | ||
585 | if (nkeys == 0) | ||
586 | return SSH_ERR_KEY_NOT_FOUND; | ||
587 | return ok == 1 ? 0 : ok; | ||
588 | } | ||
589 | |||
590 | static int | ||
591 | do_file(int agent_fd, int deleting, int key_only, char *file, int qflag, | ||
592 | const char *skprovider) | ||
533 | { | 593 | { |
534 | if (deleting) { | 594 | if (deleting) { |
535 | if (delete_file(agent_fd, file, key_only, qflag) == -1) | 595 | if (delete_file(agent_fd, file, key_only, qflag) == -1) |
536 | return -1; | 596 | return -1; |
537 | } else { | 597 | } else { |
538 | if (add_file(agent_fd, file, key_only, qflag) == -1) | 598 | if (add_file(agent_fd, file, key_only, qflag, skprovider) == -1) |
539 | return -1; | 599 | return -1; |
540 | } | 600 | } |
541 | return 0; | 601 | return 0; |
@@ -544,25 +604,16 @@ do_file(int agent_fd, int deleting, int key_only, char *file, int qflag) | |||
544 | static void | 604 | static void |
545 | usage(void) | 605 | usage(void) |
546 | { | 606 | { |
547 | fprintf(stderr, "usage: %s [options] [file ...]\n", __progname); | 607 | fprintf(stderr, |
548 | fprintf(stderr, "Options:\n"); | 608 | "usage: ssh-add [-cDdKkLlqvXx] [-E fingerprint_hash] [-S provider] [-t life]\n" |
549 | fprintf(stderr, " -l List fingerprints of all identities.\n"); | 609 | #ifdef WITH_XMSS |
550 | fprintf(stderr, " -E hash Specify hash algorithm used for fingerprints.\n"); | 610 | " [-M maxsign] [-m minleft]\n" |
551 | fprintf(stderr, " -L List public key parameters of all identities.\n"); | 611 | #endif |
552 | fprintf(stderr, " -k Load only keys and not certificates.\n"); | 612 | " [file ...]\n" |
553 | fprintf(stderr, " -c Require confirmation to sign using identities\n"); | 613 | " ssh-add -s pkcs11\n" |
554 | fprintf(stderr, " -m minleft Maxsign is only changed if less than minleft are left (for XMSS)\n"); | 614 | " ssh-add -e pkcs11\n" |
555 | fprintf(stderr, " -M maxsign Maximum number of signatures allowed (for XMSS)\n"); | 615 | " ssh-add -T pubkey ...\n" |
556 | fprintf(stderr, " -t life Set lifetime (in seconds) when adding identities.\n"); | 616 | ); |
557 | fprintf(stderr, " -d Delete identity.\n"); | ||
558 | fprintf(stderr, " -D Delete all identities.\n"); | ||
559 | fprintf(stderr, " -x Lock agent.\n"); | ||
560 | fprintf(stderr, " -X Unlock agent.\n"); | ||
561 | fprintf(stderr, " -s pkcs11 Add keys from PKCS#11 provider.\n"); | ||
562 | fprintf(stderr, " -e pkcs11 Remove keys provided by PKCS#11 provider.\n"); | ||
563 | fprintf(stderr, " -T pubkey Test if ssh-agent can access matching private key.\n"); | ||
564 | fprintf(stderr, " -q Be quiet after a successful operation.\n"); | ||
565 | fprintf(stderr, " -v Be more verbose.\n"); | ||
566 | } | 617 | } |
567 | 618 | ||
568 | int | 619 | int |
@@ -571,8 +622,8 @@ main(int argc, char **argv) | |||
571 | extern char *optarg; | 622 | extern char *optarg; |
572 | extern int optind; | 623 | extern int optind; |
573 | int agent_fd; | 624 | int agent_fd; |
574 | char *pkcs11provider = NULL; | 625 | char *pkcs11provider = NULL, *skprovider = NULL; |
575 | int r, i, ch, deleting = 0, ret = 0, key_only = 0; | 626 | int r, i, ch, deleting = 0, ret = 0, key_only = 0, do_download = 0; |
576 | int xflag = 0, lflag = 0, Dflag = 0, qflag = 0, Tflag = 0; | 627 | int xflag = 0, lflag = 0, Dflag = 0, qflag = 0, Tflag = 0; |
577 | SyslogFacility log_facility = SYSLOG_FACILITY_AUTH; | 628 | SyslogFacility log_facility = SYSLOG_FACILITY_AUTH; |
578 | LogLevel log_level = SYSLOG_LEVEL_INFO; | 629 | LogLevel log_level = SYSLOG_LEVEL_INFO; |
@@ -600,7 +651,9 @@ main(int argc, char **argv) | |||
600 | exit(2); | 651 | exit(2); |
601 | } | 652 | } |
602 | 653 | ||
603 | while ((ch = getopt(argc, argv, "vklLcdDTxXE:e:M:m:qs:t:")) != -1) { | 654 | skprovider = getenv("SSH_SK_PROVIDER"); |
655 | |||
656 | while ((ch = getopt(argc, argv, "vkKlLcdDTxXE:e:M:m:qs:S:t:")) != -1) { | ||
604 | switch (ch) { | 657 | switch (ch) { |
605 | case 'v': | 658 | case 'v': |
606 | if (log_level == SYSLOG_LEVEL_INFO) | 659 | if (log_level == SYSLOG_LEVEL_INFO) |
@@ -616,6 +669,9 @@ main(int argc, char **argv) | |||
616 | case 'k': | 669 | case 'k': |
617 | key_only = 1; | 670 | key_only = 1; |
618 | break; | 671 | break; |
672 | case 'K': | ||
673 | do_download = 1; | ||
674 | break; | ||
619 | case 'l': | 675 | case 'l': |
620 | case 'L': | 676 | case 'L': |
621 | if (lflag != 0) | 677 | if (lflag != 0) |
@@ -656,6 +712,9 @@ main(int argc, char **argv) | |||
656 | case 's': | 712 | case 's': |
657 | pkcs11provider = optarg; | 713 | pkcs11provider = optarg; |
658 | break; | 714 | break; |
715 | case 'S': | ||
716 | skprovider = optarg; | ||
717 | break; | ||
659 | case 'e': | 718 | case 'e': |
660 | deleting = 1; | 719 | deleting = 1; |
661 | pkcs11provider = optarg; | 720 | pkcs11provider = optarg; |
@@ -697,6 +756,11 @@ main(int argc, char **argv) | |||
697 | goto done; | 756 | goto done; |
698 | } | 757 | } |
699 | 758 | ||
759 | #ifdef ENABLE_SK_INTERNAL | ||
760 | if (skprovider == NULL) | ||
761 | skprovider = "internal"; | ||
762 | #endif | ||
763 | |||
700 | argc -= optind; | 764 | argc -= optind; |
701 | argv += optind; | 765 | argv += optind; |
702 | if (Tflag) { | 766 | if (Tflag) { |
@@ -713,6 +777,13 @@ main(int argc, char **argv) | |||
713 | ret = 1; | 777 | ret = 1; |
714 | goto done; | 778 | goto done; |
715 | } | 779 | } |
780 | if (do_download) { | ||
781 | if (skprovider == NULL) | ||
782 | fatal("Cannot download keys without provider"); | ||
783 | if (load_resident_keys(agent_fd, skprovider, qflag) != 0) | ||
784 | ret = 1; | ||
785 | goto done; | ||
786 | } | ||
716 | if (argc == 0) { | 787 | if (argc == 0) { |
717 | char buf[PATH_MAX]; | 788 | char buf[PATH_MAX]; |
718 | struct passwd *pw; | 789 | struct passwd *pw; |
@@ -732,7 +803,7 @@ main(int argc, char **argv) | |||
732 | if (stat(buf, &st) == -1) | 803 | if (stat(buf, &st) == -1) |
733 | continue; | 804 | continue; |
734 | if (do_file(agent_fd, deleting, key_only, buf, | 805 | if (do_file(agent_fd, deleting, key_only, buf, |
735 | qflag) == -1) | 806 | qflag, skprovider) == -1) |
736 | ret = 1; | 807 | ret = 1; |
737 | else | 808 | else |
738 | count++; | 809 | count++; |
@@ -742,13 +813,12 @@ main(int argc, char **argv) | |||
742 | } else { | 813 | } else { |
743 | for (i = 0; i < argc; i++) { | 814 | for (i = 0; i < argc; i++) { |
744 | if (do_file(agent_fd, deleting, key_only, | 815 | if (do_file(agent_fd, deleting, key_only, |
745 | argv[i], qflag) == -1) | 816 | argv[i], qflag, skprovider) == -1) |
746 | ret = 1; | 817 | ret = 1; |
747 | } | 818 | } |
748 | } | 819 | } |
749 | clear_pass(); | ||
750 | |||
751 | done: | 820 | done: |
821 | clear_pass(); | ||
752 | ssh_close_authentication_socket(agent_fd); | 822 | ssh_close_authentication_socket(agent_fd); |
753 | return ret; | 823 | return ret; |
754 | } | 824 | } |