diff options
author | Colin Watson <cjwatson@ubuntu.com> | 2014-02-09 16:09:50 +0000 |
---|---|---|
committer | Colin Watson <cjwatson@debian.org> | 2014-02-09 16:17:31 +0000 |
commit | 8909ff0e3cd07d1b042d1be1c8b8828dbf6c9a83 (patch) | |
tree | ebee4092f1411059e34da6f66b4ebd64f4411020 /authfile.c | |
parent | 07f2a771c490bd68cd5c5ea9c535705e93bd94f3 (diff) |
Reject vulnerable keys to mitigate Debian OpenSSL flaw
In 2008, Debian (and derived distributions such as Ubuntu) shipped an
OpenSSL package with a flawed random number generator, causing OpenSSH to
generate only a very limited set of keys which were subject to private half
precomputation. To mitigate this, this patch checks key authentications
against a blacklist of known-vulnerable keys, and adds a new ssh-vulnkey
program which can be used to explicitly check keys against that blacklist.
See CVE-2008-0166.
Bug: https://bugzilla.mindrot.org/show_bug.cgi?id=1469
Last-Update: 2013-09-14
Patch-Name: ssh-vulnkey.patch
Diffstat (limited to 'authfile.c')
-rw-r--r-- | authfile.c | 136 |
1 files changed, 136 insertions, 0 deletions
diff --git a/authfile.c b/authfile.c index 63ae16bbd..983359157 100644 --- a/authfile.c +++ b/authfile.c | |||
@@ -68,6 +68,7 @@ | |||
68 | #include "rsa.h" | 68 | #include "rsa.h" |
69 | #include "misc.h" | 69 | #include "misc.h" |
70 | #include "atomicio.h" | 70 | #include "atomicio.h" |
71 | #include "pathnames.h" | ||
71 | 72 | ||
72 | #define MAX_KEY_FILE_SIZE (1024 * 1024) | 73 | #define MAX_KEY_FILE_SIZE (1024 * 1024) |
73 | 74 | ||
@@ -944,3 +945,138 @@ key_in_file(Key *key, const char *filename, int strict_type) | |||
944 | return ret; | 945 | return ret; |
945 | } | 946 | } |
946 | 947 | ||
948 | /* Scan a blacklist of known-vulnerable keys in blacklist_file. */ | ||
949 | static int | ||
950 | blacklisted_key_in_file(Key *key, const char *blacklist_file, char **fp) | ||
951 | { | ||
952 | int fd = -1; | ||
953 | char *dgst_hex = NULL; | ||
954 | char *dgst_packed = NULL, *p; | ||
955 | int i; | ||
956 | size_t line_len; | ||
957 | struct stat st; | ||
958 | char buf[256]; | ||
959 | off_t start, lower, upper; | ||
960 | int ret = 0; | ||
961 | |||
962 | debug("Checking blacklist file %s", blacklist_file); | ||
963 | fd = open(blacklist_file, O_RDONLY); | ||
964 | if (fd < 0) { | ||
965 | ret = -1; | ||
966 | goto out; | ||
967 | } | ||
968 | |||
969 | dgst_hex = key_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX); | ||
970 | /* Remove all colons */ | ||
971 | dgst_packed = xcalloc(1, strlen(dgst_hex) + 1); | ||
972 | for (i = 0, p = dgst_packed; dgst_hex[i]; i++) | ||
973 | if (dgst_hex[i] != ':') | ||
974 | *p++ = dgst_hex[i]; | ||
975 | /* Only compare least-significant 80 bits (to keep the blacklist | ||
976 | * size down) | ||
977 | */ | ||
978 | line_len = strlen(dgst_packed + 12); | ||
979 | if (line_len > 32) | ||
980 | goto out; | ||
981 | |||
982 | /* Skip leading comments */ | ||
983 | start = 0; | ||
984 | for (;;) { | ||
985 | ssize_t r; | ||
986 | char *newline; | ||
987 | |||
988 | r = atomicio(read, fd, buf, sizeof(buf)); | ||
989 | if (r <= 0) | ||
990 | goto out; | ||
991 | if (buf[0] != '#') | ||
992 | break; | ||
993 | |||
994 | newline = memchr(buf, '\n', sizeof(buf)); | ||
995 | if (!newline) | ||
996 | goto out; | ||
997 | start += newline + 1 - buf; | ||
998 | if (lseek(fd, start, SEEK_SET) < 0) | ||
999 | goto out; | ||
1000 | } | ||
1001 | |||
1002 | /* Initialise binary search record numbers */ | ||
1003 | if (fstat(fd, &st) < 0) | ||
1004 | goto out; | ||
1005 | lower = 0; | ||
1006 | upper = (st.st_size - start) / (line_len + 1); | ||
1007 | |||
1008 | while (lower != upper) { | ||
1009 | off_t cur; | ||
1010 | int cmp; | ||
1011 | |||
1012 | cur = lower + (upper - lower) / 2; | ||
1013 | |||
1014 | /* Read this line and compare to digest; this is | ||
1015 | * overflow-safe since cur < max(off_t) / (line_len + 1) */ | ||
1016 | if (lseek(fd, start + cur * (line_len + 1), SEEK_SET) < 0) | ||
1017 | break; | ||
1018 | if (atomicio(read, fd, buf, line_len) != line_len) | ||
1019 | break; | ||
1020 | cmp = memcmp(buf, dgst_packed + 12, line_len); | ||
1021 | if (cmp < 0) { | ||
1022 | if (cur == lower) | ||
1023 | break; | ||
1024 | lower = cur; | ||
1025 | } else if (cmp > 0) { | ||
1026 | if (cur == upper) | ||
1027 | break; | ||
1028 | upper = cur; | ||
1029 | } else { | ||
1030 | debug("Found %s in blacklist", dgst_hex); | ||
1031 | ret = 1; | ||
1032 | break; | ||
1033 | } | ||
1034 | } | ||
1035 | |||
1036 | out: | ||
1037 | free(dgst_packed); | ||
1038 | if (ret != 1 && dgst_hex) { | ||
1039 | free(dgst_hex); | ||
1040 | dgst_hex = NULL; | ||
1041 | } | ||
1042 | if (fp) | ||
1043 | *fp = dgst_hex; | ||
1044 | if (fd >= 0) | ||
1045 | close(fd); | ||
1046 | return ret; | ||
1047 | } | ||
1048 | |||
1049 | /* | ||
1050 | * Scan blacklists of known-vulnerable keys. If a vulnerable key is found, | ||
1051 | * its fingerprint is returned in *fp, unless fp is NULL. | ||
1052 | */ | ||
1053 | int | ||
1054 | blacklisted_key(Key *key, char **fp) | ||
1055 | { | ||
1056 | Key *public; | ||
1057 | char *blacklist_file; | ||
1058 | int ret, ret2; | ||
1059 | |||
1060 | public = key_demote(key); | ||
1061 | if (public->type == KEY_RSA1) | ||
1062 | public->type = KEY_RSA; | ||
1063 | |||
1064 | xasprintf(&blacklist_file, "%s.%s-%u", | ||
1065 | _PATH_BLACKLIST, key_type(public), key_size(public)); | ||
1066 | ret = blacklisted_key_in_file(public, blacklist_file, fp); | ||
1067 | free(blacklist_file); | ||
1068 | if (ret > 0) { | ||
1069 | key_free(public); | ||
1070 | return ret; | ||
1071 | } | ||
1072 | |||
1073 | xasprintf(&blacklist_file, "%s.%s-%u", | ||
1074 | _PATH_BLACKLIST_CONFIG, key_type(public), key_size(public)); | ||
1075 | ret2 = blacklisted_key_in_file(public, blacklist_file, fp); | ||
1076 | free(blacklist_file); | ||
1077 | if (ret2 > ret) | ||
1078 | ret = ret2; | ||
1079 | |||
1080 | key_free(public); | ||
1081 | return ret; | ||
1082 | } | ||