diff options
author | 2020-05-29 04:25:40 +0000 | |
---|---|---|
committer | 2020-05-29 04:25:40 +0000 | |
commit | bc1b80f45cd449470808bef982947f242ac94bcc (patch) | |
tree | 89d6614fb80b9250e1dc141e18712848fac64146 /usr.bin/ssh/misc.c | |
parent | pointless referencing .h files in SYNOPSIS (diff) | |
download | wireguard-openbsd-bc1b80f45cd449470808bef982947f242ac94bcc.tar.xz wireguard-openbsd-bc1b80f45cd449470808bef982947f242ac94bcc.zip |
Allow some keywords to expand shell-style ${ENV} environment
variables on the client side. The supported keywords are
CertificateFile, ControlPath, IdentityAgent and IdentityFile, plus
LocalForward and RemoteForward when used for Unix domain socket
paths. This would for example allow forwarding of Unix domain
socket paths that change at runtime. bz#3140, ok djm@
Diffstat (limited to 'usr.bin/ssh/misc.c')
-rw-r--r-- | usr.bin/ssh/misc.c | 165 |
1 files changed, 134 insertions, 31 deletions
diff --git a/usr.bin/ssh/misc.c b/usr.bin/ssh/misc.c index e52656cad21..73c86438904 100644 --- a/usr.bin/ssh/misc.c +++ b/usr.bin/ssh/misc.c @@ -1,4 +1,4 @@ -/* $OpenBSD: misc.c,v 1.149 2020/05/29 01:20:46 dtucker Exp $ */ +/* $OpenBSD: misc.c,v 1.150 2020/05/29 04:25:40 dtucker Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * Copyright (c) 2005-2020 Damien Miller. All rights reserved. @@ -1049,45 +1049,90 @@ tilde_expand_filename(const char *filename, uid_t uid) } /* - * Expand a string with a set of %[char] escapes. A number of escapes may be - * specified as (char *escape_chars, char *replacement) pairs. The list must - * be terminated by a NULL escape_char. Returns replaced string in memory - * allocated by xmalloc. + * Expand a string with a set of %[char] escapes and/or ${ENVIRONMENT} + * substitutions. A number of escapes may be specified as + * (char *escape_chars, char *replacement) pairs. The list must be terminated + * by a NULL escape_char. Returns replaced string in memory allocated by + * xmalloc which the caller must free. */ -char * -percent_expand(const char *string, ...) +static char * +vdollar_percent_expand(int *parseerror, int dollar, int percent, + const char *string, va_list ap) { #define EXPAND_MAX_KEYS 16 - u_int num_keys, i; + u_int num_keys = 0, i; struct { const char *key; const char *repl; } keys[EXPAND_MAX_KEYS]; struct sshbuf *buf; - va_list ap; - int r; - char *ret; + int r, missingvar = 0; + char *ret = NULL, *var, *varend, *val; + size_t len; if ((buf = sshbuf_new()) == NULL) fatal("%s: sshbuf_new failed", __func__); - - /* Gather keys */ - va_start(ap, string); - for (num_keys = 0; num_keys < EXPAND_MAX_KEYS; num_keys++) { - keys[num_keys].key = va_arg(ap, char *); - if (keys[num_keys].key == NULL) - break; - keys[num_keys].repl = va_arg(ap, char *); - if (keys[num_keys].repl == NULL) - fatal("%s: NULL replacement", __func__); + if (parseerror == NULL) + fatal("%s: null parseerror arg", __func__); + *parseerror = 1; + + /* Gather keys if we're doing percent expansion. */ + if (percent) { + for (num_keys = 0; num_keys < EXPAND_MAX_KEYS; num_keys++) { + keys[num_keys].key = va_arg(ap, char *); + if (keys[num_keys].key == NULL) + break; + keys[num_keys].repl = va_arg(ap, char *); + if (keys[num_keys].repl == NULL) + fatal("%s: NULL replacement for token %s", __func__, keys[num_keys].key); + } + if (num_keys == EXPAND_MAX_KEYS && va_arg(ap, char *) != NULL) + fatal("%s: too many keys", __func__); + if (num_keys == 0) + fatal("%s: percent expansion without token list", + __func__); } - if (num_keys == EXPAND_MAX_KEYS && va_arg(ap, char *) != NULL) - fatal("%s: too many keys", __func__); - va_end(ap); /* Expand string */ for (i = 0; *string != '\0'; string++) { - if (*string != '%') { + /* Optionally process ${ENVIRONMENT} expansions. */ + if (dollar && string[0] == '$' && string[1] == '{') { + string += 2; /* skip over '${' */ + if ((varend = strchr(string, '}')) == NULL) { + error("%s: environment variable '%s' missing " + "closing '}'", __func__, string); + goto out; + } + len = varend - string; + if (len == 0) { + error("%s: zero-length environment variable", + __func__); + goto out; + } + var = xmalloc(len + 1); + (void)strlcpy(var, string, len + 1); + if ((val = getenv(var)) == NULL) { + error("%s: env var ${%s} has no value", + __func__, var); + missingvar = 1; + } else { + debug3("%s: expand ${%s} -> '%s'", __func__, + var, val); + if ((r = sshbuf_put(buf, val, strlen(val))) !=0) + fatal("%s: sshbuf_put: %s", __func__, + ssh_err(r)); + } + free(var); + string += len; + continue; + } + + /* + * Process percent expansions if we have a list of TOKENs. + * If we're not doing percent expansion everything just gets + * appended here. + */ + if (*string != '%' || !percent) { append: if ((r = sshbuf_put_u8(buf, *string)) != 0) { fatal("%s: sshbuf_put_u8: %s", @@ -1099,8 +1144,10 @@ percent_expand(const char *string, ...) /* %% case */ if (*string == '%') goto append; - if (*string == '\0') - fatal("%s: invalid format", __func__); + if (*string == '\0') { + error("%s: invalid format", __func__); + goto out; + } for (i = 0; i < num_keys; i++) { if (strchr(keys[i].key, *string) != NULL) { if ((r = sshbuf_put(buf, keys[i].repl, @@ -1111,16 +1158,72 @@ percent_expand(const char *string, ...) break; } } - if (i >= num_keys) - fatal("%s: unknown key %%%c", __func__, *string); + if (i >= num_keys) { + error("%s: unknown key %%%c", __func__, *string); + goto out; + } } - if ((ret = sshbuf_dup_string(buf)) == NULL) + if (!missingvar && (ret = sshbuf_dup_string(buf)) == NULL) fatal("%s: sshbuf_dup_string failed", __func__); + *parseerror = 0; + out: sshbuf_free(buf); - return ret; + return *parseerror ? NULL : ret; #undef EXPAND_MAX_KEYS } +char * +dollar_expand(int *parseerr, const char *string) +{ + char *ret; + int err; + va_list ap; + + memset(ap, 0, sizeof(ap)); /* unused */ + ret = vdollar_percent_expand(&err, 1, 0, string, ap); + if (parseerr != NULL) + *parseerr = err; + return ret; +} + +/* + * Returns expanded string or NULL if a specified environment variable is + * not defined, or calls fatal if the string is invalid. + */ +char * +percent_expand(const char *string, ...) +{ + char *ret; + int err; + va_list ap; + + va_start(ap, string); + ret = vdollar_percent_expand(&err, 0, 1, string, ap); + va_end(ap); + if (err) + fatal("%s failed", __func__); + return ret; +} + +/* + * Returns expanded string or NULL if a specified environment variable is + * not defined, or calls fatal if the string is invalid. + */ +char * +percent_dollar_expand(const char *string, ...) +{ + char *ret; + int err; + va_list ap; + + va_start(ap, string); + ret = vdollar_percent_expand(&err, 1, 1, string, ap); + va_end(ap); + if (err) + fatal("%s failed", __func__); + return ret; +} + int tun_open(int tun, int mode, char **ifname) { |