diff options
author | djm@openbsd.org <djm@openbsd.org> | 2015-01-18 21:49:42 +0000 |
---|---|---|
committer | Damien Miller <djm@mindrot.org> | 2015-01-20 00:20:44 +1100 |
commit | cecb30bc2ba6d594366e657d664d5c494b6c8a7f (patch) | |
tree | f0f904dcbf01f5d5ac869b58f7d20391dec6c320 | |
parent | ec3d065df3a9557ea96b02d061fd821a18c1a0b9 (diff) |
upstream commit
make ssh-keygen use hostkeys_foreach(). Removes some
horrendous code; ok markus@
-rw-r--r-- | ssh-keygen.c | 326 |
1 files changed, 121 insertions, 205 deletions
diff --git a/ssh-keygen.c b/ssh-keygen.c index 500a36633..02db8b2bd 100644 --- a/ssh-keygen.c +++ b/ssh-keygen.c | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: ssh-keygen.c,v 1.255 2015/01/18 13:22:28 djm Exp $ */ | 1 | /* $OpenBSD: ssh-keygen.c,v 1.256 2015/01/18 21:49:42 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 |
@@ -1031,50 +1031,110 @@ do_gen_all_hostkeys(struct passwd *pw) | |||
1031 | printf("\n"); | 1031 | printf("\n"); |
1032 | } | 1032 | } |
1033 | 1033 | ||
1034 | static void | 1034 | struct known_hosts_ctx { |
1035 | printhost(FILE *f, const char *name, struct sshkey *public, | 1035 | FILE *out; |
1036 | int ca, int revoked, int hash) | 1036 | const char *host; |
1037 | int has_unhashed, found_key, inplace, invalid; | ||
1038 | }; | ||
1039 | |||
1040 | static int | ||
1041 | known_hosts_hash(struct hostkey_foreach_line *l, void *_ctx) | ||
1037 | { | 1042 | { |
1038 | if (print_fingerprint) { | 1043 | struct known_hosts_ctx *ctx = (struct known_hosts_ctx *)_ctx; |
1039 | enum sshkey_fp_rep rep; | 1044 | char *hashed, *cp, *hosts, *ohosts; |
1040 | int fptype; | 1045 | int has_wild = l->hosts && strcspn(l->hosts, "*?!") != strlen(l->hosts); |
1041 | char *fp, *ra; | ||
1042 | |||
1043 | fptype = print_bubblebabble ? | ||
1044 | SSH_DIGEST_SHA1 : fingerprint_hash; | ||
1045 | rep = print_bubblebabble ? SSH_FP_BUBBLEBABBLE : SSH_FP_DEFAULT; | ||
1046 | fp = sshkey_fingerprint(public, fptype, rep); | ||
1047 | ra = sshkey_fingerprint(public, fingerprint_hash, | ||
1048 | SSH_FP_RANDOMART); | ||
1049 | printf("%u %s %s (%s)\n", sshkey_size(public), fp, name, | ||
1050 | sshkey_type(public)); | ||
1051 | if (log_level >= SYSLOG_LEVEL_VERBOSE) | ||
1052 | printf("%s\n", ra); | ||
1053 | free(ra); | ||
1054 | free(fp); | ||
1055 | } else { | ||
1056 | int r; | ||
1057 | 1046 | ||
1058 | if (hash && (name = host_hash(name, NULL, 0)) == NULL) | 1047 | /* Retain invalid lines when hashing, but mark file as invalid. */ |
1048 | if (l->status == HKF_STATUS_INVALID) { | ||
1049 | ctx->invalid = 1; | ||
1050 | fprintf(stderr, "%s:%ld: invalid line\n", l->path, l->linenum); | ||
1051 | fprintf(ctx->out, "%s\n", l->line); | ||
1052 | return 0; | ||
1053 | } | ||
1054 | |||
1055 | /* | ||
1056 | * Don't hash hosts already already hashed, with wildcard characters | ||
1057 | * or a CA/revocation marker. | ||
1058 | */ | ||
1059 | if (l->was_hashed || has_wild || l->marker != MRK_NONE) { | ||
1060 | fprintf(ctx->out, "%s\n", l->line); | ||
1061 | if (has_wild && !find_host) { | ||
1062 | fprintf(stderr, "%s:%ld: ignoring host name " | ||
1063 | "with wildcard: %.64s\n", l->path, | ||
1064 | l->linenum, l->hosts); | ||
1065 | ctx->has_unhashed = 1; | ||
1066 | } | ||
1067 | return 0; | ||
1068 | } | ||
1069 | /* | ||
1070 | * Split any comma-separated hostnames from the host list, | ||
1071 | * hash and store separately. | ||
1072 | */ | ||
1073 | ohosts = hosts = xstrdup(l->hosts); | ||
1074 | while ((cp = strsep(&hosts, ",")) != NULL && *cp != '\0') { | ||
1075 | if ((hashed = host_hash(cp, NULL, 0)) == NULL) | ||
1059 | fatal("hash_host failed"); | 1076 | fatal("hash_host failed"); |
1060 | fprintf(f, "%s%s%s ", ca ? CA_MARKER " " : "", | 1077 | fprintf(ctx->out, "%s %s\n", hashed, l->rawkey); |
1061 | revoked ? REVOKE_MARKER " " : "" , name); | 1078 | ctx->has_unhashed = 1; |
1062 | if ((r = sshkey_write(public, f)) != 0) | ||
1063 | fatal("key_write failed: %s", ssh_err(r)); | ||
1064 | fprintf(f, "\n"); | ||
1065 | } | 1079 | } |
1080 | free(ohosts); | ||
1081 | return 0; | ||
1082 | } | ||
1083 | |||
1084 | static int | ||
1085 | known_hosts_find_delete(struct hostkey_foreach_line *l, void *_ctx) | ||
1086 | { | ||
1087 | struct known_hosts_ctx *ctx = (struct known_hosts_ctx *)_ctx; | ||
1088 | |||
1089 | if (l->status == HKF_STATUS_HOST_MATCHED) { | ||
1090 | if (delete_host) { | ||
1091 | if (l->marker != MRK_NONE) { | ||
1092 | /* Don't remove CA and revocation lines */ | ||
1093 | fprintf(ctx->out, "%s\n", l->line); | ||
1094 | } else { | ||
1095 | /* | ||
1096 | * Hostname matches and has no CA/revoke | ||
1097 | * marker, delete it by *not* writing the | ||
1098 | * line to ctx->out. | ||
1099 | */ | ||
1100 | ctx->found_key = 1; | ||
1101 | if (!quiet) | ||
1102 | printf("# Host %s found: line %ld\n", | ||
1103 | ctx->host, l->linenum); | ||
1104 | } | ||
1105 | return 0; | ||
1106 | } else if (find_host) { | ||
1107 | ctx->found_key = 1; | ||
1108 | if (!quiet) { | ||
1109 | printf("# Host %s found: line %ld %s\n", | ||
1110 | ctx->host, | ||
1111 | l->linenum, l->marker == MRK_CA ? "CA" : | ||
1112 | (l->marker == MRK_REVOKE ? "REVOKED" : "")); | ||
1113 | } | ||
1114 | if (hash_hosts) | ||
1115 | known_hosts_hash(l, ctx); | ||
1116 | else | ||
1117 | fprintf(ctx->out, "%s\n", l->line); | ||
1118 | return 0; | ||
1119 | } | ||
1120 | } else if (delete_host) { | ||
1121 | /* Retain non-matching hosts when deleting */ | ||
1122 | if (l->status == HKF_STATUS_INVALID) { | ||
1123 | ctx->invalid = 1; | ||
1124 | fprintf(stderr, "%s:%ld: invalid line\n", | ||
1125 | l->path, l->linenum); | ||
1126 | } | ||
1127 | fprintf(ctx->out, "%s\n", l->line); | ||
1128 | } | ||
1129 | return 0; | ||
1066 | } | 1130 | } |
1067 | 1131 | ||
1068 | static void | 1132 | static void |
1069 | do_known_hosts(struct passwd *pw, const char *name) | 1133 | do_known_hosts(struct passwd *pw, const char *name) |
1070 | { | 1134 | { |
1071 | FILE *in, *out = stdout; | 1135 | char *cp, tmp[MAXPATHLEN], old[MAXPATHLEN]; |
1072 | struct sshkey *pub; | 1136 | int r, fd, oerrno; |
1073 | char *cp, *cp2, *kp, *kp2; | 1137 | struct known_hosts_ctx ctx; |
1074 | char line[16*1024], tmp[PATH_MAX], old[PATH_MAX]; | ||
1075 | int c, skip = 0, inplace = 0, num = 0, invalid = 0, has_unhashed = 0; | ||
1076 | int r, ca, revoked; | ||
1077 | int found_key = 0; | ||
1078 | 1138 | ||
1079 | if (!have_identity) { | 1139 | if (!have_identity) { |
1080 | cp = tilde_expand_filename(_PATH_SSH_USER_HOSTFILE, pw->pw_uid); | 1140 | cp = tilde_expand_filename(_PATH_SSH_USER_HOSTFILE, pw->pw_uid); |
@@ -1084,10 +1144,11 @@ do_known_hosts(struct passwd *pw, const char *name) | |||
1084 | free(cp); | 1144 | free(cp); |
1085 | have_identity = 1; | 1145 | have_identity = 1; |
1086 | } | 1146 | } |
1087 | if ((in = fopen(identity_file, "r")) == NULL) | ||
1088 | fatal("%s: %s: %s", __progname, identity_file, strerror(errno)); | ||
1089 | 1147 | ||
1090 | /* XXX this code is a mess; refactor -djm */ | 1148 | memset(&ctx, 0, sizeof(ctx)); |
1149 | ctx.out = stdout; | ||
1150 | ctx.host = name; | ||
1151 | |||
1091 | /* | 1152 | /* |
1092 | * Find hosts goes to stdout, hash and deletions happen in-place | 1153 | * Find hosts goes to stdout, hash and deletions happen in-place |
1093 | * A corner case is ssh-keygen -HF foo, which should go to stdout | 1154 | * A corner case is ssh-keygen -HF foo, which should go to stdout |
@@ -1099,184 +1160,39 @@ do_known_hosts(struct passwd *pw, const char *name) | |||
1099 | strlcat(old, ".old", sizeof(old)) >= sizeof(old)) | 1160 | strlcat(old, ".old", sizeof(old)) >= sizeof(old)) |
1100 | fatal("known_hosts path too long"); | 1161 | fatal("known_hosts path too long"); |
1101 | umask(077); | 1162 | umask(077); |
1102 | if ((c = mkstemp(tmp)) == -1) | 1163 | if ((fd = mkstemp(tmp)) == -1) |
1103 | fatal("mkstemp: %s", strerror(errno)); | 1164 | fatal("mkstemp: %s", strerror(errno)); |
1104 | if ((out = fdopen(c, "w")) == NULL) { | 1165 | if ((ctx.out = fdopen(fd, "w")) == NULL) { |
1105 | c = errno; | 1166 | oerrno = errno; |
1106 | unlink(tmp); | 1167 | unlink(tmp); |
1107 | fatal("fdopen: %s", strerror(c)); | 1168 | fatal("fdopen: %s", strerror(oerrno)); |
1108 | } | 1169 | } |
1109 | inplace = 1; | 1170 | ctx.inplace = 1; |
1110 | } | 1171 | } |
1111 | 1172 | ||
1112 | while (fgets(line, sizeof(line), in)) { | 1173 | /* XXX support identity_file == "-" for stdin */ |
1113 | if ((cp = strchr(line, '\n')) == NULL) { | 1174 | if ((r = hostkeys_foreach(identity_file, |
1114 | error("line %d too long: %.40s...", num + 1, line); | 1175 | hash_hosts ? known_hosts_hash : known_hosts_find_delete, &ctx, |
1115 | skip = 1; | 1176 | name, find_host ? HKF_WANT_MATCH_HOST : 0)) != 0) |
1116 | invalid = 1; | 1177 | fatal("%s: hostkeys_foreach failed: %s", __func__, ssh_err(r)); |
1117 | continue; | ||
1118 | } | ||
1119 | num++; | ||
1120 | if (skip) { | ||
1121 | skip = 0; | ||
1122 | continue; | ||
1123 | } | ||
1124 | *cp = '\0'; | ||
1125 | |||
1126 | /* Skip leading whitespace, empty and comment lines. */ | ||
1127 | for (cp = line; *cp == ' ' || *cp == '\t'; cp++) | ||
1128 | ; | ||
1129 | if (!*cp || *cp == '\n' || *cp == '#') { | ||
1130 | if (inplace) | ||
1131 | fprintf(out, "%s\n", cp); | ||
1132 | continue; | ||
1133 | } | ||
1134 | /* Check whether this is a CA key or revocation marker */ | ||
1135 | if (strncasecmp(cp, CA_MARKER, sizeof(CA_MARKER) - 1) == 0 && | ||
1136 | (cp[sizeof(CA_MARKER) - 1] == ' ' || | ||
1137 | cp[sizeof(CA_MARKER) - 1] == '\t')) { | ||
1138 | ca = 1; | ||
1139 | cp += sizeof(CA_MARKER); | ||
1140 | } else | ||
1141 | ca = 0; | ||
1142 | if (strncasecmp(cp, REVOKE_MARKER, | ||
1143 | sizeof(REVOKE_MARKER) - 1) == 0 && | ||
1144 | (cp[sizeof(REVOKE_MARKER) - 1] == ' ' || | ||
1145 | cp[sizeof(REVOKE_MARKER) - 1] == '\t')) { | ||
1146 | revoked = 1; | ||
1147 | cp += sizeof(REVOKE_MARKER); | ||
1148 | } else | ||
1149 | revoked = 0; | ||
1150 | |||
1151 | /* Find the end of the host name portion. */ | ||
1152 | for (kp = cp; *kp && *kp != ' ' && *kp != '\t'; kp++) | ||
1153 | ; | ||
1154 | |||
1155 | if (*kp == '\0' || *(kp + 1) == '\0') { | ||
1156 | error("line %d missing key: %.40s...", | ||
1157 | num, line); | ||
1158 | invalid = 1; | ||
1159 | continue; | ||
1160 | } | ||
1161 | *kp++ = '\0'; | ||
1162 | kp2 = kp; | ||
1163 | 1178 | ||
1164 | if ((pub = sshkey_new(KEY_RSA1)) == NULL) | 1179 | if (ctx.inplace) |
1165 | fatal("sshkey_new failed"); | 1180 | fclose(ctx.out); |
1166 | if ((r = sshkey_read(pub, &kp)) != 0) { | ||
1167 | kp = kp2; | ||
1168 | sshkey_free(pub); | ||
1169 | if ((pub = sshkey_new(KEY_UNSPEC)) == NULL) | ||
1170 | fatal("sshkey_new failed"); | ||
1171 | if ((r = sshkey_read(pub, &kp)) != 0) { | ||
1172 | error("line %d invalid key: %.40s...", | ||
1173 | num, line); | ||
1174 | sshkey_free(pub); | ||
1175 | invalid = 1; | ||
1176 | continue; | ||
1177 | } | ||
1178 | } | ||
1179 | 1181 | ||
1180 | if (*cp == HASH_DELIM) { | 1182 | if (ctx.invalid) { |
1181 | if (find_host || delete_host) { | ||
1182 | cp2 = host_hash(name, cp, strlen(cp)); | ||
1183 | if (cp2 == NULL) { | ||
1184 | error("line %d: invalid hashed " | ||
1185 | "name: %.64s...", num, line); | ||
1186 | invalid = 1; | ||
1187 | continue; | ||
1188 | } | ||
1189 | c = (strcmp(cp2, cp) == 0); | ||
1190 | if (find_host && c) { | ||
1191 | if (!quiet) | ||
1192 | printf("# Host %s found: " | ||
1193 | "line %d type %s%s\n", name, | ||
1194 | num, sshkey_type(pub), | ||
1195 | ca ? " (CA key)" : | ||
1196 | revoked? " (revoked)" : ""); | ||
1197 | printhost(out, cp, pub, ca, revoked, 0); | ||
1198 | found_key = 1; | ||
1199 | } | ||
1200 | if (delete_host) { | ||
1201 | if (!c || ca || revoked) { | ||
1202 | printhost(out, cp, pub, | ||
1203 | ca, revoked, 0); | ||
1204 | } else { | ||
1205 | printf("# Host %s found: " | ||
1206 | "line %d type %s\n", name, | ||
1207 | num, sshkey_type(pub)); | ||
1208 | } | ||
1209 | } | ||
1210 | } else if (hash_hosts) | ||
1211 | printhost(out, cp, pub, ca, revoked, 0); | ||
1212 | } else { | ||
1213 | if (find_host || delete_host) { | ||
1214 | c = (match_hostname(name, cp, | ||
1215 | strlen(cp)) == 1); | ||
1216 | if (find_host && c) { | ||
1217 | if (!quiet) | ||
1218 | printf("# Host %s found: " | ||
1219 | "line %d type %s%s\n", name, | ||
1220 | num, sshkey_type(pub), | ||
1221 | ca ? " (CA key)" : ""); | ||
1222 | printhost(out, name, pub, ca, revoked, | ||
1223 | hash_hosts && !(ca || revoked)); | ||
1224 | found_key = 1; | ||
1225 | } | ||
1226 | if (delete_host) { | ||
1227 | if (!c || ca || revoked) { | ||
1228 | printhost(out, cp, pub, | ||
1229 | ca, revoked, 0); | ||
1230 | } else { | ||
1231 | printf("# Host %s found: " | ||
1232 | "line %d type %s\n", name, | ||
1233 | num, sshkey_type(pub)); | ||
1234 | } | ||
1235 | } | ||
1236 | } else if (hash_hosts && (ca || revoked)) { | ||
1237 | /* Don't hash CA and revoked keys' hostnames */ | ||
1238 | printhost(out, cp, pub, ca, revoked, 0); | ||
1239 | has_unhashed = 1; | ||
1240 | } else if (hash_hosts) { | ||
1241 | /* Hash each hostname separately */ | ||
1242 | for (cp2 = strsep(&cp, ","); | ||
1243 | cp2 != NULL && *cp2 != '\0'; | ||
1244 | cp2 = strsep(&cp, ",")) { | ||
1245 | if (strcspn(cp2, "*?!") != | ||
1246 | strlen(cp2)) { | ||
1247 | fprintf(stderr, "Warning: " | ||
1248 | "ignoring host name with " | ||
1249 | "metacharacters: %.64s\n", | ||
1250 | cp2); | ||
1251 | printhost(out, cp2, pub, ca, | ||
1252 | revoked, 0); | ||
1253 | has_unhashed = 1; | ||
1254 | } else { | ||
1255 | printhost(out, cp2, pub, ca, | ||
1256 | revoked, 1); | ||
1257 | } | ||
1258 | } | ||
1259 | } | ||
1260 | } | ||
1261 | sshkey_free(pub); | ||
1262 | } | ||
1263 | fclose(in); | ||
1264 | |||
1265 | if (invalid) { | ||
1266 | fprintf(stderr, "%s is not a valid known_hosts file.\n", | 1183 | fprintf(stderr, "%s is not a valid known_hosts file.\n", |
1267 | identity_file); | 1184 | identity_file); |
1268 | if (inplace) { | 1185 | if (ctx.inplace) { |
1269 | fprintf(stderr, "Not replacing existing known_hosts " | 1186 | fprintf(stderr, "Not replacing existing known_hosts " |
1270 | "file because of errors\n"); | 1187 | "file because of errors\n"); |
1271 | fclose(out); | ||
1272 | unlink(tmp); | 1188 | unlink(tmp); |
1273 | } | 1189 | } |
1274 | exit(1); | 1190 | exit(1); |
1275 | } | 1191 | } else if (delete_host && !ctx.found_key) { |
1276 | 1192 | fprintf(stderr, "Host %s not found in %s\n", | |
1277 | if (inplace) { | 1193 | name, identity_file); |
1278 | fclose(out); | 1194 | unlink(tmp); |
1279 | 1195 | } else if (ctx.inplace) { | |
1280 | /* Backup existing file */ | 1196 | /* Backup existing file */ |
1281 | if (unlink(old) == -1 && errno != ENOENT) | 1197 | if (unlink(old) == -1 && errno != ENOENT) |
1282 | fatal("unlink %.100s: %s", old, strerror(errno)); | 1198 | fatal("unlink %.100s: %s", old, strerror(errno)); |
@@ -1294,7 +1210,7 @@ do_known_hosts(struct passwd *pw, const char *name) | |||
1294 | 1210 | ||
1295 | fprintf(stderr, "%s updated.\n", identity_file); | 1211 | fprintf(stderr, "%s updated.\n", identity_file); |
1296 | fprintf(stderr, "Original contents retained as %s\n", old); | 1212 | fprintf(stderr, "Original contents retained as %s\n", old); |
1297 | if (has_unhashed) { | 1213 | if (ctx.has_unhashed) { |
1298 | fprintf(stderr, "WARNING: %s contains unhashed " | 1214 | fprintf(stderr, "WARNING: %s contains unhashed " |
1299 | "entries\n", old); | 1215 | "entries\n", old); |
1300 | fprintf(stderr, "Delete this file to ensure privacy " | 1216 | fprintf(stderr, "Delete this file to ensure privacy " |
@@ -1302,7 +1218,7 @@ do_known_hosts(struct passwd *pw, const char *name) | |||
1302 | } | 1218 | } |
1303 | } | 1219 | } |
1304 | 1220 | ||
1305 | exit (find_host && !found_key); | 1221 | exit (find_host && !ctx.found_key); |
1306 | } | 1222 | } |
1307 | 1223 | ||
1308 | /* | 1224 | /* |