diff options
Diffstat (limited to 'misc.c')
-rw-r--r-- | misc.c | 165 |
1 files changed, 134 insertions, 31 deletions
@@ -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 | */ |
1092 | char * | 1093 | static char * |
1093 | percent_expand(const char *string, ...) | 1094 | vdollar_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 | ||
1210 | char * | ||
1211 | dollar_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 | */ | ||
1228 | char * | ||
1229 | percent_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 | */ | ||
1247 | char * | ||
1248 | percent_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 | |||
1159 | int | 1262 | int |
1160 | tun_open(int tun, int mode, char **ifname) | 1263 | tun_open(int tun, int mode, char **ifname) |
1161 | { | 1264 | { |