[openssh-commits] [openssh] 13/13: upstream commit

git+noreply at mindrot.org git+noreply at mindrot.org
Mon Oct 13 11:42:19 EST 2014


This is an automated email from the git hooks/post-receive script.

djm pushed a commit to branch master
in repository openssh.

commit 957fbceb0f3166e41b76fdb54075ab3b9cc84cba
Author: djm at openbsd.org <djm at openbsd.org>
Date:   Wed Oct 8 22:20:25 2014 +0000

    upstream commit
    
    Tweak config reparsing with host canonicalisation
    
    Make the second pass through the config files always run when
    hostname canonicalisation is enabled.
    
    Add a "Match canonical" criteria that allows ssh_config Match
    blocks to trigger only in the second config pass.
    
    Add a -G option to ssh that causes it to parse its configuration
    and dump the result to stdout, similar to "sshd -T"
    
    Allow ssh_config Port options set in the second config parse
    phase to be applied (they were being ignored).
    
    bz#2267 bz#2286; ok markus
---
 readconf.c    | 450 +++++++++++++++++++++++++++++++++++++++++++++++++---------
 readconf.h    |  10 +-
 ssh-keysign.c |   4 +-
 ssh.1         |  14 +-
 ssh.c         |  80 ++++++++---
 ssh_config.5  |  55 +++++--
 6 files changed, 504 insertions(+), 109 deletions(-)

