- djm@cvs.openbsd.org 2014/02/23 20:11:36
[readconf.c readconf.h ssh.c ssh_config.5]
reparse ssh_config and ~/.ssh/config if hostname canonicalisation changes
the hostname. This allows users to write configurations that always
refer to canonical hostnames, e.g.
CanonicalizeHostname yes
CanonicalDomains int.example.org example.org
CanonicalizeFallbackLocal no
Host *.int.example.org
Compression off
Host *.example.org
User djm
ok markus@
diff --git a/ssh.c b/ssh.c
index add760c..b7dbea2 100644
--- a/ssh.c
+++ b/ssh.c
@@ -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 <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -231,16 +231,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;
@@ -265,6 +275,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)
@@ -281,7 +292,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);
@@ -305,9 +316,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];
@@ -315,13 +327,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++) {
@@ -338,7 +352,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;
@@ -355,11 +371,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
@@ -832,31 +878,54 @@
if (debug_flag)
logit("%s, %s", SSH_VERSION, SSLeay_version(SSLEAY_VERSION));
- /*
- * Read per-user configuration file. Ignore the system wide config
- * file if the user specifies a config file on the command line.
- */
- 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);
+ /* Parse the configuration files */
+ process_config_files(pw);
- /* Read systemwide configuration file after user config. */
- (void)read_config_file(_PATH_HOST_CONFIG_FILE, pw, host,
- &options, 0);
+ /* 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);
+
+ /*
+ * 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 && (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);
+ }
+
+ /*
+ * 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 */
@@ -899,37 +968,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));
strlcpy(shorthost, thishost, sizeof(shorthost));