diff options
author | Damien Miller <djm@cvs.openbsd.org> | 2014-02-23 20:11:37 +0000 |
---|---|---|
committer | Damien Miller <djm@cvs.openbsd.org> | 2014-02-23 20:11:37 +0000 |
commit | 43aafbb4ead7bf4359e0f1501d324f52ab96c40b (patch) | |
tree | ca2d544043e6b5be91d9783c9cf94fff582d068d /usr.bin | |
parent | cb8b967139c39be7e9d2b12eb8bf0c913e3abe68 (diff) |
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@
Diffstat (limited to 'usr.bin')
-rw-r--r-- | usr.bin/ssh/readconf.c | 27 | ||||
-rw-r--r-- | usr.bin/ssh/readconf.h | 4 | ||||
-rw-r--r-- | usr.bin/ssh/ssh.c | 150 | ||||
-rw-r--r-- | usr.bin/ssh/ssh_config.5 | 10 |
4 files changed, 129 insertions, 62 deletions
diff --git a/usr.bin/ssh/readconf.c b/usr.bin/ssh/readconf.c index 42db3fa4f86..35dbd59dd66 100644 --- a/usr.bin/ssh/readconf.c +++ b/usr.bin/ssh/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 @@ -1458,6 +1458,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 @@ -1555,10 +1562,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) { @@ -1711,7 +1732,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/usr.bin/ssh/readconf.h b/usr.bin/ssh/readconf.h index 9723da07892..75e3f8f7ae3 100644 --- a/usr.bin/ssh/readconf.h +++ b/usr.bin/ssh/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/usr.bin/ssh/ssh.c b/usr.bin/ssh/ssh.c index aa51fd1f7d8..db6de182687 100644 --- a/usr.bin/ssh/ssh.c +++ b/usr.bin/ssh/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 @@ -218,16 +218,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; @@ -252,6 +262,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) @@ -268,7 +279,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); @@ -292,9 +303,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]; @@ -302,13 +314,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++) { @@ -325,7 +339,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; @@ -342,11 +358,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 @@ -804,31 +850,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 */ @@ -867,37 +936,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/usr.bin/ssh/ssh_config.5 b/usr.bin/ssh/ssh_config.5 index 3cadcd76778..b5803920f60 100644 --- a/usr.bin/ssh/ssh_config.5 +++ b/usr.bin/ssh/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. |