From e1d58c44bd911e5ee4dddb6205e16eb9a03cc736 Mon Sep 17 00:00:00 2001 From: Jakub Jelen Date: Fri, 7 Aug 2015 10:18:54 +0200 Subject: [PATCH] Possibility tu specify more fingerprint algorithms on client side for smother transition --- clientloop.c | 8 ++++---- readconf.c | 43 +++++++++++++++++++++++++++++-------------- readconf.h | 4 +++- ssh_config.5 | 4 ++-- sshconnect.c | 48 +++++++++++++++++++++++++++--------------------- sshconnect2.c | 6 +++--- 6 files changed, 68 insertions(+), 45 deletions(-) diff --git a/clientloop.c b/clientloop.c index 87ceb3d..4553114 100644 --- a/clientloop.c +++ b/clientloop.c @@ -2194,7 +2194,7 @@ update_known_hosts(struct hostkeys_update_ctx *ctx) 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); @@ -2202,7 +2202,7 @@ update_known_hosts(struct hostkeys_update_ctx *ctx) } 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); @@ -2245,7 +2245,7 @@ update_known_hosts(struct hostkeys_update_ctx *ctx) (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)); } @@ -2358,7 +2358,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 --git a/readconf.c b/readconf.c index 1d03bdf..6af4c62 100644 --- a/readconf.c +++ b/readconf.c @@ -1471,16 +1471,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: @@ -1673,7 +1675,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; @@ -1851,8 +1853,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 || @@ -2189,6 +2193,17 @@ dump_cfg_strarray(OpCodes code, u_int count, char **vals) } 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; @@ -2259,7 +2274,6 @@ dump_client_config(Options *o, const char *host) dump_cfg_fmtint(oControlMaster, o->control_master); dump_cfg_fmtint(oEnableSSHKeysign, o->enable_ssh_keysign); 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); @@ -2328,6 +2342,7 @@ dump_client_config(Options *o, const char *host) 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 --git a/readconf.h b/readconf.h index bb2d552..d817f92 100644 --- a/readconf.h +++ b/readconf.h @@ -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 { @@ -146,7 +147,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 --git a/ssh_config.5 b/ssh_config.5 index 5b0975f..e8e6458 100644 --- a/ssh_config.5 +++ b/ssh_config.5 @@ -647,13 +647,13 @@ or 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 --git a/sshconnect.c b/sshconnect.c index f41960c..e12932f 100644 --- a/sshconnect.c +++ b/sshconnect.c @@ -920,9 +920,9 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, "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); @@ -964,12 +964,6 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, 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) @@ -983,16 +977,28 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, } 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 */ @@ -1241,7 +1247,7 @@ verify_host_key(char *host, struct sockaddr *hostaddr, Key *host_key) 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; @@ -1405,9 +1411,9 @@ show_other_keys(struct hostkeys *hostkeys, Key *key) 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" @@ -1430,7 +1436,7 @@ warn_changed_key(Key *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 --git a/sshconnect2.c b/sshconnect2.c index 7751031..82ed92e 100644 --- a/sshconnect2.c +++ b/sshconnect2.c @@ -589,7 +589,7 @@ input_userauth_pk_ok(int type, u_int32_t seq, void *ctxt) 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); @@ -1009,7 +1009,7 @@ sign_and_send_pubkey(Authctxt *authctxt, Identity *id) 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); @@ -1635,7 +1635,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 --git a/ssh-keysign.c b/ssh-keysign.c index 1dca3e2..23bff7d 100644 --- a/ssh-keysign.c +++ b/ssh-keysign.c @@ -275,7 +275,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", -- 2.1.0 diff --git a/sshconnect.c b/sshconnect.c index de7ace6..f16e606 100644 --- a/sshconnect.c +++ b/sshconnect.c @@ -1262,7 +1262,7 @@ verify_host_key(char *host, struct sockaddr *hostaddr, Key *host_key) 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;