diff --git a/readconf.c b/readconf.c
index 7948ce1..869a1c4 100644
--- a/readconf.c
+++ b/readconf.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: readconf.c,v 1.220 2014/07/15 15:54:14 millert Exp $ */
+/* $OpenBSD: readconf.c,v 1.221 2014/10/08 22:20:25 djm Exp $ */
 /*
  * Author: Tatu Ylonen <ylo at cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo at cs.hut.fi>, Espoo, Finland
@@ -41,6 +41,9 @@
 #ifdef HAVE_UTIL_H
 #include <util.h>
 #endif
+#if defined(HAVE_STRNVIS) && defined(HAVE_VIS_H) && !defined(BROKEN_STRNVIS)
+# include <vis.h>
+#endif
 
 #include "xmalloc.h"
 #include "ssh.h"
@@ -56,6 +59,7 @@
 #include "kex.h"
 #include "mac.h"
 #include "uidswap.h"
+#include "myproposal.h"
 
 /* Format of the configuration file:
 
@@ -135,7 +139,7 @@ typedef enum {
 	oBatchMode, oCheckHostIP, oStrictHostKeyChecking, oCompression,
 	oCompressionLevel, oTCPKeepAlive, oNumberOfPasswordPrompts,
 	oUsePrivilegedPort, oLogLevel, oCiphers, oProtocol, oMacs,
-	oGlobalKnownHostsFile2, oUserKnownHostsFile2, oPubkeyAuthentication,
+	oPubkeyAuthentication,
 	oKbdInteractiveAuthentication, oKbdInteractiveDevices, oHostKeyAlias,
 	oDynamicForward, oPreferredAuthentications, oHostbasedAuthentication,
 	oHostKeyAlgorithms, oBindAddress, oPKCS11Provider,
@@ -212,7 +216,7 @@ static struct {
 	{ "globalknownhostsfile", oGlobalKnownHostsFile },
 	{ "globalknownhostsfile2", oDeprecated },
 	{ "userknownhostsfile", oUserKnownHostsFile },
-	{ "userknownhostsfile2", oDeprecated }, 
+	{ "userknownhostsfile2", oDeprecated },
 	{ "connectionattempts", oConnectionAttempts },
 	{ "batchmode", oBatchMode },
 	{ "checkhostip", oCheckHostIP },
@@ -466,7 +470,7 @@ execute_in_shell(const char *cmd)
 	if (!WIFEXITED(status)) {
 		error("command '%.100s' exited abnormally", cmd);
 		return -1;
-	} 
+	}
 	debug3("command returned status %d", WEXITSTATUS(status));
 	return WEXITSTATUS(status);
 }
@@ -476,11 +480,12 @@ execute_in_shell(const char *cmd)
  */
 static int
 match_cfg_line(Options *options, char **condition, struct passwd *pw,
-    const char *host_arg, const char *filename, int linenum)
+    const char *host_arg, const char *original_host, int post_canon,
+    const char *filename, int linenum)
 {
-	char *arg, *attrib, *cmd, *cp = *condition, *host;
+	char *arg, *oattrib, *attrib, *cmd, *cp = *condition, *host, *criteria;
 	const char *ruser;
-	int r, port, result = 1, attributes = 0;
+	int r, port, this_result, result = 1, attributes = 0, negate;
 	size_t len;
 	char thishost[NI_MAXHOST], shorthost[NI_MAXHOST], portstr[NI_MAXSERV];
 
@@ -497,21 +502,38 @@ match_cfg_line(Options *options, char **condition, struct passwd *pw,
 	} else
 		host = xstrdup(host_arg);
 
-	debug3("checking match for '%s' host %s", cp, host);
-	while ((attrib = strdelim(&cp)) && *attrib != '\0') {
-		attributes++;
+	debug2("checking match for '%s' host %s originally %s",
+	    cp, host, original_host);
+	while ((oattrib = attrib = strdelim(&cp)) && *attrib != '\0') {
+		criteria = NULL;
+		this_result = 1;
+		if ((negate = attrib[0] == '!'))
+			attrib++;
+		/* criteria "all" and "canonical" have no argument */
 		if (strcasecmp(attrib, "all") == 0) {
-			if (attributes != 1 ||
+			if (attributes > 1 ||
 			    ((arg = strdelim(&cp)) != NULL && *arg != '\0')) {
-				error("'all' cannot be combined with other "
-				    "Match attributes");
+				error("%.200s line %d: '%s' cannot be combined "
+				    "with other Match attributes",
+				    filename, linenum, oattrib);
 				result = -1;
 				goto out;
 			}
-			*condition = cp;
-			result = 1;
+			if (result)
+				result = negate ? 0 : 1;
 			goto out;
 		}
+		attributes++;
+		if (strcasecmp(attrib, "canonical") == 0) {
+			r = !!post_canon;  /* force bitmask member to boolean */
+			if (r == (negate ? 1 : 0))
+				this_result = result = 0;
+			debug3("%.200s line %d: %smatched '%s'",
+			    filename, linenum,
+			    this_result ? "" : "not ", oattrib);
+			continue;
+		}
+		/* All other criteria require an argument */
 		if ((arg = strdelim(&cp)) == NULL || *arg == '\0') {
 			error("Missing Match criteria for %s", attrib);
 			result = -1;
@@ -519,31 +541,25 @@ match_cfg_line(Options *options, char **condition, struct passwd *pw,
 		}
 		len = strlen(arg);
 		if (strcasecmp(attrib, "host") == 0) {
-			if (match_hostname(host, arg, len) != 1)
-				result = 0;
-			else
-				debug("%.200s line %d: matched 'Host %.100s' ",
-				    filename, linenum, host);
+			criteria = xstrdup(host);
+			r = match_hostname(host, arg, len) == 1;
+			if (r == (negate ? 1 : 0))
+				this_result = result = 0;
 		} else if (strcasecmp(attrib, "originalhost") == 0) {
-			if (match_hostname(host_arg, arg, len) != 1)
-				result = 0;
-			else
-				debug("%.200s line %d: matched "
-				    "'OriginalHost %.100s' ",
-				    filename, linenum, host_arg);
+			criteria = xstrdup(original_host);
+			r = match_hostname(original_host, arg, len) == 1;
+			if (r == (negate ? 1 : 0))
+				this_result = result = 0;
 		} else if (strcasecmp(attrib, "user") == 0) {
-			if (match_pattern_list(ruser, arg, len, 0) != 1)
-				result = 0;
-			else
-				debug("%.200s line %d: matched 'User %.100s' ",
-				    filename, linenum, ruser);
+			criteria = xstrdup(ruser);
+			r = match_pattern_list(ruser, arg, len, 0) == 1;
+			if (r == (negate ? 1 : 0))
+				this_result = result = 0;
 		} else if (strcasecmp(attrib, "localuser") == 0) {
-			if (match_pattern_list(pw->pw_name, arg, len, 0) != 1)
-				result = 0;
-			else
-				debug("%.200s line %d: matched "
-				    "'LocalUser %.100s' ",
-				    filename, linenum, pw->pw_name);
+			criteria = xstrdup(pw->pw_name);
+			r = match_pattern_list(pw->pw_name, arg, len, 0) == 1;
+			if (r == (negate ? 1 : 0))
+				this_result = result = 0;
 		} else if (strcasecmp(attrib, "exec") == 0) {
 			if (gethostname(thishost, sizeof(thishost)) == -1)
 				fatal("gethostname: %s", strerror(errno));
@@ -556,47 +572,49 @@ match_cfg_line(Options *options, char **condition, struct passwd *pw,
 			    "d", pw->pw_dir,
 			    "h", host,
 			    "l", thishost,
-			    "n", host_arg,
+			    "n", original_host,
 			    "p", portstr,
 			    "r", ruser,
 			    "u", pw->pw_name,
 			    (char *)NULL);
 			if (result != 1) {
 				/* skip execution if prior predicate failed */
-				debug("%.200s line %d: skipped exec \"%.100s\"",
-				    filename, linenum, cmd);
-			} else {
-				r = execute_in_shell(cmd);
-				if (r == -1) {
-					fatal("%.200s line %d: match exec "
-					    "'%.100s' error", filename,
-					    linenum, cmd);
-				} else if (r == 0) {
-					debug("%.200s line %d: matched "
-					    "'exec \"%.100s\"'", filename,
-					    linenum, cmd);
-				} else {
-					debug("%.200s line %d: no match "
-					    "'exec \"%.100s\"'", filename,
-					    linenum, cmd);
-					result = 0;
-				}
+				debug3("%.200s line %d: skipped exec "
+				    "\"%.100s\"", filename, linenum, cmd);
+				free(cmd);
+				continue;
+			}
+			r = execute_in_shell(cmd);
+			if (r == -1) {
+				fatal("%.200s line %d: match exec "
+				    "'%.100s' error", filename,
+				    linenum, cmd);
 			}
+			criteria = xstrdup(cmd);
 			free(cmd);
+			/* Force exit status to boolean */
+			r = r == 0;
+			if (r == (negate ? 1 : 0))
+				this_result = result = 0;
 		} else {
 			error("Unsupported Match attribute %s", attrib);
 			result = -1;
 			goto out;
 		}
+		debug3("%.200s line %d: %smatched '%s \"%.100s\"' ",
+		    filename, linenum, this_result ? "": "not ",
+		    oattrib, criteria);
+		free(criteria);
 	}
 	if (attributes == 0) {
 		error("One or more attributes required for Match");
 		result = -1;
 		goto out;
 	}
-	debug3("match %sfound", result ? "" : "not ");
-	*condition = cp;
  out:
+	if (result != -1)
+		debug2("match %sfound", result ? "" : "not ");
+	*condition = cp;
 	free(host);
 	return result;
 }
@@ -719,7 +737,8 @@ static const struct multistate multistate_canonicalizehostname[] = {
 #define WHITESPACE " \t\r\n"
 int
 process_config_line(Options *options, struct passwd *pw, const char *host,
-    char *line, const char *filename, int linenum, int *activep, int userconfig)
+    const char *original_host, char *line, const char *filename,
+    int linenum, int *activep, int flags)
 {
 	char *s, **charptr, *endofnumber, *keyword, *arg, *arg2;
 	char **cpptr, fwdarg[256];
@@ -775,7 +794,9 @@ parse_time:
 		if (!arg || *arg == '\0')
 			fatal("%s line %d: missing time value.",
 			    filename, linenum);
-		if ((value = convtime(arg)) == -1)
+		if (strcmp(arg, "none") == 0)
+			value = -1;
+		else if ((value = convtime(arg)) == -1)
 			fatal("%s line %d: invalid time value.",
 			    filename, linenum);
 		if (*activep && *intptr == -1)
@@ -812,7 +833,7 @@ parse_time:
 	case oForwardX11Trusted:
 		intptr = &options->forward_x11_trusted;
 		goto parse_flag;
-	
+
 	case oForwardX11Timeout:
 		intptr = &options->forward_x11_timeout;
 		goto parse_time;
@@ -947,7 +968,8 @@ parse_time:
 			if (*intptr >= SSH_MAX_IDENTITY_FILES)
 				fatal("%.200s line %d: Too many identity files specified (max %d).",
 				    filename, linenum, SSH_MAX_IDENTITY_FILES);
-			add_identity_file(options, NULL, arg, userconfig);
+			add_identity_file(options, NULL,
+			    arg, flags & SSHCONF_USERCONF);
 		}
 		break;
 
@@ -1195,8 +1217,8 @@ parse_int:
 		if (cmdline)
 			fatal("Host directive not supported as a command-line "
 			    "option");
-		value = match_cfg_line(options, &s, pw, host,
-		    filename, linenum);
+		value = match_cfg_line(options, &s, pw, host, original_host,
+		    flags & SSHCONF_POSTCANON, filename, linenum);
 		if (value < 0)
 			fatal("%.200s line %d: Bad Match condition", filename,
 			    linenum);
@@ -1444,7 +1466,7 @@ parse_int:
 		return 0;
 
 	default:
-		fatal("process_config_line: Unimplemented opcode %d", opcode);
+		fatal("%s: Unimplemented opcode %d", __func__, opcode);
 	}
 
 	/* Check that there is no garbage at end of line. */
@@ -1464,7 +1486,7 @@ parse_int:
 
 int
 read_config_file(const char *filename, struct passwd *pw, const char *host,
-    Options *options, int flags)
+    const char *original_host, Options *options, int flags)
 {
 	FILE *f;
 	char line[1024];
@@ -1495,8 +1517,8 @@ read_config_file(const char *filename, struct passwd *pw, const char *host,
 	while (fgets(line, sizeof(line), f)) {
 		/* Update line number counter. */
 		linenum++;
-		if (process_config_line(options, pw, host, line, filename,
-		    linenum, &active, flags & SSHCONF_USERCONF) != 0)
+		if (process_config_line(options, pw, host, original_host,
+		    line, filename, linenum, &active, flags) != 0)
 			bad_options++;
 	}
 	fclose(f);
@@ -2009,3 +2031,295 @@ parse_forward(struct Forward *fwd, const char *fwdspec, int dynamicfwd, int remo
 	fwd->listen_path = NULL;
 	return (0);
 }
+
+/* XXX the following is a near-vebatim copy from servconf.c; refactor */
+static const char *
+fmt_multistate_int(int val, const struct multistate *m)
+{
+	u_int i;
+
+	for (i = 0; m[i].key != NULL; i++) {
+		if (m[i].value == val)
+			return m[i].key;
+	}
+	return "UNKNOWN";
+}
+
+static const char *
+fmt_intarg(OpCodes code, int val)
+{
+	if (val == -1)
+		return "unset";
+	switch (code) {
+	case oAddressFamily:
+		return fmt_multistate_int(val, multistate_addressfamily);
+	case oVerifyHostKeyDNS:
+	case oStrictHostKeyChecking:
+		return fmt_multistate_int(val, multistate_yesnoask);
+	case oControlMaster:
+		return fmt_multistate_int(val, multistate_controlmaster);
+	case oTunnel:
+		return fmt_multistate_int(val, multistate_tunnel);
+	case oRequestTTY:
+		return fmt_multistate_int(val, multistate_requesttty);
+	case oCanonicalizeHostname:
+		return fmt_multistate_int(val, multistate_canonicalizehostname);
+	case oProtocol:
+		switch (val) {
+		case SSH_PROTO_1:
+			return "1";
+		case SSH_PROTO_2:
+			return "2";
+		case (SSH_PROTO_1|SSH_PROTO_2):
+			return "2,1";
+		default:
+			return "UNKNOWN";
+		}
+	default:
+		switch (val) {
+		case 0:
+			return "no";
+		case 1:
+			return "yes";
+		default:
+			return "UNKNOWN";
+		}
+	}
+}
+
+static const char *
+lookup_opcode_name(OpCodes code)
+{
+	u_int i;
+
+	for (i = 0; keywords[i].name != NULL; i++)
+		if (keywords[i].opcode == code)
+			return(keywords[i].name);
+	return "UNKNOWN";
+}
+
+static void
+dump_cfg_int(OpCodes code, int val)
+{
+	printf("%s %d\n", lookup_opcode_name(code), val);
+}
+
+static void
+dump_cfg_fmtint(OpCodes code, int val)
+{
+	printf("%s %s\n", lookup_opcode_name(code), fmt_intarg(code, val));
+}
+
+static void
+dump_cfg_string(OpCodes code, const char *val)
+{
+	if (val == NULL)
+		return;
+	printf("%s %s\n", lookup_opcode_name(code), val);
+}
+
+static void
+dump_cfg_strarray(OpCodes code, u_int count, char **vals)
+{
+	u_int i;
+
+	for (i = 0; i < count; i++)
+		printf("%s %s\n", lookup_opcode_name(code), vals[i]);
+}
+
+static void
+dump_cfg_strarray_oneline(OpCodes code, u_int count, char **vals)
+{
+	u_int i;
+
+	printf("%s", lookup_opcode_name(code));
+	for (i = 0; i < count; i++)
+		printf(" %s",  vals[i]);
+	printf("\n");
+}
+
+static void
+dump_cfg_forwards(OpCodes code, u_int count, const struct Forward *fwds)
+{
+	const struct Forward *fwd;
+	u_int i;
+
+	/* oDynamicForward */
+	for (i = 0; i < count; i++) {
+		fwd = &fwds[i];
+		if (code == oDynamicForward &&
+		    strcmp(fwd->connect_host, "socks") != 0)
+			continue;
+		if (code == oLocalForward &&
+		    strcmp(fwd->connect_host, "socks") == 0)
+			continue;
+		printf("%s", lookup_opcode_name(code));
+		if (fwd->listen_port == PORT_STREAMLOCAL)
+			printf(" %s", fwd->listen_path);
+		else if (fwd->listen_host == NULL)
+			printf(" %d", fwd->listen_port);
+		else {
+			printf(" [%s]:%d",
+			    fwd->listen_host, fwd->listen_port);
+		}
+		if (code != oDynamicForward) {
+			if (fwd->connect_port == PORT_STREAMLOCAL)
+				printf(" %s", fwd->connect_path);
+			else if (fwd->connect_host == NULL)
+				printf(" %d", fwd->connect_port);
+			else {
+				printf(" [%s]:%d",
+				    fwd->connect_host, fwd->connect_port);
+			}
+		}
+		printf("\n");
+	}
+}
+
+void
+dump_client_config(Options *o, const char *host)
+{
+	int i;
+	char vbuf[5];
+
+	/* Most interesting options first: user, host, port */
+	dump_cfg_string(oUser, o->user);
+	dump_cfg_string(oHostName, host);
+	dump_cfg_int(oPort, o->port);
+
+	/* Flag options */
+	dump_cfg_fmtint(oAddressFamily, o->address_family);
+	dump_cfg_fmtint(oBatchMode, o->batch_mode);
+	dump_cfg_fmtint(oCanonicalizeFallbackLocal, o->canonicalize_fallback_local);
+	dump_cfg_fmtint(oCanonicalizeHostname, o->canonicalize_hostname);
+	dump_cfg_fmtint(oChallengeResponseAuthentication, o->challenge_response_authentication);
+	dump_cfg_fmtint(oCheckHostIP, o->check_host_ip);
+	dump_cfg_fmtint(oCompression, o->compression);
+	dump_cfg_fmtint(oControlMaster, o->control_master);
+	dump_cfg_fmtint(oEnableSSHKeysign, o->enable_ssh_keysign);
+	dump_cfg_fmtint(oExitOnForwardFailure, o->exit_on_forward_failure);
+	dump_cfg_fmtint(oForwardAgent, o->forward_agent);
+	dump_cfg_fmtint(oForwardX11, o->forward_x11);
+	dump_cfg_fmtint(oForwardX11Trusted, o->forward_x11_trusted);
+	dump_cfg_fmtint(oGatewayPorts, o->fwd_opts.gateway_ports);
+#ifdef GSSAPI
+	dump_cfg_fmtint(oGssAuthentication, o->gss_authentication);
+	dump_cfg_fmtint(oGssDelegateCreds, o->gss_deleg_creds);
+#endif /* GSSAPI */
+	dump_cfg_fmtint(oHashKnownHosts, o->hash_known_hosts);
+	dump_cfg_fmtint(oHostbasedAuthentication, o->hostbased_authentication);
+	dump_cfg_fmtint(oIdentitiesOnly, o->identities_only);
+	dump_cfg_fmtint(oKbdInteractiveAuthentication, o->kbd_interactive_authentication);
+	dump_cfg_fmtint(oNoHostAuthenticationForLocalhost, o->no_host_authentication_for_localhost);
+	dump_cfg_fmtint(oPasswordAuthentication, o->password_authentication);
+	dump_cfg_fmtint(oPermitLocalCommand, o->permit_local_command);
+	dump_cfg_fmtint(oProtocol, o->protocol);
+	dump_cfg_fmtint(oProxyUseFdpass, o->proxy_use_fdpass);
+	dump_cfg_fmtint(oPubkeyAuthentication, o->pubkey_authentication);
+	dump_cfg_fmtint(oRequestTTY, o->request_tty);
+	dump_cfg_fmtint(oRhostsRSAAuthentication, o->rhosts_rsa_authentication);
+	dump_cfg_fmtint(oRSAAuthentication, o->rsa_authentication);
+	dump_cfg_fmtint(oStreamLocalBindUnlink, o->fwd_opts.streamlocal_bind_unlink);
+	dump_cfg_fmtint(oStrictHostKeyChecking, o->strict_host_key_checking);
+	dump_cfg_fmtint(oTCPKeepAlive, o->tcp_keep_alive);
+	dump_cfg_fmtint(oTunnel, o->tun_open);
+	dump_cfg_fmtint(oUsePrivilegedPort, o->use_privileged_port);
+	dump_cfg_fmtint(oVerifyHostKeyDNS, o->verify_host_key_dns);
+	dump_cfg_fmtint(oVisualHostKey, o->visual_host_key);
+
+	/* Integer options */
+	dump_cfg_int(oCanonicalizeMaxDots, o->canonicalize_max_dots);
+	dump_cfg_int(oCompressionLevel, o->compression_level);
+	dump_cfg_int(oConnectionAttempts, o->connection_attempts);
+	dump_cfg_int(oForwardX11Timeout, o->forward_x11_timeout);
+	dump_cfg_int(oNumberOfPasswordPrompts, o->number_of_password_prompts);
+	dump_cfg_int(oServerAliveCountMax, o->server_alive_count_max);
+	dump_cfg_int(oServerAliveInterval, o->server_alive_interval);
+
+	/* String options */
+	dump_cfg_string(oBindAddress, o->bind_address);
+	dump_cfg_string(oCiphers, o->ciphers ? o->ciphers : KEX_CLIENT_ENCRYPT);
+	dump_cfg_string(oControlPath, o->control_path);
+	dump_cfg_string(oHostKeyAlgorithms, o->hostkeyalgorithms ? o->hostkeyalgorithms : KEX_DEFAULT_PK_ALG);
+	dump_cfg_string(oHostKeyAlias, o->host_key_alias);
+	dump_cfg_string(oKbdInteractiveDevices, o->kbd_interactive_devices);
+	dump_cfg_string(oKexAlgorithms, o->kex_algorithms ? o->kex_algorithms : KEX_CLIENT_KEX);
+	dump_cfg_string(oLocalCommand, o->local_command);
+	dump_cfg_string(oLogLevel, log_level_name(o->log_level));
+	dump_cfg_string(oMacs, o->macs ? o->macs : KEX_CLIENT_MAC);
+	dump_cfg_string(oPKCS11Provider, o->pkcs11_provider);
+	dump_cfg_string(oPreferredAuthentications, o->preferred_authentications);
+	dump_cfg_string(oProxyCommand, o->proxy_command);
+	dump_cfg_string(oXAuthLocation, o->xauth_location);
+
+	dump_cfg_forwards(oDynamicForward, o->num_local_forwards, o->local_forwards);
+	dump_cfg_forwards(oLocalForward, o->num_local_forwards, o->local_forwards);
+	dump_cfg_forwards(oRemoteForward, o->num_remote_forwards, o->remote_forwards);
+
+	/* String array options */
+	dump_cfg_strarray(oIdentityFile, o->num_identity_files, o->identity_files);
+	dump_cfg_strarray_oneline(oCanonicalDomains, o->num_canonical_domains, o->canonical_domains);
+	dump_cfg_strarray_oneline(oGlobalKnownHostsFile, o->num_system_hostfiles, o->system_hostfiles);
+	dump_cfg_strarray_oneline(oUserKnownHostsFile, o->num_user_hostfiles, o->user_hostfiles);
+	dump_cfg_strarray(oSendEnv, o->num_send_env, o->send_env);
+
+	/* Special cases */
+
+	/* oConnectTimeout */
+	if (o->connection_timeout == -1)
+		printf("connecttimeout none\n");
+	else
+		dump_cfg_int(oConnectTimeout, o->connection_timeout);
+
+	/* oTunnelDevice */
+	printf("tunneldevice");
+	if (o->tun_local == SSH_TUNID_ANY)
+		printf(" any");
+	else
+		printf(" %d", o->tun_local);
+	if (o->tun_remote == SSH_TUNID_ANY)
+		printf(":any");
+	else
+		printf(":%d", o->tun_remote);
+	printf("\n");
+
+	/* oCanonicalizePermittedCNAMEs */
+	if ( o->num_permitted_cnames > 0) {
+		printf("canonicalizePermittedcnames");
+		for (i = 0; i < o->num_permitted_cnames; i++) {
+			printf(" %s:%s", o->permitted_cnames[i].source_list,
+			    o->permitted_cnames[i].target_list);
+		}
+		printf("\n");
+	}
+
+	/* oCipher */
+	if (o->cipher != SSH_CIPHER_NOT_SET)
+		printf("Cipher %s\n", cipher_name(o->cipher));
+
+	/* oControlPersist */
+	if (o->control_persist == 0 || o->control_persist_timeout == 0)
+		dump_cfg_fmtint(oControlPersist, o->control_persist);
+	else
+		dump_cfg_int(oControlPersist, o->control_persist_timeout);
+
+	/* oEscapeChar */
+	if (o->escape_char == SSH_ESCAPECHAR_NONE)
+		printf("escapechar none\n");
+	else {
+		vis(vbuf, o->escape_char, VIS_WHITE, 0);
+		printf("escapechar %s\n", vbuf);
+	}
+
+	/* oIPQoS */
+	printf("ipqos %s ", iptos2str(o->ip_qos_interactive));
+	printf("%s\n", iptos2str(o->ip_qos_bulk));
+
+	/* oRekeyLimit */
+	printf("rekeylimit %lld %d\n",
+	    (long long)o->rekey_limit, o->rekey_interval);
+
+	/* oStreamLocalBindMask */
+	printf("streamlocalbindmask 0%o\n",
+	    o->fwd_opts.streamlocal_bind_mask);
+}
diff --git a/readconf.h b/readconf.h
index 0b9cb77..7b58d01 100644
--- a/readconf.h
+++ b/readconf.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: readconf.h,v 1.102 2014/07/15 15:54:14 millert Exp $ */
+/* $OpenBSD: readconf.h,v 1.103 2014/10/08 22:20:25 djm Exp $ */
 
 /*
  * Author: Tatu Ylonen <ylo at cs.hut.fi>
@@ -164,17 +164,19 @@ typedef struct {
 
 #define SSHCONF_CHECKPERM	1  /* check permissions on config file */
 #define SSHCONF_USERCONF	2  /* user provided config file not system */
+#define SSHCONF_POSTCANON	4  /* After hostname canonicalisation */
 
 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	 process_config_line(Options *, struct passwd *, const char *,
+    const char *, char *, const char *, int, int *, int);
 int	 read_config_file(const char *, struct passwd *, const char *,
-    Options *, int);
+    const char *, Options *, int);
 int	 parse_forward(struct Forward *, const char *, int, int);
 int	 default_ssh_port(void);
 int	 option_clear_or_none(const char *);
+void	 dump_client_config(Options *o, const char *host);
 
 void	 add_local_forward(Options *, const struct Forward *);
 void	 add_remote_forward(Options *, const struct Forward *);
diff --git a/ssh-keysign.c b/ssh-keysign.c
index d95bb7d..6b73319 100644
--- a/ssh-keysign.c
+++ b/ssh-keysign.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ssh-keysign.c,v 1.42 2014/04/29 18:01:49 markus Exp $ */
+/* $OpenBSD: ssh-keysign.c,v 1.43 2014/10/08 22:20:25 djm Exp $ */
 /*
  * Copyright (c) 2002 Markus Friedl.  All rights reserved.
  *
@@ -187,7 +187,7 @@ main(int argc, char **argv)
 
 	/* verify that ssh-keysign is enabled by the admin */
 	initialize_options(&options);
-	(void)read_config_file(_PATH_HOST_CONFIG_FILE, pw, "", &options, 0);
+	(void)read_config_file(_PATH_HOST_CONFIG_FILE, pw, "", "", &options, 0);
 	fill_default_options(&options);
 	if (options.enable_ssh_keysign != 1)
 		fatal("ssh-keysign not enabled in %s",
diff --git a/ssh.1 b/ssh.1
index 1c889b6..34fe635 100644
--- a/ssh.1
+++ b/ssh.1
@@ -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.1,v 1.349 2014/08/30 15:33:50 sobrado Exp $
-.Dd $Mdocdate: August 30 2014 $
+.\" $OpenBSD: ssh.1,v 1.350 2014/10/08 22:20:25 djm Exp $
+.Dd $Mdocdate: October 8 2014 $
 .Dt SSH 1
 .Os
 .Sh NAME
@@ -43,7 +43,7 @@
 .Sh SYNOPSIS
 .Nm ssh
 .Bk -words
-.Op Fl 1246AaCfgKkMNnqsTtVvXxYy
+.Op Fl 1246AaCfgGKkMNnqsTtVvXxYy
 .Op Fl b Ar bind_address
 .Op Fl c Ar cipher_spec
 .Op Fl D Oo Ar bind_address : Oc Ns Ar port
@@ -251,6 +251,14 @@ then a client started with
 .Fl f
 will wait for all remote port forwards to be successfully established
 before placing itself in the background.
+.It Fl G
+Causes
+.Nm
+to print its configuration after evaluating
+.Cm Host
+and
+.Cm Match
+blocks and exit.
 .It Fl g
 Allows remote hosts to connect to local forwarded ports.
 If used on a multiplexed connection, then this option must be specified
diff --git a/ssh.c b/ssh.c
index 26e9681..97f6e82 100644
--- a/ssh.c
+++ b/ssh.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ssh.c,v 1.407 2014/07/17 07:22:19 djm Exp $ */
+/* $OpenBSD: ssh.c,v 1.408 2014/10/08 22:20:25 djm Exp $ */
 /*
  * Author: Tatu Ylonen <ylo at cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo at cs.hut.fi>, Espoo, Finland
@@ -384,27 +384,49 @@ resolve_canonicalize(char **hostp, int port)
  * file if the user specifies a config file on the command line.
  */
 static void
-process_config_files(struct passwd *pw)
+process_config_files(const char *host_arg, struct passwd *pw, int post_canon)
 {
 	char buf[MAXPATHLEN];
 	int r;
 
 	if (config != NULL) {
 		if (strcasecmp(config, "none") != 0 &&
-		    !read_config_file(config, pw, host, &options,
-		    SSHCONF_USERCONF))
+		    !read_config_file(config, pw, host, host_arg, &options,
+		    SSHCONF_USERCONF | (post_canon ? SSHCONF_POSTCANON : 0)))
 			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);
+			(void)read_config_file(buf, pw, host, host_arg,
+			    &options, SSHCONF_CHECKPERM | SSHCONF_USERCONF |
+			    (post_canon ? SSHCONF_POSTCANON : 0));
 
 		/* Read systemwide configuration file after user config. */
-		(void)read_config_file(_PATH_HOST_CONFIG_FILE, pw, host,
-		    &options, 0);
+		(void)read_config_file(_PATH_HOST_CONFIG_FILE, pw,
+		    host, host_arg, &options,
+		    post_canon ? SSHCONF_POSTCANON : 0);
+	}
+}
+
+/* Rewrite the port number in an addrinfo list of addresses */
+static void
+set_addrinfo_port(struct addrinfo *addrs, int port)
+{
+	struct addrinfo *addr;
+
+	for (addr = addrs; addr != NULL; addr = addr->ai_next) {
+		switch (addr->ai_family) {
+		case AF_INET:
+			((struct sockaddr_in *)addr->ai_addr)->
+			    sin_port = htons(port);
+			break;
+		case AF_INET6:
+			((struct sockaddr_in6 *)addr->ai_addr)->
+			    sin6_port = htons(port);
+			break;
+		}
 	}
 }
 
@@ -414,7 +436,7 @@ process_config_files(struct passwd *pw)
 int
 main(int ac, char **av)
 {
-	int i, r, opt, exit_status, use_syslog;
+	int i, r, opt, exit_status, use_syslog, config_test = 0;
 	char *p, *cp, *line, *argv0, buf[MAXPATHLEN], *host_arg, *logfile;
 	char thishost[NI_MAXHOST], shorthost[NI_MAXHOST], portstr[NI_MAXSERV];
 	char cname[NI_MAXHOST];
@@ -507,7 +529,7 @@ main(int ac, char **av)
 
  again:
 	while ((opt = getopt(ac, av, "1246ab:c:e:fgi:kl:m:no:p:qstvx"
-	    "ACD:E:F:I:KL:MNO:PQ:R:S:TVw:W:XYy")) != -1) {
+	    "ACD:E:F:GI:KL:MNO:PQ:R:S:TVw:W:XYy")) != -1) {
 		switch (opt) {
 		case '1':
 			options.protocol = SSH_PROTO_1;
@@ -540,6 +562,9 @@ main(int ac, char **av)
 		case 'E':
 			logfile = xstrdup(optarg);
 			break;
+		case 'G':
+			config_test = 1;
+			break;
 		case 'Y':
 			options.forward_x11 = 1;
 			options.forward_x11_trusted = 1;
@@ -788,9 +813,9 @@ main(int ac, char **av)
 			break;
 		case 'o':
 			line = xstrdup(optarg);
-			if (process_config_line(&options, pw, host ? host : "",
-			    line, "command-line", 0, NULL, SSHCONF_USERCONF)
-			    != 0)
+			if (process_config_line(&options, pw,
+			    host ? host : "", host ? host : "", line,
+			    "command-line", 0, NULL, SSHCONF_USERCONF) != 0)
 				exit(255);
 			free(line);
 			break;
@@ -899,7 +924,7 @@ main(int ac, char **av)
 		);
 
 	/* Parse the configuration files */
-	process_config_files(pw);
+	process_config_files(host_arg, pw, 0);
 
 	/* Hostname canonicalisation needs a few options filled. */
 	fill_default_options_for_canonicalization(&options);
@@ -911,6 +936,8 @@ main(int ac, char **av)
 		    "h", host, (char *)NULL);
 		free(host);
 		host = cp;
