diff -up openssh/clientloop.c.fingerprint openssh/clientloop.c --- openssh/clientloop.c.fingerprint 2017-09-26 15:21:22.582477729 +0200 +++ openssh/clientloop.c 2017-09-26 15:21:22.620477932 +0200 @@ -1854,7 +1854,7 @@ update_known_hosts(struct hostkeys_updat if (ctx->keys_seen[i] != 2) continue; if ((fp = sshkey_fingerprint(ctx->keys[i], - options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) + options.fingerprint_hash[0], SSH_FP_DEFAULT)) == NULL) fatal("%s: sshkey_fingerprint failed", __func__); do_log2(loglevel, "Learned new hostkey: %s %s", sshkey_type(ctx->keys[i]), fp); @@ -1862,7 +1862,7 @@ update_known_hosts(struct hostkeys_updat } for (i = 0; i < ctx->nold; i++) { if ((fp = sshkey_fingerprint(ctx->old_keys[i], - options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) + options.fingerprint_hash[0], SSH_FP_DEFAULT)) == NULL) fatal("%s: sshkey_fingerprint failed", __func__); do_log2(loglevel, "Deprecating obsolete hostkey: %s %s", sshkey_type(ctx->old_keys[i]), fp); @@ -1905,7 +1905,7 @@ update_known_hosts(struct hostkeys_updat (r = hostfile_replace_entries(options.user_hostfiles[0], ctx->host_str, ctx->ip_str, ctx->keys, ctx->nkeys, options.hash_known_hosts, 0, - options.fingerprint_hash)) != 0) + options.fingerprint_hash[0])) != 0) error("%s: hostfile_replace_entries failed: %s", __func__, ssh_err(r)); } @@ -2038,7 +2038,7 @@ client_input_hostkeys(void) error("%s: parse key: %s", __func__, ssh_err(r)); goto out; } - fp = sshkey_fingerprint(key, options.fingerprint_hash, + fp = sshkey_fingerprint(key, options.fingerprint_hash[0], SSH_FP_DEFAULT); debug3("%s: received %s key %s", __func__, sshkey_type(key), fp); diff -up openssh/readconf.c.fingerprint openssh/readconf.c --- openssh/readconf.c.fingerprint 2017-09-26 15:21:22.618477921 +0200 +++ openssh/readconf.c 2017-09-26 15:21:22.621477937 +0200 @@ -1681,16 +1681,18 @@ parse_keytypes: goto parse_string; case oFingerprintHash: - intptr = &options->fingerprint_hash; - arg = strdelim(&s); - if (!arg || *arg == '\0') - fatal("%.200s line %d: Missing argument.", - filename, linenum); - if ((value = ssh_digest_alg_by_name(arg)) == -1) - fatal("%.200s line %d: Invalid hash algorithm \"%s\".", - filename, linenum, arg); - if (*activep && *intptr == -1) - *intptr = value; + if (*activep && options->num_fingerprint_hash == 0) + while ((arg = strdelim(&s)) != NULL && *arg != '\0') { + value = ssh_digest_alg_by_name(arg); + if (value == -1) + fatal("%s line %d: unknown fingerprints algorithm specs: %s.", + filename, linenum, arg); + if (options->num_fingerprint_hash >= SSH_DIGEST_MAX) + fatal("%s line %d: too many fingerprints algorithm specs.", + filename, linenum); + options->fingerprint_hash[ + options->num_fingerprint_hash++] = value; + } break; case oUpdateHostkeys: @@ -1917,7 +1919,7 @@ initialize_options(Options * options) options->canonicalize_fallback_local = -1; options->canonicalize_hostname = -1; options->revoked_host_keys = NULL; - options->fingerprint_hash = -1; + options->num_fingerprint_hash = 0; options->update_hostkeys = -1; options->hostbased_key_types = NULL; options->pubkey_key_types = NULL; @@ -2096,8 +2098,10 @@ fill_default_options(Options * options) options->canonicalize_fallback_local = 1; if (options->canonicalize_hostname == -1) options->canonicalize_hostname = SSH_CANONICALISE_NO; - if (options->fingerprint_hash == -1) - options->fingerprint_hash = SSH_FP_HASH_DEFAULT; + if (options->num_fingerprint_hash == 0) { + options->fingerprint_hash[options->num_fingerprint_hash++] = SSH_DIGEST_SHA256; + options->fingerprint_hash[options->num_fingerprint_hash++] = SSH_DIGEST_MD5; + } if (options->update_hostkeys == -1) options->update_hostkeys = 0; if (kex_assemble_names(KEX_CLIENT_ENCRYPT, &options->ciphers) != 0 || @@ -2474,6 +2478,17 @@ dump_cfg_strarray(OpCodes code, u_int co } static void +dump_cfg_fmtarray(OpCodes code, u_int count, int *vals) +{ + u_int i; + + printf("%s", lookup_opcode_name(code)); + for (i = 0; i < count; i++) + printf(" %s", fmt_intarg(code, vals[i])); + printf("\n"); +} + +static void dump_cfg_strarray_oneline(OpCodes code, u_int count, char **vals) { u_int i; @@ -2549,7 +2564,6 @@ dump_client_config(Options *o, const cha dump_cfg_fmtint(oEnableSSHKeysign, o->enable_ssh_keysign); dump_cfg_fmtint(oClearAllForwardings, o->clear_forwardings); dump_cfg_fmtint(oExitOnForwardFailure, o->exit_on_forward_failure); - dump_cfg_fmtint(oFingerprintHash, o->fingerprint_hash); dump_cfg_fmtint(oForwardAgent, o->forward_agent); dump_cfg_fmtint(oForwardX11, o->forward_x11); dump_cfg_fmtint(oForwardX11Trusted, o->forward_x11_trusted); @@ -2618,6 +2632,7 @@ dump_client_config(Options *o, const cha dump_cfg_strarray_oneline(oGlobalKnownHostsFile, o->num_system_hostfiles, o->system_hostfiles); dump_cfg_strarray_oneline(oUserKnownHostsFile, o->num_user_hostfiles, o->user_hostfiles); dump_cfg_strarray(oSendEnv, o->num_send_env, o->send_env); + dump_cfg_fmtarray(oFingerprintHash, o->num_fingerprint_hash, o->fingerprint_hash); /* Special cases */ diff -up openssh/readconf.h.fingerprint openssh/readconf.h --- openssh/readconf.h.fingerprint 2017-09-26 15:21:22.618477921 +0200 +++ openssh/readconf.h 2017-09-26 15:21:22.621477937 +0200 @@ -21,6 +21,7 @@ #define MAX_SEND_ENV 256 #define SSH_MAX_HOSTS_FILES 32 #define MAX_CANON_DOMAINS 32 +#define MAX_SSH_DIGESTS 32 #define PATH_MAX_SUN (sizeof((struct sockaddr_un *)0)->sun_path) struct allowed_cname { @@ -157,7 +158,8 @@ typedef struct { char *revoked_host_keys; - int fingerprint_hash; + int num_fingerprint_hash; + int fingerprint_hash[MAX_SSH_DIGESTS]; int update_hostkeys; /* one of SSH_UPDATE_HOSTKEYS_* */ diff -up openssh/ssh_config.5.fingerprint openssh/ssh_config.5 --- openssh/ssh_config.5.fingerprint 2017-09-26 15:21:22.618477921 +0200 +++ openssh/ssh_config.5 2017-09-26 15:21:22.621477937 +0200 @@ -624,12 +624,13 @@ or .Cm no (the default). .It Cm FingerprintHash -Specifies the hash algorithm used when displaying key fingerprints. +Specifies the hash algorithms used when displaying key fingerprints. Valid options are: .Cm md5 and -.Cm sha256 -(the default). +.Cm sha256 . +The default is +.Cm "sha256 md5". .It Cm ForwardAgent Specifies whether the connection to the authentication agent (if any) will be forwarded to the remote machine. diff -up openssh/sshconnect2.c.fingerprint openssh/sshconnect2.c --- openssh/sshconnect2.c.fingerprint 2017-09-26 15:21:22.619477926 +0200 +++ openssh/sshconnect2.c 2017-09-26 15:21:50.677628003 +0200 @@ -679,7 +679,7 @@ input_userauth_pk_ok(int type, u_int32_t key->type, pktype); goto done; } - if ((fp = sshkey_fingerprint(key, options.fingerprint_hash, + if ((fp = sshkey_fingerprint(key, options.fingerprint_hash[0], SSH_FP_DEFAULT)) == NULL) goto done; debug2("input_userauth_pk_ok: fp %s", fp); @@ -1198,7 +1198,7 @@ sign_and_send_pubkey(Authctxt *authctxt, int matched, ret = -1, have_sig = 1; char *fp; - if ((fp = sshkey_fingerprint(id->key, options.fingerprint_hash, + if ((fp = sshkey_fingerprint(id->key, options.fingerprint_hash[0], SSH_FP_DEFAULT)) == NULL) return 0; debug3("%s: %s %s", __func__, key_type(id->key), fp); @@ -1620,7 +1620,7 @@ userauth_pubkey(Authctxt *authctxt) if (id->key != NULL) { if (try_identity(id)) { if ((fp = sshkey_fingerprint(id->key, - options.fingerprint_hash, + options.fingerprint_hash[0], SSH_FP_DEFAULT)) == NULL) { error("%s: sshkey_fingerprint failed", __func__); @@ -1914,7 +1914,7 @@ userauth_hostbased(Authctxt *authctxt) goto out; } - if ((fp = sshkey_fingerprint(private, options.fingerprint_hash, + if ((fp = sshkey_fingerprint(private, options.fingerprint_hash[0], SSH_FP_DEFAULT)) == NULL) { error("%s: sshkey_fingerprint failed", __func__); goto out; diff -up openssh/sshconnect.c.fingerprint openssh/sshconnect.c --- openssh/sshconnect.c.fingerprint 2017-09-25 01:48:10.000000000 +0200 +++ openssh/sshconnect.c 2017-09-26 15:21:22.622477943 +0200 @@ -861,9 +861,9 @@ check_host_key(char *hostname, struct so "of known hosts.", type, ip); } else if (options.visual_host_key) { fp = sshkey_fingerprint(host_key, - options.fingerprint_hash, SSH_FP_DEFAULT); + options.fingerprint_hash[0], SSH_FP_DEFAULT); ra = sshkey_fingerprint(host_key, - options.fingerprint_hash, SSH_FP_RANDOMART); + options.fingerprint_hash[0], SSH_FP_RANDOMART); if (fp == NULL || ra == NULL) fatal("%s: sshkey_fingerprint fail", __func__); logit("Host key fingerprint is %s\n%s", fp, ra); @@ -907,12 +907,6 @@ check_host_key(char *hostname, struct so else snprintf(msg1, sizeof(msg1), "."); /* The default */ - fp = sshkey_fingerprint(host_key, - options.fingerprint_hash, SSH_FP_DEFAULT); - ra = sshkey_fingerprint(host_key, - options.fingerprint_hash, SSH_FP_RANDOMART); - if (fp == NULL || ra == NULL) - fatal("%s: sshkey_fingerprint fail", __func__); msg2[0] = '\0'; if (options.verify_host_key_dns) { if (matching_host_key_dns) @@ -926,16 +920,28 @@ check_host_key(char *hostname, struct so } snprintf(msg, sizeof(msg), "The authenticity of host '%.200s (%s)' can't be " - "established%s\n" - "%s key fingerprint is %s.%s%s\n%s" + "established%s\n", host, ip, msg1); + for (i = 0; i < (u_int) options.num_fingerprint_hash; i++) { + fp = sshkey_fingerprint(host_key, + options.fingerprint_hash[i], SSH_FP_DEFAULT); + ra = sshkey_fingerprint(host_key, + options.fingerprint_hash[i], SSH_FP_RANDOMART); + if (fp == NULL || ra == NULL) + fatal("%s: sshkey_fingerprint fail", __func__); + len = strlen(msg); + snprintf(msg+len, sizeof(msg)-len, + "%s key fingerprint is %s.%s%s\n%s", + type, fp, + options.visual_host_key ? "\n" : "", + options.visual_host_key ? ra : "", + msg2); + free(ra); + free(fp); + } + len = strlen(msg); + snprintf(msg+len, sizeof(msg)-len, "Are you sure you want to continue connecting " - "(yes/no)? ", - host, ip, msg1, type, fp, - options.visual_host_key ? "\n" : "", - options.visual_host_key ? ra : "", - msg2); - free(ra); - free(fp); + "(yes/no)? "); if (!confirm(msg)) goto fail; hostkey_trusted = 1; /* user explicitly confirmed */ @@ -1192,7 +1198,7 @@ verify_host_key(char *host, struct socka struct sshkey *plain = NULL; if ((fp = sshkey_fingerprint(host_key, - options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) { + options.fingerprint_hash[0], SSH_FP_DEFAULT)) == NULL) { error("%s: fingerprint host key: %s", __func__, ssh_err(r)); r = -1; goto out; @@ -1200,7 +1206,7 @@ verify_host_key(char *host, struct socka if (sshkey_is_cert(host_key)) { if ((cafp = sshkey_fingerprint(host_key->cert->signature_key, - options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) { + options.fingerprint_hash[0], SSH_FP_DEFAULT)) == NULL) { error("%s: fingerprint CA key: %s", __func__, ssh_err(r)); r = -1; @@ -1369,9 +1375,9 @@ show_other_keys(struct hostkeys *hostkey if (!lookup_key_in_hostkeys_by_type(hostkeys, type[i], &found)) continue; fp = sshkey_fingerprint(found->key, - options.fingerprint_hash, SSH_FP_DEFAULT); + options.fingerprint_hash[0], SSH_FP_DEFAULT); ra = sshkey_fingerprint(found->key, - options.fingerprint_hash, SSH_FP_RANDOMART); + options.fingerprint_hash[0], SSH_FP_RANDOMART); if (fp == NULL || ra == NULL) fatal("%s: sshkey_fingerprint fail", __func__); logit("WARNING: %s key found for host %s\n" @@ -1394,7 +1400,7 @@ warn_changed_key(struct sshkey *host_key { char *fp; - fp = sshkey_fingerprint(host_key, options.fingerprint_hash, + fp = sshkey_fingerprint(host_key, options.fingerprint_hash[0], SSH_FP_DEFAULT); if (fp == NULL) fatal("%s: sshkey_fingerprint fail", __func__); diff -up openssh/ssh-keysign.c.fingerprint openssh/ssh-keysign.c --- openssh/ssh-keysign.c.fingerprint 2017-09-25 01:48:10.000000000 +0200 +++ openssh/ssh-keysign.c 2017-09-26 15:21:22.622477943 +0200 @@ -285,7 +285,7 @@ main(int argc, char **argv) } } if (!found) { - if ((fp = sshkey_fingerprint(key, options.fingerprint_hash, + if ((fp = sshkey_fingerprint(key, options.fingerprint_hash[0], SSH_FP_DEFAULT)) == NULL) fatal("%s: sshkey_fingerprint failed", __progname); fatal("no matching hostkey found for key %s %s",