View | Details | Raw Unified | Return to bug 2468 | Differences between
and this patch

Collapse All | Expand All

(-)a/regress/Makefile (+2 lines)
Lines 79-84 LTESTS= connect \ Link Here
79
		principals-command \
79
		principals-command \
80
		cert-file \
80
		cert-file \
81
		cfginclude \
81
		cfginclude \
82
		servcfginclude \
82
		allow-deny-users \
83
		allow-deny-users \
83
		authinfo
84
		authinfo
84
85
Lines 111-116 CLEANFILES= *.core actual agent-key.* authorized_keys_${USERNAME} \ Link Here
111
		scp-ssh-wrapper.scp setuid-allowed sftp-server.log \
112
		scp-ssh-wrapper.scp setuid-allowed sftp-server.log \
112
		sftp-server.sh sftp.log ssh-log-wrapper.sh ssh.log \
113
		sftp-server.sh sftp.log ssh-log-wrapper.sh ssh.log \
113
		ssh_config ssh_config.* ssh_proxy ssh_proxy_bak \
114
		ssh_config ssh_config.* ssh_proxy ssh_proxy_bak \
115
		sshd_config sshd_config.* \
114
		ssh_proxy_envpass sshd.log sshd_config sshd_config_minimal \
116
		ssh_proxy_envpass sshd.log sshd_config sshd_config_minimal \
115
		sshd_config.orig sshd_proxy sshd_proxy.* sshd_proxy_bak \
117
		sshd_config.orig sshd_proxy sshd_proxy.* sshd_proxy_bak \
116
		sshd_proxy_orig t10.out t10.out.pub t12.out t12.out.pub \
118
		sshd_proxy_orig t10.out t10.out.pub t12.out t12.out.pub \
(-)a/regress/servcfginclude.sh (+153 lines)
Line 0 Link Here
1
#	Placed in the Public Domain.
2
3
tid="server config include"
4
5
cat > $OBJ/sshd_config.i << _EOF
6
7
HostKey $OBJ/host.ed25519
8
9
Match host a
10
	Banner /aa
11
12
Match host b
13
	Banner /bb
14
	Include $OBJ/sshd_config.i.*
15
16
Match host c
17
	Include $OBJ/sshd_config.i.*
18
	Banner /cc
19
20
Match host m
21
	Include $OBJ/sshd_config.i.*
22
23
Match Host d
24
	Banner /dd
25
26
Match Host e
27
	Banner /ee
28
	Include $OBJ/sshd_config.i.*
29
30
Match Host f
31
	Include $OBJ/sshd_config.i.*
32
	Banner /ff
33
34
Match Host n
35
	Include $OBJ/sshd_config.i.*
36
_EOF
37
38
cat > $OBJ/sshd_config.i.0 << _EOF
39
Match host xxxxxx
40
_EOF
41
42
cat > $OBJ/sshd_config.i.1 << _EOF
43
Match host a
44
	Banner /aaa
45
46
Match host b
47
	Banner /bbb
48
49
Match host c
50
	Banner /ccc
51
52
Match Host d
53
	Banner /ddd
54
55
Match Host e
56
	Banner /eee
57
58
Match Host f
59
	Banner /fff
60
_EOF
61
62
cat > $OBJ/sshd_config.i.2 << _EOF
63
Match host a
64
	Banner /aaaa
65
66
Match host b
67
	Banner /bbbb
68
69
Match host c
70
	Banner /cccc
71
72
Match Host d
73
	Banner /dddd
74
75
Match Host e
76
	Banner /eeee
77
78
Match Host f
79
	Banner /ffff
80
81
Match all
82
	Banner /xxxx
83
_EOF
84
85
trial() {
86
	_host="$1"
87
	_exp="$2"
88
	${REAL_SSHD} -f $OBJ/sshd_config.i -T -C "host=$_host,user=test,addr=127.0.0.1" > $OBJ/sshd_config.out ||
89
		fatal "ssh config parse failed"
90
	_got=`grep -i '^banner ' $OBJ/sshd_config.out | awk '{print $2}'`
91
	if test "x$_exp" != "x$_got" ; then
92
		fail "host $_host include fail: expected $_exp got $_got"
93
	fi
94
}
95
96
trial a /aa
97
trial b /bb
98
trial c /ccc
99
trial d /dd
100
trial e /ee
101
trial f /fff
102
trial m /xxxx
103
trial n /xxxx
104
trial x 
105
106
# Prepare an included config with an error.
107
108
cat > $OBJ/sshd_config.i.3 << _EOF
109
Banner xxxx
110
	Junk
