summaryrefslogtreecommitdiff
path: root/misc.c
diff options
context:
space:
mode:
authordtucker@openbsd.org <dtucker@openbsd.org>2020-05-29 04:25:40 +0000
committerDamien Miller <djm@mindrot.org>2020-05-29 15:46:47 +1000
commit4a1b46e6d032608b7ec00ae51c4e25b82f460b05 (patch)
tree7f345cd0424c5b6f7eff6e5d0f1b52747a960f9e /misc.c
parentc9bab1d3a9e183cef3a3412f57880a0374cc8cb2 (diff)
upstream: 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@ OpenBSD-Commit-ID: a4a2e801fc2d4df2fe0e58f50d9c81b03822dffa
Diffstat (limited to 'misc.c')
-rw-r--r--misc.c165
1 files changed, 134 insertions, 31 deletions
diff --git a/misc.c b/misc.c
index 5a34107f8..3ec02d79e 100644
--- a/misc.c
+++ b/misc.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: misc.c,v 1.149 2020/05/29 01:20:46 dtucker Exp $ */ 1/* $OpenBSD: misc.c,v 1.150 2020/05/29 04:25:40 dtucker Exp $ */
2/* 2/*
3 * Copyright (c) 2000 Markus Friedl. All rights reserved. 3 * Copyright (c) 2000 Markus Friedl. All rights reserved.
4 * Copyright (c) 2005-2020 Damien Miller. All rights reserved. 4 * Copyright (c) 2005-2020 Damien Miller. All rights reserved.
@@ -1084,45 +1084,90 @@ tilde_expand_filename(const char *filename, uid_t uid)
1084} 1084}
1085 1085
1086/* 1086/*
1087 * Expand a string with a set of %[char] escapes. A number of escapes may be 1087 * Expand a string with a set of %[char] escapes and/or ${ENVIRONMENT}
1088 * specified as (char *escape_chars, char *replacement) pairs. The list must 1088 * substitutions. A number of escapes may be specified as
1089 * be terminated by a NULL escape_char. Returns replaced string in memory 1089 * (char *escape_chars, char *replacement) pairs. The list must be terminated
1090 * allocated by xmalloc. 1090 * by a NULL escape_char. Returns replaced string in memory allocated by
1091 * xmalloc which the caller must free.
1091 */ 1092 */
1092char * 1093static char *
1093percent_expand(const char *string, ...) 1094vdollar_percent_expand(int *parseerror, int dollar, int percent,
1095 const char *string, va_list ap)
1094{ 1096{
1095#define EXPAND_MAX_KEYS 16 1097#define EXPAND_MAX_KEYS 16
1096 u_int num_keys, i; 1098 u_int num_keys = 0, i;
1097 struct { 1099 struct {
1098 const char *key; 1100 const char *key;
1099 const char *repl; 1101 const char *repl;
1100 } keys[EXPAND_MAX_KEYS]; 1102 } keys[EXPAND_MAX_KEYS];
1101 struct sshbuf *buf; 1103 struct sshbuf *buf;
1102 va_list ap; 1104 int r, missingvar = 0;
1103 int r; 1105 char *ret = NULL, *var, *varend, *val;
1104 char *ret; 1106 size_t len;
1105 1107
1106 if ((buf = sshbuf_new()) == NULL) 1108 if ((buf = sshbuf_new()) == NULL)
1107 fatal("%s: sshbuf_new failed", __func__); 1109 fatal("%s: sshbuf_new failed", __func__);
1108 1110 if (parseerror == NULL)
1109 /* Gather keys */ 1111 fatal("%s: null parseerror arg", __func__);
1110 va_start(ap, string); 1112 *parseerror = 1;
1111 for (num_keys = 0; num_keys < EXPAND_MAX_KEYS; num_keys++) { 1113
1112 keys[num_keys].key = va_arg(ap, char *); 1114 /* Gather keys if we're doing percent expansion. */
1113 if (keys[num_keys].key == NULL) 1115 if (percent) {
1114 break; 1116 for (num_keys = 0; num_keys < EXPAND_MAX_KEYS; num_keys++) {
1115 keys[num_keys].repl = va_arg(ap, char *); 1117 keys[num_keys].key = va_arg(ap, char *);
1116 if (keys[num_keys].repl == NULL) 1118 if (keys[num_keys].key == NULL)
1117 fatal("%s: NULL replacement", __func__); 1119 break;
1120 keys[num_keys].repl = va_arg(ap, char *);
1121 if (keys[num_keys].repl == NULL)
1122 fatal("%s: NULL replacement for token %s", __func__, keys[num_keys].key);
1123 }
1124 if (num_keys == EXPAND_MAX_KEYS && va_arg(ap, char *) != NULL)
1125 fatal("%s: too many keys", __func__);
1126 if (num_keys == 0)
1127 fatal("%s: percent expansion without token list",
1128 __func__);
1118 } 1129 }
1119 if (num_keys == EXPAND_MAX_KEYS && va_arg(ap, char *) != NULL)
1120 fatal("%s: too many keys", __func__);
1121 va_end(ap);
1122 1130
1123 /* Expand string */ 1131 /* Expand string */
1124 for (i = 0; *string != '\0'; string++) { 1132 for (i = 0; *string != '\0'; string++) {
1125 if (*string != '%') { 1133 /* Optionally process ${ENVIRONMENT} expansions. */
1134 if (dollar && string[0] == '$' && string[1] == '{') {
1135 string += 2; /* skip over '${' */
1136 if ((varend = strchr(string, '}')) == NULL) {
1137 error("%s: environment variable '%s' missing "
1138 "closing '}'", __func__, string);
1139 goto out;
1140 }
1141 len = varend - string;
1142 if (len == 0) {
1143 error("%s: zero-length environment variable",
1144 __func__);
1145 goto out;
1146 }
1147 var = xmalloc(len + 1);
1148 (void)strlcpy(var, string, len + 1);
1149 if ((val = getenv(var)) == NULL) {
1150 error("%s: env var ${%s} has no value",
1151 __func__, var);
1152 missingvar = 1;
1153 } else {
1154 debug3("%s: expand ${%s} -> '%s'", __func__,
1155 var, val);
1156 if ((r = sshbuf_put(buf, val, strlen(val))) !=0)
1157 fatal("%s: sshbuf_put: %s", __func__,
1158 ssh_err(r));
1159 }
1160 free(var);
1161 string += len;
1162 continue;
1163 }
1164
1165 /*
1166 * Process percent expansions if we have a list of TOKENs.
1167 * If we're not doing percent expansion everything just gets
1168 * appended here.
1169 */
1170 if (*string != '%' || !percent) {
1126 append: 1171 append:
1127 if ((r = sshbuf_put_u8(buf, *string)) != 0) { 1172 if ((r = sshbuf_put_u8(buf, *string)) != 0) {
1128 fatal("%s: sshbuf_put_u8: %s", 1173 fatal("%s: sshbuf_put_u8: %s",
@@ -1134,8 +1179,10 @@ percent_expand(const char *string, ...)
1134 /* %% case */ 1179 /* %% case */
1135 if (*string == '%') 1180 if (*string == '%')
1136 goto append; 1181 goto append;
1137 if (*string == '\0') 1182 if (*string == '\0') {
1138 fatal("%s: invalid format", __func__); 1183 error("%s: invalid format", __func__);
1184 goto out;
1185 }
1139 for (i = 0; i < num_keys; i++) { 1186 for (i = 0; i < num_keys; i++) {
1140 if (strchr(keys[i].key, *string) != NULL) { 1187 if (strchr(keys[i].key, *string) != NULL) {
1141 if ((r = sshbuf_put(buf, keys[i].repl, 1188 if ((r = sshbuf_put(buf, keys[i].repl,
@@ -1146,16 +1193,72 @@ percent_expand(const char *string, ...)
1146 break; 1193 break;
1147 } 1194 }
1148 } 1195 }
1149 if (i >= num_keys) 1196 if (i >= num_keys) {
1150 fatal("%s: unknown key %%%c", __func__, *string); 1197 error("%s: unknown key %%%c", __func__, *string);
1198 goto out;
1199 }
1151 } 1200 }
1152 if ((ret = sshbuf_dup_string(buf)) == NULL) 1201 if (!missingvar && (ret = sshbuf_dup_string(buf)) == NULL)
1153 fatal("%s: sshbuf_dup_string failed", __func__); 1202 fatal("%s: sshbuf_dup_string failed", __func__);
1203 *parseerror = 0;
1204 out:
1154 sshbuf_free(buf); 1205 sshbuf_free(buf);
1155 return ret; 1206 return *parseerror ? NULL : ret;
1156#undef EXPAND_MAX_KEYS 1207#undef EXPAND_MAX_KEYS
1157} 1208}
1158 1209
1210char *
1211dollar_expand(int *parseerr, const char *string)
1212{
1213 char *ret;
1214 int err;
1215 va_list ap;
1216
1217 memset(ap, 0, sizeof(ap)); /* unused */
1218 ret = vdollar_percent_expand(&err, 1, 0, string, ap);
1219 if (parseerr != NULL)
1220 *parseerr = err;
1221 return ret;
1222}
1223
1224/*
1225 * Returns expanded string or NULL if a specified environment variable is
1226 * not defined, or calls fatal if the string is invalid.
1227 */
1228char *
1229percent_expand(const char *string, ...)
1230{
1231 char *ret;
1232 int err;
1233 va_list ap;
1234
1235 va_start(ap, string);
1236 ret = vdollar_percent_expand(&err, 0, 1, string, ap);
1237 va_end(ap);
1238 if (err)
1239 fatal("%s failed", __func__);
1240 return ret;
1241}
1242
1243/*
1244 * Returns expanded string or NULL if a specified environment variable is
1245 * not defined, or calls fatal if the string is invalid.
1246 */
1247char *
1248percent_dollar_expand(const char *string, ...)
1249{
1250 char *ret;
1251 int err;
1252 va_list ap;
1253
1254 va_start(ap, string);
1255 ret = vdollar_percent_expand(&err, 1, 1, string, ap);
1256 va_end(ap);
1257 if (err)
1258 fatal("%s failed", __func__);
1259 return ret;
1260}
1261
1159int 1262int
1160tun_open(int tun, int mode, char **ifname) 1263tun_open(int tun, int mode, char **ifname)
1161{ 1264{