- 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:
parent
bee3a234f3
commit
13f97b2286
16
ChangeLog
16
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
|
||||
|
|
27
readconf.c
27
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
|
||||
|
@ -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; \
|
||||
} \
|
||||
|
|
|
@ -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
154
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,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));
|
||||
|
|
10
ssh_config.5
10
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.
|
||||
|
|
Loading…
Reference in New Issue