summaryrefslogtreecommitdiff
path: root/misc.c
diff options
context:
space:
mode:
Diffstat (limited to 'misc.c')
-rw-r--r--misc.c696
1 files changed, 491 insertions, 205 deletions
diff --git a/misc.c b/misc.c
index 40aeeef36..75c4113f0 100644
--- a/misc.c
+++ b/misc.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: misc.c,v 1.113 2017/08/18 05:48:04 djm Exp $ */ 1/* $OpenBSD: misc.c,v 1.127 2018/03/12 00:52:01 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,2006 Damien Miller. All rights reserved.
@@ -168,6 +168,73 @@ set_nodelay(int fd)
168 error("setsockopt TCP_NODELAY: %.100s", strerror(errno)); 168 error("setsockopt TCP_NODELAY: %.100s", strerror(errno));
169} 169}
170 170
171/* Allow local port reuse in TIME_WAIT */
172int
173set_reuseaddr(int fd)
174{
175 int on = 1;
176
177 if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) {
178 error("setsockopt SO_REUSEADDR fd %d: %s", fd, strerror(errno));
179 return -1;
180 }
181 return 0;
182}
183
184/* Get/set routing domain */
185char *
186get_rdomain(int fd)
187{
188#if defined(HAVE_SYS_GET_RDOMAIN)
189 return sys_get_rdomain(fd);
190#elif defined(__OpenBSD__)
191 int rtable;
192 char *ret;
193 socklen_t len = sizeof(rtable);
194
195 if (getsockopt(fd, SOL_SOCKET, SO_RTABLE, &rtable, &len) == -1) {
196 error("Failed to get routing domain for fd %d: %s",
197 fd, strerror(errno));
198 return NULL;
199 }
200 xasprintf(&ret, "%d", rtable);
201 return ret;
202#else /* defined(__OpenBSD__) */
203 return NULL;
204#endif
205}
206
207int
208set_rdomain(int fd, const char *name)
209{
210#if defined(HAVE_SYS_SET_RDOMAIN)
211 return sys_set_rdomain(fd, name);
212#elif defined(__OpenBSD__)
213 int rtable;
214 const char *errstr;
215
216 if (name == NULL)
217 return 0; /* default table */
218
219 rtable = (int)strtonum(name, 0, 255, &errstr);
220 if (errstr != NULL) {
221 /* Shouldn't happen */
222 error("Invalid routing domain \"%s\": %s", name, errstr);
223 return -1;
224 }
225 if (setsockopt(fd, SOL_SOCKET, SO_RTABLE,
226 &rtable, sizeof(rtable)) == -1) {
227 error("Failed to set routing domain %d on fd %d: %s",
228 rtable, fd, strerror(errno));
229 return -1;
230 }
231 return 0;
232#else /* defined(__OpenBSD__) */
233 error("Setting routing domain is not supported on this platform");
234 return -1;
235#endif
236}
237
171/* Characters considered whitespace in strsep calls. */ 238/* Characters considered whitespace in strsep calls. */
172#define WHITESPACE " \t\r\n" 239#define WHITESPACE " \t\r\n"
173#define QUOTE "\"" 240#define QUOTE "\""
@@ -396,11 +463,12 @@ put_host_port(const char *host, u_short port)
396 * Search for next delimiter between hostnames/addresses and ports. 463 * Search for next delimiter between hostnames/addresses and ports.
397 * Argument may be modified (for termination). 464 * Argument may be modified (for termination).
398 * Returns *cp if parsing succeeds. 465 * Returns *cp if parsing succeeds.
399 * *cp is set to the start of the next delimiter, if one was found. 466 * *cp is set to the start of the next field, if one was found.
467 * The delimiter char, if present, is stored in delim.
400 * If this is the last field, *cp is set to NULL. 468 * If this is the last field, *cp is set to NULL.
401 */ 469 */
402char * 470static char *
403hpdelim(char **cp) 471hpdelim2(char **cp, char *delim)
404{ 472{
405 char *s, *old; 473 char *s, *old;
406 474
@@ -423,6 +491,8 @@ hpdelim(char **cp)
423 491
424 case ':': 492 case ':':
425 case '/': 493 case '/':
494 if (delim != NULL)
495 *delim = *s;
426 *s = '\0'; /* terminate */ 496 *s = '\0'; /* terminate */
427 *cp = s + 1; 497 *cp = s + 1;
428 break; 498 break;
@@ -435,6 +505,12 @@ hpdelim(char **cp)
435} 505}
436 506
437char * 507char *
508hpdelim(char **cp)
509{
510 return hpdelim2(cp, NULL);
511}
512
513char *
438cleanhostname(char *host) 514cleanhostname(char *host)
439{ 515{
440 if (*host == '[' && host[strlen(host) - 1] == ']') { 516 if (*host == '[' && host[strlen(host) - 1] == ']') {
@@ -468,6 +544,75 @@ colon(char *cp)
468} 544}
469 545
470/* 546/*
547 * Parse a [user@]host:[path] string.
548 * Caller must free returned user, host and path.
549 * Any of the pointer return arguments may be NULL (useful for syntax checking).
550 * If user was not specified then *userp will be set to NULL.
551 * If host was not specified then *hostp will be set to NULL.
552 * If path was not specified then *pathp will be set to ".".
553 * Returns 0 on success, -1 on failure.
554 */
555int
556parse_user_host_path(const char *s, char **userp, char **hostp, char **pathp)
557{
558 char *user = NULL, *host = NULL, *path = NULL;
559 char *sdup, *tmp;
560 int ret = -1;
561
562 if (userp != NULL)
563 *userp = NULL;
564 if (hostp != NULL)
565 *hostp = NULL;
566 if (pathp != NULL)
567 *pathp = NULL;
568
569 sdup = xstrdup(s);
570
571 /* Check for remote syntax: [user@]host:[path] */
572 if ((tmp = colon(sdup)) == NULL)
573 goto out;
574
575 /* Extract optional path */
576 *tmp++ = '\0';
577 if (*tmp == '\0')
578 tmp = ".";
579 path = xstrdup(tmp);
580
581 /* Extract optional user and mandatory host */
582 tmp = strrchr(sdup, '@');
583 if (tmp != NULL) {
584 *tmp++ = '\0';
585 host = xstrdup(cleanhostname(tmp));
586 if (*sdup != '\0')
587 user = xstrdup(sdup);
588 } else {
589 host = xstrdup(cleanhostname(sdup));
590 user = NULL;
591 }
592
593 /* Success */
594 if (userp != NULL) {
595 *userp = user;
596 user = NULL;
597 }
598 if (hostp != NULL) {
599 *hostp = host;
600 host = NULL;
601 }
602 if (pathp != NULL) {
603 *pathp = path;
604 path = NULL;
605 }
606 ret = 0;
607out:
608 free(sdup);
609 free(user);
610 free(host);
611 free(path);
612 return ret;
613}
614
615/*
471 * Parse a [user@]host[:port] string. 616 * Parse a [user@]host[:port] string.
472 * Caller must free returned user and host. 617 * Caller must free returned user and host.
473 * Any of the pointer return arguments may be NULL (useful for syntax checking). 618 * Any of the pointer return arguments may be NULL (useful for syntax checking).
@@ -492,7 +637,7 @@ parse_user_host_port(const char *s, char **userp, char **hostp, int *portp)
492 if ((sdup = tmp = strdup(s)) == NULL) 637 if ((sdup = tmp = strdup(s)) == NULL)
493 return -1; 638 return -1;
494 /* Extract optional username */ 639 /* Extract optional username */
495 if ((cp = strchr(tmp, '@')) != NULL) { 640 if ((cp = strrchr(tmp, '@')) != NULL) {
496 *cp = '\0'; 641 *cp = '\0';
497 if (*tmp == '\0') 642 if (*tmp == '\0')
498 goto out; 643 goto out;
@@ -528,6 +673,168 @@ parse_user_host_port(const char *s, char **userp, char **hostp, int *portp)
528 return ret; 673 return ret;
529} 674}
530 675
676/*
677 * Converts a two-byte hex string to decimal.
678 * Returns the decimal value or -1 for invalid input.
679 */
680static int
681hexchar(const char *s)
682{
683 unsigned char result[2];
684 int i;
685
686 for (i = 0; i < 2; i++) {
687 if (s[i] >= '0' && s[i] <= '9')
688 result[i] = (unsigned char)(s[i] - '0');
689 else if (s[i] >= 'a' && s[i] <= 'f')
690 result[i] = (unsigned char)(s[i] - 'a') + 10;
691 else if (s[i] >= 'A' && s[i] <= 'F')
692 result[i] = (unsigned char)(s[i] - 'A') + 10;
693 else
694 return -1;
695 }
696 return (result[0] << 4) | result[1];
697}
698
699/*
700 * Decode an url-encoded string.
701 * Returns a newly allocated string on success or NULL on failure.
702 */
703static char *
704urldecode(const char *src)
705{
706 char *ret, *dst;
707 int ch;
708
709 ret = xmalloc(strlen(src) + 1);
710 for (dst = ret; *src != '\0'; src++) {
711 switch (*src) {
712 case '+':
713 *dst++ = ' ';
714 break;
715 case '%':
716 if (!isxdigit((unsigned char)src[1]) ||
717 !isxdigit((unsigned char)src[2]) ||
718 (ch = hexchar(src + 1)) == -1) {
719 free(ret);
720 return NULL;
721 }
722 *dst++ = ch;
723 src += 2;
724 break;
725 default:
726 *dst++ = *src;
727 break;
728 }
729 }
730 *dst = '\0';
731
732 return ret;
733}
734
735/*
736 * Parse an (scp|ssh|sftp)://[user@]host[:port][/path] URI.
737 * See https://tools.ietf.org/html/draft-ietf-secsh-scp-sftp-ssh-uri-04
738 * Either user or path may be url-encoded (but not host or port).
739 * Caller must free returned user, host and path.
740 * Any of the pointer return arguments may be NULL (useful for syntax checking)
741 * but the scheme must always be specified.
742 * If user was not specified then *userp will be set to NULL.
743 * If port was not specified then *portp will be -1.
744 * If path was not specified then *pathp will be set to NULL.
745 * Returns 0 on success, 1 if non-uri/wrong scheme, -1 on error/invalid uri.
746 */
747int
748parse_uri(const char *scheme, const char *uri, char **userp, char **hostp,
749 int *portp, char **pathp)
750{
751 char *uridup, *cp, *tmp, ch;
752 char *user = NULL, *host = NULL, *path = NULL;
753 int port = -1, ret = -1;
754 size_t len;
755
756 len = strlen(scheme);
757 if (strncmp(uri, scheme, len) != 0 || strncmp(uri + len, "://", 3) != 0)
758 return 1;
759 uri += len + 3;
760
761 if (userp != NULL)
762 *userp = NULL;
763 if (hostp != NULL)
764 *hostp = NULL;
765 if (portp != NULL)
766 *portp = -1;
767 if (pathp != NULL)
768 *pathp = NULL;
769
770 uridup = tmp = xstrdup(uri);
771
772 /* Extract optional ssh-info (username + connection params) */
773 if ((cp = strchr(tmp, '@')) != NULL) {
774 char *delim;
775
776 *cp = '\0';
777 /* Extract username and connection params */
778 if ((delim = strchr(tmp, ';')) != NULL) {
779 /* Just ignore connection params for now */
780 *delim = '\0';
781 }
782 if (*tmp == '\0') {
783 /* Empty username */
784 goto out;
785 }
786 if ((user = urldecode(tmp)) == NULL)
787 goto out;
788 tmp = cp + 1;
789 }
790
791 /* Extract mandatory hostname */
792 if ((cp = hpdelim2(&tmp, &ch)) == NULL || *cp == '\0')
793 goto out;
794 host = xstrdup(cleanhostname(cp));
795 if (!valid_domain(host, 0, NULL))
796 goto out;
797
798 if (tmp != NULL && *tmp != '\0') {
799 if (ch == ':') {
800 /* Convert and verify port. */
801 if ((cp = strchr(tmp, '/')) != NULL)
802 *cp = '\0';
803 if ((port = a2port(tmp)) <= 0)
804 goto out;
805 tmp = cp ? cp + 1 : NULL;
806 }
807 if (tmp != NULL && *tmp != '\0') {
808 /* Extract optional path */
809 if ((path = urldecode(tmp)) == NULL)
810 goto out;
811 }
812 }
813
814 /* Success */
815 if (userp != NULL) {
816 *userp = user;
817 user = NULL;
818 }
819 if (hostp != NULL) {
820 *hostp = host;
821 host = NULL;
822 }
823 if (portp != NULL)
824 *portp = port;
825 if (pathp != NULL) {
826 *pathp = path;
827 path = NULL;
828 }
829 ret = 0;
830 out:
831 free(uridup);
832 free(user);
833 free(host);
834 free(path);
835 return ret;
836}
837
531/* function to assist building execv() arguments */ 838/* function to assist building execv() arguments */
532void 839void
533addargs(arglist *args, char *fmt, ...) 840addargs(arglist *args, char *fmt, ...)
@@ -774,16 +1081,19 @@ secure_permissions(struct stat *st, uid_t uid)
774} 1081}
775 1082
776int 1083int
777tun_open(int tun, int mode) 1084tun_open(int tun, int mode, char **ifname)
778{ 1085{
779#if defined(CUSTOM_SYS_TUN_OPEN) 1086#if defined(CUSTOM_SYS_TUN_OPEN)
780 return (sys_tun_open(tun, mode)); 1087 return (sys_tun_open(tun, mode, ifname));
781#elif defined(SSH_TUN_OPENBSD) 1088#elif defined(SSH_TUN_OPENBSD)
782 struct ifreq ifr; 1089 struct ifreq ifr;
783 char name[100]; 1090 char name[100];
784 int fd = -1, sock; 1091 int fd = -1, sock;
785 const char *tunbase = "tun"; 1092 const char *tunbase = "tun";
786 1093
1094 if (ifname != NULL)
1095 *ifname = NULL;
1096
787 if (mode == SSH_TUNMODE_ETHERNET) 1097 if (mode == SSH_TUNMODE_ETHERNET)
788 tunbase = "tap"; 1098 tunbase = "tap";
789 1099
@@ -830,6 +1140,9 @@ tun_open(int tun, int mode)
830 } 1140 }
831 } 1141 }
832 1142
1143 if (ifname != NULL)
1144 *ifname = xstrdup(ifr.ifr_name);
1145
833 close(sock); 1146 close(sock);
834 return fd; 1147 return fd;
835 1148
@@ -996,8 +1309,8 @@ ms_subtract_diff(struct timeval *start, int *ms)
996{ 1309{
997 struct timeval diff, finish; 1310 struct timeval diff, finish;
998 1311
999 gettimeofday(&finish, NULL); 1312 monotime_tv(&finish);
1000 timersub(&finish, start, &diff); 1313 timersub(&finish, start, &diff);
1001 *ms -= (diff.tv_sec * 1000) + (diff.tv_usec / 1000); 1314 *ms -= (diff.tv_sec * 1000) + (diff.tv_usec / 1000);
1002} 1315}
1003 1316
@@ -1010,54 +1323,63 @@ ms_to_timeval(struct timeval *tv, int ms)
1010 tv->tv_usec = (ms % 1000) * 1000; 1323 tv->tv_usec = (ms % 1000) * 1000;
1011} 1324}
1012 1325
1013time_t 1326void
1014monotime(void) 1327monotime_ts(struct timespec *ts)
1015{ 1328{
1016#if defined(HAVE_CLOCK_GETTIME) && \ 1329 struct timeval tv;
1017 (defined(CLOCK_MONOTONIC) || defined(CLOCK_BOOTTIME)) 1330#if defined(HAVE_CLOCK_GETTIME) && (defined(CLOCK_BOOTTIME) || \
1018 struct timespec ts; 1331 defined(CLOCK_MONOTONIC) || defined(CLOCK_REALTIME))
1019 static int gettime_failed = 0; 1332 static int gettime_failed = 0;
1020 1333
1021 if (!gettime_failed) { 1334 if (!gettime_failed) {
1022#if defined(CLOCK_BOOTTIME) 1335# ifdef CLOCK_BOOTTIME
1023 if (clock_gettime(CLOCK_BOOTTIME, &ts) == 0) 1336 if (clock_gettime(CLOCK_BOOTTIME, ts) == 0)
1024 return (ts.tv_sec); 1337 return;
1025#endif 1338# endif /* CLOCK_BOOTTIME */
1026#if defined(CLOCK_MONOTONIC) 1339# ifdef CLOCK_MONOTONIC
1027 if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) 1340 if (clock_gettime(CLOCK_MONOTONIC, ts) == 0)
1028 return (ts.tv_sec); 1341 return;
1029#endif 1342# endif /* CLOCK_MONOTONIC */
1343# ifdef CLOCK_REALTIME
1344 /* Not monotonic, but we're almost out of options here. */
1345 if (clock_gettime(CLOCK_REALTIME, ts) == 0)
1346 return;
1347# endif /* CLOCK_REALTIME */
1030 debug3("clock_gettime: %s", strerror(errno)); 1348 debug3("clock_gettime: %s", strerror(errno));
1031 gettime_failed = 1; 1349 gettime_failed = 1;
1032 } 1350 }
1033#endif /* HAVE_CLOCK_GETTIME && (CLOCK_MONOTONIC || CLOCK_BOOTTIME */ 1351#endif /* HAVE_CLOCK_GETTIME && (BOOTTIME || MONOTONIC || REALTIME) */
1352 gettimeofday(&tv, NULL);
1353 ts->tv_sec = tv.tv_sec;
1354 ts->tv_nsec = (long)tv.tv_usec * 1000;
1355}
1034 1356
1035 return time(NULL); 1357void
1358monotime_tv(struct timeval *tv)
1359{
1360 struct timespec ts;
1361
1362 monotime_ts(&ts);
1363 tv->tv_sec = ts.tv_sec;
1364 tv->tv_usec = ts.tv_nsec / 1000;
1365}
1366
1367time_t
1368monotime(void)
1369{
1370 struct timespec ts;
1371
1372 monotime_ts(&ts);
1373 return ts.tv_sec;
1036} 1374}
1037 1375
1038double 1376double
1039monotime_double(void) 1377monotime_double(void)
1040{ 1378{
1041#if defined(HAVE_CLOCK_GETTIME) && \
1042 (defined(CLOCK_MONOTONIC) || defined(CLOCK_BOOTTIME))
1043 struct timespec ts; 1379 struct timespec ts;
1044 static int gettime_failed = 0;
1045
1046 if (!gettime_failed) {
1047#if defined(CLOCK_BOOTTIME)
1048 if (clock_gettime(CLOCK_BOOTTIME, &ts) == 0)
1049 return (ts.tv_sec + (double)ts.tv_nsec / 1000000000);
1050#endif
1051#if defined(CLOCK_MONOTONIC)
1052 if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0)
1053 return (ts.tv_sec + (double)ts.tv_nsec / 1000000000);
1054#endif
1055 debug3("clock_gettime: %s", strerror(errno));
1056 gettime_failed = 1;
1057 }
1058#endif /* HAVE_CLOCK_GETTIME && (CLOCK_MONOTONIC || CLOCK_BOOTTIME */
1059 1380
1060 return (double)time(NULL); 1381 monotime_ts(&ts);
1382 return ts.tv_sec + ((double)ts.tv_nsec / 1000000000);
1061} 1383}
1062 1384
1063void 1385void
@@ -1079,7 +1401,7 @@ bandwidth_limit(struct bwlimit *bw, size_t read_len)
1079 struct timespec ts, rm; 1401 struct timespec ts, rm;
1080 1402
1081 if (!timerisset(&bw->bwstart)) { 1403 if (!timerisset(&bw->bwstart)) {
1082 gettimeofday(&bw->bwstart, NULL); 1404 monotime_tv(&bw->bwstart);
1083 return; 1405 return;
1084 } 1406 }
1085 1407
@@ -1087,7 +1409,7 @@ bandwidth_limit(struct bwlimit *bw, size_t read_len)
1087 if (bw->lamt < bw->thresh) 1409 if (bw->lamt < bw->thresh)
1088 return; 1410 return;
1089 1411
1090 gettimeofday(&bw->bwend, NULL); 1412 monotime_tv(&bw->bwend);
1091 timersub(&bw->bwend, &bw->bwstart, &bw->bwend); 1413 timersub(&bw->bwend, &bw->bwstart, &bw->bwend);
1092 if (!timerisset(&bw->bwend)) 1414 if (!timerisset(&bw->bwend))
1093 return; 1415 return;
@@ -1121,7 +1443,7 @@ bandwidth_limit(struct bwlimit *bw, size_t read_len)
1121 } 1443 }
1122 1444
1123 bw->lamt = 0; 1445 bw->lamt = 0;
1124 gettimeofday(&bw->bwstart, NULL); 1446 monotime_tv(&bw->bwstart);
1125} 1447}
1126 1448
1127/* Make a template filename for mk[sd]temp() */ 1449/* Make a template filename for mk[sd]temp() */
@@ -1222,9 +1544,10 @@ unix_listener(const char *path, int backlog, int unlink_first)
1222 1544
1223 memset(&sunaddr, 0, sizeof(sunaddr)); 1545 memset(&sunaddr, 0, sizeof(sunaddr));
1224 sunaddr.sun_family = AF_UNIX; 1546 sunaddr.sun_family = AF_UNIX;
1225 if (strlcpy(sunaddr.sun_path, path, sizeof(sunaddr.sun_path)) >= sizeof(sunaddr.sun_path)) { 1547 if (strlcpy(sunaddr.sun_path, path,
1226 error("%s: \"%s\" too long for Unix domain socket", __func__, 1548 sizeof(sunaddr.sun_path)) >= sizeof(sunaddr.sun_path)) {
1227 path); 1549 error("%s: path \"%s\" too long for Unix domain socket",
1550 __func__, path);
1228 errno = ENAMETOOLONG; 1551 errno = ENAMETOOLONG;
1229 return -1; 1552 return -1;
1230 } 1553 }
@@ -1232,7 +1555,7 @@ unix_listener(const char *path, int backlog, int unlink_first)
1232 sock = socket(PF_UNIX, SOCK_STREAM, 0); 1555 sock = socket(PF_UNIX, SOCK_STREAM, 0);
1233 if (sock < 0) { 1556 if (sock < 0) {
1234 saved_errno = errno; 1557 saved_errno = errno;
1235 error("socket: %.100s", strerror(errno)); 1558 error("%s: socket: %.100s", __func__, strerror(errno));
1236 errno = saved_errno; 1559 errno = saved_errno;
1237 return -1; 1560 return -1;
1238 } 1561 }
@@ -1242,18 +1565,18 @@ unix_listener(const char *path, int backlog, int unlink_first)
1242 } 1565 }
1243 if (bind(sock, (struct sockaddr *)&sunaddr, sizeof(sunaddr)) < 0) { 1566 if (bind(sock, (struct sockaddr *)&sunaddr, sizeof(sunaddr)) < 0) {
1244 saved_errno = errno; 1567 saved_errno = errno;
1245 error("bind: %.100s", strerror(errno)); 1568 error("%s: cannot bind to path %s: %s",
1569 __func__, path, strerror(errno));
1246 close(sock); 1570 close(sock);
1247 error("%s: cannot bind to path: %s", __func__, path);
1248 errno = saved_errno; 1571 errno = saved_errno;
1249 return -1; 1572 return -1;
1250 } 1573 }
1251 if (listen(sock, backlog) < 0) { 1574 if (listen(sock, backlog) < 0) {
1252 saved_errno = errno; 1575 saved_errno = errno;
1253 error("listen: %.100s", strerror(errno)); 1576 error("%s: cannot listen on path %s: %s",
1577 __func__, path, strerror(errno));
1254 close(sock); 1578 close(sock);
1255 unlink(path); 1579 unlink(path);
1256 error("%s: cannot listen on path: %s", __func__, path);
1257 errno = saved_errno; 1580 errno = saved_errno;
1258 return -1; 1581 return -1;
1259 } 1582 }
@@ -1467,158 +1790,6 @@ argv_assemble(int argc, char **argv)
1467 return ret; 1790 return ret;
1468} 1791}
1469 1792
1470/*
1471 * Runs command in a subprocess wuth a minimal environment.
1472 * Returns pid on success, 0 on failure.
1473 * The child stdout and stderr maybe captured, left attached or sent to
1474 * /dev/null depending on the contents of flags.
1475 * "tag" is prepended to log messages.
1476 * NB. "command" is only used for logging; the actual command executed is
1477 * av[0].
1478 */
1479pid_t
1480subprocess(const char *tag, struct passwd *pw, const char *command,
1481 int ac, char **av, FILE **child, u_int flags)
1482{
1483 FILE *f = NULL;
1484 struct stat st;
1485 int fd, devnull, p[2], i;
1486 pid_t pid;
1487 char *cp, errmsg[512];
1488 u_int envsize;
1489 char **child_env;
1490
1491 if (child != NULL)
1492 *child = NULL;
1493
1494 debug3("%s: %s command \"%s\" running as %s (flags 0x%x)", __func__,
1495 tag, command, pw->pw_name, flags);
1496
1497 /* Check consistency */
1498 if ((flags & SSH_SUBPROCESS_STDOUT_DISCARD) != 0 &&
1499 (flags & SSH_SUBPROCESS_STDOUT_CAPTURE) != 0) {
1500 error("%s: inconsistent flags", __func__);
1501 return 0;
1502 }
1503 if (((flags & SSH_SUBPROCESS_STDOUT_CAPTURE) == 0) != (child == NULL)) {
1504 error("%s: inconsistent flags/output", __func__);
1505 return 0;
1506 }
1507
1508 /*
1509 * If executing an explicit binary, then verify the it exists
1510 * and appears safe-ish to execute
1511 */
1512 if (*av[0] != '/') {
1513 error("%s path is not absolute", tag);
1514 return 0;
1515 }
1516 temporarily_use_uid(pw);
1517 if (stat(av[0], &st) < 0) {
1518 error("Could not stat %s \"%s\": %s", tag,
1519 av[0], strerror(errno));
1520 restore_uid();
1521 return 0;
1522 }
1523 if (safe_path(av[0], &st, NULL, 0, errmsg, sizeof(errmsg)) != 0) {
1524 error("Unsafe %s \"%s\": %s", tag, av[0], errmsg);
1525 restore_uid();
1526 return 0;
1527 }
1528 /* Prepare to keep the child's stdout if requested */
1529 if (pipe(p) != 0) {
1530 error("%s: pipe: %s", tag, strerror(errno));
1531 restore_uid();
1532 return 0;
1533 }
1534 restore_uid();
1535
1536 switch ((pid = fork())) {
1537 case -1: /* error */
1538 error("%s: fork: %s", tag, strerror(errno));
1539 close(p[0]);
1540 close(p[1]);
1541 return 0;
1542 case 0: /* child */
1543 /* Prepare a minimal environment for the child. */
1544 envsize = 5;
1545 child_env = xcalloc(sizeof(*child_env), envsize);
1546 child_set_env(&child_env, &envsize, "PATH", _PATH_STDPATH);
1547 child_set_env(&child_env, &envsize, "USER", pw->pw_name);
1548 child_set_env(&child_env, &envsize, "LOGNAME", pw->pw_name);
1549 child_set_env(&child_env, &envsize, "HOME", pw->pw_dir);
1550 if ((cp = getenv("LANG")) != NULL)
1551 child_set_env(&child_env, &envsize, "LANG", cp);
1552
1553 for (i = 0; i < NSIG; i++)
1554 signal(i, SIG_DFL);
1555
1556 if ((devnull = open(_PATH_DEVNULL, O_RDWR)) == -1) {
1557 error("%s: open %s: %s", tag, _PATH_DEVNULL,
1558 strerror(errno));
1559 _exit(1);
1560 }
1561 if (dup2(devnull, STDIN_FILENO) == -1) {
1562 error("%s: dup2: %s", tag, strerror(errno));
1563 _exit(1);
1564 }
1565
1566 /* Set up stdout as requested; leave stderr in place for now. */
1567 fd = -1;
1568 if ((flags & SSH_SUBPROCESS_STDOUT_CAPTURE) != 0)
1569 fd = p[1];
1570 else if ((flags & SSH_SUBPROCESS_STDOUT_DISCARD) != 0)
1571 fd = devnull;
1572 if (fd != -1 && dup2(fd, STDOUT_FILENO) == -1) {
1573 error("%s: dup2: %s", tag, strerror(errno));
1574 _exit(1);
1575 }
1576 closefrom(STDERR_FILENO + 1);
1577
1578 /* Don't use permanently_set_uid() here to avoid fatal() */
1579 if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) != 0) {
1580 error("%s: setresgid %u: %s", tag, (u_int)pw->pw_gid,
1581 strerror(errno));
1582 _exit(1);
1583 }
1584 if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) != 0) {
1585 error("%s: setresuid %u: %s", tag, (u_int)pw->pw_uid,
1586 strerror(errno));
1587 _exit(1);
1588 }
1589 /* stdin is pointed to /dev/null at this point */
1590 if ((flags & SSH_SUBPROCESS_STDOUT_DISCARD) != 0 &&
1591 dup2(STDIN_FILENO, STDERR_FILENO) == -1) {
1592 error("%s: dup2: %s", tag, strerror(errno));
1593 _exit(1);
1594 }
1595
1596 execve(av[0], av, child_env);
1597 error("%s exec \"%s\": %s", tag, command, strerror(errno));
1598 _exit(127);
1599 default: /* parent */
1600 break;
1601 }
1602
1603 close(p[1]);
1604 if ((flags & SSH_SUBPROCESS_STDOUT_CAPTURE) == 0)
1605 close(p[0]);
1606 else if ((f = fdopen(p[0], "r")) == NULL) {
1607 error("%s: fdopen: %s", tag, strerror(errno));
1608 close(p[0]);
1609 /* Don't leave zombie child */
1610 kill(pid, SIGTERM);
1611 while (waitpid(pid, NULL, 0) == -1 && errno == EINTR)
1612 ;
1613 return 0;
1614 }
1615 /* Success */
1616 debug3("%s: %s pid %ld", __func__, tag, (long)pid);
1617 if (child != NULL)
1618 *child = f;
1619 return pid;
1620}
1621
1622/* Returns 0 if pid exited cleanly, non-zero otherwise */ 1793/* Returns 0 if pid exited cleanly, non-zero otherwise */
1623int 1794int
1624exited_cleanly(pid_t pid, const char *tag, const char *cmd, int quiet) 1795exited_cleanly(pid_t pid, const char *tag, const char *cmd, int quiet)
@@ -1787,7 +1958,122 @@ child_set_env(char ***envp, u_int *envsizep, const char *name,
1787 } 1958 }
1788 1959
1789 /* Allocate space and format the variable in the appropriate slot. */ 1960 /* Allocate space and format the variable in the appropriate slot. */
1961 /* XXX xasprintf */
1790 env[i] = xmalloc(strlen(name) + 1 + strlen(value) + 1); 1962 env[i] = xmalloc(strlen(name) + 1 + strlen(value) + 1);
1791 snprintf(env[i], strlen(name) + 1 + strlen(value) + 1, "%s=%s", name, value); 1963 snprintf(env[i], strlen(name) + 1 + strlen(value) + 1, "%s=%s", name, value);
1792} 1964}
1793 1965
1966/*
1967 * Check and optionally lowercase a domain name, also removes trailing '.'
1968 * Returns 1 on success and 0 on failure, storing an error message in errstr.
1969 */
1970int
1971valid_domain(char *name, int makelower, const char **errstr)
1972{
1973 size_t i, l = strlen(name);
1974 u_char c, last = '\0';
1975 static char errbuf[256];
1976
1977 if (l == 0) {
1978 strlcpy(errbuf, "empty domain name", sizeof(errbuf));
1979 goto bad;
1980 }
1981 if (!isalpha((u_char)name[0]) && !isdigit((u_char)name[0])) {
1982 snprintf(errbuf, sizeof(errbuf), "domain name \"%.100s\" "
1983 "starts with invalid character", name);
1984 goto bad;
1985 }
1986 for (i = 0; i < l; i++) {
1987 c = tolower((u_char)name[i]);
1988 if (makelower)
1989 name[i] = (char)c;
1990 if (last == '.' && c == '.') {
1991 snprintf(errbuf, sizeof(errbuf), "domain name "
1992 "\"%.100s\" contains consecutive separators", name);
1993 goto bad;
1994 }
1995 if (c != '.' && c != '-' && !isalnum(c) &&
1996 c != '_') /* technically invalid, but common */ {
1997 snprintf(errbuf, sizeof(errbuf), "domain name "
1998 "\"%.100s\" contains invalid characters", name);
1999 goto bad;
2000 }
2001 last = c;
2002 }
2003 if (name[l - 1] == '.')
2004 name[l - 1] = '\0';
2005 if (errstr != NULL)
2006 *errstr = NULL;
2007 return 1;
2008bad:
2009 if (errstr != NULL)
2010 *errstr = errbuf;
2011 return 0;
2012}
2013
2014const char *
2015atoi_err(const char *nptr, int *val)
2016{
2017 const char *errstr = NULL;
2018 long long num;
2019
2020 if (nptr == NULL || *nptr == '\0')
2021 return "missing";
2022 num = strtonum(nptr, 0, INT_MAX, &errstr);
2023 if (errstr == NULL)
2024 *val = (int)num;
2025 return errstr;
2026}
2027
2028int
2029parse_absolute_time(const char *s, uint64_t *tp)
2030{
2031 struct tm tm;
2032 time_t tt;
2033 char buf[32], *fmt;
2034
2035 *tp = 0;
2036
2037 /*
2038 * POSIX strptime says "The application shall ensure that there
2039 * is white-space or other non-alphanumeric characters between
2040 * any two conversion specifications" so arrange things this way.
2041 */
2042 switch (strlen(s)) {
2043 case 8: /* YYYYMMDD */
2044 fmt = "%Y-%m-%d";
2045 snprintf(buf, sizeof(buf), "%.4s-%.2s-%.2s", s, s + 4, s + 6);
2046 break;
2047 case 12: /* YYYYMMDDHHMM */
2048 fmt = "%Y-%m-%dT%H:%M";
2049 snprintf(buf, sizeof(buf), "%.4s-%.2s-%.2sT%.2s:%.2s",
2050 s, s + 4, s + 6, s + 8, s + 10);
2051 break;
2052 case 14: /* YYYYMMDDHHMMSS */
2053 fmt = "%Y-%m-%dT%H:%M:%S";
2054 snprintf(buf, sizeof(buf), "%.4s-%.2s-%.2sT%.2s:%.2s:%.2s",
2055 s, s + 4, s + 6, s + 8, s + 10, s + 12);
2056 break;
2057 default:
2058 return SSH_ERR_INVALID_FORMAT;
2059 }
2060
2061 memset(&tm, 0, sizeof(tm));
2062 if (strptime(buf, fmt, &tm) == NULL)
2063 return SSH_ERR_INVALID_FORMAT;
2064 if ((tt = mktime(&tm)) < 0)
2065 return SSH_ERR_INVALID_FORMAT;
2066 /* success */
2067 *tp = (uint64_t)tt;
2068 return 0;
2069}
2070
2071void
2072format_absolute_time(uint64_t t, char *buf, size_t len)
2073{
2074 time_t tt = t > INT_MAX ? INT_MAX : t; /* XXX revisit in 2038 :P */
2075 struct tm tm;
2076
2077 localtime_r(&tt, &tm);
2078 strftime(buf, len, "%Y-%m-%dT%H:%M:%S", &tm);
2079}