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
This commit is contained in:
djm@openbsd.org 2014-10-08 22:20:25 +00:00 committed by Damien Miller
parent 5c0dafd38b
commit 957fbceb0f
6 changed files with 504 additions and 109 deletions

View File

@ -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@cs.hut.fi> * Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@ -41,6 +41,9 @@
#ifdef HAVE_UTIL_H #ifdef HAVE_UTIL_H
#include <util.h> #include <util.h>
#endif #endif
#if defined(HAVE_STRNVIS) && defined(HAVE_VIS_H) && !defined(BROKEN_STRNVIS)
# include <vis.h>
#endif
#include "xmalloc.h" #include "xmalloc.h"
#include "ssh.h" #include "ssh.h"
@ -56,6 +59,7 @@
#include "kex.h" #include "kex.h"
#include "mac.h" #include "mac.h"
#include "uidswap.h" #include "uidswap.h"
#include "myproposal.h"
/* Format of the configuration file: /* Format of the configuration file:
@ -135,7 +139,7 @@ typedef enum {
oBatchMode, oCheckHostIP, oStrictHostKeyChecking, oCompression, oBatchMode, oCheckHostIP, oStrictHostKeyChecking, oCompression,
oCompressionLevel, oTCPKeepAlive, oNumberOfPasswordPrompts, oCompressionLevel, oTCPKeepAlive, oNumberOfPasswordPrompts,
oUsePrivilegedPort, oLogLevel, oCiphers, oProtocol, oMacs, oUsePrivilegedPort, oLogLevel, oCiphers, oProtocol, oMacs,
oGlobalKnownHostsFile2, oUserKnownHostsFile2, oPubkeyAuthentication, oPubkeyAuthentication,
oKbdInteractiveAuthentication, oKbdInteractiveDevices, oHostKeyAlias, oKbdInteractiveAuthentication, oKbdInteractiveDevices, oHostKeyAlias,
oDynamicForward, oPreferredAuthentications, oHostbasedAuthentication, oDynamicForward, oPreferredAuthentications, oHostbasedAuthentication,
oHostKeyAlgorithms, oBindAddress, oPKCS11Provider, oHostKeyAlgorithms, oBindAddress, oPKCS11Provider,
@ -212,7 +216,7 @@ static struct {
{ "globalknownhostsfile", oGlobalKnownHostsFile }, { "globalknownhostsfile", oGlobalKnownHostsFile },
{ "globalknownhostsfile2", oDeprecated }, { "globalknownhostsfile2", oDeprecated },
{ "userknownhostsfile", oUserKnownHostsFile }, { "userknownhostsfile", oUserKnownHostsFile },
{ "userknownhostsfile2", oDeprecated }, { "userknownhostsfile2", oDeprecated },
{ "connectionattempts", oConnectionAttempts }, { "connectionattempts", oConnectionAttempts },
{ "batchmode", oBatchMode }, { "batchmode", oBatchMode },
{ "checkhostip", oCheckHostIP }, { "checkhostip", oCheckHostIP },
@ -466,7 +470,7 @@ execute_in_shell(const char *cmd)
if (!WIFEXITED(status)) { if (!WIFEXITED(status)) {
error("command '%.100s' exited abnormally", cmd); error("command '%.100s' exited abnormally", cmd);
return -1; return -1;
} }
debug3("command returned status %d", WEXITSTATUS(status)); debug3("command returned status %d", WEXITSTATUS(status));
return WEXITSTATUS(status); return WEXITSTATUS(status);
} }
@ -476,11 +480,12 @@ execute_in_shell(const char *cmd)
*/ */
static int static int
match_cfg_line(Options *options, char **condition, struct passwd *pw, 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; const char *ruser;
int r, port, result = 1, attributes = 0; int r, port, this_result, result = 1, attributes = 0, negate;
size_t len; size_t len;
char thishost[NI_MAXHOST], shorthost[NI_MAXHOST], portstr[NI_MAXSERV]; 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 } else
host = xstrdup(host_arg); host = xstrdup(host_arg);
debug3("checking match for '%s' host %s", cp, host); debug2("checking match for '%s' host %s originally %s",
while ((attrib = strdelim(&cp)) && *attrib != '\0') { cp, host, original_host);
attributes++; 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 (strcasecmp(attrib, "all") == 0) {
if (attributes != 1 || if (attributes > 1 ||
((arg = strdelim(&cp)) != NULL && *arg != '\0')) { ((arg = strdelim(&cp)) != NULL && *arg != '\0')) {
error("'all' cannot be combined with other " error("%.200s line %d: '%s' cannot be combined "
"Match attributes"); "with other Match attributes",
filename, linenum, oattrib);
result = -1; result = -1;
goto out; goto out;
} }
*condition = cp; if (result)
result = 1; result = negate ? 0 : 1;
goto out; 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') { if ((arg = strdelim(&cp)) == NULL || *arg == '\0') {
error("Missing Match criteria for %s", attrib); error("Missing Match criteria for %s", attrib);
result = -1; result = -1;
@ -519,31 +541,25 @@ match_cfg_line(Options *options, char **condition, struct passwd *pw,
} }
len = strlen(arg); len = strlen(arg);
if (strcasecmp(attrib, "host") == 0) { if (strcasecmp(attrib, "host") == 0) {
if (match_hostname(host, arg, len) != 1) criteria = xstrdup(host);
result = 0; r = match_hostname(host, arg, len) == 1;
else if (r == (negate ? 1 : 0))
debug("%.200s line %d: matched 'Host %.100s' ", this_result = result = 0;
filename, linenum, host);
} else if (strcasecmp(attrib, "originalhost") == 0) { } else if (strcasecmp(attrib, "originalhost") == 0) {
if (match_hostname(host_arg, arg, len) != 1) criteria = xstrdup(original_host);
result = 0; r = match_hostname(original_host, arg, len) == 1;
else if (r == (negate ? 1 : 0))
debug("%.200s line %d: matched " this_result = result = 0;
"'OriginalHost %.100s' ",
filename, linenum, host_arg);
} else if (strcasecmp(attrib, "user") == 0) { } else if (strcasecmp(attrib, "user") == 0) {
if (match_pattern_list(ruser, arg, len, 0) != 1) criteria = xstrdup(ruser);
result = 0; r = match_pattern_list(ruser, arg, len, 0) == 1;
else if (r == (negate ? 1 : 0))
debug("%.200s line %d: matched 'User %.100s' ", this_result = result = 0;
filename, linenum, ruser);
} else if (strcasecmp(attrib, "localuser") == 0) { } else if (strcasecmp(attrib, "localuser") == 0) {
if (match_pattern_list(pw->pw_name, arg, len, 0) != 1) criteria = xstrdup(pw->pw_name);
result = 0; r = match_pattern_list(pw->pw_name, arg, len, 0) == 1;
else if (r == (negate ? 1 : 0))
debug("%.200s line %d: matched " this_result = result = 0;
"'LocalUser %.100s' ",
filename, linenum, pw->pw_name);
} else if (strcasecmp(attrib, "exec") == 0) { } else if (strcasecmp(attrib, "exec") == 0) {
if (gethostname(thishost, sizeof(thishost)) == -1) if (gethostname(thishost, sizeof(thishost)) == -1)
fatal("gethostname: %s", strerror(errno)); fatal("gethostname: %s", strerror(errno));
@ -556,47 +572,49 @@ match_cfg_line(Options *options, char **condition, struct passwd *pw,
"d", pw->pw_dir, "d", pw->pw_dir,
"h", host, "h", host,
"l", thishost, "l", thishost,
"n", host_arg, "n", original_host,
"p", portstr, "p", portstr,
"r", ruser, "r", ruser,
"u", pw->pw_name, "u", pw->pw_name,
(char *)NULL); (char *)NULL);
if (result != 1) { if (result != 1) {
/* skip execution if prior predicate failed */ /* skip execution if prior predicate failed */
debug("%.200s line %d: skipped exec \"%.100s\"", debug3("%.200s line %d: skipped exec "
filename, linenum, cmd); "\"%.100s\"", filename, linenum, cmd);
} else { free(cmd);
r = execute_in_shell(cmd); continue;
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;
}
} }
r = execute_in_shell(cmd);
if (r == -1) {
fatal("%.200s line %d: match exec "
"'%.100s' error", filename,
linenum, cmd);
}
criteria = xstrdup(cmd);
free(cmd); free(cmd);
/* Force exit status to boolean */
r = r == 0;
if (r == (negate ? 1 : 0))
this_result = result = 0;
} else { } else {
error("Unsupported Match attribute %s", attrib); error("Unsupported Match attribute %s", attrib);
result = -1; result = -1;
goto out; goto out;
} }
debug3("%.200s line %d: %smatched '%s \"%.100s\"' ",
filename, linenum, this_result ? "": "not ",
oattrib, criteria);
free(criteria);
} }
if (attributes == 0) { if (attributes == 0) {
error("One or more attributes required for Match"); error("One or more attributes required for Match");
result = -1; result = -1;
goto out; goto out;
} }
debug3("match %sfound", result ? "" : "not ");
*condition = cp;
out: out:
if (result != -1)
debug2("match %sfound", result ? "" : "not ");
*condition = cp;
free(host); free(host);
return result; return result;
} }
@ -719,7 +737,8 @@ static const struct multistate multistate_canonicalizehostname[] = {
#define WHITESPACE " \t\r\n" #define WHITESPACE " \t\r\n"
int int
process_config_line(Options *options, struct passwd *pw, const char *host, 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 *s, **charptr, *endofnumber, *keyword, *arg, *arg2;
char **cpptr, fwdarg[256]; char **cpptr, fwdarg[256];
@ -775,7 +794,9 @@ parse_time:
if (!arg || *arg == '\0') if (!arg || *arg == '\0')
fatal("%s line %d: missing time value.", fatal("%s line %d: missing time value.",
filename, linenum); 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.", fatal("%s line %d: invalid time value.",
filename, linenum); filename, linenum);
if (*activep && *intptr == -1) if (*activep && *intptr == -1)
@ -812,7 +833,7 @@ parse_time:
case oForwardX11Trusted: case oForwardX11Trusted:
intptr = &options->forward_x11_trusted; intptr = &options->forward_x11_trusted;
goto parse_flag; goto parse_flag;
case oForwardX11Timeout: case oForwardX11Timeout:
intptr = &options->forward_x11_timeout; intptr = &options->forward_x11_timeout;
goto parse_time; goto parse_time;
@ -947,7 +968,8 @@ parse_time:
if (*intptr >= SSH_MAX_IDENTITY_FILES) if (*intptr >= SSH_MAX_IDENTITY_FILES)
fatal("%.200s line %d: Too many identity files specified (max %d).", fatal("%.200s line %d: Too many identity files specified (max %d).",
filename, linenum, SSH_MAX_IDENTITY_FILES); filename, linenum, SSH_MAX_IDENTITY_FILES);
add_identity_file(options, NULL, arg, userconfig); add_identity_file(options, NULL,
arg, flags & SSHCONF_USERCONF);
} }
break; break;
@ -1195,8 +1217,8 @@ parse_int:
if (cmdline) if (cmdline)
fatal("Host directive not supported as a command-line " fatal("Host directive not supported as a command-line "
"option"); "option");
value = match_cfg_line(options, &s, pw, host, value = match_cfg_line(options, &s, pw, host, original_host,
filename, linenum); flags & SSHCONF_POSTCANON, filename, linenum);
if (value < 0) if (value < 0)
fatal("%.200s line %d: Bad Match condition", filename, fatal("%.200s line %d: Bad Match condition", filename,
linenum); linenum);
@ -1444,7 +1466,7 @@ parse_int:
return 0; return 0;
default: 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. */ /* Check that there is no garbage at end of line. */
@ -1464,7 +1486,7 @@ parse_int:
int int
read_config_file(const char *filename, struct passwd *pw, const char *host, 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; FILE *f;
char line[1024]; 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)) { while (fgets(line, sizeof(line), f)) {
/* Update line number counter. */ /* Update line number counter. */
linenum++; linenum++;
if (process_config_line(options, pw, host, line, filename, if (process_config_line(options, pw, host, original_host,
linenum, &active, flags & SSHCONF_USERCONF) != 0) line, filename, linenum, &active, flags) != 0)
bad_options++; bad_options++;
} }
fclose(f); fclose(f);
@ -2009,3 +2031,295 @@ parse_forward(struct Forward *fwd, const char *fwdspec, int dynamicfwd, int remo
fwd->listen_path = NULL; fwd->listen_path = NULL;
return (0); 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);
}