111
_EOF
112
113
${REAL_SSHD} -f $OBJ/sshd_config.i -C "host=a,user=test,addr=127.0.0.1" 2>/dev/null && \
114
	fail "sshd include allowed invalid config"
115
116
${REAL_SSHD} -f $OBJ/sshd_config.i -C "host=x,user=test,addr=127.0.0.1" 2>/dev/null && \
117
	fail "sshd include allowed invalid config"
118
119
rm -f $OBJ/sshd_config.i.*
120
121
# Ensure that a missing include is not fatal.
122
cat > $OBJ/sshd_config.i << _EOF
123
124
HostKey $OBJ/host.ed25519
125
Include $OBJ/sshd_config.i.*
126
Banner /aa
127
_EOF
128
129
trial a /aa
130
131
# Ensure that Match/Host in an included config does not affect parent.
132
cat > $OBJ/sshd_config.i.x << _EOF
133
Match host x
134
_EOF
135
136
trial a /aa
137
138
cat > $OBJ/sshd_config.i.x << _EOF
139
Match Host x
140
_EOF
141
142
trial a /aa
143
144
# Ensure the empty include directive is not accepted
145
cat > $OBJ/sshd_config.i.x << _EOF
146
Include
147
_EOF
148
149
${REAL_SSHD} -f $OBJ/sshd_config.i.x -C "host=x,user=test,addr=127.0.0.1" 2>/dev/null && \
150
	fail "sshd allowed empty Include option"
151
152
# cleanup
153
rm -f $OBJ/sshd_config.i $OBJ/sshd_config.i.* $OBJ/sshd_config.out
(-)a/regress/test-exec.sh (+1 lines)
Lines 217-222 echo "exec ${SSH} -E${TEST_SSH_LOGFILE} "'"$@"' >>$SSHLOGWRAP Link Here
217
217
218
chmod a+rx $OBJ/ssh-log-wrapper.sh
218
chmod a+rx $OBJ/ssh-log-wrapper.sh
219
REAL_SSH="$SSH"
219
REAL_SSH="$SSH"
220
REAL_SSHD="$SSHD"
220
SSH="$SSHLOGWRAP"
221
SSH="$SSHLOGWRAP"
221
222
222
# Some test data.  We make a copy because some tests will overwrite it.
223
# Some test data.  We make a copy because some tests will overwrite it.
(-)a/servconf.c (-8 / +132 lines)
Lines 34-39 Link Here
34
#ifdef HAVE_UTIL_H
34
#ifdef HAVE_UTIL_H
35
#include <util.h>
35
#include <util.h>
36
#endif
36
#endif
37
#ifdef USE_SYSTEM_GLOB
38
# include <glob.h>
39
#else
40
# include "openbsd-compat/glob.h"
41
#endif
37
42
38
#include "openbsd-compat/sys-queue.h"
43
#include "openbsd-compat/sys-queue.h"
39
#include "xmalloc.h"
44
#include "xmalloc.h"
Lines 64-69 static void add_one_listen_addr(ServerOptions *, char *, int); Link Here
64
/* Use of privilege separation or not */
69
/* Use of privilege separation or not */
65
extern int use_privsep;
70
extern int use_privsep;
66
extern struct sshbuf *cfg;
71
extern struct sshbuf *cfg;
72
struct include_item *include_list = NULL, *include_last = NULL;
73
74
#define INCLUDE_LIST_APPEND(item) \
75
	(item)->next = NULL; \
76
	if (include_list == NULL) { \
77
		include_list = (item); \
78
	} else \
79
		include_last->next = (item); \
80
	include_last = (item);
67
81
68
/* Initializes the server options to their default values. */
82
/* Initializes the server options to their default values. */
69
83
Lines 383-388 fill_default_server_options(ServerOptions *options) Link Here
383
397
384
}
398
}
385
399
400
int process_server_config_line_depth(ServerOptions *options, char *line,
401
    const char *filename, int linenum, int *activep,
402
    struct connection_info *connectinfo, int inc_flags, int depth);
403
void parse_server_config_depth(ServerOptions *options, const char *filename,
404
    struct sshbuf *conf, struct connection_info *connectinfo,
405
    int flags, int *activep, int depth);
