View | Details | Raw Unified | Return to bug 3140
Collapse All | Expand All

(-)usr.bin/ssh/misc.c (+62 lines)
Lines 1089-1094 percent_expand(const char *string, ...) Link Here
1089
#undef EXPAND_MAX_KEYS
1089
#undef EXPAND_MAX_KEYS
1090
}
1090
}
1091
1091
1092
/*
1093
 * Expand a string with a set of ${ENVIRONMENT_VARIABLES}.  Returns a
1094
 * dynamically allocated expanded string which caller must free or NULL in
1095
 * the case of an error.
1096
 */
1097
char *
1098
dollar_env_expand(const char *string)
1099
{
1100
	u_int i;
1101
	struct sshbuf *buf;
1102
	int r;
1103
	char *ret = NULL, *var, *varend, *val;
1104
	size_t len;
1105
1106
	if ((buf = sshbuf_new()) == NULL)
1107
		fatal("%s: sshbuf_new failed", __func__);
1108
1109
	for (i = 0; *string != '\0'; string++) {
1110
		if (*string != '$' || (string[0] == '$' && string[1] != '{')) {
1111
			if ((r = sshbuf_put_u8(buf, *string)) != 0) {
1112
				fatal("%s: sshbuf_put_u8: %s",
1113
				    __func__, ssh_err(r));
1114
			}
1115
			continue;
1116
		}
1117
		string += 2;  /* skip over '${' */
1118
		if (*string == '\0') {
1119
			error("%s: invalid environment variable", __func__);
1120
			goto out;
1121
		}
1122
		if ((varend = strchr(string, '}')) == NULL) {
1123
			error("%s: environment variable '%s' missing closing "
1124
			    "'}'", __func__, string);
1125
			goto out;
1126
		}
1127
		len = varend - string;
1128
		if (len == 0) {
1129
			error("%s: zero-length environment variable", __func__);
1130
			goto out;
1131
		}
1132
		var = xmalloc(len + 1);
1133
		(void)strlcpy(var, string, len + 1);
1134
		if ((val = getenv(var)) == NULL) {
1135
			error("%s: env var ${%s} has no value", __func__, var);
1136
			free(var);
1137
			goto out;
1138
		} else {
1139
			debug3("%s: expand ${%s} -> '%s'", __func__, var, val);
1140
			if ((r = sshbuf_put(buf, val, strlen(val))) != 0)
1141
				fatal("%s: sshbuf_put: %s", __func__,
1142
				    ssh_err(r));
1143
		}
1144
		free(var);
1145
		string += len;
1146
	}
1147
	if ((ret = sshbuf_dup_string(buf)) == NULL)
1148
		fatal("%s: sshbuf_dup_string failed", __func__);
1149
 out:
1150
	sshbuf_free(buf);
1151
	return ret;
1152
}
1153
1092
int
1154
int
1093
tun_open(int tun, int mode, char **ifname)
1155
tun_open(int tun, int mode, char **ifname)
1094
{
1156
{
(-)usr.bin/ssh/misc.h (+1 lines)
Lines 68-73 int parse_uri(const char *, const char Link Here
68
long	 convtime(const char *);
68
long	 convtime(const char *);
69
char	*tilde_expand_filename(const char *, uid_t);
69
char	*tilde_expand_filename(const char *, uid_t);
70
char	*percent_expand(const char *, ...) __attribute__((__sentinel__));
70
char	*percent_expand(const char *, ...) __attribute__((__sentinel__));
71
char	*dollar_env_expand(const char *);
71
char	*tohex(const void *, size_t);
72
char	*tohex(const void *, size_t);
72
void	 xextendf(char **s, const char *sep, const char *fmt, ...)
73
void	 xextendf(char **s, const char *sep, const char *fmt, ...)
73
    __attribute__((__format__ (printf, 3, 4))) __attribute__((__nonnull__ (3)));
74
    __attribute__((__format__ (printf, 3, 4))) __attribute__((__nonnull__ (3)));
(-)usr.bin/ssh/readconf.c (-2 / +12 lines)
Lines 1790-1796 parse_keytypes: Link Here
1790
			    filename, linenum);
1790
			    filename, linenum);
1791
  parse_agent_path:
1791
  parse_agent_path:
1792
		/* Extra validation if the string represents an env var. */
1792
		/* Extra validation if the string represents an env var. */
1793
		if (arg[0] == '$' && !valid_env_name(arg + 1)) {
1793
		if ((arg2 = dollar_env_expand(arg)) == NULL)
1794
			fatal("%.200s line %d: Invalid environment expansion "
1795
			    "%s.", filename, linenum, arg);
1796
		free(arg2);
1797
		/* check for legacy environment format */
1798
		if (arg[0] == '$' && arg[1] != '{' && !valid_env_name(arg + 1)) {
1794
			fatal("%.200s line %d: Invalid environment name %s.",
1799
			fatal("%.200s line %d: Invalid environment name %s.",
1795
			    filename, linenum, arg);
1800
			    filename, linenum, arg);
1796
		}
1801
		}
Lines 2334-2340 parse_forward(struct Forward *fwd, const Link Here
2334
	memset(fwd, 0, sizeof(*fwd));
2339
	memset(fwd, 0, sizeof(*fwd));
2335
	memset(fwdargs, 0, sizeof(fwdargs));
2340
	memset(fwdargs, 0, sizeof(fwdargs));
2336
2341
2337
	cp = p = xstrdup(fwdspec);
2342
	/*
2343
	 * We expand environment variables before checking if we think they're
2344
	 * paths so that if ${VAR} expands to a fully qualified path it is
2345
	 * treated as a path.
2346
	 */
2347
	cp = p = dollar_env_expand(fwdspec);
2338
2348
2339
	/* skip leading spaces */
2349
	/* skip leading spaces */
2340
	while (isspace((u_char)*cp))
2350
	while (isspace((u_char)*cp))
(-)usr.bin/ssh/ssh.c (-6 / +25 lines)
Lines 244-249 default_client_percent_expand(const char Link Here
244
}
244
}
245
245
246
/*
246
/*
247
 * Expand percent then environment variables, percent first as they're less
248
 * likely to influence environment expansion.
249
 */
250
static char *
251
default_client_percent_dollar_expand(const char *str, const char *homedir,
252
    const char *remhost, const char *remuser, const char *locuser)
253
{
254
	char *str1, *str2;
255
256
	str1 = default_client_percent_expand(str, homedir, remhost, remuser,
257
	    locuser);
258
	if ((str2 = dollar_env_expand(str1)) == NULL)
259
		fatal("invalid environment variable expansion");
260
	free(str1);
261
	return(str2);
262
}
263
264
/*
247
 * Attempt to resolve a host name / port to a set of addresses and
265
 * Attempt to resolve a host name / port to a set of addresses and
248
 * optionally return any CNAMEs encountered along the way.
266
 * optionally return any CNAMEs encountered along the way.
249
 * Returns NULL on failure.
267
 * Returns NULL on failure.
Lines 1357-1370 main(int ac, char **av) Link Here
1357
	if (options.control_path != NULL) {
1375
	if (options.control_path != NULL) {
1358
		cp = tilde_expand_filename(options.control_path, getuid());
1376
		cp = tilde_expand_filename(options.control_path, getuid());
1359
		free(options.control_path);
1377
		free(options.control_path);
1360
		options.control_path = default_client_percent_expand(cp,
1378
		options.control_path = default_client_percent_dollar_expand(cp,
1361
		    pw->pw_dir, host, options.user, pw->pw_name);
1379
		    pw->pw_dir, host, options.user, pw->pw_name);
1362
		free(cp);
1380
		free(cp);
1363
	}
1381
	}
1364
1382
1365
	if (options.identity_agent != NULL) {
1383
	if (options.identity_agent != NULL) {
1366
		p = tilde_expand_filename(options.identity_agent, getuid());
1384
		p = tilde_expand_filename(options.identity_agent, getuid());
1367
		cp = default_client_percent_expand(p,
1385
		cp = default_client_percent_dollar_expand(p,
1368
		    pw->pw_dir, host, options.user, pw->pw_name);
1386
		    pw->pw_dir, host, options.user, pw->pw_name);
1369
		free(p);
1387
		free(p);
1370
		free(options.identity_agent);
1388
		free(options.identity_agent);
Lines 1374-1380 main(int ac, char **av) Link Here
1374
	if (options.forward_agent_sock_path != NULL) {
1392
	if (options.forward_agent_sock_path != NULL) {
1375
		p = tilde_expand_filename(options.forward_agent_sock_path,
1393
		p = tilde_expand_filename(options.forward_agent_sock_path,
1376
		    getuid());
1394
		    getuid());
1377
		cp = default_client_percent_expand(p,
1395
		cp = default_client_percent_dollar_expand(p,
1378
		    pw->pw_dir, host, options.user, pw->pw_name);
1396
		    pw->pw_dir, host, options.user, pw->pw_name);
1379
		free(p);
1397
		free(p);
1380
		free(options.forward_agent_sock_path);
1398
		free(options.forward_agent_sock_path);
Lines 1546-1552 main(int ac, char **av) Link Here
1546
			unsetenv(SSH_AUTHSOCKET_ENV_NAME);
1564
			unsetenv(SSH_AUTHSOCKET_ENV_NAME);
1547
		} else {
1565
		} else {
1548
			cp = options.identity_agent;
1566
			cp = options.identity_agent;
1549
			if (cp[0] == '$') {
1567
			/* legacy (limited) format */
1568
			if (cp[0] == '$' && cp[1] != '{') {
1550
				if (!valid_env_name(cp + 1)) {
1569
				if (!valid_env_name(cp + 1)) {
1551
					fatal("Invalid IdentityAgent "
1570
					fatal("Invalid IdentityAgent "
1552
					    "environment variable name %s", cp);
1571
					    "environment variable name %s", cp);
Lines 2174-2180 load_public_identity_files(struct passwd Link Here
2174
			continue;
2193
			continue;
2175
		}
2194
		}
2176
		cp = tilde_expand_filename(options.identity_files[i], getuid());
2195
		cp = tilde_expand_filename(options.identity_files[i], getuid());
2177
		filename = default_client_percent_expand(cp,
2196
		filename = default_client_percent_dollar_expand(cp,
2178
		    pw->pw_dir, host, options.user, pw->pw_name);
2197
		    pw->pw_dir, host, options.user, pw->pw_name);
2179
		free(cp);
2198
		free(cp);
2180
		check_load(sshkey_load_public(filename, &public, NULL),
2199
		check_load(sshkey_load_public(filename, &public, NULL),
Lines 2224-2230 load_public_identity_files(struct passwd Link Here
2224
	for (i = 0; i < options.num_certificate_files; i++) {
2243
	for (i = 0; i < options.num_certificate_files; i++) {
2225
		cp = tilde_expand_filename(options.certificate_files[i],
2244
		cp = tilde_expand_filename(options.certificate_files[i],
2226
		    getuid());
2245
		    getuid());
2227
		filename = default_client_percent_expand(cp,
2246
		filename = default_client_percent_dollar_expand(cp,
2228
		    pw->pw_dir, host, options.user, pw->pw_name);
2247
		    pw->pw_dir, host, options.user, pw->pw_name);
2229
		free(cp);
2248
		free(cp);
2230
2249
(-)usr.bin/ssh/ssh_config.5 (-2 / +4 lines)
Lines 551-559 section above or the string Link Here
551
to disable connection sharing.
551
to disable connection sharing.
552
Arguments to
552
Arguments to
553
.Cm ControlPath
553
.Cm ControlPath
554
may use the tilde syntax to refer to a user's home directory
554
may use the tilde syntax to refer to a user's home directory,
555
or the tokens described in the
555
tokens described in the
556
.Sx TOKENS
556
.Sx TOKENS
557
or environment variables as described in the
558
.Sx ENVIRONMENT VARIABLES
557
section.
559
section.
558
It is recommended that any
560
It is recommended that any
559
.Cm ControlPath
561
.Cm ControlPath
(-)regress/usr.bin/ssh/percent.sh (-1 / +12 lines)
Lines 51-57 trial() Link Here
51
51
52
for i in matchexec localcommand remotecommand controlpath identityagent \
52
for i in matchexec localcommand remotecommand controlpath identityagent \
53
    forwardagent localforward remoteforward; do
53
    forwardagent localforward remoteforward; do
54
	verbose $tid $i
54
	verbose $tid $i percent
55
	if [ "$i" = "localcommand" ]; then
55
	if [ "$i" = "localcommand" ]; then
56
		REMUSER=$USER
56
		REMUSER=$USER
57
		trial $i '%T' NONE
57
		trial $i '%T' NONE
Lines 76-83 for i in matchexec localcommand remoteco Link Here
76
	    "%/$HASH/$USERID/127.0.0.1/$HOME/$HOST/$HOSTNAME/somehost/$PORT/$REMUSER/$USER"
76
	    "%/$HASH/$USERID/127.0.0.1/$HOME/$HOST/$HOSTNAME/somehost/$PORT/$REMUSER/$USER"
77
done
77
done
78
78
79
# Subset of above since don't expand shell-style variables on anything that
80
# runs a command
81
for i in controlpath identityagent forwardagent localforward remoteforward; do
82
	verbose $tid $i dollar
83
	FOO=bar
84
	export FOO
85
	trial $i '${FOO}' $FOO
86
done
87
88
79
# A subset of options support tilde expansion
89
# A subset of options support tilde expansion
80
for i in controlpath identityagent forwardagent; do
90
for i in controlpath identityagent forwardagent; do
91
	verbose $tid $i tilde
81
	trial $i '~' $HOME/
92
	trial $i '~' $HOME/
82
	trial $i '~/.ssh' $HOME/.ssh
93
	trial $i '~/.ssh' $HOME/.ssh
83
done
94
done
(-)regress/usr.bin/ssh/unittests/misc/tests.c (+36 lines)
Lines 76-79 tests(void) Link Here
76
	ASSERT_STRING_EQ(path, "some/path");
76
	ASSERT_STRING_EQ(path, "some/path");
77
	free(user); free(host); free(path);
77
	free(user); free(host); free(path);
78
	TEST_DONE();
78
	TEST_DONE();
79
80
#define ASSERT_DOLLAR_EQ(x, y) do { \
81
	char *str = dollar_env_expand((x)); \
82
	ASSERT_STRING_EQ(str, (y)); \
83
	free(str); \
84
} while(0)
85
86
	TEST_START("dollar_environment_expand");
87
	if (setenv("FOO", "bar", 1) != 0)
88
		abort();
89
	if (setenv("BAR", "baz", 1) != 0)
90
		abort();
91
	if (unsetenv("BAZ") != 0)
92
		abort();
93
	ASSERT_DOLLAR_EQ("${FOO}", "bar");
94
	ASSERT_DOLLAR_EQ(" ${FOO}", " bar");
95
	ASSERT_DOLLAR_EQ("${FOO} ", "bar ");
96
	ASSERT_DOLLAR_EQ(" ${FOO} ", " bar ");
97
	ASSERT_DOLLAR_EQ("${FOO}${BAR}", "barbaz");
98
	ASSERT_DOLLAR_EQ(" ${FOO} ${BAR}", " bar baz");
99
	ASSERT_DOLLAR_EQ("${FOO}${BAR} ", "barbaz ");
100
	ASSERT_DOLLAR_EQ(" ${FOO} ${BAR} ", " bar baz ");
101
	ASSERT_DOLLAR_EQ("$", "$");
102
	ASSERT_DOLLAR_EQ(" $", " $");
103
	ASSERT_DOLLAR_EQ("$ ", "$ ");
104
	/* error checking, non existent variable */
105
	ASSERT_PTR_EQ(dollar_env_expand("a${BAZ}"), NULL);
106
	ASSERT_PTR_EQ(dollar_env_expand("${BAZ}b"), NULL);
107
	ASSERT_PTR_EQ(dollar_env_expand("a${BAZ}b"), NULL);
108
	/* invalid format */
109
	ASSERT_PTR_EQ(dollar_env_expand("${"), NULL);
110
	ASSERT_PTR_EQ(dollar_env_expand("${F"), NULL);
111
	ASSERT_PTR_EQ(dollar_env_expand("${FO"), NULL);
112
	/* empty variable name */
113
	ASSERT_PTR_EQ(dollar_env_expand("${}"), NULL);
114
	TEST_DONE();
79
}
115
}

Return to bug 3140