Skip to content

Commit

Permalink
upstream: allow the "Include" directive to expand the same set of
Browse files Browse the repository at this point in the history
%-tokens that "Match Exec" and environment variables.

ok dtucker@

OpenBSD-Commit-ID: 12ef521eaa966a9241e684258564f52f1f3c5d37
  • Loading branch information
djmdjm committed Sep 3, 2024
1 parent 51b8264 commit 8c4d6a6
Show file tree
Hide file tree
Showing 2 changed files with 95 additions and 47 deletions.
129 changes: 85 additions & 44 deletions readconf.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/* $OpenBSD: readconf.c,v 1.388 2024/08/23 04:51:00 deraadt Exp $ */
/* $OpenBSD: readconf.c,v 1.389 2024/09/03 05:29:55 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
Expand Down Expand Up @@ -645,6 +645,63 @@ check_match_ifaddrs(const char *addrlist)
#endif /* HAVE_IFADDRS_H */
}

/*
* Expand a "match exec" command or an Include path, caller must free returned
* value.
*/
static char *
expand_match_exec_or_include_path(const char *path, Options *options,
struct passwd *pw, const char *host_arg, const char *original_host,
int final_pass, int is_include_path)
{
char thishost[NI_MAXHOST], shorthost[NI_MAXHOST], portstr[NI_MAXSERV];
char uidstr[32], *conn_hash_hex, *keyalias, *jmphost, *ruser;
char *host, *ret;
int port;

port = options->port <= 0 ? default_ssh_port() : options->port;
ruser = options->user == NULL ? pw->pw_name : options->user;
if (final_pass) {
host = xstrdup(options->hostname);
} else if (options->hostname != NULL) {
/* NB. Please keep in sync with ssh.c:main() */
host = percent_expand(options->hostname,
"h", host_arg, (char *)NULL);
} else {
host = xstrdup(host_arg);
}
if (gethostname(thishost, sizeof(thishost)) == -1)
fatal("gethostname: %s", strerror(errno));
jmphost = option_clear_or_none(options->jump_host) ?
"" : options->jump_host;
strlcpy(shorthost, thishost, sizeof(shorthost));
shorthost[strcspn(thishost, ".")] = '\0';
snprintf(portstr, sizeof(portstr), "%d", port);
snprintf(uidstr, sizeof(uidstr), "%llu",
(unsigned long long)pw->pw_uid);
conn_hash_hex = ssh_connection_hash(thishost, host,
portstr, ruser, jmphost);
keyalias = options->host_key_alias ? options->host_key_alias : host;

ret = (is_include_path ? percent_dollar_expand : percent_expand)(path,
"C", conn_hash_hex,
"L", shorthost,
"d", pw->pw_dir,
"h", host,
"k", keyalias,
"l", thishost,
"n", original_host,
"p", portstr,
"r", ruser,
"u", pw->pw_name,
"i", uidstr,
"j", jmphost,
(char *)NULL);
free(host);
free(conn_hash_hex);
return ret;
}