+		free(options.hostname);
+		options.hostname = xstrdup(host);
 	}
 
 	/* If canonicalization requested then try to apply it */
@@ -945,12 +972,22 @@ main(int ac, char **av)
 	}
 
 	/*
-	 * If the target hostname has changed as a result of canonicalisation
-	 * then re-parse the configuration files as new stanzas may match.
+	 * If canonicalisation is enabled 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);
+	if (options.canonicalize_hostname != 0) {
+		debug("Re-reading configuration after hostname "
+		    "canonicalisation");
+		free(options.hostname);
+		options.hostname = xstrdup(host);
+		process_config_files(host_arg, pw, 1);
+		/*
+		 * Address resolution happens early with canonicalisation
+		 * enabled and the port number may have changed since, so
+		 * reset it in address list
+		 */
+		if (addrs != NULL && options.port > 0)
+			set_addrinfo_port(addrs, options.port);
 	}
 
 	/* Fill configuration defaults. */
@@ -1052,6 +1089,11 @@ main(int ac, char **av)
 	}
 	free(conn_hash_hex);
 
+	if (config_test) {
+		dump_client_config(&options, host);
+		exit(0);
+	}
+
 	if (muxclient_command != 0 && options.control_path == NULL)
 		fatal("No ControlPath specified for \"-O\" command");
 	if (options.control_path != NULL)
