From e1776155d19db4f3ab2ff42323d6499f0712cfa4 Mon Sep 17 00:00:00 2001 From: Damien Miller Date: Tue, 1 Mar 2005 21:47:37 +1100 Subject: - djm@cvs.openbsd.org 2005/03/01 10:40:27 [hostfile.c hostfile.h readconf.c readconf.h ssh.1 ssh_config.5] [sshconnect.c sshd.8] add support for hashing host names and addresses added to known_hosts files, to improve privacy of which hosts user have been visiting; ok markus@ deraadt@ --- ChangeLog | 8 +++- hostfile.c | 123 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- hostfile.h | 9 ++++- readconf.c | 12 +++++- readconf.h | 4 +- ssh.1 | 3 +- ssh_config.5 | 17 ++++++++- sshconnect.c | 7 ++-- sshd.8 | 15 +++++++- 9 files changed, 180 insertions(+), 18 deletions(-) diff --git a/ChangeLog b/ChangeLog index f31a5266..b69fe2f5 100644 --- a/ChangeLog +++ b/ChangeLog @@ -27,6 +27,12 @@ Patch originally by Dan Astorian, but worked on by several people Adds GatewayPorts=clientspecified option on server to allow remote forwards to bind to client-specified ports. + - djm@cvs.openbsd.org 2005/03/01 10:40:27 + [hostfile.c hostfile.h readconf.c readconf.h ssh.1 ssh_config.5] + [sshconnect.c sshd.8] + add support for hashing host names and addresses added to known_hosts + files, to improve privacy of which hosts user have been visiting; ok + markus@ deraadt@ 20050226 - (dtucker) [openbsd-compat/bsd-openpty.c openbsd-compat/inet_ntop.c] @@ -2203,4 +2209,4 @@ - (djm) Trim deprecated options from INSTALL. Mention UsePAM - (djm) Fix quote handling in sftp; Patch from admorten AT umich.edu -$Id: ChangeLog,v 1.3672 2005/03/01 10:24:33 djm Exp $ +$Id: ChangeLog,v 1.3673 2005/03/01 10:47:37 djm Exp $ diff --git a/hostfile.c b/hostfile.c index 88c05491..2e1c8bcd 100644 --- a/hostfile.c +++ b/hostfile.c @@ -36,13 +36,102 @@ */ #include "includes.h" -RCSID("$OpenBSD: hostfile.c,v 1.32 2003/11/10 16:23:41 jakob Exp $"); +RCSID("$OpenBSD: hostfile.c,v 1.33 2005/03/01 10:40:26 djm Exp $"); + +#include +#include +#include #include "packet.h" #include "match.h" #include "key.h" #include "hostfile.h" #include "log.h" +#include "xmalloc.h" + +static int +extract_salt(const char *s, u_int l, char *salt, size_t salt_len) +{ + char *p, *b64salt; + u_int b64len; + int ret; + + if (l < sizeof(HASH_MAGIC) - 1) { + debug2("extract_salt: string too short"); + return (-1); + } + if (strncmp(s, HASH_MAGIC, sizeof(HASH_MAGIC) - 1) != 0) { + debug2("extract_salt: invalid magic identifier"); + return (-1); + } + s += sizeof(HASH_MAGIC) - 1; + l -= sizeof(HASH_MAGIC) - 1; + if ((p = memchr(s, HASH_DELIM, l)) == NULL) { + debug2("extract_salt: missing salt termination character"); + return (-1); + } + + b64len = p - s; + /* Sanity check */ + if (b64len == 0 || b64len > 1024) { + debug2("extract_salt: bad encoded salt length %u", b64len); + return (-1); + } + b64salt = xmalloc(1 + b64len); + memcpy(b64salt, s, b64len); + b64salt[b64len] = '\0'; + + ret = __b64_pton(b64salt, salt, salt_len); + xfree(b64salt); + if (ret == -1) { + debug2("extract_salt: salt decode error"); + return (-1); + } + if (ret != SHA_DIGEST_LENGTH) { + debug2("extract_salt: expected salt len %u, got %u", + salt_len, ret); + return (-1); + } + + return (0); +} + +char * +host_hash(const char *host, const char *name_from_hostfile, u_int src_len) +{ + const EVP_MD *md = EVP_sha1(); + HMAC_CTX mac_ctx; + char salt[256], result[256], uu_salt[512], uu_result[512]; + static char encoded[1024]; + u_int i, len; + + len = EVP_MD_size(md); + + if (name_from_hostfile == NULL) { + /* Create new salt */ + for (i = 0; i < len; i++) + salt[i] = arc4random(); + } else { + /* Extract salt from known host entry */ + if (extract_salt(name_from_hostfile, src_len, salt, + sizeof(salt)) == -1) + return (NULL); + } + + HMAC_Init(&mac_ctx, salt, len, md); + HMAC_Update(&mac_ctx, host, strlen(host)); + HMAC_Final(&mac_ctx, result, NULL); + HMAC_cleanup(&mac_ctx); + + if (__b64_ntop(salt, len, uu_salt, sizeof(uu_salt)) == -1 || + __b64_ntop(result, len, uu_result, sizeof(uu_result)) == -1) + fatal("host_hash: __b64_ntop failed"); + + snprintf(encoded, sizeof(encoded), "%s%s%c%s", HASH_MAGIC, uu_salt, + HASH_DELIM, uu_result); + + return (encoded); +} /* * Parses an RSA (number of bits, e, n) or DSA key from a string. Moves the @@ -104,7 +193,7 @@ check_host_in_hostfile_by_key_or_type(const char *filename, char line[8192]; int linenum = 0; u_int kbits; - char *cp, *cp2; + char *cp, *cp2, *hashed_host; HostStatus end_return; debug3("check_host_in_hostfile: filename %s", filename); @@ -137,8 +226,18 @@ check_host_in_hostfile_by_key_or_type(const char *filename, ; /* Check if the host name matches. */ - if (match_hostname(host, cp, (u_int) (cp2 - cp)) != 1) - continue; + if (match_hostname(host, cp, (u_int) (cp2 - cp)) != 1) { + if (*cp != HASH_DELIM) + continue; + hashed_host = host_hash(host, cp, (u_int) (cp2 - cp)); + if (hashed_host == NULL) { + debug("Invalid hashed host line %d of %s", + linenum, filename); + continue; + } + if (strncmp(hashed_host, cp, (u_int) (cp2 - cp)) != 0) + continue; + } /* Got a match. Skip host name. */ cp = cp2; @@ -211,16 +310,28 @@ lookup_key_in_hostfile_by_type(const char *filename, const char *host, */ int -add_host_to_hostfile(const char *filename, const char *host, const Key *key) +add_host_to_hostfile(const char *filename, const char *host, const Key *key, + int store_hash) { FILE *f; int success = 0; + char *hashed_host; + if (key == NULL) return 1; /* XXX ? */ f = fopen(filename, "a"); if (!f) return 0; - fprintf(f, "%s ", host); + + if (store_hash) { + if ((hashed_host = host_hash(host, NULL, 0)) == NULL) { + error("add_host_to_hostfile: host_hash failed"); + fclose(f); + return 0; + } + } + fprintf(f, "%s ", store_hash ? hashed_host : host); + if (key_write(key, f)) { success = 1; } else { diff --git a/hostfile.h b/hostfile.h index efcddc9f..d6330752 100644 --- a/hostfile.h +++ b/hostfile.h @@ -1,4 +1,4 @@ -/* $OpenBSD: hostfile.h,v 1.14 2003/11/10 16:23:41 jakob Exp $ */ +/* $OpenBSD: hostfile.h,v 1.15 2005/03/01 10:40:26 djm Exp $ */ /* * Author: Tatu Ylonen @@ -21,8 +21,13 @@ typedef enum { int hostfile_read_key(char **, u_int *, Key *); HostStatus check_host_in_hostfile(const char *, const char *, const Key *, Key *, int *); -int add_host_to_hostfile(const char *, const char *, const Key *); +int add_host_to_hostfile(const char *, const char *, const Key *, int); int lookup_key_in_hostfile_by_type(const char *, const char *, int, Key *, int *); +#define HASH_MAGIC "|1|" +#define HASH_DELIM '|' + +char *host_hash(const char *, const char *, u_int); + #endif diff --git a/readconf.c b/readconf.c index c3dc71e6..e50a4222 100644 --- a/readconf.c +++ b/readconf.c @@ -12,7 +12,7 @@ */ #include "includes.h" -RCSID("$OpenBSD: readconf.c,v 1.135 2005/03/01 10:09:52 djm Exp $"); +RCSID("$OpenBSD: readconf.c,v 1.136 2005/03/01 10:40:26 djm Exp $"); #include "ssh.h" #include "xmalloc.h" @@ -106,7 +106,7 @@ typedef enum { oEnableSSHKeysign, oRekeyLimit, oVerifyHostKeyDNS, oConnectTimeout, oAddressFamily, oGssAuthentication, oGssDelegateCreds, oServerAliveInterval, oServerAliveCountMax, oIdentitiesOnly, - oSendEnv, oControlPath, oControlMaster, + oSendEnv, oControlPath, oControlMaster, oHashKnownHosts, oDeprecated, oUnsupported } OpCodes; @@ -197,6 +197,7 @@ static struct { { "sendenv", oSendEnv }, { "controlpath", oControlPath }, { "controlmaster", oControlMaster }, + { "hashknownhosts", oHashKnownHosts }, { NULL, oBadOption } }; @@ -790,6 +791,10 @@ parse_int: intptr = &options->control_master; goto parse_yesnoask; + case oHashKnownHosts: + intptr = &options->hash_known_hosts; + goto parse_flag; + case oDeprecated: debug("%s line %d: Deprecated option \"%s\"", filename, linenum, keyword); @@ -933,6 +938,7 @@ initialize_options(Options * options) options->num_send_env = 0; options->control_path = NULL; options->control_master = -1; + options->hash_known_hosts = -1; } /* @@ -1055,6 +1061,8 @@ fill_default_options(Options * options) options->server_alive_count_max = 3; if (options->control_master == -1) options->control_master = 0; + if (options->hash_known_hosts == -1) + options->hash_known_hosts = 0; /* options->proxy_command should not be set by default */ /* options->user will be set in the main program if appropriate */ /* options->hostname will be set in the main program if appropriate */ diff --git a/readconf.h b/readconf.h index 03b772a2..de4b4cb2 100644 --- a/readconf.h +++ b/readconf.h @@ -1,4 +1,4 @@ -/* $OpenBSD: readconf.h,v 1.65 2005/03/01 10:09:52 djm Exp $ */ +/* $OpenBSD: readconf.h,v 1.66 2005/03/01 10:40:27 djm Exp $ */ /* * Author: Tatu Ylonen @@ -112,6 +112,8 @@ typedef struct { char *control_path; int control_master; + + int hash_known_hosts; } Options; diff --git a/ssh.1 b/ssh.1 index 27da08c6..c371b7cf 100644 --- a/ssh.1 +++ b/ssh.1 @@ -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. .\" -.\" $OpenBSD: ssh.1,v 1.200 2005/03/01 10:09:52 djm Exp $ +.\" $OpenBSD: ssh.1,v 1.201 2005/03/01 10:40:27 djm Exp $ .Dd September 25, 1999 .Dt SSH 1 .Os @@ -701,6 +701,7 @@ For full details of the options listed below, and their possible values, see .It GlobalKnownHostsFile .It GSSAPIAuthentication .It GSSAPIDelegateCredentials +.It HashKnownHosts .It Host .It HostbasedAuthentication .It HostKeyAlgorithms diff --git a/ssh_config.5 b/ssh_config.5 index 6b6cfc5e..9077acbe 100644 --- a/ssh_config.5 +++ b/ssh_config.5 @@ -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. .\" -.\" $OpenBSD: ssh_config.5,v 1.43 2005/03/01 10:09:52 djm Exp $ +.\" $OpenBSD: ssh_config.5,v 1.44 2005/03/01 10:40:27 djm Exp $ .Dd September 25, 1999 .Dt SSH_CONFIG 5 .Os @@ -407,6 +407,21 @@ Forward (delegate) credentials to the server. The default is .Dq no . Note that this option applies to protocol version 2 only. +.It Cm HashKnownHosts +Indicates that +.Nm ssh +should hash host names and addresses when they are added to +.Pa $HOME/.ssh/known_hosts . +These hashed names may be used normally by +.Nm ssh +and +.Nm sshd , +but they do not reveal identifying information should the file's contents +be disclosed. +The default is +.Dq no . +Note that hashing of names and addresses will not be retrospectively applied +to existing known hosts files. .It Cm HostbasedAuthentication Specifies whether to try rhosts based authentication with public key authentication. diff --git a/sshconnect.c b/sshconnect.c index 33ec4753..bafe7ba9 100644 --- a/sshconnect.c +++ b/sshconnect.c @@ -13,7 +13,7 @@ */ #include "includes.h" -RCSID("$OpenBSD: sshconnect.c,v 1.159 2005/01/05 08:51:32 markus Exp $"); +RCSID("$OpenBSD: sshconnect.c,v 1.160 2005/03/01 10:40:27 djm Exp $"); #include @@ -678,7 +678,7 @@ check_host_key(char *host, struct sockaddr *hostaddr, Key *host_key, "'%.128s' not in list of known hosts.", type, ip); else if (!add_host_to_hostfile(user_hostfile, ip, - host_key)) + host_key, options.hash_known_hosts)) logit("Failed to add the %s host key for IP " "address '%.128s' to the list of known " "hosts (%.30s).", type, ip, user_hostfile); @@ -744,7 +744,8 @@ check_host_key(char *host, struct sockaddr *hostaddr, Key *host_key, * If not in strict mode, add the key automatically to the * local known_hosts file. */ - if (!add_host_to_hostfile(user_hostfile, hostp, host_key)) + if (!add_host_to_hostfile(user_hostfile, hostp, host_key, + options.hash_known_hosts)) logit("Failed to add the host to the list of known " "hosts (%.500s).", user_hostfile); else diff --git a/sshd.8 b/sshd.8 index 9d30369c..05b88f59 100644 --- a/sshd.8 +++ b/sshd.8 @@ -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. .\" -.\" $OpenBSD: sshd.8,v 1.204 2005/02/25 10:55:13 jmc Exp $ +.\" $OpenBSD: sshd.8,v 1.205 2005/03/01 10:40:27 djm Exp $ .Dd September 25, 1999 .Dt SSHD 8 .Os @@ -553,6 +553,14 @@ to indicate negation: if the host name matches a negated pattern, it is not accepted (by that line) even if it matched another pattern on the line. .Pp +Alternately, hostnames may be stored in a hashed form which hides host names +and addresses should the file's contents be disclosed. Hashed hostnames start +with a +.Ql \&| +character. +Only one hashed hostname may appear on a single line and none of the above +negation or wildcard operators may be applied. +.Pp Bits, exponent, and modulus are taken directly from the RSA host key; they can be obtained, e.g., from .Pa /etc/ssh/ssh_host_key.pub . @@ -584,6 +592,11 @@ and adding the host names at the front. closenet,...,130.233.208.41 1024 37 159...93 closenet.hut.fi cvs.openbsd.org,199.185.137.3 ssh-rsa AAAA1234.....= .Ed +.Bd -literal +# A hashed hostname +|1|JfKTdBh7rNbXkVAQCRp4OQoPfmI=|USECr3SWf1JUPsms5AqfD5QfxkM= ssh-rsa +AAAA1234.....= +.Ed .Sh FILES .Bl -tag -width Ds .It Pa /etc/ssh/sshd_config -- cgit v1.2.3