diff options
author | Damien Miller <djm@mindrot.org> | 2005-03-01 21:48:35 +1100 |
---|---|---|
committer | Damien Miller <djm@mindrot.org> | 2005-03-01 21:48:35 +1100 |
commit | 4b42d7f195fc38bc6eda25c2e0782321ff1f2a16 (patch) | |
tree | f945653699b303d57203f950eba69946d2a59039 /ssh-keygen.c | |
parent | db7b8171ee084cbbe3a5ec5ef26df88e17aa3905 (diff) |
- djm@cvs.openbsd.org 2005/03/01 10:42:49
[ssh-keygen.1 ssh-keygen.c ssh_config.5]
add tools for managing known_hosts files with hashed hostnames, including
hashing existing files and deleting hosts by name; ok markus@ deraadt@
Diffstat (limited to 'ssh-keygen.c')
-rw-r--r-- | ssh-keygen.c | 228 |
1 files changed, 218 insertions, 10 deletions
diff --git a/ssh-keygen.c b/ssh-keygen.c index 7ed62a30c..00ddb90c8 100644 --- a/ssh-keygen.c +++ b/ssh-keygen.c | |||
@@ -12,7 +12,7 @@ | |||
12 | */ | 12 | */ |
13 | 13 | ||
14 | #include "includes.h" | 14 | #include "includes.h" |
15 | RCSID("$OpenBSD: ssh-keygen.c,v 1.118 2004/12/23 17:38:07 markus Exp $"); | 15 | RCSID("$OpenBSD: ssh-keygen.c,v 1.119 2005/03/01 10:42:49 djm Exp $"); |
16 | 16 | ||
17 | #include <openssl/evp.h> | 17 | #include <openssl/evp.h> |
18 | #include <openssl/pem.h> | 18 | #include <openssl/pem.h> |
@@ -27,6 +27,8 @@ RCSID("$OpenBSD: ssh-keygen.c,v 1.118 2004/12/23 17:38:07 markus Exp $"); | |||
27 | #include "pathnames.h" | 27 | #include "pathnames.h" |
28 | #include "log.h" | 28 | #include "log.h" |
29 | #include "misc.h" | 29 | #include "misc.h" |
30 | #include "match.h" | ||
31 | #include "hostfile.h" | ||
30 | 32 | ||
31 | #ifdef SMARTCARD | 33 | #ifdef SMARTCARD |
32 | #include "scard.h" | 34 | #include "scard.h" |
@@ -50,6 +52,13 @@ int change_comment = 0; | |||
50 | 52 | ||
51 | int quiet = 0; | 53 | int quiet = 0; |
52 | 54 | ||
55 | /* Flag indicating that we want to hash a known_hosts file */ | ||
56 | int hash_hosts = 0; | ||
57 | /* Flag indicating that we want lookup a host in known_hosts file */ | ||
58 | int find_host = 0; | ||
59 | /* Flag indicating that we want to delete a host from a known_hosts file */ | ||
60 | int delete_host = 0; | ||
61 | |||
53 | /* Flag indicating that we just want to see the key fingerprint */ | 62 | /* Flag indicating that we just want to see the key fingerprint */ |
54 | int print_fingerprint = 0; | 63 | int print_fingerprint = 0; |
55 | int print_bubblebabble = 0; | 64 | int print_bubblebabble = 0; |
@@ -541,6 +550,194 @@ do_fingerprint(struct passwd *pw) | |||
541 | exit(0); | 550 | exit(0); |
542 | } | 551 | } |
543 | 552 | ||
553 | static void | ||
554 | print_host(FILE *f, char *name, Key *public, int hash) | ||
555 | { | ||
556 | if (hash && (name = host_hash(name, NULL, 0)) == NULL) | ||
557 | fatal("hash_host failed"); | ||
558 | fprintf(f, "%s ", name); | ||
559 | if (!key_write(public, f)) | ||
560 | fatal("key_write failed"); | ||
561 | fprintf(f, "\n"); | ||
562 | } | ||
563 | |||
564 | static void | ||
565 | do_known_hosts(struct passwd *pw, const char *name) | ||
566 | { | ||
567 | FILE *in, *out = stdout; | ||
568 | Key *public; | ||
569 | char *cp, *cp2, *kp, *kp2; | ||
570 | char line[16*1024], tmp[MAXPATHLEN], old[MAXPATHLEN]; | ||
571 | int c, i, skip = 0, inplace = 0, num = 0, invalid = 0, has_unhashed = 0; | ||
572 | |||
573 | if (!have_identity) { | ||
574 | cp = tilde_expand_filename(_PATH_SSH_USER_HOSTFILE, pw->pw_uid); | ||
575 | if (strlcpy(identity_file, cp, sizeof(identity_file)) >= | ||
576 | sizeof(identity_file)) | ||
577 | fatal("Specified known hosts path too long"); | ||
578 | xfree(cp); | ||
579 | have_identity = 1; | ||
580 | } | ||
581 | if ((in = fopen(identity_file, "r")) == NULL) | ||
582 | fatal("fopen: %s", strerror(errno)); | ||
583 | |||
584 | /* | ||
585 | * Find hosts goes to stdout, hash and deletions happen in-place | ||
586 | * A corner case is ssh-keygen -HF foo, which should go to stdout | ||
587 | */ | ||
588 | if (!find_host && (hash_hosts || delete_host)) { | ||
589 | if (strlcpy(tmp, identity_file, sizeof(tmp)) >= sizeof(tmp) || | ||
590 | strlcat(tmp, ".XXXXXXXXXX", sizeof(tmp)) >= sizeof(tmp) || | ||
591 | strlcpy(old, identity_file, sizeof(old)) >= sizeof(old) || | ||
592 | strlcat(old, ".old", sizeof(old)) >= sizeof(old)) | ||
593 | fatal("known_hosts path too long"); | ||
594 | umask(077); | ||
595 | if ((c = mkstemp(tmp)) == -1) | ||
596 | fatal("mkstemp: %s", strerror(errno)); | ||
597 | if ((out = fdopen(c, "w")) == NULL) { | ||
598 | c = errno; | ||
599 | unlink(tmp); | ||
600 | fatal("fdopen: %s", strerror(c)); | ||
601 | } | ||
602 | inplace = 1; | ||
603 | } | ||
604 | |||
605 | while (fgets(line, sizeof(line), in)) { | ||
606 | num++; | ||
607 | i = strlen(line) - 1; | ||
608 | if (line[i] != '\n') { | ||
609 | error("line %d too long: %.40s...", num, line); | ||
610 | skip = 1; | ||
611 | invalid = 1; | ||
612 | continue; | ||
613 | } | ||
614 | if (skip) { | ||
615 | skip = 0; | ||
616 | continue; | ||
617 | } | ||
618 | line[i] = '\0'; | ||
619 | |||
620 | /* Skip leading whitespace, empty and comment lines. */ | ||
621 | for (cp = line; *cp == ' ' || *cp == '\t'; cp++) | ||
622 | ; | ||
623 | if (!*cp || *cp == '\n' || *cp == '#') { | ||
624 | if (inplace) | ||
625 | fprintf(out, "%s\n", cp); | ||
626 | continue; | ||
627 | } | ||
628 | /* Find the end of the host name portion. */ | ||
629 | for (kp = cp; *kp && *kp != ' ' && *kp != '\t'; kp++) | ||
630 | ; | ||
631 | if (*kp == '\0' || *(kp + 1) == '\0') { | ||
632 | error("line %d missing key: %.40s...", | ||
633 | num, line); | ||
634 | invalid = 1; | ||
635 | continue; | ||
636 | } | ||
637 | *kp++ = '\0'; | ||
638 | kp2 = kp; | ||
639 | |||
640 | public = key_new(KEY_RSA1); | ||
641 | if (key_read(public, &kp) != 1) { | ||
642 | kp = kp2; | ||
643 | key_free(public); | ||
644 | public = key_new(KEY_UNSPEC); | ||
645 | if (key_read(public, &kp) != 1) { | ||
646 | error("line %d invalid key: %.40s...", | ||
647 | num, line); | ||
648 | key_free(public); | ||
649 | invalid = 1; | ||
650 | continue; | ||
651 | } | ||
652 | } | ||
653 | |||
654 | if (*cp == HASH_DELIM) { | ||
655 | if (find_host || delete_host) { | ||
656 | cp2 = host_hash(name, cp, strlen(cp)); | ||
657 | if (cp2 == NULL) { | ||
658 | error("line %d: invalid hashed " | ||
659 | "name: %.64s...", num, line); | ||
660 | invalid = 1; | ||
661 | continue; | ||
662 | } | ||
663 | c = (strcmp(cp2, cp) == 0); | ||
664 | if (find_host && c) { | ||
665 | printf("# Host %s found: " | ||
666 | "line %d type %s\n", name, | ||
667 | num, key_type(public)); | ||
668 | print_host(out, cp, public, 0); | ||
669 | } | ||
670 | if (delete_host && !c) | ||
671 | print_host(out, cp, public, 0); | ||
672 | } else if (hash_hosts) | ||
673 | print_host(out, cp, public, 0); | ||
674 | } else { | ||
675 | if (find_host || delete_host) { | ||
676 | c = (match_hostname(name, cp, | ||
677 | strlen(cp)) == 1); | ||
678 | if (find_host && c) { | ||
679 | printf("# Host %s found: " | ||
680 | "line %d type %s\n", name, | ||
681 | num, key_type(public)); | ||
682 | print_host(out, cp, public, hash_hosts); | ||
683 | } | ||
684 | if (delete_host && !c) | ||
685 | print_host(out, cp, public, 0); | ||
686 | } else if (hash_hosts) { | ||
687 | for(cp2 = strsep(&cp, ","); | ||
688 | cp2 != NULL && *cp2 != '\0'; | ||
689 | cp2 = strsep(&cp, ",")) | ||
690 | print_host(out, cp2, public, 1); | ||
691 | has_unhashed = 1; | ||
692 | } | ||
693 | } | ||
694 | key_free(public); | ||
695 | } | ||
696 | fclose(in); | ||
697 | |||
698 | if (invalid) { | ||
699 | fprintf(stderr, "%s is not a valid known_host file.\n", | ||
700 | identity_file); | ||
701 | if (inplace) { | ||
702 | fprintf(stderr, "Not replacing existing known_hosts " | ||
703 | "file beacuse of errors"); | ||
704 | fclose(out); | ||
705 | unlink(tmp); | ||
706 | } | ||
707 | exit(1); | ||
708 | } | ||
709 | |||
710 | if (inplace) { | ||
711 | fclose(out); | ||
712 | |||
713 | /* Backup existing file */ | ||
714 | if (unlink(old) == -1 && errno != ENOENT) | ||
715 | fatal("unlink %.100s: %s", old, strerror(errno)); | ||
716 | if (link(identity_file, old) == -1) | ||
717 | fatal("link %.100s to %.100s: %s", identity_file, old, | ||
718 | strerror(errno)); | ||
719 | /* Move new one into place */ | ||
720 | if (rename(tmp, identity_file) == -1) { | ||
721 | error("rename\"%s\" to \"%s\": %s", tmp, identity_file, | ||
722 | strerror(errno)); | ||
723 | unlink(tmp); | ||
724 | unlink(old); | ||
725 | exit(1); | ||
726 | } | ||
727 | |||
728 | fprintf(stderr, "%s updated.\n", identity_file); | ||
729 | fprintf(stderr, "Original contents retained as %s\n", old); | ||
730 | if (has_unhashed) { | ||
731 | fprintf(stderr, "WARNING: %s contains unhashed " | ||
732 | "entries\n", old); | ||
733 | fprintf(stderr, "Delete this file to ensure privacy " | ||
734 | "of hostnames\n"); | ||
735 | } | ||
736 | } | ||
737 | |||
738 | exit(0); | ||
739 | } | ||
740 | |||
544 | /* | 741 | /* |
545 | * Perform changing a passphrase. The argument is the passwd structure | 742 | * Perform changing a passphrase. The argument is the passwd structure |
546 | * for the current user. | 743 | * for the current user. |
@@ -767,6 +964,8 @@ usage(void) | |||
767 | fprintf(stderr, " -y Read private key file and print public key.\n"); | 964 | fprintf(stderr, " -y Read private key file and print public key.\n"); |
768 | fprintf(stderr, " -t type Specify type of key to create.\n"); | 965 | fprintf(stderr, " -t type Specify type of key to create.\n"); |
769 | fprintf(stderr, " -B Show bubblebabble digest of key file.\n"); | 966 | fprintf(stderr, " -B Show bubblebabble digest of key file.\n"); |
967 | fprintf(stderr, " -H Hash names in known_hosts file\n"); | ||
968 | fprintf(stderr, " -F hostname Find hostname in known hosts file\n"); | ||
770 | fprintf(stderr, " -C comment Provide new comment.\n"); | 969 | fprintf(stderr, " -C comment Provide new comment.\n"); |
771 | fprintf(stderr, " -N phrase Provide new passphrase.\n"); | 970 | fprintf(stderr, " -N phrase Provide new passphrase.\n"); |
772 | fprintf(stderr, " -P phrase Provide old passphrase.\n"); | 971 | fprintf(stderr, " -P phrase Provide old passphrase.\n"); |
@@ -790,7 +989,7 @@ main(int ac, char **av) | |||
790 | { | 989 | { |
791 | char dotsshdir[MAXPATHLEN], comment[1024], *passphrase1, *passphrase2; | 990 | char dotsshdir[MAXPATHLEN], comment[1024], *passphrase1, *passphrase2; |
792 | char out_file[MAXPATHLEN], *reader_id = NULL; | 991 | char out_file[MAXPATHLEN], *reader_id = NULL; |
793 | char *resource_record_hostname = NULL; | 992 | char *rr_hostname = NULL; |
794 | Key *private, *public; | 993 | Key *private, *public; |
795 | struct passwd *pw; | 994 | struct passwd *pw; |
796 | struct stat st; | 995 | struct stat st; |
@@ -824,7 +1023,7 @@ main(int ac, char **av) | |||
824 | } | 1023 | } |
825 | 1024 | ||
826 | while ((opt = getopt(ac, av, | 1025 | while ((opt = getopt(ac, av, |
827 | "degiqpclBRvxXyb:f:t:U:D:P:N:C:r:g:T:G:M:S:a:W:")) != -1) { | 1026 | "degiqpclBHvxXyF:b:f:t:U:D:P:N:C:r:g:R:T:G:M:S:a:W:")) != -1) { |
828 | switch (opt) { | 1027 | switch (opt) { |
829 | case 'b': | 1028 | case 'b': |
830 | bits = atoi(optarg); | 1029 | bits = atoi(optarg); |
@@ -833,6 +1032,17 @@ main(int ac, char **av) | |||
833 | exit(1); | 1032 | exit(1); |
834 | } | 1033 | } |
835 | break; | 1034 | break; |
1035 | case 'F': | ||
1036 | find_host = 1; | ||
1037 | rr_hostname = optarg; | ||
1038 | break; | ||
1039 | case 'H': | ||
1040 | hash_hosts = 1; | ||
1041 | break; | ||
1042 | case 'R': | ||
1043 | delete_host = 1; | ||
1044 | rr_hostname = optarg; | ||
1045 | break; | ||
836 | case 'l': | 1046 | case 'l': |
837 | print_fingerprint = 1; | 1047 | print_fingerprint = 1; |
838 | break; | 1048 | break; |
@@ -864,10 +1074,6 @@ main(int ac, char **av) | |||
864 | case 'q': | 1074 | case 'q': |
865 | quiet = 1; | 1075 | quiet = 1; |
866 | break; | 1076 | break; |
867 | case 'R': | ||
868 | /* unused */ | ||
869 | exit(0); | ||
870 | break; | ||
871 | case 'e': | 1077 | case 'e': |
872 | case 'x': | 1078 | case 'x': |
873 | /* export key */ | 1079 | /* export key */ |
@@ -902,7 +1108,7 @@ main(int ac, char **av) | |||
902 | } | 1108 | } |
903 | break; | 1109 | break; |
904 | case 'r': | 1110 | case 'r': |
905 | resource_record_hostname = optarg; | 1111 | rr_hostname = optarg; |
906 | break; | 1112 | break; |
907 | case 'W': | 1113 | case 'W': |
908 | generator_wanted = atoi(optarg); | 1114 | generator_wanted = atoi(optarg); |
@@ -945,6 +1151,8 @@ main(int ac, char **av) | |||
945 | printf("Can only have one of -p and -c.\n"); | 1151 | printf("Can only have one of -p and -c.\n"); |
946 | usage(); | 1152 | usage(); |
947 | } | 1153 | } |
1154 | if (delete_host || hash_hosts || find_host) | ||
1155 | do_known_hosts(pw, rr_hostname); | ||
948 | if (print_fingerprint || print_bubblebabble) | 1156 | if (print_fingerprint || print_bubblebabble) |
949 | do_fingerprint(pw); | 1157 | do_fingerprint(pw); |
950 | if (change_passphrase) | 1158 | if (change_passphrase) |
@@ -957,8 +1165,8 @@ main(int ac, char **av) | |||
957 | do_convert_from_ssh2(pw); | 1165 | do_convert_from_ssh2(pw); |
958 | if (print_public) | 1166 | if (print_public) |
959 | do_print_public(pw); | 1167 | do_print_public(pw); |
960 | if (resource_record_hostname != NULL) { | 1168 | if (rr_hostname != NULL) { |
961 | do_print_resource_record(pw, resource_record_hostname); | 1169 | do_print_resource_record(pw, rr_hostname); |
962 | } | 1170 | } |
963 | if (reader_id != NULL) { | 1171 | if (reader_id != NULL) { |
964 | #ifdef SMARTCARD | 1172 | #ifdef SMARTCARD |