- 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@
This commit is contained in:
Damien Miller 2014-02-24 15:57:55 +11:00
parent bee3a234f3
commit 13f97b2286
5 changed files with 147 additions and 64 deletions

View File

@ -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

View File

@ -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
@ -1563,11 +1570,25 @@ initialize_options(Options * options)
options->canonicalize_hostname = -1;
}
/*
* 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; \
} \

View File

@ -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 *);

154
ssh.c
View File

@ -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,10 +371,40 @@ 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.
*/
@ -832,31 +878,54 @@ main(int ac, char **av)
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 @@ 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));

View File

@ -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.