diff --git a/ssh_config.5 b/ssh_config.5
index 4396aa9..b702e32 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.192 2014/08/30 15:33:50 sobrado Exp $
-.Dd $Mdocdate: August 30 2014 $
+.\" $OpenBSD: ssh_config.5,v 1.193 2014/10/08 22:20:25 djm Exp $
+.Dd $Mdocdate: October 8 2014 $
 .Dt SSH_CONFIG 5
 .Os
 .Sh NAME
@@ -65,7 +65,10 @@ The configuration files contain sections separated by
 .Dq Host
 specifications, and that section is only applied for hosts that
 match one of the patterns given in the specification.
-The matched host name is the one given on the command line.
+The matched host name is usually the one given on the command line
+(see the
+.Cm CanonicalizeHostname
+option for exceptions.)
 .Pp
 Since the first obtained value for each parameter is used, more
 host-specific declarations should be given near the beginning of the
@@ -109,10 +112,12 @@ A single
 .Ql *
 as a pattern can be used to provide global
 defaults for all hosts.
-The host is the
+The host is usually the
 .Ar hostname
-argument given on the command line (i.e. the name is not converted to
-a canonicalized host name before matching).
+argument given on the command line
+(see the
+.Cm CanonicalizeHostname
+option for exceptions.)
 .Pp
 A pattern entry may be negated by prefixing it with an exclamation mark
 .Pq Sq !\& .
