diff options
Diffstat (limited to 'misc.c')
-rw-r--r-- | misc.c | 696 |
1 files changed, 491 insertions, 205 deletions
@@ -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 */ | ||
172 | int | ||
173 | set_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 */ | ||
185 | char * | ||
186 | get_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 | |||
207 | int | ||
208 | set_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 | */ |
402 | char * | 470 | static char * |
403 | hpdelim(char **cp) | 471 | hpdelim2(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 | ||
437 | char * | 507 | char * |
508 | hpdelim(char **cp) | ||
509 | { | ||
510 | return hpdelim2(cp, NULL); | ||
511 | } | ||
512 | |||
513 | char * | ||
438 | cleanhostname(char *host) | 514 | cleanhostname(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 | */ | ||
555 | int | ||
556 | parse_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; | ||
607 | out: | ||
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 | */ | ||
680 | static int | ||
681 | hexchar(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 | */ | ||
703 | static char * | ||
704 | urldecode(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 | */ | ||
747 | int | ||
748 | parse_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 */ |
532 | void | 839 | void |
533 | addargs(arglist *args, char *fmt, ...) | 840 | addargs(arglist *args, char *fmt, ...) |
@@ -774,16 +1081,19 @@ secure_permissions(struct stat *st, uid_t uid) | |||
774 | } | 1081 | } |
775 | 1082 | ||
776 | int | 1083 | int |
777 | tun_open(int tun, int mode) | 1084 | tun_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 | ||
1013 | time_t | 1326 | void |
1014 | monotime(void) | 1327 | monotime_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); | 1357 | void |
1358 | monotime_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 | |||
1367 | time_t | ||
1368 | monotime(void) | ||
1369 | { | ||
1370 | struct timespec ts; | ||
1371 | |||
1372 | monotime_ts(&ts); | ||
1373 | return ts.tv_sec; | ||
1036 | } | 1374 | } |
1037 | 1375 | ||
1038 | double | 1376 | double |
1039 | monotime_double(void) | 1377 | monotime_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 | ||
1063 | void | 1385 | void |
@@ -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 | */ | ||
1479 | pid_t | ||
1480 | subprocess(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 */ |
1623 | int | 1794 | int |
1624 | exited_cleanly(pid_t pid, const char *tag, const char *cmd, int quiet) | 1795 | exited_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 | */ | ||
1970 | int | ||
1971 | valid_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; | ||
2008 | bad: | ||
2009 | if (errstr != NULL) | ||
2010 | *errstr = errbuf; | ||
2011 | return 0; | ||
2012 | } | ||
2013 | |||
2014 | const char * | ||
2015 | atoi_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 | |||
2028 | int | ||
2029 | parse_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 | |||
2071 | void | ||
2072 | format_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 | } | ||