View File

@ -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@cs.hut.fi> * Author: Tatu Ylonen <ylo@cs.hut.fi>
@ -164,17 +164,19 @@ typedef struct {
#define SSHCONF_CHECKPERM 1 /* check permissions on config file */ #define SSHCONF_CHECKPERM 1 /* check permissions on config file */
#define SSHCONF_USERCONF 2 /* user provided config file not system */ #define SSHCONF_USERCONF 2 /* user provided config file not system */
#define SSHCONF_POSTCANON 4 /* After hostname canonicalisation */
void initialize_options(Options *); void initialize_options(Options *);
void fill_default_options(Options *); void fill_default_options(Options *);
void fill_default_options_for_canonicalization(Options *); void fill_default_options_for_canonicalization(Options *);
int process_config_line(Options *, struct passwd *, const char *, char *, int process_config_line(Options *, struct passwd *, const char *,
const char *, int, int *, int); const char *, char *, const char *, int, int *, int);
int read_config_file(const char *, struct passwd *, const char *, 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 parse_forward(struct Forward *, const char *, int, int);
int default_ssh_port(void); int default_ssh_port(void);
int option_clear_or_none(const char *); 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_local_forward(Options *, const struct Forward *);
void add_remote_forward(Options *, const struct Forward *); void add_remote_forward(Options *, const struct Forward *);

View File

@ -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. * 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 */ /* verify that ssh-keysign is enabled by the admin */
initialize_options(&options); 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); fill_default_options(&options);
if (options.enable_ssh_keysign != 1) if (options.enable_ssh_keysign != 1)
fatal("ssh-keysign not enabled in %s", fatal("ssh-keysign not enabled in %s",

14
ssh.1
View File

@ -33,8 +33,8 @@
.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" 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 $ .\" $OpenBSD: ssh.1,v 1.350 2014/10/08 22:20:25 djm Exp $
.Dd $Mdocdate: August 30 2014 $ .Dd $Mdocdate: October 8 2014 $
.Dt SSH 1 .Dt SSH 1
.Os .Os
.Sh NAME .Sh NAME
@ -43,7 +43,7 @@
.Sh SYNOPSIS .Sh SYNOPSIS
.Nm ssh .Nm ssh
.Bk -words .Bk -words
.Op Fl 1246AaCfgKkMNnqsTtVvXxYy .Op Fl 1246AaCfgGKkMNnqsTtVvXxYy
.Op Fl b Ar bind_address .Op Fl b Ar bind_address
.Op Fl c Ar cipher_spec .Op Fl c Ar cipher_spec
.Op Fl D Oo Ar bind_address : Oc Ns Ar port .Op Fl D Oo Ar bind_address : Oc Ns Ar port
@ -251,6 +251,14 @@ then a client started with
.Fl f .Fl f
will wait for all remote port forwards to be successfully established will wait for all remote port forwards to be successfully established
before placing itself in the background. 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 .It Fl g
Allows remote hosts to connect to local forwarded ports. Allows remote hosts to connect to local forwarded ports.
If used on a multiplexed connection, then this option must be specified If used on a multiplexed connection, then this option must be specified

80
ssh.c
View File

@ -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@cs.hut.fi> * Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland * Copyright (c) 1995 Tatu Ylonen <ylo@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. * file if the user specifies a config file on the command line.
*/ */
static void static void
process_config_files(struct passwd *pw) process_config_files(const char *host_arg, struct passwd *pw, int post_canon)
{ {
char buf[MAXPATHLEN]; char buf[MAXPATHLEN];
int r; int r;
if (config != NULL) { if (config != NULL) {
if (strcasecmp(config, "none") != 0 && if (strcasecmp(config, "none") != 0 &&
!read_config_file(config, pw, host, &options, !read_config_file(config, pw, host, host_arg, &options,
SSHCONF_USERCONF)) SSHCONF_USERCONF | (post_canon ? SSHCONF_POSTCANON : 0)))
fatal("Can't open user config file %.100s: " fatal("Can't open user config file %.100s: "
"%.100s", config, strerror(errno)); "%.100s", config, strerror(errno));
} else { } else {
r = snprintf(buf, sizeof buf, "%s/%s", pw->pw_dir, r = snprintf(buf, sizeof buf, "%s/%s", pw->pw_dir,
_PATH_SSH_USER_CONFFILE); _PATH_SSH_USER_CONFFILE);
if (r > 0 && (size_t)r < sizeof(buf)) if (r > 0 && (size_t)r < sizeof(buf))
(void)read_config_file(buf, pw, host, &options, (void)read_config_file(buf, pw, host, host_arg,
SSHCONF_CHECKPERM|SSHCONF_USERCONF); &options, SSHCONF_CHECKPERM | SSHCONF_USERCONF |
(post_canon ? SSHCONF_POSTCANON : 0));
/* Read systemwide configuration file after user config. */ /* Read systemwide configuration file after user config. */
(void)read_config_file(_PATH_HOST_CONFIG_FILE, pw, host, (void)read_config_file(_PATH_HOST_CONFIG_FILE, pw,
&options, 0); 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 int
main(int ac, char **av) 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 *p, *cp, *line, *argv0, buf[MAXPATHLEN], *host_arg, *logfile;
char thishost[NI_MAXHOST], shorthost[NI_MAXHOST], portstr[NI_MAXSERV]; char thishost[NI_MAXHOST], shorthost[NI_MAXHOST], portstr[NI_MAXSERV];
char cname[NI_MAXHOST]; char cname[NI_MAXHOST];
@ -507,7 +529,7 @@ main(int ac, char **av)
again: again:
while ((opt = getopt(ac, av, "1246ab:c:e:fgi:kl:m:no:p:qstvx" 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) { switch (opt) {
case '1': case '1':
options.protocol = SSH_PROTO_1; options.protocol = SSH_PROTO_1;
@ -540,6 +562,9 @@ main(int ac, char **av)
case 'E': case 'E':
logfile = xstrdup(optarg); logfile = xstrdup(optarg);
break; break;
case 'G':
config_test = 1;
break;
case 'Y': case 'Y':
options.forward_x11 = 1; options.forward_x11 = 1;
options.forward_x11_trusted = 1; options.forward_x11_trusted = 1;
@ -788,9 +813,9 @@ main(int ac, char **av)
break; break;
case 'o': case 'o':
line = xstrdup(optarg); line = xstrdup(optarg);
if (process_config_line(&options, pw, host ? host : "", if (process_config_line(&options, pw,
line, "command-line", 0, NULL, SSHCONF_USERCONF) host ? host : "", host ? host : "", line,
!= 0) "command-line", 0, NULL, SSHCONF_USERCONF) != 0)
exit(255); exit(255);
free(line); free(line);
break; break;
@ -899,7 +924,7 @@ main(int ac, char **av)
); );
/* Parse the configuration files */ /* Parse the configuration files */
process_config_files(pw); process_config_files(host_arg, pw, 0);
/* Hostname canonicalisation needs a few options filled. */ /* Hostname canonicalisation needs a few options filled. */
fill_default_options_for_canonicalization(&options); fill_default_options_for_canonicalization(&options);
@ -911,6 +936,8 @@ main(int ac, char **av)
"h", host, (char *)NULL); "h", host, (char *)NULL);
free(host); free(host);
host = cp; host = cp;
free(options.hostname);
options.hostname = xstrdup(host);
} }
/* If canonicalization requested then try to apply it */ /* 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 * If canonicalisation is enabled then re-parse the configuration
* then re-parse the configuration files as new stanzas may match. * files as new stanzas may match.
*/ */
if (strcasecmp(host_arg, host) != 0) { if (options.canonicalize_hostname != 0) {
debug("Hostname has changed; re-reading configuration"); debug("Re-reading configuration after hostname "
process_config_files(pw); "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. */ /* Fill configuration defaults. */
@ -1052,6 +1089,11 @@ main(int ac, char **av)
} }
free(conn_hash_hex); free(conn_hash_hex);
if (config_test) {
dump_client_config(&options, host);
exit(0);
}
if (muxclient_command != 0 && options.control_path == NULL) if (muxclient_command != 0 && options.control_path == NULL)
fatal("No ControlPath specified for \"-O\" command"); fatal("No ControlPath specified for \"-O\" command");
if (options.control_path != NULL) if (options.control_path != NULL)

View File

@ -33,8 +33,8 @@
.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" 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 $ .\" $OpenBSD: ssh_config.5,v 1.193 2014/10/08 22:20:25 djm Exp $
.Dd $Mdocdate: August 30 2014 $ .Dd $Mdocdate: October 8 2014 $
.Dt SSH_CONFIG 5 .Dt SSH_CONFIG 5
.Os .Os
.Sh NAME .Sh NAME
@ -65,7 +65,10 @@ The configuration files contain sections separated by
.Dq Host .Dq Host
specifications, and that section is only applied for hosts that specifications, and that section is only applied for hosts that
match one of the patterns given in the specification. 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 .Pp
Since the first obtained value for each parameter is used, more Since the first obtained value for each parameter is used, more
host-specific declarations should be given near the beginning of the host-specific declarations should be given near the beginning of the
@ -109,10 +112,12 @@ A single
.Ql * .Ql *
as a pattern can be used to provide global as a pattern can be used to provide global
defaults for all hosts. defaults for all hosts.
The host is the The host is usually the
.Ar hostname .Ar hostname
argument given on the command line (i.e. the name is not converted to argument given on the command line
a canonicalized host name before matching). (see the
.Cm CanonicalizeHostname
option for exceptions.)
.Pp .Pp
A pattern entry may be negated by prefixing it with an exclamation mark A pattern entry may be negated by prefixing it with an exclamation mark
.Pq Sq !\& . .Pq Sq !\& .
@ -134,19 +139,40 @@ or
keyword) to be used only when the conditions following the keyword) to be used only when the conditions following the
.Cm Match .Cm Match
keyword are satisfied. 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 or the single token
.Cm all .Cm all
which matches all criteria. which always matches.
The available keywords are: The available criteria keywords are:
.Cm canonical ,
.Cm exec , .Cm exec ,
.Cm host , .Cm host ,
.Cm originalhost , .Cm originalhost ,
.Cm user , .Cm user ,
and and
.Cm localuser . .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 .Pp
The 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 .Cm exec
keyword executes the specified command under the user's shell. keyword executes the specified command under the user's shell.
If the command returns a zero exit status then the condition is considered true. 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 keyword are matched against the target hostname, after any substitution
by the by the
.Cm Hostname .Cm Hostname
option. or
.Cm CanonicalizeHostname
options.
The The
.Cm originalhost .Cm originalhost
keyword matches against the hostname as it was specified on the command-line. keyword matches against the hostname as it was specified on the command-line.
@ -264,10 +292,11 @@ is set to
.Dq always , .Dq always ,
then canonicalization is applied to proxied connections too. then canonicalization is applied to proxied connections too.
.Pp .Pp
If this option is enabled and canonicalisation results in the target hostname If this option is enabled, then the configuration files are processed
changing, then the configuration files are processed again using the new again using the new target name to pick up any new configuration in matching
target name to pick up any new configuration in matching
.Cm Host .Cm Host
and
.Cm Match
stanzas. stanzas.
.It Cm CanonicalizeMaxDots .It Cm CanonicalizeMaxDots
Specifies the maximum number of dot characters in a hostname before Specifies the maximum number of dot characters in a hostname before