=================================================================== RCS file: /cvsrepo/anoncvs/cvs/src/usr.bin/ssh/ssh.c,v retrieving revision 1.399 retrieving revision 1.400 diff -u -r1.399 -r1.400 --- src/usr.bin/ssh/ssh.c 2014/02/04 00:24:29 1.399 +++ src/usr.bin/ssh/ssh.c 2014/02/23 20:11:36 1.400 @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh.c,v 1.399 2014/02/04 00:24:29 djm Exp $ */ +/* $OpenBSD: ssh.c,v 1.400 2014/02/23 20:11:36 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -218,16 +218,26 @@ } } +/* + * Attempt to resolve a host name / port to a set of addresses and + * optionally return any CNAMEs encountered along the way. + * Returns NULL on failure. + * NB. this function must operate with a options having undefined members. + */ static struct addrinfo * -resolve_host(const char *name, u_int port, int logerr, char *cname, size_t clen) +resolve_host(const char *name, int port, int logerr, char *cname, size_t clen) { char strport[NI_MAXSERV]; struct addrinfo hints, *res; int gaierr, loglevel = SYSLOG_LEVEL_DEBUG1; + if (port <= 0) + port = default_ssh_port(); + snprintf(strport, sizeof strport, "%u", port); memset(&hints, 0, sizeof(hints)); - hints.ai_family = options.address_family; + hints.ai_family = options.address_family == -1 ? + AF_UNSPEC : options.address_family; hints.ai_socktype = SOCK_STREAM; if (cname != NULL) hints.ai_flags = AI_CANONNAME; @@ -252,6 +262,7 @@ /* * Check whether the cname is a permitted replacement for the hostname * and perform the replacement if it is. + * NB. this function must operate with a options having undefined members. */ static int check_follow_cname(char **namep, const char *cname) @@ -268,7 +279,7 @@ * Don't attempt to canonicalize names that will be interpreted by * a proxy unless the user specifically requests so. */ - if (options.proxy_command != NULL && + if (!option_clear_or_none(options.proxy_command) && options.canonicalize_hostname != SSH_CANONICALISE_ALWAYS) return 0; debug3("%s: check \"%s\" CNAME \"%s\"", __func__, *namep, cname); @@ -292,9 +303,10 @@ * Attempt to resolve the supplied hostname after applying the user's * canonicalization rules. Returns the address list for the host or NULL * if no name was found after canonicalization. + * NB. this function must operate with a options having undefined members. */ static struct addrinfo * -resolve_canonicalize(char **hostp, u_int port) +resolve_canonicalize(char **hostp, int port) { int i, ndots; char *cp, *fullhost, cname_target[NI_MAXHOST]; @@ -302,13 +314,15 @@ 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. */ - if (options.proxy_command != NULL && + if (!option_clear_or_none(options.proxy_command) && options.canonicalize_hostname != SSH_CANONICALISE_ALWAYS) return NULL; + /* Don't apply canonicalization to sufficiently-qualified hostnames */ ndots = 0; for (cp = *hostp; *cp != '\0'; cp++) { @@ -325,7 +339,9 @@ *cname_target = '\0'; xasprintf(&fullhost, "%s.%s.", *hostp, options.canonical_domains[i]); - if ((addrs = resolve_host(fullhost, options.port, 0, + debug3("%s: attempting \"%s\" => \"%s\"", __func__, + *hostp, fullhost); + if ((addrs = resolve_host(fullhost, port, 0, cname_target, sizeof(cname_target))) == NULL) { free(fullhost); continue; @@ -342,11 +358,41 @@ return addrs; } if (!options.canonicalize_fallback_local) - fatal("%s: Could not resolve host \"%s\"", __progname, host); + fatal("%s: Could not resolve host \"%s\"", __progname, *hostp); + debug2("%s: host %s not found in any suffix", __func__, *hostp); return NULL; } /* + * Read per-user configuration file. Ignore the system wide config + * file if the user specifies a config file on the command line. + */ +static void +process_config_files(struct passwd *pw) +{ + char buf[MAXPATHLEN]; + int r; + + if (config != NULL) { + if (strcasecmp(config, "none") != 0 && + !read_config_file(config, pw, host, &options, + SSHCONF_USERCONF)) + fatal("Can't open user config file %.100s: " + "%.100s", config, strerror(errno)); + } else { + r = snprintf(buf, sizeof buf, "%s/%s", pw->pw_dir, + _PATH_SSH_USER_CONFFILE); + if (r > 0 && (size_t)r < sizeof(buf)) + (void)read_config_file(buf, pw, host, &options, + SSHCONF_CHECKPERM|SSHCONF_USERCONF); + + /* Read systemwide configuration file after user config. */ + (void)read_config_file(_PATH_HOST_CONFIG_FILE, pw, host, + &options, 0); + } +} + +/* * Main program for the ssh client. */ int @@ -804,31 +850,54 @@ if (debug_flag) logit("%s, %s", SSH_VERSION, SSLeay_version(SSLEAY_VERSION)); + /* Parse the configuration files */ + process_config_files(pw); + + /* Hostname canonicalisation needs a few options filled. */ + fill_default_options_for_canonicalization(&options); + + /* If the user has replaced the hostname then take it into use now */ + if (options.hostname != NULL) { + /* NB. Please keep in sync with readconf.c:match_cfg_line() */ + cp = percent_expand(options.hostname, + "h", host, (char *)NULL); + free(host); + host = cp; + } + + /* If canonicalization requested then try to apply it */ + lowercase(host); + if (options.canonicalize_hostname != SSH_CANONICALISE_NO) + addrs = resolve_canonicalize(&host, options.port); + /* - * Read per-user configuration file. Ignore the system wide config - * file if the user specifies a config file on the command line. + * If canonicalization not requested, or if it failed then try to + * resolve the bare hostname name using the system resolver's usual + * search rules. Skip the lookup if a ProxyCommand is being used + * unless the user has specifically requested canonicalisation. */ - if (config != NULL) { - if (strcasecmp(config, "none") != 0 && - !read_config_file(config, pw, host, &options, - SSHCONF_USERCONF)) - fatal("Can't open user config file %.100s: " - "%.100s", config, strerror(errno)); - } else { - r = snprintf(buf, sizeof buf, "%s/%s", pw->pw_dir, - _PATH_SSH_USER_CONFFILE); - if (r > 0 && (size_t)r < sizeof(buf)) - (void)read_config_file(buf, pw, host, &options, - SSHCONF_CHECKPERM|SSHCONF_USERCONF); + if (addrs == NULL && (option_clear_or_none(options.proxy_command) || + options.canonicalize_hostname == SSH_CANONICALISE_ALWAYS)) { + if ((addrs = resolve_host(host, options.port, 1, + cname, sizeof(cname))) == NULL) + cleanup_exit(255); /* resolve_host logs the error */ + check_follow_cname(&host, cname); + } - /* Read systemwide configuration file after user config. */ - (void)read_config_file(_PATH_HOST_CONFIG_FILE, pw, host, - &options, 0); + /* + * If the target hostname has changed as a result of canonicalisation + * then re-parse the configuration files as new stanzas may match. + */ + if (strcasecmp(host_arg, host) != 0) { + debug("Hostname has changed; re-reading configuration"); + process_config_files(pw); } /* Fill configuration defaults. */ fill_default_options(&options); + if (options.port == 0) + options.port = default_ssh_port(); channel_set_af(options.address_family); /* Tidy and check options */ @@ -866,37 +935,6 @@ if (options.user == NULL) options.user = xstrdup(pw->pw_name); - - /* Get default port if port has not been set. */ - if (options.port == 0) - options.port = default_ssh_port(); - - /* preserve host name given on command line for %n expansion */ - if (options.hostname != NULL) { - /* NB. Please keep in sync with readconf.c:match_cfg_line() */ - cp = percent_expand(options.hostname, - "h", host, (char *)NULL); - free(host); - host = cp; - } - - /* If canonicalization requested then try to apply it */ - lowercase(host); - if (options.canonicalize_hostname != SSH_CANONICALISE_NO) - addrs = resolve_canonicalize(&host, options.port); - /* - * If canonicalization not requested, or if it failed then try to - * resolve the bare hostname name using the system resolver's usual - * search rules. Skip the lookup if a ProxyCommand is being used - * unless the user has specifically requested canonicalisation. - */ - if (addrs == NULL && (options.proxy_command == NULL || - options.canonicalize_hostname == SSH_CANONICALISE_ALWAYS)) { - if ((addrs = resolve_host(host, options.port, 1, - cname, sizeof(cname))) == NULL) - cleanup_exit(255); /* resolve_host logs the error */ - check_follow_cname(&host, cname); - } if (gethostname(thishost, sizeof(thishost)) == -1) fatal("gethostname: %s", strerror(errno));