@@ -134,19 +139,40 @@ or
 keyword) to be used only when the conditions following the
 .Cm Match
 keyword are satisfied.
-Match conditions are specified using one or more keyword/criteria pairs
+Match conditions are specified using one or more critera
 or the single token
 .Cm all
-which matches all criteria.
-The available keywords are:
+which always matches.
+The available criteria keywords are:
+.Cm canonical ,
 .Cm exec ,
 .Cm host ,
 .Cm originalhost ,
 .Cm user ,
 and
 .Cm localuser .
+The
+.Cm all
+criteria must appear alone or immediately after
+.Cm canonical.
+Other criteria may be combined arbitrarily.
+All criteria but
+.Cm all
+and
+.Cm canonical
+require an argument.
+Criteria may be negated by prepending an exclamation mark
+.Pq Sq !\& .
 .Pp
 The
+.Cm canonical
+keywork matches only when the configuration file is being re-parsed
+after hostname canonicalization (see the
+.Cm CanonicalizeHostname
+option.)
+This may be useful to specify conditions that work with canonical host
+names only.
+The
 .Cm exec
 keyword executes the specified command under the user's shell.
 If the command returns a zero exit status then the condition is considered true.
@@ -179,7 +205,9 @@ The criteria for the
 keyword are matched against the target hostname, after any substitution
 by the
 .Cm Hostname
-option.
+or
+.Cm CanonicalizeHostname
+options.
 The
 .Cm originalhost
 keyword matches against the hostname as it was specified on the command-line.
@@ -264,10 +292,11 @@ 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
+If this option is enabled, then the configuration files are processed
+again using the new target name to pick up any new configuration in matching
 .Cm Host
+and
+.Cm Match
 stanzas.
 .It Cm CanonicalizeMaxDots
 Specifies the maximum number of dot characters in a hostname before

-- 
To stop receiving notification emails like this one, please contact
djm at mindrot.org.


More information about the openssh-commits mailing list