From 3dbedef4011325ea7a4efc52a92720f76ce10a1d Mon Sep 17 00:00:00 2001 From: djm Date: Sun, 21 Dec 2014 22:27:55 +0000 Subject: [PATCH] Add FingerprintHash option to control algorithm used for key fingerprints. Default changes from MD5 to SHA256 and format from hex to base64. Feedback and ok naddy@ markus@ --- usr.bin/ssh/auth-rsa.c | 5 +- usr.bin/ssh/auth.c | 5 +- usr.bin/ssh/auth2-hostbased.c | 7 ++- usr.bin/ssh/auth2-pubkey.c | 16 ++--- usr.bin/ssh/digest-libc.c | 22 ++++++- usr.bin/ssh/digest-openssl.c | 22 ++++++- usr.bin/ssh/digest.h | 8 ++- usr.bin/ssh/dns.c | 11 ++-- usr.bin/ssh/key.c | 7 +-- usr.bin/ssh/key.h | 4 +- usr.bin/ssh/krl.c | 8 ++- usr.bin/ssh/readconf.c | 24 ++++++- usr.bin/ssh/readconf.h | 4 +- usr.bin/ssh/servconf.c | 24 ++++++- usr.bin/ssh/servconf.h | 4 +- usr.bin/ssh/ssh-add.1 | 13 +++- usr.bin/ssh/ssh-add.c | 54 ++++++++++++---- usr.bin/ssh/ssh-agent.1 | 13 +++- usr.bin/ssh/ssh-agent.c | 15 +++-- usr.bin/ssh/ssh-keygen.1 | 13 +++- usr.bin/ssh/ssh-keygen.c | 58 ++++++++++------- usr.bin/ssh/ssh-keysign.c | 5 +- usr.bin/ssh/ssh.1 | 6 +- usr.bin/ssh/sshconnect.c | 27 ++++---- usr.bin/ssh/sshconnect2.c | 6 +- usr.bin/ssh/sshd_config.5 | 13 +++- usr.bin/ssh/sshkey.c | 114 ++++++++++++++++++++++------------ usr.bin/ssh/sshkey.h | 16 +++-- 28 files changed, 375 insertions(+), 149 deletions(-) diff --git a/usr.bin/ssh/auth-rsa.c b/usr.bin/ssh/auth-rsa.c index d736d5a3231..76546b85bd6 100644 --- a/usr.bin/ssh/auth-rsa.c +++ b/usr.bin/ssh/auth-rsa.c @@ -1,4 +1,4 @@ -/* $OpenBSD: auth-rsa.c,v 1.88 2014/07/15 15:54:14 millert Exp $ */ +/* $OpenBSD: auth-rsa.c,v 1.89 2014/12/21 22:27:56 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -233,7 +233,8 @@ rsa_key_allowed_in_file(struct passwd *pw, char *file, "actual %d vs. announced %d.", file, linenum, BN_num_bits(key->rsa->n), bits); - fp = key_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX); + fp = key_fingerprint(key, options.fingerprint_hash, + SSH_FP_DEFAULT); debug("matching key found: file %s, line %lu %s %s", file, linenum, key_type(key), fp); free(fp); diff --git a/usr.bin/ssh/auth.c b/usr.bin/ssh/auth.c index 4f823defbd3..e27944d0929 100644 --- a/usr.bin/ssh/auth.c +++ b/usr.bin/ssh/auth.c @@ -1,4 +1,4 @@ -/* $OpenBSD: auth.c,v 1.107 2014/12/04 02:24:32 djm Exp $ */ +/* $OpenBSD: auth.c,v 1.108 2014/12/21 22:27:56 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * @@ -552,7 +552,8 @@ auth_key_is_revoked(Key *key) if (options.revoked_keys_file == NULL) return 0; - if ((fp = sshkey_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX)) == NULL) { + if ((fp = sshkey_fingerprint(key, options.fingerprint_hash, + SSH_FP_DEFAULT)) == NULL) { r = SSH_ERR_ALLOC_FAIL; error("%s: fingerprint key: %s", __func__, ssh_err(r)); goto out; diff --git a/usr.bin/ssh/auth2-hostbased.c b/usr.bin/ssh/auth2-hostbased.c index 17d382ab355..2bd8fcb38a2 100644 --- a/usr.bin/ssh/auth2-hostbased.c +++ b/usr.bin/ssh/auth2-hostbased.c @@ -1,4 +1,4 @@ -/* $OpenBSD: auth2-hostbased.c,v 1.18 2014/07/15 15:54:14 millert Exp $ */ +/* $OpenBSD: auth2-hostbased.c,v 1.19 2014/12/21 22:27:56 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * @@ -207,13 +207,14 @@ hostbased_key_allowed(struct passwd *pw, const char *cuser, char *chost, if (host_status == HOST_OK) { if (key_is_cert(key)) { fp = key_fingerprint(key->cert->signature_key, - SSH_FP_MD5, SSH_FP_HEX); + options.fingerprint_hash, SSH_FP_DEFAULT); verbose("Accepted certificate ID \"%s\" signed by " "%s CA %s from %s@%s", key->cert->key_id, key_type(key->cert->signature_key), fp, cuser, lookup); } else { - fp = key_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX); + fp = key_fingerprint(key, options.fingerprint_hash, + SSH_FP_DEFAULT); verbose("Accepted %s public key %s from %s@%s", key_type(key), fp, cuser, lookup); } diff --git a/usr.bin/ssh/auth2-pubkey.c b/usr.bin/ssh/auth2-pubkey.c index 69a319f7663..6fec8e5bdfb 100644 --- a/usr.bin/ssh/auth2-pubkey.c +++ b/usr.bin/ssh/auth2-pubkey.c @@ -1,4 +1,4 @@ -/* $OpenBSD: auth2-pubkey.c,v 1.42 2014/12/04 02:24:32 djm Exp $ */ +/* $OpenBSD: auth2-pubkey.c,v 1.43 2014/12/21 22:27:56 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * @@ -210,7 +210,7 @@ pubkey_auth_info(Authctxt *authctxt, const Key *key, const char *fmt, ...) if (key_is_cert(key)) { fp = key_fingerprint(key->cert->signature_key, - SSH_FP_MD5, SSH_FP_HEX); + options.fingerprint_hash, SSH_FP_DEFAULT); auth_info(authctxt, "%s ID %s (serial %llu) CA %s %s%s%s", key_type(key), key->cert->key_id, (unsigned long long)key->cert->serial, @@ -218,7 +218,8 @@ pubkey_auth_info(Authctxt *authctxt, const Key *key, const char *fmt, ...) extra == NULL ? "" : ", ", extra == NULL ? "" : extra); free(fp); } else { - fp = key_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX); + fp = key_fingerprint(key, options.fingerprint_hash, + SSH_FP_DEFAULT); auth_info(authctxt, "%s %s%s%s", key_type(key), fp, extra == NULL ? "" : ", ", extra == NULL ? "" : extra); free(fp); @@ -362,8 +363,8 @@ check_authkeys_file(FILE *f, char *file, Key* key, struct passwd *pw) continue; if (!key_is_cert_authority) continue; - fp = key_fingerprint(found, SSH_FP_MD5, - SSH_FP_HEX); + fp = key_fingerprint(found, options.fingerprint_hash, + SSH_FP_DEFAULT); debug("matching CA found: file %s, line %lu, %s %s", file, linenum, key_type(found), fp); /* @@ -403,7 +404,8 @@ check_authkeys_file(FILE *f, char *file, Key* key, struct passwd *pw) if (key_is_cert_authority) continue; found_key = 1; - fp = key_fingerprint(found, SSH_FP_MD5, SSH_FP_HEX); + fp = key_fingerprint(found, options.fingerprint_hash, + SSH_FP_DEFAULT); debug("matching key found: file %s, line %lu %s %s", file, linenum, key_type(found), fp); free(fp); @@ -429,7 +431,7 @@ user_cert_trusted_ca(struct passwd *pw, Key *key) return 0; ca_fp = key_fingerprint(key->cert->signature_key, - SSH_FP_MD5, SSH_FP_HEX); + options.fingerprint_hash, SSH_FP_DEFAULT); if (sshkey_in_file(key->cert->signature_key, options.trusted_user_ca_keys, 1, 0) != 0) { diff --git a/usr.bin/ssh/digest-libc.c b/usr.bin/ssh/digest-libc.c index a922019357c..8291de41d6a 100644 --- a/usr.bin/ssh/digest-libc.c +++ b/usr.bin/ssh/digest-libc.c @@ -1,4 +1,4 @@ -/* $OpenBSD: digest-libc.c,v 1.3 2014/06/24 01:13:21 djm Exp $ */ +/* $OpenBSD: digest-libc.c,v 1.4 2014/12/21 22:27:56 djm Exp $ */ /* * Copyright (c) 2013 Damien Miller * Copyright (c) 2014 Markus Friedl. All rights reserved. @@ -124,6 +124,26 @@ ssh_digest_by_alg(int alg) return &(digests[alg]); } +int +ssh_digest_alg_by_name(const char *name) +{ + int alg; + + for (alg = 0; alg < SSH_DIGEST_MAX; alg++) { + if (strcasecmp(name, digests[alg].name) == 0) + return digests[alg].id; + } + return -1; +} + +const char * +ssh_digest_alg_name(int alg) +{ + const struct ssh_digest *digest = ssh_digest_by_alg(alg); + + return digest == NULL ? NULL : digest->name; +} + size_t ssh_digest_bytes(int alg) { diff --git a/usr.bin/ssh/digest-openssl.c b/usr.bin/ssh/digest-openssl.c index e524a05aac9..9346cb7905f 100644 --- a/usr.bin/ssh/digest-openssl.c +++ b/usr.bin/ssh/digest-openssl.c @@ -1,4 +1,4 @@ -/* $OpenBSD: digest-openssl.c,v 1.4 2014/07/03 03:26:43 djm Exp $ */ +/* $OpenBSD: digest-openssl.c,v 1.5 2014/12/21 22:27:56 djm Exp $ */ /* * Copyright (c) 2013 Damien Miller * @@ -59,6 +59,26 @@ ssh_digest_by_alg(int alg) return &(digests[alg]); } +int +ssh_digest_alg_by_name(const char *name) +{ + int alg; + + for (alg = 0; digests[alg].id != -1; alg++) { + if (strcasecmp(name, digests[alg].name) == 0) + return digests[alg].id; + } + return -1; +} + +const char * +ssh_digest_alg_name(int alg) +{ + const struct ssh_digest *digest = ssh_digest_by_alg(alg); + + return digest == NULL ? NULL : digest->name; +} + size_t ssh_digest_bytes(int alg) { diff --git a/usr.bin/ssh/digest.h b/usr.bin/ssh/digest.h index 6afb197f0a2..3fe07346852 100644 --- a/usr.bin/ssh/digest.h +++ b/usr.bin/ssh/digest.h @@ -1,4 +1,4 @@ -/* $OpenBSD: digest.h,v 1.6 2014/07/03 04:36:45 djm Exp $ */ +/* $OpenBSD: digest.h,v 1.7 2014/12/21 22:27:56 djm Exp $ */ /* * Copyright (c) 2013 Damien Miller * @@ -33,6 +33,12 @@ struct sshbuf; struct ssh_digest_ctx; +/* Looks up a digest algorithm by name */ +int ssh_digest_alg_by_name(const char *name); + +/* Returns the algorithm name for a digest identifier */ +const char *ssh_digest_alg_name(int alg); + /* Returns the algorithm's digest length in bytes or 0 for invalid algorithm */ size_t ssh_digest_bytes(int alg); diff --git a/usr.bin/ssh/dns.c b/usr.bin/ssh/dns.c index 1918915f03d..978046b9a32 100644 --- a/usr.bin/ssh/dns.c +++ b/usr.bin/ssh/dns.c @@ -1,4 +1,4 @@ -/* $OpenBSD: dns.c,v 1.31 2014/06/24 01:13:21 djm Exp $ */ +/* $OpenBSD: dns.c,v 1.32 2014/12/21 22:27:56 djm Exp $ */ /* * Copyright (c) 2003 Wesley Griffin. All rights reserved. @@ -38,6 +38,7 @@ #include "key.h" #include "dns.h" #include "log.h" +#include "digest.h" static const char *errset_text[] = { "success", /* 0 ERRSET_SUCCESS */ @@ -77,7 +78,7 @@ dns_read_key(u_int8_t *algorithm, u_int8_t *digest_type, u_char **digest, u_int *digest_len, Key *key) { int success = 0; - enum fp_type fp_type = 0; + int fp_alg = -1; switch (key->type) { case KEY_RSA: @@ -107,17 +108,17 @@ dns_read_key(u_int8_t *algorithm, u_int8_t *digest_type, switch (*digest_type) { case SSHFP_HASH_SHA1: - fp_type = SSH_FP_SHA1; + fp_alg = SSH_DIGEST_SHA1; break; case SSHFP_HASH_SHA256: - fp_type = SSH_FP_SHA256; + fp_alg = SSH_DIGEST_SHA256; break; default: *digest_type = SSHFP_HASH_RESERVED; /* 0 */ } if (*algorithm && *digest_type) { - *digest = key_fingerprint_raw(key, fp_type, digest_len); + *digest = key_fingerprint_raw(key, fp_alg, digest_len); if (*digest == NULL) fatal("dns_read_key: null from key_fingerprint_raw()"); success = 1; diff --git a/usr.bin/ssh/key.c b/usr.bin/ssh/key.c index d8703adc3b2..2cc6949b62e 100644 --- a/usr.bin/ssh/key.c +++ b/usr.bin/ssh/key.c @@ -1,4 +1,4 @@ -/* $OpenBSD: key.c,v 1.123 2014/12/04 20:47:36 djm Exp $ */ +/* $OpenBSD: key.c,v 1.124 2014/12/21 22:27:56 djm Exp $ */ /* * placed in the public domain */ @@ -38,8 +38,7 @@ key_new_private(int type) } u_char* -key_fingerprint_raw(const Key *k, enum fp_type dgst_type, - u_int *dgst_raw_length) +key_fingerprint_raw(const Key *k, int dgst_alg, u_int *dgst_raw_length) { u_char *ret = NULL; size_t dlen; @@ -47,7 +46,7 @@ key_fingerprint_raw(const Key *k, enum fp_type dgst_type, if (dgst_raw_length != NULL) *dgst_raw_length = 0; - if ((r = sshkey_fingerprint_raw(k, dgst_type, &ret, &dlen)) != 0) + if ((r = sshkey_fingerprint_raw(k, dgst_alg, &ret, &dlen)) != 0) fatal("%s: %s", __func__, ssh_err(r)); if (dlen > INT_MAX) fatal("%s: giant len %zu", __func__, dlen); diff --git a/usr.bin/ssh/key.h b/usr.bin/ssh/key.h index 24aa8da6a80..2cf9bc22608 100644 --- a/usr.bin/ssh/key.h +++ b/usr.bin/ssh/key.h @@ -1,4 +1,4 @@ -/* $OpenBSD: key.h,v 1.43 2014/12/04 20:47:36 djm Exp $ */ +/* $OpenBSD: key.h,v 1.44 2014/12/21 22:27:56 djm Exp $ */ /* * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. @@ -67,7 +67,7 @@ void key_add_private(Key *); Key *key_new_private(int); void key_free(Key *); Key *key_demote(const Key *); -u_char *key_fingerprint_raw(const Key *, enum fp_type, u_int *); +u_char *key_fingerprint_raw(const Key *, int, u_int *); int key_write(const Key *, FILE *); int key_read(Key *, char **); diff --git a/usr.bin/ssh/krl.c b/usr.bin/ssh/krl.c index d8270ec1737..dae6aed546b 100644 --- a/usr.bin/ssh/krl.c +++ b/usr.bin/ssh/krl.c @@ -14,7 +14,7 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* $OpenBSD: krl.c,v 1.20 2014/12/04 01:49:59 djm Exp $ */ +/* $OpenBSD: krl.c,v 1.21 2014/12/21 22:27:56 djm Exp $ */ #include #include @@ -34,6 +34,7 @@ #include "misc.h" #include "log.h" #include "ssherr.h" +#include "digest.h" #include "krl.h" @@ -409,7 +410,8 @@ ssh_krl_revoke_key_sha1(struct ssh_krl *krl, const struct sshkey *key) int r; debug3("%s: revoke type %s by sha1", __func__, sshkey_type(key)); - if ((r = sshkey_fingerprint_raw(key, SSH_FP_SHA1, &blob, &len)) != 0) + if ((r = sshkey_fingerprint_raw(key, SSH_DIGEST_SHA1, + &blob, &len)) != 0) return r; return revoke_blob(&krl->revoked_sha1s, blob, len); } @@ -1149,7 +1151,7 @@ is_key_revoked(struct ssh_krl *krl, const struct sshkey *key) /* Check explicitly revoked hashes first */ memset(&rb, 0, sizeof(rb)); - if ((r = sshkey_fingerprint_raw(key, SSH_FP_SHA1, + if ((r = sshkey_fingerprint_raw(key, SSH_DIGEST_SHA1, &rb.blob, &rb.len)) != 0) return r; erb = RB_FIND(revoked_blob_tree, &krl->revoked_sha1s, &rb); diff --git a/usr.bin/ssh/readconf.c b/usr.bin/ssh/readconf.c index 16e83f23179..24ff6ad4a32 100644 --- a/usr.bin/ssh/readconf.c +++ b/usr.bin/ssh/readconf.c @@ -1,4 +1,4 @@ -/* $OpenBSD: readconf.c,v 1.223 2014/12/04 02:24:32 djm Exp $ */ +/* $OpenBSD: readconf.c,v 1.224 2014/12/21 22:27:56 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -49,6 +49,7 @@ #include "mac.h" #include "uidswap.h" #include "myproposal.h" +#include "digest.h" /* Format of the configuration file: @@ -144,6 +145,7 @@ typedef enum { oCanonicalDomains, oCanonicalizeHostname, oCanonicalizeMaxDots, oCanonicalizeFallbackLocal, oCanonicalizePermittedCNAMEs, oStreamLocalBindMask, oStreamLocalBindUnlink, oRevokedHostKeys, + oFingerprintHash, oIgnoredUnknownOption, oDeprecated, oUnsupported } OpCodes; @@ -259,6 +261,7 @@ static struct { { "streamlocalbindmask", oStreamLocalBindMask }, { "streamlocalbindunlink", oStreamLocalBindUnlink }, { "revokedhostkeys", oRevokedHostKeys }, + { "fingerprinthash", oFingerprintHash }, { "ignoreunknown", oIgnoreUnknown }, { NULL, oBadOption } @@ -1448,6 +1451,18 @@ parse_int: charptr = &options->revoked_host_keys; goto parse_string; + case oFingerprintHash: + 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) + options->fingerprint_hash = value; + break; + case oDeprecated: debug("%s line %d: Deprecated option \"%s\"", filename, linenum, keyword); @@ -1625,6 +1640,7 @@ initialize_options(Options * options) options->canonicalize_fallback_local = -1; options->canonicalize_hostname = -1; options->revoked_host_keys = NULL; + options->fingerprint_hash = -1; } /* @@ -1800,6 +1816,9 @@ 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; + #define CLEAR_ON_NONE(v) \ do { \ if (option_clear_or_none(v)) { \ @@ -2057,6 +2076,8 @@ fmt_intarg(OpCodes code, int val) return fmt_multistate_int(val, multistate_requesttty); case oCanonicalizeHostname: return fmt_multistate_int(val, multistate_canonicalizehostname); + case oFingerprintHash: + return ssh_digest_alg_name(val); case oProtocol: switch (val) { case SSH_PROTO_1: @@ -2191,6 +2212,7 @@ 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); diff --git a/usr.bin/ssh/readconf.h b/usr.bin/ssh/readconf.h index 49858bff365..11a7332c225 100644 --- a/usr.bin/ssh/readconf.h +++ b/usr.bin/ssh/readconf.h @@ -1,4 +1,4 @@ -/* $OpenBSD: readconf.h,v 1.104 2014/12/04 02:24:32 djm Exp $ */ +/* $OpenBSD: readconf.h,v 1.105 2014/12/21 22:27:56 djm Exp $ */ /* * Author: Tatu Ylonen @@ -146,6 +146,8 @@ typedef struct { char *revoked_host_keys; + int fingerprint_hash; + char *ignored_unknown; /* Pattern list of unknown tokens to ignore */ } Options; diff --git a/usr.bin/ssh/servconf.c b/usr.bin/ssh/servconf.c index dc3543eaf2f..994b344f761 100644 --- a/usr.bin/ssh/servconf.c +++ b/usr.bin/ssh/servconf.c @@ -1,5 +1,5 @@ -/* $OpenBSD: servconf.c,v 1.255 2014/11/24 03:39:22 jsg Exp $ */ +/* $OpenBSD: servconf.c,v 1.256 2014/12/21 22:27:56 djm Exp $ */ /* * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved @@ -50,6 +50,7 @@ #include "hostfile.h" #include "auth.h" #include "myproposal.h" +#include "digest.h" static void add_listen_addr(ServerOptions *, char *, int); static void add_one_listen_addr(ServerOptions *, char *, int); @@ -148,6 +149,7 @@ initialize_server_options(ServerOptions *options) options->ip_qos_interactive = -1; options->ip_qos_bulk = -1; options->version_addendum = NULL; + options->fingerprint_hash = -1; } void @@ -296,6 +298,8 @@ fill_default_server_options(ServerOptions *options) options->fwd_opts.streamlocal_bind_mask = 0177; if (options->fwd_opts.streamlocal_bind_unlink == -1) options->fwd_opts.streamlocal_bind_unlink = 0; + if (options->fingerprint_hash == -1) + options->fingerprint_hash = SSH_FP_HASH_DEFAULT; /* Turn privilege separation on by default */ if (use_privsep == -1) use_privsep = PRIVSEP_NOSANDBOX; @@ -332,7 +336,7 @@ typedef enum { sAuthorizedKeysCommand, sAuthorizedKeysCommandUser, sAuthenticationMethods, sHostKeyAgent, sPermitUserRC, sStreamLocalBindMask, sStreamLocalBindUnlink, - sAllowStreamLocalForwarding, + sAllowStreamLocalForwarding, sFingerprintHash, sDeprecated, sUnsupported } ServerOpCodes; @@ -451,6 +455,7 @@ static struct { { "streamlocalbindmask", sStreamLocalBindMask, SSHCFG_ALL }, { "streamlocalbindunlink", sStreamLocalBindUnlink, SSHCFG_ALL }, { "allowstreamlocalforwarding", sAllowStreamLocalForwarding, SSHCFG_ALL }, + { "fingerprinthash", sFingerprintHash, SSHCFG_GLOBAL }, { NULL, sBadOption, 0 } }; @@ -1622,6 +1627,18 @@ process_server_config_line(ServerOptions *options, char *line, intptr = &options->fwd_opts.streamlocal_bind_unlink; goto parse_flag; + case sFingerprintHash: + arg = strdelim(&cp); + 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) + options->fingerprint_hash = value; + break; + case sDeprecated: logit("%s line %d: Deprecated option %s", filename, linenum, arg); @@ -1864,6 +1881,8 @@ fmt_intarg(ServerOpCodes code, int val) return fmt_multistate_int(val, multistate_tcpfwd); case sAllowStreamLocalForwarding: return fmt_multistate_int(val, multistate_tcpfwd); + case sFingerprintHash: + return ssh_digest_alg_name(val); case sProtocol: switch (val) { case SSH_PROTO_1: @@ -2020,6 +2039,7 @@ dump_config(ServerOptions *o) dump_cfg_fmtint(sAllowTcpForwarding, o->allow_tcp_forwarding); dump_cfg_fmtint(sAllowStreamLocalForwarding, o->allow_streamlocal_forwarding); dump_cfg_fmtint(sUsePrivilegeSeparation, use_privsep); + dump_cfg_fmtint(sFingerprintHash, o->fingerprint_hash); /* string arguments */ dump_cfg_string(sPidFile, o->pid_file); diff --git a/usr.bin/ssh/servconf.h b/usr.bin/ssh/servconf.h index 2bca3cbc85d..88a4bd4bbad 100644 --- a/usr.bin/ssh/servconf.h +++ b/usr.bin/ssh/servconf.h @@ -1,4 +1,4 @@ -/* $OpenBSD: servconf.h,v 1.114 2014/07/15 15:54:14 millert Exp $ */ +/* $OpenBSD: servconf.h,v 1.115 2014/12/21 22:27:56 djm Exp $ */ /* * Author: Tatu Ylonen @@ -183,6 +183,8 @@ typedef struct { u_int num_auth_methods; char *auth_methods[MAX_AUTH_METHODS]; + + int fingerprint_hash; } ServerOptions; /* Information about the incoming connection as used by Match */ diff --git a/usr.bin/ssh/ssh-add.1 b/usr.bin/ssh/ssh-add.1 index 9da7a283568..926456f0bfe 100644 --- a/usr.bin/ssh/ssh-add.1 +++ b/usr.bin/ssh/ssh-add.1 @@ -1,4 +1,4 @@ -.\" $OpenBSD: ssh-add.1,v 1.60 2014/08/30 15:33:50 sobrado Exp $ +.\" $OpenBSD: ssh-add.1,v 1.61 2014/12/21 22:27:56 djm Exp $ .\" .\" Author: Tatu Ylonen .\" Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -35,7 +35,7 @@ .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.Dd $Mdocdate: August 30 2014 $ +.Dd $Mdocdate: December 21 2014 $ .Dt SSH-ADD 1 .Os .Sh NAME @@ -44,6 +44,7 @@ .Sh SYNOPSIS .Nm ssh-add .Op Fl cDdkLlXx +.Op Fl E Ar fingerprint_hash .Op Fl t Ar life .Op Ar .Nm ssh-add @@ -108,6 +109,14 @@ If no public key is found at a given path, will append .Pa .pub and retry. +.It Fl E Ar fingerprint_hash +Specifies the hash algorithm used when displaying key fingerprints. +Valid options are: +.Dq md5 +and +.Dq sha256 . +The default is +.Dq sha256 . .It Fl e Ar pkcs11 Remove keys provided by the PKCS#11 shared library .Ar pkcs11 . diff --git a/usr.bin/ssh/ssh-add.c b/usr.bin/ssh/ssh-add.c index fa224afe541..2b7fa11a7b5 100644 --- a/usr.bin/ssh/ssh-add.c +++ b/usr.bin/ssh/ssh-add.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-add.c,v 1.114 2014/11/26 18:34:51 millert Exp $ */ +/* $OpenBSD: ssh-add.c,v 1.115 2014/12/21 22:27:56 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -59,6 +59,7 @@ #include "pathnames.h" #include "misc.h" #include "ssherr.h" +#include "digest.h" /* argv0 */ extern char *__progname; @@ -73,6 +74,8 @@ static char *default_files[] = { NULL }; +static int fingerprint_hash = SSH_FP_HASH_DEFAULT; + /* Default lifetime (0 == forever) */ static int lifetime = 0; @@ -334,8 +337,8 @@ list_identities(AuthenticationConnection *ac, int do_fp) key = ssh_get_next_identity(ac, &comment, version)) { had_identities = 1; if (do_fp) { - fp = key_fingerprint(key, SSH_FP_MD5, - SSH_FP_HEX); + fp = key_fingerprint(key, fingerprint_hash, + SSH_FP_DEFAULT); printf("%d %s %s (%s)\n", key_size(key), fp, comment, key_type(key)); free(fp); @@ -402,6 +405,7 @@ usage(void) fprintf(stderr, "usage: %s [options] [file ...]\n", __progname); fprintf(stderr, "Options:\n"); fprintf(stderr, " -l List fingerprints of all identities.\n"); + fprintf(stderr, " -E hash Specify hash algorithm used for fingerprints.\n"); fprintf(stderr, " -L List public key parameters of all identities.\n"); fprintf(stderr, " -k Load only keys and not certificates.\n"); fprintf(stderr, " -c Require confirmation to sign using identities\n"); @@ -422,6 +426,7 @@ main(int argc, char **argv) AuthenticationConnection *ac = NULL; char *pkcs11provider = NULL; int i, ch, deleting = 0, ret = 0, key_only = 0; + int xflag = 0, lflag = 0, Dflag = 0; /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ sanitise_stdfd(); @@ -437,21 +442,28 @@ main(int argc, char **argv) "Could not open a connection to your authentication agent.\n"); exit(2); } - while ((ch = getopt(argc, argv, "klLcdDxXe:s:t:")) != -1) { + while ((ch = getopt(argc, argv, "klLcdDxXE:e:s:t:")) != -1) { switch (ch) { + case 'E': + fingerprint_hash = ssh_digest_alg_by_name(optarg); + if (fingerprint_hash == -1) + fatal("Invalid hash algorithm \"%s\"", optarg); + break; case 'k': key_only = 1; break; case 'l': case 'L': - if (list_identities(ac, ch == 'l' ? 1 : 0) == -1) - ret = 1; - goto done; + if (lflag != 0) + fatal("-%c flag already specified", lflag); + lflag = ch; + break; case 'x': case 'X': - if (lock_agent(ac, ch == 'x' ? 1 : 0) == -1) - ret = 1; - goto done; + if (xflag != 0) + fatal("-%c flag already specified", xflag); + xflag = ch; + break; case 'c': confirm = 1; break; @@ -459,9 +471,8 @@ main(int argc, char **argv) deleting = 1; break; case 'D': - if (delete_all(ac) == -1) - ret = 1; - goto done; + Dflag = 1; + break; case 's': pkcs11provider = optarg; break; @@ -482,6 +493,23 @@ main(int argc, char **argv) goto done; } } + + if ((xflag != 0) + (lflag != 0) + (Dflag != 0) > 1) + fatal("Invalid combination of actions"); + else if (xflag) { + if (lock_agent(ac, xflag == 'x' ? 1 : 0) == -1) + ret = 1; + goto done; + } else if (lflag) { + if (list_identities(ac, lflag == 'l' ? 1 : 0) == -1) + ret = 1; + goto done; + } else if (Dflag) { + if (delete_all(ac) == -1) + ret = 1; + goto done; + } + argc -= optind; argv += optind; if (pkcs11provider != NULL) { diff --git a/usr.bin/ssh/ssh-agent.1 b/usr.bin/ssh/ssh-agent.1 index b550653277f..6759afec322 100644 --- a/usr.bin/ssh/ssh-agent.1 +++ b/usr.bin/ssh/ssh-agent.1 @@ -1,4 +1,4 @@ -.\" $OpenBSD: ssh-agent.1,v 1.56 2014/08/30 15:33:50 sobrado Exp $ +.\" $OpenBSD: ssh-agent.1,v 1.57 2014/12/21 22:27:56 djm Exp $ .\" .\" Author: Tatu Ylonen .\" Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -34,7 +34,7 @@ .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.Dd $Mdocdate: August 30 2014 $ +.Dd $Mdocdate: December 21 2014 $ .Dt SSH-AGENT 1 .Os .Sh NAME @@ -45,6 +45,7 @@ .Op Fl c | s .Op Fl d .Op Fl a Ar bind_address +.Op Fl E Ar fingerprint_hash .Op Fl t Ar life .Op Ar command Op Ar arg ... .Nm ssh-agent @@ -96,6 +97,14 @@ Debug mode. When this option is specified .Nm will not fork. +.It Fl E Ar fingerprint_hash +Specifies the hash algorithm used when displaying key fingerprints. +Valid options are: +.Dq md5 +and +.Dq sha256 . +The default is +.Dq sha256 . .It Fl k Kill the current agent (given by the .Ev SSH_AGENT_PID diff --git a/usr.bin/ssh/ssh-agent.c b/usr.bin/ssh/ssh-agent.c index 0fb6ba6ce07..ca00a99f6e8 100644 --- a/usr.bin/ssh/ssh-agent.c +++ b/usr.bin/ssh/ssh-agent.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-agent.c,v 1.191 2014/11/18 20:54:28 krw Exp $ */ +/* $OpenBSD: ssh-agent.c,v 1.192 2014/12/21 22:27:56 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -128,6 +128,8 @@ extern char *__progname; /* Default lifetime in seconds (0 == forever) */ static long lifetime = 0; +static int fingerprint_hash = SSH_FP_HASH_DEFAULT; + static void close_socket(SocketEntry *e) { @@ -189,7 +191,7 @@ confirm_key(Identity *id) char *p; int ret = -1; - p = key_fingerprint(id->key, SSH_FP_MD5, SSH_FP_HEX); + p = key_fingerprint(id->key, fingerprint_hash, SSH_FP_DEFAULT); if (ask_permission("Allow use of key %s?\nKey fingerprint %s.", id->comment, p)) ret = 0; @@ -1010,7 +1012,7 @@ usage(void) { fprintf(stderr, "usage: ssh-agent [-c | -s] [-d] [-a bind_address] [-t life]\n" - " [command [arg ...]]\n" + " [-E fingerprint_hash] [command [arg ...]]\n" " ssh-agent [-c | -s] -k\n"); exit(1); } @@ -1043,8 +1045,13 @@ main(int ac, char **av) OpenSSL_add_all_algorithms(); #endif - while ((ch = getopt(ac, av, "cdksa:t:")) != -1) { + while ((ch = getopt(ac, av, "cdksE:a:t:")) != -1) { switch (ch) { + case 'E': + fingerprint_hash = ssh_digest_alg_by_name(optarg); + if (fingerprint_hash == -1) + fatal("Invalid hash algorithm \"%s\"", optarg); + break; case 'c': if (s_flag) usage(); diff --git a/usr.bin/ssh/ssh-keygen.1 b/usr.bin/ssh/ssh-keygen.1 index bf5f87bd370..b73c4606ef7 100644 --- a/usr.bin/ssh/ssh-keygen.1 +++ b/usr.bin/ssh/ssh-keygen.1 @@ -1,4 +1,4 @@ -.\" $OpenBSD: ssh-keygen.1,v 1.123 2014/08/30 15:33:50 sobrado Exp $ +.\" $OpenBSD: ssh-keygen.1,v 1.124 2014/12/21 22:27:56 djm Exp $ .\" .\" Author: Tatu Ylonen .\" Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -35,7 +35,7 @@ .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.Dd $Mdocdate: August 30 2014 $ +.Dd $Mdocdate: December 21 2014 $ .Dt SSH-KEYGEN 1 .Os .Sh NAME @@ -73,6 +73,7 @@ .Op Fl f Ar keyfile .Nm ssh-keygen .Fl l +.Op Fl E Ar fingerprint_hash .Op Fl f Ar input_keyfile .Nm ssh-keygen .Fl B @@ -269,6 +270,14 @@ When used in combination with this option indicates that a CA key resides in a PKCS#11 token (see the .Sx CERTIFICATES section for details). +.It Fl E Ar fingerprint_hash +Specifies the hash algorithm used when displaying key fingerprints. +Valid options are: +.Dq md5 +and +.Dq sha256 . +The default is +.Dq sha256 . .It Fl e This option will read a private or public OpenSSH key file and print to stdout the key in one of the formats specified by the diff --git a/usr.bin/ssh/ssh-keygen.c b/usr.bin/ssh/ssh-keygen.c index bddac020018..3c54067602f 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.250 2014/08/21 01:08:52 doug Exp $ */ +/* $OpenBSD: ssh-keygen.c,v 1.251 2014/12/21 22:27:56 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1994 Tatu Ylonen , Espoo, Finland @@ -45,6 +45,7 @@ #include "ssh2.h" #include "atomicio.h" #include "krl.h" +#include "digest.h" #ifdef ENABLE_PKCS11 #include "ssh-pkcs11.h" @@ -86,6 +87,9 @@ int show_cert = 0; int print_fingerprint = 0; int print_bubblebabble = 0; +/* Hash algorithm to use for fingerprints. */ +int fingerprint_hash = SSH_FP_HASH_DEFAULT; + /* The identity file name, given on the command line or entered by the user. */ char identity_file[1024]; int have_identity = 0; @@ -737,11 +741,11 @@ do_download(struct passwd *pw) Key **keys = NULL; int i, nkeys; enum fp_rep rep; - enum fp_type fptype; + int fptype; char *fp, *ra; - fptype = print_bubblebabble ? SSH_FP_SHA1 : SSH_FP_MD5; - rep = print_bubblebabble ? SSH_FP_BUBBLEBABBLE : SSH_FP_HEX; + fptype = print_bubblebabble ? SSH_DIGEST_SHA1 : fingerprint_hash; + rep = print_bubblebabble ? SSH_FP_BUBBLEBABBLE : SSH_FP_DEFAULT; pkcs11_init(0); nkeys = pkcs11_add_provider(pkcs11provider, NULL, &keys); @@ -750,7 +754,7 @@ do_download(struct passwd *pw) for (i = 0; i < nkeys; i++) { if (print_fingerprint) { fp = key_fingerprint(keys[i], fptype, rep); - ra = key_fingerprint(keys[i], SSH_FP_MD5, + ra = key_fingerprint(keys[i], fingerprint_hash, SSH_FP_RANDOMART); printf("%u %s %s (PKCS11 key)\n", key_size(keys[i]), fp, key_type(keys[i])); @@ -780,12 +784,11 @@ do_fingerprint(struct passwd *pw) char *comment = NULL, *cp, *ep, line[16*1024], *fp, *ra; int i, skip = 0, num = 0, invalid = 1; enum fp_rep rep; - enum fp_type fptype; + int fptype; struct stat st; - fptype = print_bubblebabble ? SSH_FP_SHA1 : SSH_FP_MD5; - rep = print_bubblebabble ? SSH_FP_BUBBLEBABBLE : SSH_FP_HEX; - + fptype = print_bubblebabble ? SSH_DIGEST_SHA1 : fingerprint_hash; + rep = print_bubblebabble ? SSH_FP_BUBBLEBABBLE : SSH_FP_DEFAULT; if (!have_identity) ask_filename(pw, "Enter file in which the key is"); if (stat(identity_file, &st) < 0) { @@ -795,7 +798,8 @@ do_fingerprint(struct passwd *pw) public = key_load_public(identity_file, &comment); if (public != NULL) { fp = key_fingerprint(public, fptype, rep); - ra = key_fingerprint(public, SSH_FP_MD5, SSH_FP_RANDOMART); + ra = key_fingerprint(public, fingerprint_hash, + SSH_FP_RANDOMART); printf("%u %s %s (%s)\n", key_size(public), fp, comment, key_type(public)); if (log_level >= SYSLOG_LEVEL_VERBOSE) @@ -861,7 +865,8 @@ do_fingerprint(struct passwd *pw) } comment = *cp ? cp : comment; fp = key_fingerprint(public, fptype, rep); - ra = key_fingerprint(public, SSH_FP_MD5, SSH_FP_RANDOMART); + ra = key_fingerprint(public, fingerprint_hash, + SSH_FP_RANDOMART); printf("%u %s %s (%s)\n", key_size(public), fp, comment ? comment : "no comment", key_type(public)); if (log_level >= SYSLOG_LEVEL_VERBOSE) @@ -979,13 +984,15 @@ printhost(FILE *f, const char *name, Key *public, int ca, int revoked, int hash) { if (print_fingerprint) { enum fp_rep rep; - enum fp_type fptype; + int fptype; char *fp, *ra; - fptype = print_bubblebabble ? SSH_FP_SHA1 : SSH_FP_MD5; - rep = print_bubblebabble ? SSH_FP_BUBBLEBABBLE : SSH_FP_HEX; + fptype = print_bubblebabble ? + SSH_DIGEST_SHA1 : fingerprint_hash; + rep = print_bubblebabble ? SSH_FP_BUBBLEBABBLE : SSH_FP_DEFAULT; fp = key_fingerprint(public, fptype, rep); - ra = key_fingerprint(public, SSH_FP_MD5, SSH_FP_RANDOMART); + ra = key_fingerprint(public, fingerprint_hash, + SSH_FP_RANDOMART); printf("%u %s %s (%s)\n", key_size(public), fp, name, key_type(public)); if (log_level >= SYSLOG_LEVEL_VERBOSE) @@ -1894,9 +1901,9 @@ do_show_cert(struct passwd *pw) fatal("%s is not a certificate", identity_file); v00 = key->type == KEY_RSA_CERT_V00 || key->type == KEY_DSA_CERT_V00; - key_fp = key_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX); + key_fp = key_fingerprint(key, fingerprint_hash, SSH_FP_DEFAULT); ca_fp = key_fingerprint(key->cert->signature_key, - SSH_FP_MD5, SSH_FP_HEX); + fingerprint_hash, SSH_FP_DEFAULT); printf("%s:\n", identity_file); printf(" Type: %s %s certificate\n", key_ssh_name(key), @@ -2175,7 +2182,7 @@ usage(void) " ssh-keygen -e [-m key_format] [-f input_keyfile]\n" " ssh-keygen -y [-f input_keyfile]\n" " ssh-keygen -c [-P passphrase] [-C comment] [-f keyfile]\n" - " ssh-keygen -l [-f input_keyfile]\n" + " ssh-keygen -l [-E fingerprint_hash] [-f input_keyfile]\n" " ssh-keygen -B [-f input_keyfile]\n"); #ifdef ENABLE_PKCS11 fprintf(stderr, @@ -2240,9 +2247,10 @@ main(int argc, char **argv) exit(1); } - /* Remaining characters: EUYdw */ + /* Remaining characters: UYdw */ while ((opt = getopt(argc, argv, "ABHLQXceghiklopquvxy" - "C:D:F:G:I:J:K:M:N:O:P:R:S:T:V:W:Z:a:b:f:g:j:m:n:r:s:t:z:")) != -1) { + "C:D:E:F:G:I:J:K:M:N:O:P:R:S:T:V:W:Z:" + "a:b:f:g:j:m:n:r:s:t:z:")) != -1) { switch (opt) { case 'A': gen_all_hostkeys = 1; @@ -2253,6 +2261,11 @@ main(int argc, char **argv) fatal("Bits has bad value %s (%s)", optarg, errstr); break; + case 'E': + fingerprint_hash = ssh_digest_alg_by_name(optarg); + if (fingerprint_hash == -1) + fatal("Invalid hash algorithm \"%s\"", optarg); + break; case 'F': find_host = 1; rr_hostname = optarg; @@ -2684,8 +2697,9 @@ passphrase_again: fclose(f); if (!quiet) { - char *fp = key_fingerprint(public, SSH_FP_MD5, SSH_FP_HEX); - char *ra = key_fingerprint(public, SSH_FP_MD5, + char *fp = key_fingerprint(public, fingerprint_hash, + SSH_FP_DEFAULT); + char *ra = key_fingerprint(public, fingerprint_hash, SSH_FP_RANDOMART); printf("Your public key has been saved in %s.\n", identity_file); diff --git a/usr.bin/ssh/ssh-keysign.c b/usr.bin/ssh/ssh-keysign.c index 79127adb073..7b119c993ed 100644 --- a/usr.bin/ssh/ssh-keysign.c +++ b/usr.bin/ssh/ssh-keysign.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-keysign.c,v 1.43 2014/10/08 22:20:25 djm Exp $ */ +/* $OpenBSD: ssh-keysign.c,v 1.44 2014/12/21 22:27:56 djm Exp $ */ /* * Copyright (c) 2002 Markus Friedl. All rights reserved. * @@ -235,7 +235,8 @@ main(int argc, char **argv) } } if (!found) { - fp = key_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX); + fp = key_fingerprint(key, options.fingerprint_hash, + SSH_FP_DEFAULT); fatal("no matching hostkey found for key %s %s", key_type(key), fp); } diff --git a/usr.bin/ssh/ssh.1 b/usr.bin/ssh/ssh.1 index 51201861bf3..d489047ce1f 100644 --- a/usr.bin/ssh/ssh.1 +++ b/usr.bin/ssh/ssh.1 @@ -33,8 +33,8 @@ .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.\" $OpenBSD: ssh.1,v 1.351 2014/10/09 06:21:31 jmc Exp $ -.Dd $Mdocdate: October 9 2014 $ +.\" $OpenBSD: ssh.1,v 1.352 2014/12/21 22:27:56 djm Exp $ +.Dd $Mdocdate: December 21 2014 $ .Dt SSH 1 .Os .Sh NAME @@ -1091,7 +1091,7 @@ Fingerprints can be determined using If the fingerprint is already known, it can be matched and the key can be accepted or rejected. Because of the difficulty of comparing host keys -just by looking at hex strings, +just by looking at fingerprint strings, there is also support to compare host keys visually, using .Em random art . diff --git a/usr.bin/ssh/sshconnect.c b/usr.bin/ssh/sshconnect.c index feab699ad95..7aa789fdfc3 100644 --- a/usr.bin/ssh/sshconnect.c +++ b/usr.bin/ssh/sshconnect.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sshconnect.c,v 1.253 2014/12/11 08:20:09 djm Exp $ */ +/* $OpenBSD: sshconnect.c,v 1.254 2014/12/21 22:27:56 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -892,9 +892,10 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, "key for IP address '%.128s' to the list " "of known hosts.", type, ip); } else if (options.visual_host_key) { - fp = key_fingerprint(host_key, SSH_FP_MD5, SSH_FP_HEX); - ra = key_fingerprint(host_key, SSH_FP_MD5, - SSH_FP_RANDOMART); + fp = key_fingerprint(host_key, + options.fingerprint_hash, SSH_FP_DEFAULT); + ra = key_fingerprint(host_key, + options.fingerprint_hash, SSH_FP_RANDOMART); logit("Host key fingerprint is %s\n%s\n", fp, ra); free(ra); free(fp); @@ -933,9 +934,10 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, else snprintf(msg1, sizeof(msg1), "."); /* The default */ - fp = key_fingerprint(host_key, SSH_FP_MD5, SSH_FP_HEX); - ra = key_fingerprint(host_key, SSH_FP_MD5, - SSH_FP_RANDOMART); + fp = key_fingerprint(host_key, + options.fingerprint_hash, SSH_FP_DEFAULT); + ra = key_fingerprint(host_key, + options.fingerprint_hash, SSH_FP_RANDOMART); msg2[0] = '\0'; if (options.verify_host_key_dns) { if (matching_host_key_dns) @@ -1200,7 +1202,7 @@ verify_host_key(char *host, struct sockaddr *hostaddr, Key *host_key) struct sshkey *plain = NULL; if ((fp = sshkey_fingerprint(host_key, - SSH_FP_MD5, SSH_FP_HEX)) == NULL) { + options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) { error("%s: fingerprint host key: %s", __func__, ssh_err(r)); r = -1; goto out; @@ -1361,8 +1363,10 @@ show_other_keys(struct hostkeys *hostkeys, Key *key) continue; if (!lookup_key_in_hostkeys_by_type(hostkeys, type[i], &found)) continue; - fp = key_fingerprint(found->key, SSH_FP_MD5, SSH_FP_HEX); - ra = key_fingerprint(found->key, SSH_FP_MD5, SSH_FP_RANDOMART); + fp = key_fingerprint(found->key, + options.fingerprint_hash, SSH_FP_DEFAULT); + ra = key_fingerprint(found->key, + options.fingerprint_hash, SSH_FP_RANDOMART); logit("WARNING: %s key found for host %s\n" "in %s:%lu\n" "%s key fingerprint %s.", @@ -1383,7 +1387,8 @@ warn_changed_key(Key *host_key) { char *fp; - fp = key_fingerprint(host_key, SSH_FP_MD5, SSH_FP_HEX); + fp = key_fingerprint(host_key, options.fingerprint_hash, + SSH_FP_DEFAULT); error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); error("@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @"); diff --git a/usr.bin/ssh/sshconnect2.c b/usr.bin/ssh/sshconnect2.c index 8b223c6d4fc..476cf4447e4 100644 --- a/usr.bin/ssh/sshconnect2.c +++ b/usr.bin/ssh/sshconnect2.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sshconnect2.c,v 1.211 2014/12/11 05:13:28 djm Exp $ */ +/* $OpenBSD: sshconnect2.c,v 1.212 2014/12/21 22:27:56 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * Copyright (c) 2008 Damien Miller. All rights reserved. @@ -576,7 +576,7 @@ input_userauth_pk_ok(int type, u_int32_t seq, void *ctxt) key->type, pktype); goto done; } - fp = key_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX); + fp = key_fingerprint(key, options.fingerprint_hash, SSH_FP_DEFAULT); debug2("input_userauth_pk_ok: fp %s", fp); free(fp); @@ -985,7 +985,7 @@ sign_and_send_pubkey(Authctxt *authctxt, Identity *id) int have_sig = 1; char *fp; - fp = key_fingerprint(id->key, SSH_FP_MD5, SSH_FP_HEX); + fp = key_fingerprint(id->key, options.fingerprint_hash, SSH_FP_DEFAULT); debug3("sign_and_send_pubkey: %s %s", key_type(id->key), fp); free(fp); diff --git a/usr.bin/ssh/sshd_config.5 b/usr.bin/ssh/sshd_config.5 index d431ec594a0..f347658a5b7 100644 --- a/usr.bin/ssh/sshd_config.5 +++ b/usr.bin/ssh/sshd_config.5 @@ -33,8 +33,8 @@ .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.\" $OpenBSD: sshd_config.5,v 1.182 2014/12/12 00:02:17 djm Exp $ -.Dd $Mdocdate: December 12 2014 $ +.\" $OpenBSD: sshd_config.5,v 1.183 2014/12/21 22:27:55 djm Exp $ +.Dd $Mdocdate: December 21 2014 $ .Dt SSHD_CONFIG 5 .Os .Sh NAME @@ -486,6 +486,15 @@ and finally See PATTERNS in .Xr ssh_config 5 for more information on patterns. +.It Cm FingerprintHash +Specifies the hash algorithm used when logging key fingerprints. +Valid options are: +.Dq md5 +and +.Dq sha256 . +The default is +.Dq sha256 . +.Pp .It Cm ForceCommand Forces the execution of the command specified by .Cm ForceCommand , diff --git a/usr.bin/ssh/sshkey.c b/usr.bin/ssh/sshkey.c index 87c9498cfcd..84e39a1636a 100644 --- a/usr.bin/ssh/sshkey.c +++ b/usr.bin/ssh/sshkey.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sshkey.c,v 1.6 2014/12/10 01:24:09 djm Exp $ */ +/* $OpenBSD: sshkey.c,v 1.7 2014/12/21 22:27:55 djm Exp $ */ /* * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. * Copyright (c) 2008 Alexander von Gernler. All rights reserved. @@ -27,6 +27,7 @@ #include #include +#include #include #include @@ -38,6 +39,7 @@ #include #include #include +#include #include "ssh2.h" #include "ssherr.h" @@ -826,29 +828,18 @@ sshkey_plain_to_blob(const struct sshkey *key, u_char **blobp, size_t *lenp) } int -sshkey_fingerprint_raw(const struct sshkey *k, enum sshkey_fp_type dgst_type, +sshkey_fingerprint_raw(const struct sshkey *k, int dgst_alg, u_char **retp, size_t *lenp) { u_char *blob = NULL, *ret = NULL; size_t blob_len = 0; - int hash_alg = -1, r = SSH_ERR_INTERNAL_ERROR; + int r = SSH_ERR_INTERNAL_ERROR; if (retp != NULL) *retp = NULL; if (lenp != NULL) *lenp = 0; - - switch (dgst_type) { - case SSH_FP_MD5: - hash_alg = SSH_DIGEST_MD5; - break; - case SSH_FP_SHA1: - hash_alg = SSH_DIGEST_SHA1; - break; - case SSH_FP_SHA256: - hash_alg = SSH_DIGEST_SHA256; - break; - default: + if (ssh_digest_bytes(dgst_alg) == 0) { r = SSH_ERR_INVALID_ARGUMENT; goto out; } @@ -873,7 +864,7 @@ sshkey_fingerprint_raw(const struct sshkey *k, enum sshkey_fp_type dgst_type, r = SSH_ERR_ALLOC_FAIL; goto out; } - if ((r = ssh_digest_memory(hash_alg, blob, blob_len, + if ((r = ssh_digest_memory(dgst_alg, blob, blob_len, ret, SSH_DIGEST_MAX_LENGTH)) != 0) goto out; /* success */ @@ -882,7 +873,7 @@ sshkey_fingerprint_raw(const struct sshkey *k, enum sshkey_fp_type dgst_type, ret = NULL; } if (lenp != NULL) - *lenp = ssh_digest_bytes(hash_alg); + *lenp = ssh_digest_bytes(dgst_alg); r = 0; out: free(ret); @@ -894,21 +885,45 @@ sshkey_fingerprint_raw(const struct sshkey *k, enum sshkey_fp_type dgst_type, } static char * -fingerprint_hex(u_char *dgst_raw, size_t dgst_raw_len) +fingerprint_b64(const char *alg, u_char *dgst_raw, size_t dgst_raw_len) { - char *retval; - size_t i; + char *ret; + size_t plen = strlen(alg) + 1; + size_t rlen = ((dgst_raw_len + 2) / 3) * 4 + plen + 1; + int r; - if ((retval = calloc(1, dgst_raw_len * 3 + 1)) == NULL) + if (dgst_raw_len > 65536 || (ret = calloc(1, rlen)) == NULL) + return NULL; + strlcpy(ret, alg, rlen); + strlcat(ret, ":", rlen); + if (dgst_raw_len == 0) + return ret; + if ((r = b64_ntop(dgst_raw, dgst_raw_len, + ret + plen, rlen - plen)) == -1) { + explicit_bzero(ret, rlen); + free(ret); return NULL; - for (i = 0; i < dgst_raw_len; i++) { - char hex[4]; - snprintf(hex, sizeof(hex), "%02x:", dgst_raw[i]); - strlcat(retval, hex, dgst_raw_len * 3 + 1); } + /* Trim padding characters from end */ + ret[strcspn(ret, "=")] = '\0'; + return ret; +} - /* Remove the trailing ':' character */ - retval[(dgst_raw_len * 3) - 1] = '\0'; +static char * +fingerprint_hex(const char *alg, u_char *dgst_raw, size_t dgst_raw_len) +{ + char *retval, hex[5]; + size_t i, rlen = dgst_raw_len * 3 + strlen(alg) + 2; + + if (dgst_raw_len > 65536 || (retval = calloc(1, rlen)) == NULL) + return NULL; + strlcpy(retval, alg, rlen); + strlcat(retval, ":", rlen); + for (i = 0; i < dgst_raw_len; i++) { + snprintf(hex, sizeof(hex), "%s%02x", + i > 0 ? ":" : "", dgst_raw[i]); + strlcat(retval, hex, rlen); + } return retval; } @@ -994,7 +1009,7 @@ fingerprint_bubblebabble(u_char *dgst_raw, size_t dgst_raw_len) #define FLDSIZE_Y (FLDBASE + 1) #define FLDSIZE_X (FLDBASE * 2 + 1) static char * -fingerprint_randomart(u_char *dgst_raw, size_t dgst_raw_len, +fingerprint_randomart(const char *alg, u_char *dgst_raw, size_t dgst_raw_len, const struct sshkey *k) { /* @@ -1002,9 +1017,9 @@ fingerprint_randomart(u_char *dgst_raw, size_t dgst_raw_len, * intersects with itself. Matter of taste. */ char *augmentation_string = " .o+=*BOX@%&#/^SE"; - char *retval, *p, title[FLDSIZE_X]; + char *retval, *p, title[FLDSIZE_X], hash[FLDSIZE_X]; u_char field[FLDSIZE_X][FLDSIZE_Y]; - size_t i, tlen; + size_t i, tlen, hlen; u_int b; int x, y, r; size_t len = strlen(augmentation_string) - 1; @@ -1049,8 +1064,12 @@ fingerprint_randomart(u_char *dgst_raw, size_t dgst_raw_len, sshkey_type(k), sshkey_size(k)); /* If [type size] won't fit, then try [type]; fits "[ED25519-CERT]" */ if (r < 0 || r > (int)sizeof(title)) - snprintf(title, sizeof(title), "[%s]", sshkey_type(k)); - tlen = strlen(title); + r = snprintf(title, sizeof(title), "[%s]", sshkey_type(k)); + tlen = (r <= 0) ? 0 : strlen(title); + + /* assemble hash ID. */ + r = snprintf(hash, sizeof(hash), "[%s]", alg); + hlen = (r <= 0) ? 0 : strlen(hash); /* output upper border */ p = retval; @@ -1059,7 +1078,7 @@ fingerprint_randomart(u_char *dgst_raw, size_t dgst_raw_len, *p++ = '-'; memcpy(p, title, tlen); p += tlen; - for (i = p - retval - 1; i < FLDSIZE_X; i++) + for (i += tlen; i < FLDSIZE_X; i++) *p++ = '-'; *p++ = '+'; *p++ = '\n'; @@ -1075,7 +1094,11 @@ fingerprint_randomart(u_char *dgst_raw, size_t dgst_raw_len, /* output lower border */ *p++ = '+'; - for (i = 0; i < FLDSIZE_X; i++) + for (i = 0; i < (FLDSIZE_X - hlen) / 2; i++) + *p++ = '-'; + memcpy(p, hash, hlen); + p += hlen; + for (i += hlen; i < FLDSIZE_X; i++) *p++ = '-'; *p++ = '+'; @@ -1083,24 +1106,39 @@ fingerprint_randomart(u_char *dgst_raw, size_t dgst_raw_len, } char * -sshkey_fingerprint(const struct sshkey *k, enum sshkey_fp_type dgst_type, +sshkey_fingerprint(const struct sshkey *k, int dgst_alg, enum sshkey_fp_rep dgst_rep) { char *retval = NULL; u_char *dgst_raw; size_t dgst_raw_len; - if (sshkey_fingerprint_raw(k, dgst_type, &dgst_raw, &dgst_raw_len) != 0) + if (sshkey_fingerprint_raw(k, dgst_alg, &dgst_raw, &dgst_raw_len) != 0) return NULL; switch (dgst_rep) { + case SSH_FP_DEFAULT: + if (dgst_alg == SSH_DIGEST_MD5) { + retval = fingerprint_hex(ssh_digest_alg_name(dgst_alg), + dgst_raw, dgst_raw_len); + } else { + retval = fingerprint_b64(ssh_digest_alg_name(dgst_alg), + dgst_raw, dgst_raw_len); + } + break; case SSH_FP_HEX: - retval = fingerprint_hex(dgst_raw, dgst_raw_len); + retval = fingerprint_hex(ssh_digest_alg_name(dgst_alg), + dgst_raw, dgst_raw_len); + break; + case SSH_FP_BASE64: + retval = fingerprint_b64(ssh_digest_alg_name(dgst_alg), + dgst_raw, dgst_raw_len); break; case SSH_FP_BUBBLEBABBLE: retval = fingerprint_bubblebabble(dgst_raw, dgst_raw_len); break; case SSH_FP_RANDOMART: - retval = fingerprint_randomart(dgst_raw, dgst_raw_len, k); + retval = fingerprint_randomart(ssh_digest_alg_name(dgst_alg), + dgst_raw, dgst_raw_len, k); break; default: explicit_bzero(dgst_raw, dgst_raw_len); diff --git a/usr.bin/ssh/sshkey.h b/usr.bin/ssh/sshkey.h index 4127db2442e..7f595c18894 100644 --- a/usr.bin/ssh/sshkey.h +++ b/usr.bin/ssh/sshkey.h @@ -1,4 +1,4 @@ -/* $OpenBSD: sshkey.h,v 1.1 2014/06/24 01:16:58 djm Exp $ */ +/* $OpenBSD: sshkey.h,v 1.2 2014/12/21 22:27:55 djm Exp $ */ /* * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. @@ -61,16 +61,14 @@ enum sshkey_types { KEY_UNSPEC }; -/* Fingerprint hash algorithms */ -enum sshkey_fp_type { - SSH_FP_SHA1, - SSH_FP_MD5, - SSH_FP_SHA256 -}; +/* Default fingerprint hash */ +#define SSH_FP_HASH_DEFAULT SSH_DIGEST_SHA256 /* Fingerprint representation formats */ enum sshkey_fp_rep { + SSH_FP_DEFAULT = 0, SSH_FP_HEX, + SSH_FP_BASE64, SSH_FP_BUBBLEBABBLE, SSH_FP_RANDOMART }; @@ -118,9 +116,9 @@ int sshkey_equal_public(const struct sshkey *, const struct sshkey *); int sshkey_equal(const struct sshkey *, const struct sshkey *); char *sshkey_fingerprint(const struct sshkey *, - enum sshkey_fp_type, enum sshkey_fp_rep); + int, enum sshkey_fp_rep); int sshkey_fingerprint_raw(const struct sshkey *k, - enum sshkey_fp_type dgst_type, u_char **retp, size_t *lenp); + int, u_char **retp, size_t *lenp); const char *sshkey_type(const struct sshkey *); const char *sshkey_cert_type(const struct sshkey *); int sshkey_write(const struct sshkey *, FILE *); -- 2.20.1