From: djm Date: Tue, 23 Jan 2018 05:06:25 +0000 (+0000) Subject: don't attempt to force hostnames that are addresses to lowercase, but X-Git-Url: http://artulab.com/gitweb/?a=commitdiff_plain;h=aed3a8d5113d031feb7929243a101d76538afa5f;p=openbsd don't attempt to force hostnames that are addresses to lowercase, but instead canonicalise them through getnameinfo/getaddrinfo to remove ambiguities (e.g. ::0001 => ::1) before they are matched against known_hosts; bz#2763, ok dtucker@ --- diff --git a/usr.bin/ssh/ssh.c b/usr.bin/ssh/ssh.c index 3eb956341c6..0c15c1978e9 100644 --- a/usr.bin/ssh/ssh.c +++ b/usr.bin/ssh/ssh.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh.c,v 1.469 2017/11/01 00:04:15 djm Exp $ */ +/* $OpenBSD: ssh.c,v 1.470 2018/01/23 05:06:25 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -255,6 +255,40 @@ resolve_host(const char *name, int port, int logerr, char *cname, size_t clen) return res; } +/* Returns non-zero if name can only be an address and not a hostname */ +static int +is_addr_fast(const char *name) +{ + return (strchr(name, '%') != NULL || strchr(name, ':') != NULL || + strspn(name, "0123456789.") == strlen(name)); +} + +/* Returns non-zero if name represents a valid, single address */ +static int +is_addr(const char *name) +{ + char strport[NI_MAXSERV]; + struct addrinfo hints, *res; + + if (is_addr_fast(name)) + return 1; + + snprintf(strport, sizeof strport, "%u", default_ssh_port()); + memset(&hints, 0, sizeof(hints)); + hints.ai_family = options.address_family == -1 ? + AF_UNSPEC : options.address_family; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_NUMERICHOST|AI_NUMERICSERV; + if (getaddrinfo(name, strport, &hints, &res) != 0) + return 0; + if (res == NULL || res->ai_next != NULL) { + freeaddrinfo(res); + return 0; + } + freeaddrinfo(res); + return 1; +} + /* * Attempt to resolve a numeric host address / port to a single address. * Returns a canonical address string. @@ -360,20 +394,10 @@ resolve_canonicalize(char **hostp, int port) char *cp, *fullhost, newname[NI_MAXHOST]; struct addrinfo *addrs; - if (options.canonicalize_hostname == SSH_CANONICALISE_NO) - return NULL; - /* - * Don't attempt to canonicalize names that will be interpreted by - * a proxy unless the user specifically requests so. + * Attempt to canonicalise addresses, regardless of + * whether hostname canonicalisation was requested */ - direct = option_clear_or_none(options.proxy_command) && - options.jump_host == NULL; - if (!direct && - options.canonicalize_hostname != SSH_CANONICALISE_ALWAYS) - return NULL; - - /* Try numeric hostnames first */ if ((addrs = resolve_addr(*hostp, port, newname, sizeof(newname))) != NULL) { debug2("%s: hostname %.100s is address", __func__, *hostp); @@ -386,6 +410,30 @@ resolve_canonicalize(char **hostp, int port) return addrs; } + /* + * If this looks like an address but didn't parse as one, it might + * be an address with an invalid interface scope. Skip further + * attempts at canonicalisation. + */ + if (is_addr_fast(*hostp)) { + debug("%s: hostname %.100s is an unrecognised address", + __func__, *hostp); + return NULL; + } + + if (options.canonicalize_hostname == SSH_CANONICALISE_NO) + return NULL; + + /* + * Don't attempt to canonicalize names that will be interpreted by + * a proxy unless the user specifically requests so. + */ + direct = option_clear_or_none(options.proxy_command) && + options.jump_host == NULL; + if (!direct && + options.canonicalize_hostname != SSH_CANONICALISE_ALWAYS) + return NULL; + /* If domain name is anchored, then resolve it now */ if ((*hostp)[strlen(*hostp) - 1] == '.') { debug3("%s: name is fully qualified", __func__); @@ -498,7 +546,7 @@ main(int ac, char **av) { struct ssh *ssh = NULL; int i, r, opt, exit_status, use_syslog, direct, timeout_ms; - int config_test = 0, opt_terminated = 0; + int was_addr, config_test = 0, opt_terminated = 0; char *p, *cp, *line, *argv0, buf[PATH_MAX], *logfile; char cname[NI_MAXHOST]; struct stat st; @@ -1024,9 +1072,15 @@ main(int ac, char **av) options.hostname = xstrdup(host); } - /* If canonicalization requested then try to apply it */ - lowercase(host); - if (options.canonicalize_hostname != SSH_CANONICALISE_NO) + /* Don't lowercase addresses, they will be explicitly canonicalised */ + if ((was_addr = is_addr(host)) == 0) + lowercase(host); + + /* + * Try to canonicalize if requested by configuration or the + * hostname is an address. + */ + if (options.canonicalize_hostname != SSH_CANONICALISE_NO || was_addr) addrs = resolve_canonicalize(&host, options.port); /*