/*
* Parse and execute a Match directive.
*/
Expand All @@ -655,15 +712,12 @@ match_cfg_line(Options *options, char **condition, struct passwd *pw,
{
char *arg, *oattrib, *attrib, *cmd, *cp = *condition, *host, *criteria;
const char *ruser;
int r, port, this_result, result = 1, attributes = 0, negate;
char thishost[NI_MAXHOST], shorthost[NI_MAXHOST], portstr[NI_MAXSERV];
char uidstr[32];
int r, this_result, result = 1, attributes = 0, negate;

/*
* Configuration is likely to be incomplete at this point so we
* must be prepared to use default values.
*/
port = options->port <= 0 ? default_ssh_port() : options->port;
ruser = options->user == NULL ? pw->pw_name : options->user;
if (final_pass) {
host = xstrdup(options->hostname);
Expand Down Expand Up @@ -765,37 +819,12 @@ match_cfg_line(Options *options, char **condition, struct passwd *pw,
if (r == (negate ? 1 : 0))
this_result = result = 0;
} else if (strcasecmp(attrib, "exec") == 0) {
char *conn_hash_hex, *keyalias, *jmphost;

if (gethostname(thishost, sizeof(thishost)) == -1)
fatal("gethostname: %s", strerror(errno));
jmphost = option_clear_or_none(options->jump_host) ?
"" : options->jump_host;
strlcpy(shorthost, thishost, sizeof(shorthost));
shorthost[strcspn(thishost, ".")] = '\0';
snprintf(portstr, sizeof(portstr), "%d", port);
snprintf(uidstr, sizeof(uidstr), "%llu",
(unsigned long long)pw->pw_uid);
conn_hash_hex = ssh_connection_hash(thishost, host,
portstr, ruser, jmphost);
keyalias = options->host_key_alias ?
options->host_key_alias : host;

cmd = percent_expand(arg,
"C", conn_hash_hex,
"L", shorthost,
"d", pw->pw_dir,
"h", host,
"k", keyalias,
"l", thishost,
"n", original_host,
"p", portstr,
"r", ruser,
"u", pw->pw_name,
"i", uidstr,
"j", jmphost,
(char *)NULL);
free(conn_hash_hex);
if ((cmd = expand_match_exec_or_include_path(arg,
options, pw, host_arg, original_host,
final_pass, 0)) == NULL) {
fatal("%.200s line %d: failed to expand match "
"exec '%.100s'", filename, linenum, arg);
}
if (result != 1) {
/* skip execution if prior predicate failed */
debug3("%.200s line %d: skipped exec "
Expand Down Expand Up @@ -1990,24 +2019,35 @@ process_config_line_depth(Options *options, struct passwd *pw, const char *host,
filename, linenum, keyword);
goto out;
}
/* Expand %tokens and environment variables */
if ((p = expand_match_exec_or_include_path(arg,
options, pw, host, original_host,
flags & SSHCONF_FINAL, 1)) == NULL) {
error("%.200s line %d: Unable to expand user "
"config file '%.100s'",
filename, linenum, arg);
continue;
}
/*
* Ensure all paths are anchored. User configuration
* files may begin with '~/' but system configurations
* must not. If the path is relative, then treat it
* as living in ~/.ssh for user configurations or
* /etc/ssh for system ones.
*/
if (*arg == '~' && (flags & SSHCONF_USERCONF) == 0) {
if (*p == '~' && (flags & SSHCONF_USERCONF) == 0) {
error("%.200s line %d: bad include path %s.",
filename, linenum, arg);
filename, linenum, p);
goto out;
}
if (!path_absolute(arg) && *arg != '~') {
if (!path_absolute(p) && *p != '~') {
xasprintf(&arg2, "%s/%s",
(flags & SSHCONF_USERCONF) ?
"~/" _PATH_SSH_USER_DIR : SSHDIR, arg);
} else
arg2 = xstrdup(arg);
"~/" _PATH_SSH_USER_DIR : SSHDIR, p);
} else {
arg2 = xstrdup(p);
}
free(p);
memset(&gl, 0, sizeof(gl));
r = glob(arg2, GLOB_TILDE, NULL, &gl);
if (r == GLOB_NOMATCH) {
Expand All @@ -2033,8 +2073,9 @@ process_config_line_depth(Options *options, struct passwd *pw, const char *host,
(oactive ? 0 : SSHCONF_NEVERMATCH),
activep, want_final_pass, depth + 1);
if (r != 1 && errno != ENOENT) {
error("Can't open user config file "
"%.100s: %.100s", gl.gl_pathv[i],
error("%.200s line %d: Can't open user "
"config file %.100s: %.100s",
filename, linenum, gl.gl_pathv[i],
strerror(errno));
globfree(&gl);
goto out;
Expand Down
13 changes: 10 additions & 3 deletions ssh_config.5
Original file line number Diff line number Diff line change
Expand Up @@ -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.399 2024/08/22 23:11:30 djm Exp $
.Dd $Mdocdate: August 22 2024 $
.\" $OpenBSD: ssh_config.5,v 1.400 2024/09/03 05:29:56 djm Exp $
.Dd $Mdocdate: September 3 2024 $
.Dt SSH_CONFIG 5
.Os
.Sh NAME
Expand Down Expand Up @@ -1182,7 +1182,12 @@ to unknown options that appear before it.
Include the specified configuration file(s).
Multiple pathnames may be specified and each pathname may contain
.Xr glob 7
wildcards and, for user configurations, shell-like
wildcards,
tokens as described in the
.Sx TOKENS
section, envrionment variables as described in the
.Sx ENVIRONMENT VARIABLES
section and, for user configurations, shell-like
.Sq ~
references to user home directories.
Wildcards will be expanded and processed in lexical order.
Expand Down Expand Up @@ -2270,6 +2275,7 @@ The local username.
.Cm ControlPath ,
.Cm IdentityAgent ,
.Cm IdentityFile ,
.Cm Include ,
.Cm KnownHostsCommand ,
.Cm LocalForward ,
.Cm Match exec ,
Expand Down Expand Up @@ -2318,6 +2324,7 @@ The keywords
.Cm ControlPath ,
.Cm IdentityAgent ,
.Cm IdentityFile ,
.Cm Include ,
.Cm KnownHostsCommand ,
and
.Cm UserKnownHostsFile
Expand Down

0 comments on commit 8c4d6a6

Please sign in to comment.