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>
* Copyright (c) 1995 Tatu Ylonen <ylo@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);
}

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

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.
*
@ -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",

14
ssh.1
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.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

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>
* 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.
*/
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)

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