diff options
Diffstat (limited to 'misc.c')
-rw-r--r-- | misc.c | 263 |
1 files changed, 205 insertions, 58 deletions
@@ -1,29 +1,23 @@ | |||
1 | /* $OpenBSD: misc.c,v 1.147 2020/04/25 06:59:36 dtucker Exp $ */ | 1 | /* $OpenBSD: misc.c,v 1.153 2020/06/26 05:16:38 djm 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,2006 Damien Miller. All rights reserved. | 4 | * Copyright (c) 2005-2020 Damien Miller. All rights reserved. |
5 | * Copyright (c) 2004 Henning Brauer <henning@openbsd.org> | ||
5 | * | 6 | * |
6 | * Redistribution and use in source and binary forms, with or without | 7 | * Permission to use, copy, modify, and distribute this software for any |
7 | * modification, are permitted provided that the following conditions | 8 | * purpose with or without fee is hereby granted, provided that the above |
8 | * are met: | 9 | * copyright notice and this permission notice appear in all copies. |
9 | * 1. Redistributions of source code must retain the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer. | ||
11 | * 2. Redistributions in binary form must reproduce the above copyright | ||
12 | * notice, this list of conditions and the following disclaimer in the | ||
13 | * documentation and/or other materials provided with the distribution. | ||
14 | * | 10 | * |
15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | 11 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
16 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | 12 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
17 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | 13 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
18 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | 14 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
19 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | 15 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
20 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 16 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
21 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 17 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
22 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
23 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | ||
24 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
25 | */ | 18 | */ |
26 | 19 | ||
20 | |||
27 | #include "includes.h" | 21 | #include "includes.h" |
28 | 22 | ||
29 | #include <sys/types.h> | 23 | #include <sys/types.h> |
@@ -260,7 +254,7 @@ waitfd(int fd, int *timeoutp, short events) | |||
260 | errno = oerrno; | 254 | errno = oerrno; |
261 | if (r > 0) | 255 | if (r > 0) |
262 | return 0; | 256 | return 0; |
263 | else if (r == -1 && errno != EAGAIN) | 257 | else if (r == -1 && errno != EAGAIN && errno != EINTR) |
264 | return -1; | 258 | return -1; |
265 | else if (r == 0) | 259 | else if (r == 0) |
266 | break; | 260 | break; |
@@ -299,12 +293,17 @@ timeout_connect(int sockfd, const struct sockaddr *serv_addr, | |||
299 | return connect(sockfd, serv_addr, addrlen); | 293 | return connect(sockfd, serv_addr, addrlen); |
300 | 294 | ||
301 | set_nonblock(sockfd); | 295 | set_nonblock(sockfd); |
302 | if (connect(sockfd, serv_addr, addrlen) == 0) { | 296 | for (;;) { |
303 | /* Succeeded already? */ | 297 | if (connect(sockfd, serv_addr, addrlen) == 0) { |
304 | unset_nonblock(sockfd); | 298 | /* Succeeded already? */ |
305 | return 0; | 299 | unset_nonblock(sockfd); |
306 | } else if (errno != EINPROGRESS) | 300 | return 0; |
307 | return -1; | 301 | } else if (errno == EINTR) |
302 | continue; | ||
303 | else if (errno != EINPROGRESS) | ||
304 | return -1; | ||
305 | break; | ||
306 | } | ||
308 | 307 | ||
309 | if (waitfd(sockfd, timeoutp, POLLIN | POLLOUT) == -1) | 308 | if (waitfd(sockfd, timeoutp, POLLIN | POLLOUT) == -1) |
310 | return -1; | 309 | return -1; |
@@ -494,7 +493,7 @@ a2tun(const char *s, int *remote) | |||
494 | long | 493 | long |
495 | convtime(const char *s) | 494 | convtime(const char *s) |
496 | { | 495 | { |
497 | long total, secs, multiplier = 1; | 496 | long total, secs, multiplier; |
498 | const char *p; | 497 | const char *p; |
499 | char *endp; | 498 | char *endp; |
500 | 499 | ||
@@ -512,6 +511,7 @@ convtime(const char *s) | |||
512 | secs < 0) | 511 | secs < 0) |
513 | return -1; | 512 | return -1; |
514 | 513 | ||
514 | multiplier = 1; | ||
515 | switch (*endp++) { | 515 | switch (*endp++) { |
516 | case '\0': | 516 | case '\0': |
517 | endp--; | 517 | endp--; |
@@ -552,6 +552,43 @@ convtime(const char *s) | |||
552 | return total; | 552 | return total; |
553 | } | 553 | } |
554 | 554 | ||
555 | #define TF_BUFS 8 | ||
556 | #define TF_LEN 9 | ||
557 | |||
558 | const char * | ||
559 | fmt_timeframe(time_t t) | ||
560 | { | ||
561 | char *buf; | ||
562 | static char tfbuf[TF_BUFS][TF_LEN]; /* ring buffer */ | ||
563 | static int idx = 0; | ||
564 | unsigned int sec, min, hrs, day; | ||
565 | unsigned long long week; | ||
566 | |||
567 | buf = tfbuf[idx++]; | ||
568 | if (idx == TF_BUFS) | ||
569 | idx = 0; | ||
570 | |||
571 | week = t; | ||
572 | |||
573 | sec = week % 60; | ||
574 | week /= 60; | ||
575 | min = week % 60; | ||
576 | week /= 60; | ||
577 | hrs = week % 24; | ||
578 | week /= 24; | ||
579 | day = week % 7; | ||
580 | week /= 7; | ||
581 | |||
582 | if (week > 0) | ||
583 | snprintf(buf, TF_LEN, "%02lluw%01ud%02uh", week, day, hrs); | ||
584 | else if (day > 0) | ||
585 | snprintf(buf, TF_LEN, "%01ud%02uh%02um", day, hrs, min); | ||
586 | else | ||
587 | snprintf(buf, TF_LEN, "%02u:%02u:%02u", hrs, min, sec); | ||
588 | |||
589 | return (buf); | ||
590 | } | ||
591 | |||
555 | /* | 592 | /* |
556 | * Returns a standardized host+port identifier string. | 593 | * Returns a standardized host+port identifier string. |
557 | * Caller must free returned string. | 594 | * Caller must free returned string. |
@@ -1053,45 +1090,90 @@ tilde_expand_filename(const char *filename, uid_t uid) | |||
1053 | } | 1090 | } |
1054 | 1091 | ||
1055 | /* | 1092 | /* |
1056 | * Expand a string with a set of %[char] escapes. A number of escapes may be | 1093 | * Expand a string with a set of %[char] escapes and/or ${ENVIRONMENT} |
1057 | * specified as (char *escape_chars, char *replacement) pairs. The list must | 1094 | * substitutions. A number of escapes may be specified as |
1058 | * be terminated by a NULL escape_char. Returns replaced string in memory | 1095 | * (char *escape_chars, char *replacement) pairs. The list must be terminated |
1059 | * allocated by xmalloc. | 1096 | * by a NULL escape_char. Returns replaced string in memory allocated by |
1097 | * xmalloc which the caller must free. | ||
1060 | */ | 1098 | */ |
1061 | char * | 1099 | static char * |
1062 | percent_expand(const char *string, ...) | 1100 | vdollar_percent_expand(int *parseerror, int dollar, int percent, |
1101 | const char *string, va_list ap) | ||
1063 | { | 1102 | { |
1064 | #define EXPAND_MAX_KEYS 16 | 1103 | #define EXPAND_MAX_KEYS 16 |
1065 | u_int num_keys, i; | 1104 | u_int num_keys = 0, i; |
1066 | struct { | 1105 | struct { |
1067 | const char *key; | 1106 | const char *key; |
1068 | const char *repl; | 1107 | const char *repl; |
1069 | } keys[EXPAND_MAX_KEYS]; | 1108 | } keys[EXPAND_MAX_KEYS]; |
1070 | struct sshbuf *buf; | 1109 | struct sshbuf *buf; |
1071 | va_list ap; | 1110 | int r, missingvar = 0; |
1072 | int r; | 1111 | char *ret = NULL, *var, *varend, *val; |
1073 | char *ret; | 1112 | size_t len; |
1074 | 1113 | ||
1075 | if ((buf = sshbuf_new()) == NULL) | 1114 | if ((buf = sshbuf_new()) == NULL) |
1076 | fatal("%s: sshbuf_new failed", __func__); | 1115 | fatal("%s: sshbuf_new failed", __func__); |
1077 | 1116 | if (parseerror == NULL) | |
1078 | /* Gather keys */ | 1117 | fatal("%s: null parseerror arg", __func__); |
1079 | va_start(ap, string); | 1118 | *parseerror = 1; |
1080 | for (num_keys = 0; num_keys < EXPAND_MAX_KEYS; num_keys++) { | 1119 | |
1081 | keys[num_keys].key = va_arg(ap, char *); | 1120 | /* Gather keys if we're doing percent expansion. */ |
1082 | if (keys[num_keys].key == NULL) | 1121 | if (percent) { |
1083 | break; | 1122 | for (num_keys = 0; num_keys < EXPAND_MAX_KEYS; num_keys++) { |
1084 | keys[num_keys].repl = va_arg(ap, char *); | 1123 | keys[num_keys].key = va_arg(ap, char *); |
1085 | if (keys[num_keys].repl == NULL) | 1124 | if (keys[num_keys].key == NULL) |
1086 | fatal("%s: NULL replacement", __func__); | 1125 | break; |
1126 | keys[num_keys].repl = va_arg(ap, char *); | ||
1127 | if (keys[num_keys].repl == NULL) | ||
1128 | fatal("%s: NULL replacement for token %s", __func__, keys[num_keys].key); | ||
1129 | } | ||
1130 | if (num_keys == EXPAND_MAX_KEYS && va_arg(ap, char *) != NULL) | ||
1131 | fatal("%s: too many keys", __func__); | ||
1132 | if (num_keys == 0) | ||
1133 | fatal("%s: percent expansion without token list", | ||
1134 | __func__); | ||
1087 | } | 1135 | } |
1088 | if (num_keys == EXPAND_MAX_KEYS && va_arg(ap, char *) != NULL) | ||
1089 | fatal("%s: too many keys", __func__); | ||
1090 | va_end(ap); | ||
1091 | 1136 | ||
1092 | /* Expand string */ | 1137 | /* Expand string */ |
1093 | for (i = 0; *string != '\0'; string++) { | 1138 | for (i = 0; *string != '\0'; string++) { |
1094 | if (*string != '%') { | 1139 | /* Optionally process ${ENVIRONMENT} expansions. */ |
1140 | if (dollar && string[0] == '$' && string[1] == '{') { | ||
1141 | string += 2; /* skip over '${' */ | ||
1142 | if ((varend = strchr(string, '}')) == NULL) { | ||
1143 | error("%s: environment variable '%s' missing " | ||
1144 | "closing '}'", __func__, string); | ||
1145 | goto out; | ||
1146 | } | ||
1147 | len = varend - string; | ||
1148 | if (len == 0) { | ||
1149 | error("%s: zero-length environment variable", | ||
1150 | __func__); | ||
1151 | goto out; | ||
1152 | } | ||
1153 | var = xmalloc(len + 1); | ||
1154 | (void)strlcpy(var, string, len + 1); | ||
1155 | if ((val = getenv(var)) == NULL) { | ||
1156 | error("%s: env var ${%s} has no value", | ||
1157 | __func__, var); | ||
1158 | missingvar = 1; | ||
1159 | } else { | ||
1160 | debug3("%s: expand ${%s} -> '%s'", __func__, | ||
1161 | var, val); | ||
1162 | if ((r = sshbuf_put(buf, val, strlen(val))) !=0) | ||
1163 | fatal("%s: sshbuf_put: %s", __func__, | ||
1164 | ssh_err(r)); | ||
1165 | } | ||
1166 | free(var); | ||
1167 | string += len; | ||
1168 | continue; | ||
1169 | } | ||
1170 | |||
1171 | /* | ||
1172 | * Process percent expansions if we have a list of TOKENs. | ||
1173 | * If we're not doing percent expansion everything just gets | ||
1174 | * appended here. | ||
1175 | */ | ||
1176 | if (*string != '%' || !percent) { | ||
1095 | append: | 1177 | append: |
1096 | if ((r = sshbuf_put_u8(buf, *string)) != 0) { | 1178 | if ((r = sshbuf_put_u8(buf, *string)) != 0) { |
1097 | fatal("%s: sshbuf_put_u8: %s", | 1179 | fatal("%s: sshbuf_put_u8: %s", |
@@ -1103,8 +1185,10 @@ percent_expand(const char *string, ...) | |||
1103 | /* %% case */ | 1185 | /* %% case */ |
1104 | if (*string == '%') | 1186 | if (*string == '%') |
1105 | goto append; | 1187 | goto append; |
1106 | if (*string == '\0') | 1188 | if (*string == '\0') { |
1107 | fatal("%s: invalid format", __func__); | 1189 | error("%s: invalid format", __func__); |
1190 | goto out; | ||
1191 | } | ||
1108 | for (i = 0; i < num_keys; i++) { | 1192 | for (i = 0; i < num_keys; i++) { |
1109 | if (strchr(keys[i].key, *string) != NULL) { | 1193 | if (strchr(keys[i].key, *string) != NULL) { |
1110 | if ((r = sshbuf_put(buf, keys[i].repl, | 1194 | if ((r = sshbuf_put(buf, keys[i].repl, |
@@ -1115,16 +1199,79 @@ percent_expand(const char *string, ...) | |||
1115 | break; | 1199 | break; |
1116 | } | 1200 | } |
1117 | } | 1201 | } |
1118 | if (i >= num_keys) | 1202 | if (i >= num_keys) { |
1119 | fatal("%s: unknown key %%%c", __func__, *string); | 1203 | error("%s: unknown key %%%c", __func__, *string); |
1204 | goto out; | ||
1205 | } | ||
1120 | } | 1206 | } |
1121 | if ((ret = sshbuf_dup_string(buf)) == NULL) | 1207 | if (!missingvar && (ret = sshbuf_dup_string(buf)) == NULL) |
1122 | fatal("%s: sshbuf_dup_string failed", __func__); | 1208 | fatal("%s: sshbuf_dup_string failed", __func__); |
1209 | *parseerror = 0; | ||
1210 | out: | ||
1123 | sshbuf_free(buf); | 1211 | sshbuf_free(buf); |
1124 | return ret; | 1212 | return *parseerror ? NULL : ret; |
1125 | #undef EXPAND_MAX_KEYS | 1213 | #undef EXPAND_MAX_KEYS |
1126 | } | 1214 | } |
1127 | 1215 | ||
1216 | /* | ||
1217 | * Expand only environment variables. | ||
1218 | * Note that although this function is variadic like the other similar | ||
1219 | * functions, any such arguments will be unused. | ||
1220 | */ | ||
1221 | |||
1222 | char * | ||
1223 | dollar_expand(int *parseerr, const char *string, ...) | ||
1224 | { | ||
1225 | char *ret; | ||
1226 | int err; | ||
1227 | va_list ap; | ||
1228 | |||
1229 | va_start(ap, string); | ||
1230 | ret = vdollar_percent_expand(&err, 1, 0, string, ap); | ||
1231 | va_end(ap); | ||
1232 | if (parseerr != NULL) | ||
1233 | *parseerr = err; | ||
1234 | return ret; | ||
1235 | } | ||
1236 | |||
1237 | /* | ||
1238 | * Returns expanded string or NULL if a specified environment variable is | ||
1239 | * not defined, or calls fatal if the string is invalid. | ||
1240 | */ | ||
1241 | char * | ||
1242 | percent_expand(const char *string, ...) | ||
1243 | { | ||
1244 | char *ret; | ||
1245 | int err; | ||
1246 | va_list ap; | ||
1247 | |||
1248 | va_start(ap, string); | ||
1249 | ret = vdollar_percent_expand(&err, 0, 1, string, ap); | ||
1250 | va_end(ap); | ||
1251 | if (err) | ||
1252 | fatal("%s failed", __func__); | ||
1253 | return ret; | ||
1254 | } | ||
1255 | |||
1256 | /* | ||
1257 | * Returns expanded string or NULL if a specified environment variable is | ||
1258 | * not defined, or calls fatal if the string is invalid. | ||
1259 | */ | ||
1260 | char * | ||
1261 | percent_dollar_expand(const char *string, ...) | ||
1262 | { | ||
1263 | char *ret; | ||
1264 | int err; | ||
1265 | va_list ap; | ||
1266 | |||
1267 | va_start(ap, string); | ||
1268 | ret = vdollar_percent_expand(&err, 1, 1, string, ap); | ||
1269 | va_end(ap); | ||
1270 | if (err) | ||
1271 | fatal("%s failed", __func__); | ||
1272 | return ret; | ||
1273 | } | ||
1274 | |||
1128 | int | 1275 | int |
1129 | secure_permissions(struct stat *st, uid_t uid) | 1276 | secure_permissions(struct stat *st, uid_t uid) |
1130 | { | 1277 | { |