diff -Naur -x '*.rej' -x '*.orig' -x '*~' openssh-7.3p1/clientloop.c openssh-7.3p1-show-more-fingerprints/clientloop.c --- openssh-7.3p1/clientloop.c 2016-07-28 00:54:27.000000000 +0200 +++ openssh-7.3p1-show-more-fingerprints/clientloop.c 2016-08-07 19:27:25.650122678 +0200 @@ -2235,7 +2235,7 @@ 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); @@ -2243,7 +2243,7 @@ } 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); @@ -2286,7 +2286,7 @@ (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)); } @@ -2399,7 +2399,7 @@ 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 -Naur -x '*.rej' -x '*.orig' -x '*~' openssh-7.3p1/readconf.c openssh-7.3p1-show-more-fingerprints/readconf.c --- openssh-7.3p1/readconf.c 2016-07-28 00:54:27.000000000 +0200 +++ openssh-7.3p1-show-more-fingerprints/readconf.c 2016-08-07 19:27:25.651122668 +0200 @@ -1623,16 +1623,18 @@ 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: @@ -1854,7 +1856,7 @@ 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; @@ -2041,8 +2043,10 @@ 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 || @@ -2428,6 +2432,17 @@ } 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; @@ -2503,7 +2518,6 @@ 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); @@ -2573,6 +2587,7 @@ 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 -Naur -x '*.rej' -x '*.orig' -x '*~' openssh-7.3p1/readconf.h openssh-7.3p1-show-more-fingerprints/readconf.h --- openssh-7.3p1/readconf.h 2016-07-28 00:54:27.000000000 +0200 +++ openssh-7.3p1-show-more-fingerprints/readconf.h 2016-08-07 19:27:25.651122668 +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 { @@ -156,7 +157,8 @@ 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 -Naur -x '*.rej' -x '*.orig' -x '*~' openssh-7.3p1/ssh_config.5 openssh-7.3p1-show-more-fingerprints/ssh_config.5 --- openssh-7.3p1/ssh_config.5 2016-07-28 00:54:27.000000000 +0200 +++ openssh-7.3p1-show-more-fingerprints/ssh_config.5 2016-08-07 19:27:25.651122668 +0200 @@ -725,13 +725,13 @@ The default is .Dq no . .It Cm FingerprintHash -Specifies the hash algorithm used when displaying key fingerprints. +Specifies the hash algorithms used when displaying key fingerprints. Valid options are: .Dq md5 and .Dq sha256 . The default is -.Dq sha256 . +.Dq "sha256 md5". .It Cm ForwardAgent Specifies whether the connection to the authentication agent (if any) will be forwarded to the remote machine. diff -Naur -x '*.rej' -x '*.orig' -x '*~' openssh-7.3p1/sshconnect2.c openssh-7.3p1-show-more-fingerprints/sshconnect2.c --- openssh-7.3p1/sshconnect2.c 2016-07-28 00:54:27.000000000 +0200 +++ openssh-7.3p1-show-more-fingerprints/sshconnect2.c 2016-08-07 19:27:25.652122658 +0200 @@ -612,7 +612,7 @@ 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); @@ -1050,7 +1050,7 @@ 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); @@ -1732,7 +1732,7 @@ 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 -Naur -x '*.rej' -x '*.orig' -x '*~' openssh-7.3p1/sshconnect.c openssh-7.3p1-show-more-fingerprints/sshconnect.c --- openssh-7.3p1/sshconnect.c 2016-07-28 00:54:27.000000000 +0200 +++ openssh-7.3p1-show-more-fingerprints/sshconnect.c 2016-08-07 19:27:25.654122639 +0200 @@ -923,9 +923,9 @@ "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); @@ -967,12 +967,6 @@ 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) @@ -986,16 +980,28 @@ } 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 < 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 */ @@ -1245,7 +1251,7 @@ 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; @@ -1253,7 +1259,7 @@ 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; @@ -1433,9 +1439,9 @@ 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" @@ -1458,7 +1464,7 @@ { 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 -Naur -x '*.rej' -x '*.orig' -x '*~' openssh-7.3p1/ssh-keysign.c openssh-7.3p1-show-more-fingerprints/ssh-keysign.c --- openssh-7.3p1/ssh-keysign.c 2016-07-28 00:54:27.000000000 +0200 +++ openssh-7.3p1-show-more-fingerprints/ssh-keysign.c 2016-08-07 19:27:25.652122658 +0200 @@ -285,7 +285,7 @@ } } 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",