summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDamien Miller <djm@mindrot.org>2014-02-24 15:57:55 +1100
committerDamien Miller <djm@mindrot.org>2014-02-24 15:57:55 +1100
commit13f97b2286142fd0b8eab94e4ce84fe124eeb752 (patch)
treeb332b16a032b4670b60db1261b6dac48e059d916
parentbee3a234f3d1ad4244952bcff1b4b7c525330dc2 (diff)
- 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@
-rw-r--r--ChangeLog16
-rw-r--r--readconf.c27
-rw-r--r--readconf.h4
-rw-r--r--ssh.c150
-rw-r--r--ssh_config.510
5 files changed, 145 insertions, 62 deletions
diff --git a/ChangeLog b/ChangeLog
index e05b8698..48d49dea 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -16,6 +16,22 @@
[ssh-ed25519.c]
check for unsigned overflow; not reachable in OpenSSH but others might
copy our code...
+ - 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@
20140213
- (dtucker) [configure.ac openbsd-compat/openssl-compat.{c,h}] Add compat
diff --git a/readconf.c b/readconf.c
index 94e64590..dc884c9b 100644
--- a/readconf.c
+++ b/readconf.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: readconf.c,v 1.217 2014/02/22 01:32:19 djm Exp $ */
+/* $OpenBSD: readconf.c,v 1.218 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
@@ -1467,6 +1467,13 @@ read_config_file(const char *filename, struct passwd *pw, const char *host,
return 1;
}
+/* Returns 1 if a string option is unset or set to "none" or 0 otherwise. */
+int
+option_clear_or_none(const char *o)
+{
+ return o == NULL || strcasecmp(o, "none") == 0;
+}
+
/*
* Initializes options to special values that indicate that they have not yet
* been set. Read_config_file will only set options with this value. Options
@@ -1564,10 +1571,24 @@ initialize_options(Options * options)
}
/*
+ * A petite version of fill_default_options() that just fills the options
+ * needed for hostname canonicalization to proceed.
+ */
+void
+fill_default_options_for_canonicalization(Options *options)
+{
+ if (options->canonicalize_max_dots == -1)
+ options->canonicalize_max_dots = 1;
+ if (options->canonicalize_fallback_local == -1)
+ options->canonicalize_fallback_local = 1;
+ if (options->canonicalize_hostname == -1)
+ options->canonicalize_hostname = SSH_CANONICALISE_NO;
+}
+
+/*
* Called after processing other sources of option data, this fills those
* options for which no value has been specified with their default values.
*/
-
void
fill_default_options(Options * options)
{
@@ -1722,7 +1743,7 @@ fill_default_options(Options * options)
options->canonicalize_hostname = SSH_CANONICALISE_NO;
#define CLEAR_ON_NONE(v) \
do { \
- if (v != NULL && strcasecmp(v, "none") == 0) { \
+ if (option_clear_or_none(v)) { \
free(v); \
v = NULL; \
} \
diff --git a/readconf.h b/readconf.h
index 9723da07..75e3f8f7 100644
--- a/readconf.h
+++ b/readconf.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: readconf.h,v 1.100 2014/01/29 06:18:35 djm Exp $ */
+/* $OpenBSD: readconf.h,v 1.101 2014/02/23 20:11:36 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -176,12 +176,14 @@ typedef struct {
void initialize_options(Options *);
void fill_default_options(Options *);
+void fill_default_options_for_canonicalization(Options *);
int process_config_line(Options *, struct passwd *, const char *, char *,
const char *, int, int *, int);
int read_config_file(const char *, struct passwd *, const char *,
Options *, int);
int parse_forward(Forward *, const char *, int, int);
int default_ssh_port(void);
+int option_clear_or_none(const char *);
void add_local_forward(Options *, const Forward *);
void add_remote_forward(Options *, const Forward *);
diff --git a/ssh.c b/ssh.c
index add760ca..b7dbea2b 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 @@ tilde_expand_paths(char **paths, u_int num_paths)
}
}
+/*
+ * 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 @@ resolve_host(const char *name, u_int port, int logerr, char *cname, size_t clen)
/*
* 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 @@ check_follow_cname(char **namep, const char *cname)
* 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 @@ check_follow_cname(char **namep, const char *cname)
* 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 @@ resolve_canonicalize(char **hostp, u_int port)
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 @@ resolve_canonicalize(char **hostp, u_int port)
*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 @@ resolve_canonicalize(char **hostp, u_int port)
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 @@ main(int ac, char **av)
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 */
@@ -899,37 +968,6 @@ main(int ac, char **av)
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));
diff --git a/ssh_config.5 b/ssh_config.5
index 3cadcd76..b5803920 100644
--- a/ssh_config.5
+++ b/ssh_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: ssh_config.5,v 1.184 2014/01/19 04:48:08 djm Exp $
-.Dd $Mdocdate: January 19 2014 $
+.\" $OpenBSD: ssh_config.5,v 1.185 2014/02/23 20:11:36 djm Exp $
+.Dd $Mdocdate: February 23 2014 $
.Dt SSH_CONFIG 5
.Os
.Sh NAME
@@ -263,6 +263,12 @@ If
is set to
.Dq always ,
then canonicalization is applied to proxied connections too.
+.Pp
+If this option is enabled and canonicalisation results in the target hostname
+changing, then the configuration files are processed again using the new
+target name to pick up any new configuration in matching
+.Cm Host
+stanzas.
.It Cm CanonicalizeMaxDots
Specifies the maximum number of dot characters in a hostname before
canonicalization is disabled.