summaryrefslogtreecommitdiff
path: root/ssh-keygen.c
diff options
context:
space:
mode:
authordjm@openbsd.org <djm@openbsd.org>2015-01-18 21:49:42 +0000
committerDamien Miller <djm@mindrot.org>2015-01-20 00:20:44 +1100
commitcecb30bc2ba6d594366e657d664d5c494b6c8a7f (patch)
treef0f904dcbf01f5d5ac869b58f7d20391dec6c320 /ssh-keygen.c
parentec3d065df3a9557ea96b02d061fd821a18c1a0b9 (diff)
upstream commit
make ssh-keygen use hostkeys_foreach(). Removes some horrendous code; ok markus@
Diffstat (limited to 'ssh-keygen.c')
-rw-r--r--ssh-keygen.c326
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
1034static void 1034struct known_hosts_ctx {
1035printhost(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
1040static int
1041known_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
1084static int
1085known_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
1068static void 1132static void
1069do_known_hosts(struct passwd *pw, const char *name) 1133do_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/*