From c0026960b4e3f57fd5593d273a27dd65c8ac5066 Mon Sep 17 00:00:00 2001 From: djm Date: Sun, 18 Jan 2015 21:49:42 +0000 Subject: [PATCH] make ssh-keygen use hostkeys_foreach(). Removes some horrendous code; ok markus@ --- usr.bin/ssh/ssh-keygen.c | 326 +++++++++++++++------------------------ 1 file changed, 121 insertions(+), 205 deletions(-) diff --git a/usr.bin/ssh/ssh-keygen.c b/usr.bin/ssh/ssh-keygen.c index bcbd5538122..64519d23568 100644 --- a/usr.bin/ssh/ssh-keygen.c +++ b/usr.bin/ssh/ssh-keygen.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-keygen.c,v 1.255 2015/01/18 13:22:28 djm Exp $ */ +/* $OpenBSD: ssh-keygen.c,v 1.256 2015/01/18 21:49:42 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1994 Tatu Ylonen , Espoo, Finland @@ -1012,50 +1012,110 @@ do_gen_all_hostkeys(struct passwd *pw) printf("\n"); } -static void -printhost(FILE *f, const char *name, struct sshkey *public, - int ca, int revoked, int hash) +struct known_hosts_ctx { + FILE *out; + const char *host; + int has_unhashed, found_key, inplace, invalid; +}; + +static int +known_hosts_hash(struct hostkey_foreach_line *l, void *_ctx) { - if (print_fingerprint) { - enum sshkey_fp_rep rep; - int fptype; - char *fp, *ra; - - fptype = print_bubblebabble ? - SSH_DIGEST_SHA1 : fingerprint_hash; - rep = print_bubblebabble ? SSH_FP_BUBBLEBABBLE : SSH_FP_DEFAULT; - fp = sshkey_fingerprint(public, fptype, rep); - ra = sshkey_fingerprint(public, fingerprint_hash, - SSH_FP_RANDOMART); - printf("%u %s %s (%s)\n", sshkey_size(public), fp, name, - sshkey_type(public)); - if (log_level >= SYSLOG_LEVEL_VERBOSE) - printf("%s\n", ra); - free(ra); - free(fp); - } else { - int r; + struct known_hosts_ctx *ctx = (struct known_hosts_ctx *)_ctx; + char *hashed, *cp, *hosts, *ohosts; + int has_wild = l->hosts && strcspn(l->hosts, "*?!") != strlen(l->hosts); - if (hash && (name = host_hash(name, NULL, 0)) == NULL) + /* Retain invalid lines when hashing, but mark file as invalid. */ + if (l->status == HKF_STATUS_INVALID) { + ctx->invalid = 1; + fprintf(stderr, "%s:%ld: invalid line\n", l->path, l->linenum); + fprintf(ctx->out, "%s\n", l->line); + return 0; + } + + /* + * Don't hash hosts already already hashed, with wildcard characters + * or a CA/revocation marker. + */ + if (l->was_hashed || has_wild || l->marker != MRK_NONE) { + fprintf(ctx->out, "%s\n", l->line); + if (has_wild && !find_host) { + fprintf(stderr, "%s:%ld: ignoring host name " + "with wildcard: %.64s\n", l->path, + l->linenum, l->hosts); + ctx->has_unhashed = 1; + } + return 0; + } + /* + * Split any comma-separated hostnames from the host list, + * hash and store separately. + */ + ohosts = hosts = xstrdup(l->hosts); + while ((cp = strsep(&hosts, ",")) != NULL && *cp != '\0') { + if ((hashed = host_hash(cp, NULL, 0)) == NULL) fatal("hash_host failed"); - fprintf(f, "%s%s%s ", ca ? CA_MARKER " " : "", - revoked ? REVOKE_MARKER " " : "" , name); - if ((r = sshkey_write(public, f)) != 0) - fatal("key_write failed: %s", ssh_err(r)); - fprintf(f, "\n"); + fprintf(ctx->out, "%s %s\n", hashed, l->rawkey); + ctx->has_unhashed = 1; } + free(ohosts); + return 0; +} + +static int +known_hosts_find_delete(struct hostkey_foreach_line *l, void *_ctx) +{ + struct known_hosts_ctx *ctx = (struct known_hosts_ctx *)_ctx; + + if (l->status == HKF_STATUS_HOST_MATCHED) { + if (delete_host) { + if (l->marker != MRK_NONE) { + /* Don't remove CA and revocation lines */ + fprintf(ctx->out, "%s\n", l->line); + } else { + /* + * Hostname matches and has no CA/revoke + * marker, delete it by *not* writing the + * line to ctx->out. + */ + ctx->found_key = 1; + if (!quiet) + printf("# Host %s found: line %ld\n", + ctx->host, l->linenum); + } + return 0; + } else if (find_host) { + ctx->found_key = 1; + if (!quiet) { + printf("# Host %s found: line %ld %s\n", + ctx->host, + l->linenum, l->marker == MRK_CA ? "CA" : + (l->marker == MRK_REVOKE ? "REVOKED" : "")); + } + if (hash_hosts) + known_hosts_hash(l, ctx); + else + fprintf(ctx->out, "%s\n", l->line); + return 0; + } + } else if (delete_host) { + /* Retain non-matching hosts when deleting */ + if (l->status == HKF_STATUS_INVALID) { + ctx->invalid = 1; + fprintf(stderr, "%s:%ld: invalid line\n", + l->path, l->linenum); + } + fprintf(ctx->out, "%s\n", l->line); + } + return 0; } static void do_known_hosts(struct passwd *pw, const char *name) { - FILE *in, *out = stdout; - struct sshkey *pub; - char *cp, *cp2, *kp, *kp2; - char line[16*1024], tmp[PATH_MAX], old[PATH_MAX]; - int c, skip = 0, inplace = 0, num = 0, invalid = 0, has_unhashed = 0; - int r, ca, revoked; - int found_key = 0; + char *cp, tmp[MAXPATHLEN], old[MAXPATHLEN]; + int r, fd, oerrno; + struct known_hosts_ctx ctx; if (!have_identity) { cp = tilde_expand_filename(_PATH_SSH_USER_HOSTFILE, pw->pw_uid); @@ -1065,10 +1125,11 @@ do_known_hosts(struct passwd *pw, const char *name) free(cp); have_identity = 1; } - if ((in = fopen(identity_file, "r")) == NULL) - fatal("%s: %s: %s", __progname, identity_file, strerror(errno)); - /* XXX this code is a mess; refactor -djm */ + memset(&ctx, 0, sizeof(ctx)); + ctx.out = stdout; + ctx.host = name; + /* * Find hosts goes to stdout, hash and deletions happen in-place * A corner case is ssh-keygen -HF foo, which should go to stdout @@ -1080,184 +1141,39 @@ do_known_hosts(struct passwd *pw, const char *name) strlcat(old, ".old", sizeof(old)) >= sizeof(old)) fatal("known_hosts path too long"); umask(077); - if ((c = mkstemp(tmp)) == -1) + if ((fd = mkstemp(tmp)) == -1) fatal("mkstemp: %s", strerror(errno)); - if ((out = fdopen(c, "w")) == NULL) { - c = errno; + if ((ctx.out = fdopen(fd, "w")) == NULL) { + oerrno = errno; unlink(tmp); - fatal("fdopen: %s", strerror(c)); + fatal("fdopen: %s", strerror(oerrno)); } - inplace = 1; + ctx.inplace = 1; } - while (fgets(line, sizeof(line), in)) { - if ((cp = strchr(line, '\n')) == NULL) { - error("line %d too long: %.40s...", num + 1, line); - skip = 1; - invalid = 1; - continue; - } - num++; - if (skip) { - skip = 0; - continue; - } - *cp = '\0'; - - /* Skip leading whitespace, empty and comment lines. */ - for (cp = line; *cp == ' ' || *cp == '\t'; cp++) - ; - if (!*cp || *cp == '\n' || *cp == '#') { - if (inplace) - fprintf(out, "%s\n", cp); - continue; - } - /* Check whether this is a CA key or revocation marker */ - if (strncasecmp(cp, CA_MARKER, sizeof(CA_MARKER) - 1) == 0 && - (cp[sizeof(CA_MARKER) - 1] == ' ' || - cp[sizeof(CA_MARKER) - 1] == '\t')) { - ca = 1; - cp += sizeof(CA_MARKER); - } else - ca = 0; - if (strncasecmp(cp, REVOKE_MARKER, - sizeof(REVOKE_MARKER) - 1) == 0 && - (cp[sizeof(REVOKE_MARKER) - 1] == ' ' || - cp[sizeof(REVOKE_MARKER) - 1] == '\t')) { - revoked = 1; - cp += sizeof(REVOKE_MARKER); - } else - revoked = 0; - - /* Find the end of the host name portion. */ - for (kp = cp; *kp && *kp != ' ' && *kp != '\t'; kp++) - ; - - if (*kp == '\0' || *(kp + 1) == '\0') { - error("line %d missing key: %.40s...", - num, line); - invalid = 1; - continue; - } - *kp++ = '\0'; - kp2 = kp; + /* XXX support identity_file == "-" for stdin */ + if ((r = hostkeys_foreach(identity_file, + hash_hosts ? known_hosts_hash : known_hosts_find_delete, &ctx, + name, find_host ? HKF_WANT_MATCH_HOST : 0)) != 0) + fatal("%s: hostkeys_foreach failed: %s", __func__, ssh_err(r)); - if ((pub = sshkey_new(KEY_RSA1)) == NULL) - fatal("sshkey_new failed"); - if ((r = sshkey_read(pub, &kp)) != 0) { - kp = kp2; - sshkey_free(pub); - if ((pub = sshkey_new(KEY_UNSPEC)) == NULL) - fatal("sshkey_new failed"); - if ((r = sshkey_read(pub, &kp)) != 0) { - error("line %d invalid key: %.40s...", - num, line); - sshkey_free(pub); - invalid = 1; - continue; - } - } + if (ctx.inplace) + fclose(ctx.out); - if (*cp == HASH_DELIM) { - if (find_host || delete_host) { - cp2 = host_hash(name, cp, strlen(cp)); - if (cp2 == NULL) { - error("line %d: invalid hashed " - "name: %.64s...", num, line); - invalid = 1; - continue; - } - c = (strcmp(cp2, cp) == 0); - if (find_host && c) { - if (!quiet) - printf("# Host %s found: " - "line %d type %s%s\n", name, - num, sshkey_type(pub), - ca ? " (CA key)" : - revoked? " (revoked)" : ""); - printhost(out, cp, pub, ca, revoked, 0); - found_key = 1; - } - if (delete_host) { - if (!c || ca || revoked) { - printhost(out, cp, pub, - ca, revoked, 0); - } else { - printf("# Host %s found: " - "line %d type %s\n", name, - num, sshkey_type(pub)); - } - } - } else if (hash_hosts) - printhost(out, cp, pub, ca, revoked, 0); - } else { - if (find_host || delete_host) { - c = (match_hostname(name, cp, - strlen(cp)) == 1); - if (find_host && c) { - if (!quiet) - printf("# Host %s found: " - "line %d type %s%s\n", name, - num, sshkey_type(pub), - ca ? " (CA key)" : ""); - printhost(out, name, pub, ca, revoked, - hash_hosts && !(ca || revoked)); - found_key = 1; - } - if (delete_host) { - if (!c || ca || revoked) { - printhost(out, cp, pub, - ca, revoked, 0); - } else { - printf("# Host %s found: " - "line %d type %s\n", name, - num, sshkey_type(pub)); - } - } - } else if (hash_hosts && (ca || revoked)) { - /* Don't hash CA and revoked keys' hostnames */ - printhost(out, cp, pub, ca, revoked, 0); - has_unhashed = 1; - } else if (hash_hosts) { - /* Hash each hostname separately */ - for (cp2 = strsep(&cp, ","); - cp2 != NULL && *cp2 != '\0'; - cp2 = strsep(&cp, ",")) { - if (strcspn(cp2, "*?!") != - strlen(cp2)) { - fprintf(stderr, "Warning: " - "ignoring host name with " - "metacharacters: %.64s\n", - cp2); - printhost(out, cp2, pub, ca, - revoked, 0); - has_unhashed = 1; - } else { - printhost(out, cp2, pub, ca, - revoked, 1); - } - } - } - } - sshkey_free(pub); - } - fclose(in); - - if (invalid) { + if (ctx.invalid) { fprintf(stderr, "%s is not a valid known_hosts file.\n", identity_file); - if (inplace) { + if (ctx.inplace) { fprintf(stderr, "Not replacing existing known_hosts " "file because of errors\n"); - fclose(out); unlink(tmp); } exit(1); - } - - if (inplace) { - fclose(out); - + } else if (delete_host && !ctx.found_key) { + fprintf(stderr, "Host %s not found in %s\n", + name, identity_file); + unlink(tmp); + } else if (ctx.inplace) { /* Backup existing file */ if (unlink(old) == -1 && errno != ENOENT) fatal("unlink %.100s: %s", old, strerror(errno)); @@ -1275,7 +1191,7 @@ do_known_hosts(struct passwd *pw, const char *name) fprintf(stderr, "%s updated.\n", identity_file); fprintf(stderr, "Original contents retained as %s\n", old); - if (has_unhashed) { + if (ctx.has_unhashed) { fprintf(stderr, "WARNING: %s contains unhashed " "entries\n", old); fprintf(stderr, "Delete this file to ensure privacy " @@ -1283,7 +1199,7 @@ do_known_hosts(struct passwd *pw, const char *name) } } - exit (find_host && !found_key); + exit (find_host && !ctx.found_key); } /* -- 2.20.1