diff options
author | djm@openbsd.org <djm@openbsd.org> | 2015-11-16 22:53:07 +0000 |
---|---|---|
committer | Damien Miller <djm@mindrot.org> | 2015-11-17 11:22:15 +1100 |
commit | c56a255162c2166884539c0a1f7511575325b477 (patch) | |
tree | ff54e2e3b3a72f73a2dbab49eebfc6ec9474e0a1 | |
parent | 5b4010d9b923cf1b46c9c7b1887c013c2967e204 (diff) |
upstream commit
Allow fingerprinting from standard input "ssh-keygen -lf
-"
Support fingerprinting multiple plain keys in a file and authorized_keys
files too (bz#1319)
ok markus@
Upstream-ID: 903f8b4502929d6ccf53509e4e07eae084574b77
-rw-r--r-- | ssh-keygen.c | 215 |
1 files changed, 127 insertions, 88 deletions
diff --git a/ssh-keygen.c b/ssh-keygen.c index f58462044..5c02d7817 100644 --- a/ssh-keygen.c +++ b/ssh-keygen.c | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: ssh-keygen.c,v 1.278 2015/11/13 04:34:15 djm Exp $ */ | 1 | /* $OpenBSD: ssh-keygen.c,v 1.279 2015/11/16 22:53:07 djm Exp $ */ |
2 | /* | 2 | /* |
3 | * Author: Tatu Ylonen <ylo@cs.hut.fi> | 3 | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
4 | * Copyright (c) 1994 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 4 | * Copyright (c) 1994 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
@@ -808,116 +808,155 @@ do_download(struct passwd *pw) | |||
808 | #endif /* ENABLE_PKCS11 */ | 808 | #endif /* ENABLE_PKCS11 */ |
809 | } | 809 | } |
810 | 810 | ||
811 | static struct sshkey * | ||
812 | try_read_key(char **cpp) | ||
813 | { | ||
814 | struct sshkey *ret; | ||
815 | int r; | ||
816 | |||
817 | if ((ret = sshkey_new(KEY_RSA1)) == NULL) | ||
818 | fatal("sshkey_new failed"); | ||
819 | /* Try RSA1 */ | ||
820 | if ((r = sshkey_read(ret, cpp)) == 0) | ||
821 | return ret; | ||
822 | /* Try modern */ | ||
823 | sshkey_free(ret); | ||
824 | if ((ret = sshkey_new(KEY_UNSPEC)) == NULL) | ||
825 | fatal("sshkey_new failed"); | ||
826 | if ((r = sshkey_read(ret, cpp)) == 0) | ||
827 | return ret; | ||
828 | /* Not a key */ | ||
829 | sshkey_free(ret); | ||
830 | return NULL; | ||
831 | } | ||
832 | |||
811 | static void | 833 | static void |
812 | do_fingerprint(struct passwd *pw) | 834 | fingerprint_one_key(const struct sshkey *public, const char *comment) |
813 | { | 835 | { |
814 | FILE *f; | 836 | char *fp = NULL, *ra = NULL; |
815 | struct sshkey *public; | ||
816 | char *comment = NULL, *cp, *ep, line[16*1024], *fp, *ra; | ||
817 | int r, i, skip = 0, num = 0, invalid = 1; | ||
818 | enum sshkey_fp_rep rep; | 837 | enum sshkey_fp_rep rep; |
819 | int fptype; | 838 | int fptype; |
820 | struct stat st; | ||
821 | 839 | ||
822 | fptype = print_bubblebabble ? SSH_DIGEST_SHA1 : fingerprint_hash; | 840 | fptype = print_bubblebabble ? SSH_DIGEST_SHA1 : fingerprint_hash; |
823 | rep = print_bubblebabble ? SSH_FP_BUBBLEBABBLE : SSH_FP_DEFAULT; | 841 | rep = print_bubblebabble ? SSH_FP_BUBBLEBABBLE : SSH_FP_DEFAULT; |
842 | fp = sshkey_fingerprint(public, fptype, rep); | ||
843 | ra = sshkey_fingerprint(public, fingerprint_hash, SSH_FP_RANDOMART); | ||
844 | if (fp == NULL || ra == NULL) | ||
845 | fatal("%s: sshkey_fingerprint failed", __func__); | ||
846 | printf("%u %s %s (%s)\n", sshkey_size(public), fp, | ||
847 | comment ? comment : "no comment", sshkey_type(public)); | ||
848 | if (log_level >= SYSLOG_LEVEL_VERBOSE) | ||
849 | printf("%s\n", ra); | ||
850 | free(ra); | ||
851 | free(fp); | ||
852 | } | ||
853 | |||
854 | static void | ||
855 | fingerprint_private(const char *path) | ||
856 | { | ||
857 | struct stat st; | ||
858 | char *comment = NULL; | ||
859 | struct sshkey *public = NULL; | ||
860 | int r; | ||
861 | |||
862 | if (stat(identity_file, &st) < 0) | ||
863 | fatal("%s: %s", path, strerror(errno)); | ||
864 | if ((r = sshkey_load_public(path, &public, &comment)) != 0) | ||
865 | fatal("Error loading public key \"%s\": %s", path, ssh_err(r)); | ||
866 | fingerprint_one_key(public, comment); | ||
867 | sshkey_free(public); | ||
868 | free(comment); | ||
869 | } | ||
870 | |||
871 | static void | ||
872 | do_fingerprint(struct passwd *pw) | ||
873 | { | ||
874 | FILE *f; | ||
875 | struct sshkey *public = NULL; | ||
876 | char *comment = NULL, *cp, *ep, line[16*1024]; | ||
877 | int i, invalid = 1; | ||
878 | const char *path; | ||
879 | long int lnum = 0; | ||
880 | |||
824 | if (!have_identity) | 881 | if (!have_identity) |
825 | ask_filename(pw, "Enter file in which the key is"); | 882 | ask_filename(pw, "Enter file in which the key is"); |
826 | if (stat(identity_file, &st) < 0) | 883 | path = identity_file; |
827 | fatal("%s: %s", identity_file, strerror(errno)); | ||
828 | if ((r = sshkey_load_public(identity_file, &public, &comment)) != 0) | ||
829 | debug2("Error loading public key \"%s\": %s", | ||
830 | identity_file, ssh_err(r)); | ||
831 | else { | ||
832 | fp = sshkey_fingerprint(public, fptype, rep); | ||
833 | ra = sshkey_fingerprint(public, fingerprint_hash, | ||
834 | SSH_FP_RANDOMART); | ||
835 | if (fp == NULL || ra == NULL) | ||
836 | fatal("%s: sshkey_fingerprint fail", __func__); | ||
837 | printf("%u %s %s (%s)\n", sshkey_size(public), fp, comment, | ||
838 | sshkey_type(public)); | ||
839 | if (log_level >= SYSLOG_LEVEL_VERBOSE) | ||
840 | printf("%s\n", ra); | ||
841 | sshkey_free(public); | ||
842 | free(comment); | ||
843 | free(ra); | ||
844 | free(fp); | ||
845 | exit(0); | ||
846 | } | ||
847 | if (comment) { | ||
848 | free(comment); | ||
849 | comment = NULL; | ||
850 | } | ||
851 | 884 | ||
852 | if ((f = fopen(identity_file, "r")) == NULL) | 885 | if (strcmp(identity_file, "-") == 0) { |
853 | fatal("%s: %s: %s", __progname, identity_file, strerror(errno)); | 886 | f = stdin; |
887 | path = "(stdin)"; | ||
888 | } else if ((f = fopen(path, "r")) == NULL) | ||
889 | fatal("%s: %s: %s", __progname, path, strerror(errno)); | ||
854 | 890 | ||
855 | while (fgets(line, sizeof(line), f)) { | 891 | while (read_keyfile_line(f, path, line, sizeof(line), &lnum) == 0) { |
856 | if ((cp = strchr(line, '\n')) == NULL) { | 892 | cp = line; |
857 | error("line %d too long: %.40s...", | 893 | cp[strcspn(cp, "\n")] = '\0'; |
858 | num + 1, line); | 894 | /* Trim leading space and comments */ |
859 | skip = 1; | 895 | cp = line + strspn(line, " \t"); |
896 | if (*cp == '#' || *cp == '\0') | ||
860 | continue; | 897 | continue; |
898 | |||
899 | /* | ||
900 | * Input may be plain keys, private keys, authorized_keys | ||
901 | * or known_hosts. | ||
902 | */ | ||
903 | |||
904 | /* | ||
905 | * Try private keys first. Assume a key is private if | ||
906 | * "SSH PRIVATE KEY" appears on the first line and we're | ||
907 | * not reading from stdin (XXX support private keys on stdin). | ||
908 | */ | ||
909 | if (lnum == 1 && strcmp(identity_file, "-") != 0 && | ||
910 | strstr(cp, "SSH PRIVATE KEY") != NULL) { | ||
911 | fclose(f); | ||
912 | fingerprint_private(path); | ||
913 | exit(0); | ||
914 | } | ||
915 | |||
916 | /* | ||
917 | * If it's not a private key, then this must be prepared to | ||
918 | * accept a public key prefixed with a hostname or options. | ||
919 | * Try a bare key first, otherwise skip the leading stuff. | ||
920 | */ | ||
921 | if ((public = try_read_key(&cp)) == NULL) { | ||
922 | i = strtol(cp, &ep, 10); | ||
923 | if (i == 0 || ep == NULL || | ||
924 | (*ep != ' ' && *ep != '\t')) { | ||
925 | int quoted = 0; | ||
926 | |||
927 | comment = cp; | ||
928 | for (; *cp && (quoted || (*cp != ' ' && | ||
929 | *cp != '\t')); cp++) { | ||
930 | if (*cp == '\\' && cp[1] == '"') | ||
931 | cp++; /* Skip both */ | ||
932 | else if (*cp == '"') | ||
933 | quoted = !quoted; | ||
934 | } | ||
935 | if (!*cp) | ||
936 | continue; | ||
937 | *cp++ = '\0'; | ||
938 | } | ||
861 | } | 939 | } |
862 | num++; | 940 | /* Retry after parsing leading hostname/key options */ |
863 | if (skip) { | 941 | if (public == NULL && (public = try_read_key(&cp)) == NULL) { |
864 | skip = 0; | 942 | debug("%s:%ld: not a public key", path, lnum); |
865 | continue; | 943 | continue; |
866 | } | 944 | } |
867 | *cp = '\0'; | ||
868 | 945 | ||
869 | /* Skip leading whitespace, empty and comment lines. */ | 946 | /* Find trailing comment, if any */ |
870 | for (cp = line; *cp == ' ' || *cp == '\t'; cp++) | 947 | for (; *cp == ' ' || *cp == '\t'; cp++) |
871 | ; | 948 | ; |
872 | if (!*cp || *cp == '\n' || *cp == '#') | 949 | if (*cp != '\0' && *cp != '#') |
873 | continue; | ||
874 | i = strtol(cp, &ep, 10); | ||
875 | if (i == 0 || ep == NULL || (*ep != ' ' && *ep != '\t')) { | ||
876 | int quoted = 0; | ||
877 | comment = cp; | 950 | comment = cp; |
878 | for (; *cp && (quoted || (*cp != ' ' && | 951 | |
879 | *cp != '\t')); cp++) { | 952 | fingerprint_one_key(public, comment); |
880 | if (*cp == '\\' && cp[1] == '"') | ||
881 | cp++; /* Skip both */ | ||
882 | else if (*cp == '"') | ||
883 | quoted = !quoted; | ||
884 | } | ||
885 | if (!*cp) | ||
886 | continue; | ||
887 | *cp++ = '\0'; | ||
888 | } | ||
889 | ep = cp; | ||
890 | if ((public = sshkey_new(KEY_RSA1)) == NULL) | ||
891 | fatal("sshkey_new failed"); | ||
892 | if ((r = sshkey_read(public, &cp)) != 0) { | ||
893 | cp = ep; | ||
894 | sshkey_free(public); | ||
895 | if ((public = sshkey_new(KEY_UNSPEC)) == NULL) | ||
896 | fatal("sshkey_new failed"); | ||
897 | if ((r = sshkey_read(public, &cp)) != 0) { | ||
898 | sshkey_free(public); | ||
899 | continue; | ||
900 | } | ||
901 | } | ||
902 | comment = *cp ? cp : comment; | ||
903 | fp = sshkey_fingerprint(public, fptype, rep); | ||
904 | ra = sshkey_fingerprint(public, fingerprint_hash, | ||
905 | SSH_FP_RANDOMART); | ||
906 | if (fp == NULL || ra == NULL) | ||
907 | fatal("%s: sshkey_fingerprint fail", __func__); | ||
908 | printf("%u %s %s (%s)\n", sshkey_size(public), fp, | ||
909 | comment ? comment : "no comment", sshkey_type(public)); | ||
910 | if (log_level >= SYSLOG_LEVEL_VERBOSE) | ||
911 | printf("%s\n", ra); | ||
912 | free(ra); | ||
913 | free(fp); | ||
914 | sshkey_free(public); | 953 | sshkey_free(public); |
915 | invalid = 0; | 954 | invalid = 0; /* One good key in the file is sufficient */ |
916 | } | 955 | } |
917 | fclose(f); | 956 | fclose(f); |
918 | 957 | ||
919 | if (invalid) | 958 | if (invalid) |
920 | fatal("%s is not a public key file.", identity_file); | 959 | fatal("%s is not a public key file.", path); |
921 | exit(0); | 960 | exit(0); |
922 | } | 961 | } |
923 | 962 | ||