Bugzilla – Attachment 2790 Details for
Bug 1585
Allow an `Include' option which reads another config file in place and does not error out when `Include' file not readable
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
Include support and regress test
bz1585.diff (text/plain), 10.12 KB, created by
Damien Miller
on 2016-02-17 13:16:17 AEDT
(
hide
)
Description:
Include support and regress test
Filename:
MIME Type:
Creator:
Damien Miller
Created:
2016-02-17 13:16:17 AEDT
Size:
10.12 KB
patch
obsolete
>diff --git a/readconf.c b/readconf.c >index 69d4553..fcd855f 100644 >--- a/readconf.c >+++ b/readconf.c >@@ -45,6 +45,7 @@ > #if defined(HAVE_STRNVIS) && defined(HAVE_VIS_H) && !defined(BROKEN_STRNVIS) > # include <vis.h> > #endif >+#include <glob.h> > > #include "xmalloc.h" > #include "ssh.h" >@@ -125,11 +126,18 @@ > > */ > >+static int read_config_file_depth(const char *filename, struct passwd *pw, >+ const char *host, const char *original_host, Options *options, >+ int flags, int *activep, int depth); >+static int process_config_line_depth(Options *options, struct passwd *pw, >+ const char *host, const char *original_host, char *line, >+ const char *filename, int linenum, int *activep, int flags, int depth); >+ > /* Keyword tokens. */ > > typedef enum { > oBadOption, >- oHost, oMatch, >+ oHost, oMatch, oInclude, > oForwardAgent, oForwardX11, oForwardX11Trusted, oForwardX11Timeout, > oGatewayPorts, oExitOnForwardFailure, > oPasswordAuthentication, oRSAAuthentication, >@@ -258,6 +266,7 @@ static struct { > { "controlmaster", oControlMaster }, > { "controlpersist", oControlPersist }, > { "hashknownhosts", oHashKnownHosts }, >+ { "include", oInclude }, > { "tunnel", oTunnel }, > { "tunneldevice", oTunnelDevice }, > { "localcommand", oLocalCommand }, >@@ -772,22 +781,32 @@ static const struct multistate multistate_canonicalizehostname[] = { > * Processes a single option line as used in the configuration files. This > * only sets those values that have not already been set. > */ >-#define WHITESPACE " \t\r\n" > int > process_config_line(Options *options, struct passwd *pw, const char *host, > const char *original_host, char *line, const char *filename, > int linenum, int *activep, int flags) > { >+ return process_config_line_depth(options, pw, host, original_host, >+ line, filename, linenum, activep, flags, 0); >+} >+ >+#define WHITESPACE " \t\r\n" >+static int >+process_config_line_depth(Options *options, struct passwd *pw, const char *host, >+ const char *original_host, char *line, const char *filename, >+ int linenum, int *activep, int flags, int depth) >+{ > char *s, **charptr, *endofnumber, *keyword, *arg, *arg2; > char **cpptr, fwdarg[256]; > u_int i, *uintptr, max_entries = 0; >- int negated, opcode, *intptr, value, value2, cmdline = 0; >+ int r, oactive, negated, opcode, *intptr, value, value2, cmdline = 0; > LogLevel *log_level_ptr; > long long val64; > size_t len; > struct Forward fwd; > const struct multistate *multistate_ptr; > struct allowed_cname *cname; >+ glob_t gl; > > if (activep == NULL) { /* We are processing a command line directive */ > cmdline = 1; >@@ -1247,6 +1266,8 @@ parse_keytypes: > *activep = 0; > arg2 = NULL; > while ((arg = strdelim(&s)) != NULL && *arg != '\0') { >+ if ((flags & SSHCONF_NEVERMATCH) != 0) >+ break; > negated = *arg == '!'; > if (negated) > arg++; >@@ -1279,7 +1300,7 @@ parse_keytypes: > if (value < 0) > fatal("%.200s line %d: Bad Match condition", filename, > linenum); >- *activep = value; >+ *activep = (flags & SSHCONF_NEVERMATCH) ? 0 : value; > break; > > case oEscapeChar: >@@ -1407,6 +1428,48 @@ parse_keytypes: > intptr = &options->visual_host_key; > goto parse_flag; > >+ case oInclude: >+ if (cmdline) >+ fatal("Include directive not supported as a " >+ "command-line option"); >+ value = 0; >+ while ((arg = strdelim(&s)) != NULL && *arg != '\0') { >+ /* Ensure all paths are anchored */ >+ if (*arg != '/' && *arg != '~') { >+ xasprintf(&arg2, "~/%s/%s", >+ _PATH_SSH_USER_DIR, arg); >+ } else >+ arg2 = xstrdup(arg); >+ memset(&gl, 0, sizeof(gl)); >+ r = glob(arg2, GLOB_TILDE, NULL, &gl); >+ if (r == GLOB_NOMATCH) >+ continue; >+ else if (r != 0) >+ fatal("%s: glob failed %s", __func__, arg2); >+ free(arg2); >+ oactive = *activep; >+ for (i = 0; i < gl.gl_pathc; i++) { >+ debug3("%.200s line %d: Including file %s%s", >+ filename, linenum, gl.gl_pathv[i], >+ oactive ? " (parse only)" : ""); >+ r = read_config_file_depth(gl.gl_pathv[i], >+ pw, host, original_host, options, >+ flags | (oactive ? 0 : SSHCONF_NEVERMATCH), >+ activep, depth + 1); >+ *activep = oactive; >+ if (r != 1) >+ value = -1; >+ } >+ globfree(&gl); >+ /* >+ * don't let Match in includes clobber the >+ * containing file's Match state. >+ */ >+ } >+ if (value != 0) >+ return value; >+ break; >+ > case oIPQoS: > arg = strdelim(&s); > if ((value = parse_ipqos(arg)) == -1) >@@ -1565,22 +1628,35 @@ parse_keytypes: > return 0; > } > >- > /* > * Reads the config file and modifies the options accordingly. Options > * should already be initialized before this call. This never returns if > * there is an error. If the file does not exist, this returns 0. > */ >- > int > read_config_file(const char *filename, struct passwd *pw, const char *host, > const char *original_host, Options *options, int flags) > { >+ int active = 1; >+ >+ return read_config_file_depth(filename, pw, host, original_host, >+ options, flags, &active, 0); >+} >+ >+#define READCONF_MAX_DEPTH 16 >+static int >+read_config_file_depth(const char *filename, struct passwd *pw, >+ const char *host, const char *original_host, Options *options, >+ int flags, int *activep, int depth) >+{ > FILE *f; > char line[1024]; >- int active, linenum; >+ int linenum; > int bad_options = 0; > >+ if (depth < 0 || depth > 16) >+ fatal("Too many recursive configuration includes"); >+ > if ((f = fopen(filename, "r")) == NULL) > return 0; > >@@ -1600,13 +1676,12 @@ read_config_file(const char *filename, struct passwd *pw, const char *host, > * Mark that we are now processing the options. This flag is turned > * on/off by Host specifications. > */ >- active = 1; > linenum = 0; > while (fgets(line, sizeof(line), f)) { > /* Update line number counter. */ > linenum++; > if (process_config_line(options, pw, host, original_host, >- line, filename, linenum, &active, flags) != 0) >+ line, filename, linenum, activep, flags) != 0) > bad_options++; > } > fclose(f); >diff --git a/readconf.h b/readconf.h >index c84d068..8091dbe 100644 >--- a/readconf.h >+++ b/readconf.h >@@ -179,6 +179,7 @@ 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 */ >+#define SSHCONF_NEVERMATCH 8 /* Match/Host never matches; internal only */ > > #define SSH_UPDATE_HOSTKEYS_NO 0 > #define SSH_UPDATE_HOSTKEYS_YES 1 >diff --git a/regress/cfginclude.sh b/regress/cfginclude.sh >new file mode 100644 >index 0000000..0f2bf12 >--- /dev/null >+++ b/regress/cfginclude.sh >@@ -0,0 +1,141 @@ >+# $OpenBSD$ >+# Placed in the Public Domain. >+ >+tid="config include" >+ >+cat > $OBJ/ssh_config.i << _EOF >+Match host a >+ Hostname aa >+ >+Match host b >+ Hostname bb >+ Include $OBJ/ssh_config.i.* >+ >+Match host c >+ Include $OBJ/ssh_config.i.* >+ Hostname cc >+ >+Match host m >+ Include $OBJ/ssh_config.i.* >+ >+Host d >+ Hostname dd >+ >+Host e >+ Hostname ee >+ Include $OBJ/ssh_config.i.* >+ >+Host f >+ Include $OBJ/ssh_config.i.* >+ Hostname ff >+ >+Host n >+ Include $OBJ/ssh_config.i.* >+_EOF >+ >+cat > $OBJ/ssh_config.i.0 << _EOF >+Match host xxxxxx >+_EOF >+ >+cat > $OBJ/ssh_config.i.1 << _EOF >+Match host a >+ Hostname aaa >+ >+Match host b >+ Hostname bbb >+ >+Match host c >+ Hostname ccc >+ >+Host d >+ Hostname ddd >+ >+Host e >+ Hostname eee >+ >+Host f >+ Hostname fff >+_EOF >+ >+cat > $OBJ/ssh_config.i.2 << _EOF >+Match host a >+ Hostname aaaa >+ >+Match host b >+ Hostname bbbb >+ >+Match host c >+ Hostname cccc >+ >+Host d >+ Hostname dddd >+ >+Host e >+ Hostname eeee >+ >+Host f >+ Hostname ffff >+ >+Match all >+ Hostname xxxx >+_EOF >+ >+trial() { >+ _host="$1" >+ _exp="$2" >+ ${REAL_SSH} -F $OBJ/ssh_config.i -G "$_host" > $OBJ/ssh_config.out || >+ fatal "ssh config parse failed" >+ _got=`grep -i '^hostname ' $OBJ/ssh_config.out | awk '{print $2}'` >+ if test "x$_exp" != "x$_got" ; then >+ fail "host $_host include fail: expected $_exp got $_got" >+ fi >+} >+ >+trial a aa >+trial b bb >+trial c ccc >+trial d dd >+trial e ee >+trial f fff >+trial m xxxx >+trial n xxxx >+trial x x >+ >+# Prepare an included config with an error. >+ >+cat > $OBJ/ssh_config.i.3 << _EOF >+Hostname xxxx >+ Junk >+_EOF >+ >+${REAL_SSH} -F $OBJ/ssh_config.i -G a 2>/dev/null && \ >+ fail "ssh include allowed invalid config" >+ >+${REAL_SSH} -F $OBJ/ssh_config.i -G x 2>/dev/null && \ >+ fail "ssh include allowed invalid config" >+ >+rm -f $OBJ/ssh_config.i.* >+ >+# Ensure that a missing include is not fatal. >+cat > $OBJ/ssh_config.i << _EOF >+#Include $OBJ/ssh_config.i.* >+Hostname aa >+_EOF >+ >+trial a aa >+ >+# Ensure that Match/Host in an included config does not affect parent. >+cat > $OBJ/ssh_config.i.x << _EOF >+Match host x >+_EOF >+ >+trial a aa >+ >+cat > $OBJ/ssh_config.i.x << _EOF >+Host x >+_EOF >+ >+trial a aa >+ >+# cleanup >+rm -f $OBJ/ssh_config.i $OBJ/ssh_config.i.* $OBJ/ssh_config.out >diff --git a/regress/test-exec.sh b/regress/test-exec.sh >index 114e129..31a5cfa 100644 >--- a/regress/test-exec.sh >+++ b/regress/test-exec.sh >@@ -221,6 +221,7 @@ echo "#!/bin/sh" > $SSHLOGWRAP > echo "exec ${SSH} -E${TEST_SSH_LOGFILE} "'"$@"' >>$SSHLOGWRAP > > chmod a+rx $OBJ/ssh-log-wrapper.sh >+REAL_SSH="$SSH" > SSH="$SSHLOGWRAP" > > # Some test data. We make a copy because some tests will overwrite it. >diff --git a/ssh.1 b/ssh.1 >index 5b35b6c..18995dd 100644 >--- a/ssh.1 >+++ b/ssh.1 >@@ -505,6 +505,7 @@ For full details of the options listed below, and their possible values, see > .It HostName > .It IdentityFile > .It IdentitiesOnly >+.It Include > .It IPQoS > .It KbdInteractiveAuthentication > .It KbdInteractiveDevices >diff --git a/ssh_config.5 b/ssh_config.5 >index 5b09547..6298384 100644 >--- a/ssh_config.5 >+++ b/ssh_config.5 >@@ -1022,6 +1022,23 @@ It is recommended that > .Cm IgnoreUnknown > be listed early in the configuration file as it will not be applied > to unknown options that appear before it. >+.It Cm Include >+Include the specified configuration file(s). >+Multiple path names may be specified and each pathname may contain >+.Xr glob 3 >+wildcards and shell-like >+.Dq ~ >+references to user home directories. >+Files without absolute paths are assumed to be in >+.Pa ~/.ssh . >+An >+.Cm Include >+directive may appear inside a >+.Cm Match >+or >+.Cm Host >+block >+to perform conditional inclusion. > .It Cm IPQoS > Specifies the IPv4 type-of-service or DSCP class for connections. > Accepted values are
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
Attachments on
bug 1585
:
1623
|
2274
|
2647
| 2790 |
2859