406
386
/* Keyword tokens. */
407
/* Keyword tokens. */
387
typedef enum {
408
typedef enum {
388
	sBadOption,		/* == unknown option */
409
	sBadOption,		/* == unknown option */
Lines 434-440 typedef enum { Link Here
434
	sAcceptEnv, sPermitTunnel,
455
	sAcceptEnv, sPermitTunnel,
435
	sMatch, sPermitOpen, sForceCommand, sChrootDirectory,
456
	sMatch, sPermitOpen, sForceCommand, sChrootDirectory,
436
	sUsePrivilegeSeparation, sAllowAgentForwarding,
457
	sUsePrivilegeSeparation, sAllowAgentForwarding,
437
	sHostCertificate,
458
	sHostCertificate, sInclude,
438
	sRevokedKeys, sTrustedUserCAKeys, sAuthorizedPrincipalsFile,
459
	sRevokedKeys, sTrustedUserCAKeys, sAuthorizedPrincipalsFile,
439
	sAuthorizedPrincipalsCommand, sAuthorizedPrincipalsCommandUser,
460
	sAuthorizedPrincipalsCommand, sAuthorizedPrincipalsCommandUser,
440
	sKexAlgorithms, sIPQoS, sVersionAddendum,
461
	sKexAlgorithms, sIPQoS, sVersionAddendum,
Lines 429-434 typedef enum { Link Here
429
#define SSHCFG_MATCH	0x02	/* allowed inside a Match section */
450
#define SSHCFG_MATCH	0x02	/* allowed inside a Match section */
430
#define SSHCFG_ALL	(SSHCFG_GLOBAL|SSHCFG_MATCH)
451
#define SSHCFG_ALL	(SSHCFG_GLOBAL|SSHCFG_MATCH)
431
452
453
#define SSHCFG_NEVERMATCH 0x04  /* Match/Host never matches; internal only */
454
432
/* Textual representation of the tokens. */
455
/* Textual representation of the tokens. */
433
static struct {
456
static struct {
434
	const char *name;
457
	const char *name;
Lines 576-581 static struct { Link Here
576
	{ "noneenabled", sNoneEnabled, SSHCFG_ALL },
599
	{ "noneenabled", sNoneEnabled, SSHCFG_ALL },
577
	{ "kexalgorithms", sKexAlgorithms, SSHCFG_GLOBAL },
600
	{ "kexalgorithms", sKexAlgorithms, SSHCFG_GLOBAL },
578
	{ "disableMTAES", sDisableMTAES, SSHCFG_ALL },
601
	{ "disableMTAES", sDisableMTAES, SSHCFG_ALL },
602
	{ "include", sInclude, SSHCFG_ALL },
579
	{ "ipqos", sIPQoS, SSHCFG_ALL },
603
	{ "ipqos", sIPQoS, SSHCFG_ALL },
580
	{ "authorizedkeyscommand", sAuthorizedKeysCommand, SSHCFG_ALL },
604
	{ "authorizedkeyscommand", sAuthorizedKeysCommand, SSHCFG_ALL },
581
	{ "authorizedkeyscommanduser", sAuthorizedKeysCommandUser, SSHCFG_ALL },
605
	{ "authorizedkeyscommanduser", sAuthorizedKeysCommandUser, SSHCFG_ALL },
Lines 991-999 int Link Here
991
process_server_config_line(ServerOptions *options, char *line,
1015
process_server_config_line(ServerOptions *options, char *line,
992
    const char *filename, int linenum, int *activep,
1016
    const char *filename, int linenum, int *activep,
993
    struct connection_info *connectinfo)
1017
    struct connection_info *connectinfo)
1018
{
1019
	return process_server_config_line_depth(options, line, filename, linenum,
1020
	    activep, connectinfo, 0, 0);
1021
}
1022
1023
int
1024
process_server_config_line_depth(ServerOptions *options, char *line,
1025
    const char *filename, int linenum, int *activep,
1026
    struct connection_info *connectinfo, int inc_flags, int depth)
994
{
1027
{
995
	int cmdline = 0, *intptr, value, value2, n, port;
1028
	int cmdline = 0, *intptr, value, value2, n, port, oactive;
996
	SyslogFacility *log_facility_ptr;
1029
	SyslogFacility *log_facility_ptr;
997
	LogLevel *log_level_ptr;
1030
	LogLevel *log_level_ptr;
998
	ServerOpCodes opcode;
1031
	ServerOpCodes opcode;
Lines 1033-1038 process_server_config_line_depth(ServerOptions *options, char *line, Link Here
1033
	long long val64;
1066
	long long val64;
1034
	const struct multistate *multistate_ptr;
1067
	const struct multistate *multistate_ptr;
1035
	const char *errstr;
1068
	const char *errstr;
1069
	struct include_item *item;
1070
	int found = 0;
1071
	glob_t gbuf;
1036
1072
1037
	/* Strip trailing whitespace. Allow \f (form feed) at EOL only */
1073
	/* Strip trailing whitespace. Allow \f (form feed) at EOL only */
1038
	if ((len = strlen(line)) == 0)
1074
	if ((len = strlen(line)) == 0)
Lines 1027-1033 process_server_config_line(ServerOptions *options, char *line, Link Here
1027
		cmdline = 1;
1063
		cmdline = 1;
1028
		activep = &cmdline;
1064
		activep = &cmdline;
1029
	}
1065
	}
1030
	if (*activep && opcode != sMatch)
1066
	if (*activep && opcode != sMatch && opcode != sInclude)
1031
		debug3("%s:%d setting %s %s", filename, linenum, arg, cp);
1067
		debug3("%s:%d setting %s %s", filename, linenum, arg, cp);
1032
	if (*activep == 0 && !(flags & SSHCFG_MATCH)) {
1068
	if (*activep == 0 && !(flags & SSHCFG_MATCH)) {
1033
		if (connectinfo == NULL) {
1069
		if (connectinfo == NULL) {
Lines 1647-1652 process_server_config_line(ServerOptions *options, char *line, Link Here
1647
			*intptr = value;
1683
			*intptr = value;
1648
		break;
1684
		break;
1649
1685
1686
	case sInclude:
1687
		if (cmdline)
1688
			fatal("Include directive not supported as a command-line "
1689
			   "option");
1690
1691
		value = 0;
1692
		while ((arg = strdelim(&cp)) != NULL && *arg != '\0') {
1693
			value++;
1694
			found = 0;
1695
			if (*arg != '/' && *arg != '~') {
1696
				xasprintf(&arg2, "%s/%s",
1697
				    SSHDIR, arg);
1698
			} else
1699
				arg2 = xstrdup(arg);
1700
1701
			/*
1702
			 * don't let the Match in Included clobber
1703
			 * the containing file's Match state.
1704
			 */
1705
			oactive = *activep;
1706
			/* browse cached list of files */
1707
			for (item = include_list; item != NULL; item = item->next) {
1708
				if (strcmp(item->selector, arg2) == 0) {
1709
					if (item->filename != NULL)
1710
						parse_server_config_depth(options, item->filename,
1711
							item->buffer, connectinfo,
1712
							(oactive ? 0 : SSHCFG_NEVERMATCH),
1713
							activep, depth + 1);
1714
					found = 1;
1715
					*activep = oactive;
1716
				}
1717
			}
1718
			if (found != 0) {
1719
				free(arg2);
1720
				continue;
1721
			}
1722
1723
			/* not in cache, a new glob */
1724
			debug3("Glob configuration file to include %s", arg2);
1725
			if (glob(arg2, 0, NULL, &gbuf) == 0) {
1726
				for (i = 0; (int) i < gbuf.gl_pathc; i++) {
1727
					debug3("Including configuration file %s",
1728
						gbuf.gl_pathv[i]);
1729
					item = malloc(sizeof(struct include_item));
1730
					item->selector = strdup(arg2);
1731
					item->filename = strdup(gbuf.gl_pathv[i]);
1732
					item->buffer = sshbuf_new();
1733
					load_server_config(item->filename, item->buffer);
1734
					parse_server_config_depth(options, item->filename,
1735
						item->buffer, connectinfo,
1736
					    (oactive ? 0 : SSHCFG_NEVERMATCH),
1737
					    activep, depth + 1);
1738
1739
					// append item to the end of the list
1740
					INCLUDE_LIST_APPEND(item)
1741
					*activep = oactive;
1742
				}
1743
			} else { /* no match or other error */
1744
				// store placeholder to avoid aditional empty globs
1745
				item = malloc(sizeof(struct include_item));
1746
				item->selector = strdup(arg2);
1747
				item->filename = NULL;
1748
				item->buffer = sshbuf_new();
1749
				// append item to the end of the list
1750
				INCLUDE_LIST_APPEND(item)
1751
			}
1752
			globfree(&gbuf);
1753
			free(arg2);
1754
		}
1755
		if (value == 0)
1756
			fatal("%s line %d: missing argument - file to include",
1757
			    filename, linenum);
1758
		break;
1759
1650
	case sMatch:
1760
	case sMatch:
1651
		if (cmdline)
1761
		if (cmdline)
1652
			fatal("Match directive not supported as a command-line "
1762
			fatal("Match directive not supported as a command-line "
Lines 1655-1661 process_server_config_line(ServerOptions *options, char *line, Link Here
1655
		if (value < 0)
1765
		if (value < 0)
1656
			fatal("%s line %d: Bad Match condition", filename,
1766
			fatal("%s line %d: Bad Match condition", filename,
1657
			    linenum);
1767
			    linenum);
1658
		*activep = value;
1768
		*activep = (inc_flags & SSHCFG_NEVERMATCH) ? 0 : value;
1659
		break;
1769
		break;
1660
1770
1661
	case sPermitOpen:
1771
	case sPermitOpen:
Lines 2106-2123 void Link Here
2106
parse_server_config(ServerOptions *options, const char *filename,
2216
parse_server_config(ServerOptions *options, const char *filename,
2107
    struct sshbuf *conf, struct connection_info *connectinfo)
2217
    struct sshbuf *conf, struct connection_info *connectinfo)
2108
{
2218
{
2109
	int active, linenum, bad_options = 0;
2219
	int active = connectinfo ? 0 : 1;
2220
	parse_server_config_depth(options, filename, conf, connectinfo,
2221
	    0, &active, 0);
2222
}
2223
2224
#define SERVCONF_MAX_DEPTH	16
2225
2226
void
2227
parse_server_config_depth(ServerOptions *options, const char *filename,
2228
    struct sshbuf *conf, struct connection_info *connectinfo,
2229
    int flags, int *activep, int depth)
2230
{
2231
	int linenum, bad_options = 0;
2110
	char *cp, *obuf, *cbuf;
2232
	char *cp, *obuf, *cbuf;
2111
2233
2234
	if (depth < 0 || depth > SERVCONF_MAX_DEPTH)
2235
		fatal("Too many recursive configuration includes");
2236
2112
	debug2("%s: config %s len %zu", __func__, filename, sshbuf_len(conf));
2237
	debug2("%s: config %s len %zu", __func__, filename, sshbuf_len(conf));
2113
2238
2114
	if ((obuf = cbuf = sshbuf_dup_string(conf)) == NULL)
2239
	if ((obuf = cbuf = sshbuf_dup_string(conf)) == NULL)
2115
		fatal("%s: sshbuf_dup_string failed", __func__);
2240
		fatal("%s: sshbuf_dup_string failed", __func__);
2116
	active = connectinfo ? 0 : 1;
2117
	linenum = 1;
2241
	linenum = 1;
2118
	while ((cp = strsep(&cbuf, "\n")) != NULL) {
2242
	while ((cp = strsep(&cbuf, "\n")) != NULL) {
2119
		if (process_server_config_line(options, cp, filename,
2243
		if (process_server_config_line_depth(options, cp, filename,
2120
		    linenum++, &active, connectinfo) != 0)
2244
		    linenum++, activep, connectinfo, flags, depth) != 0)
2121
			bad_options++;
2245
			bad_options++;
2122
	}
2246
	}
2123
	free(obuf);
2247
	free(obuf);
(-)a/servconf.h (+7 lines)
Lines 209-214 struct connection_info { Link Here
209
	int lport;		/* local port */
209
	int lport;		/* local port */
210
};
210
};
211
211
212
struct include_item {
213
	const char *selector;
214
	const char *filename;
215
	struct sshbuf *buffer;
216
	struct include_item *next;
217
};
218
212
219
213
/*
220
/*
214
 * These are string config options that must be copied between the
221
 * These are string config options that must be copied between the
(-)a/sshd_config.5 (+12 lines)
Lines 779-784 during Link Here
779
.Cm HostbasedAuthentication .
779
.Cm HostbasedAuthentication .
780
The default is
780
The default is
781
.Cm no .
781
.Cm no .
782
.It Cm Include
783
Include the specified configuration file(s).
784
Multiple path names may be specified and each pathname may contain
785
.Xr glob 3
786
wildcards.
787
Files without absolute paths are assumed to be in
788
.Pa /etc/ssh .
789
.Cm Include
790
directive may appear inside a
791
.Cm Match
792
block
793
to perform conditional inclusion.
782
.It Cm IPQoS
794
.It Cm IPQoS
783
Specifies the IPv4 type-of-service or DSCP class for the connection.
795
Specifies the IPv4 type-of-service or DSCP class for the connection.
784
Accepted values are
796
Accepted values are

Return to bug 2468