diff options
author | deraadt <deraadt@openbsd.org> | 2015-10-29 03:16:15 +0000 |
---|---|---|
committer | deraadt <deraadt@openbsd.org> | 2015-10-29 03:16:15 +0000 |
commit | 6a6f1b391b11875979a1f4222092611c791615d3 (patch) | |
tree | e37b9c30ffb090ef8b9e170446b010ed8292133e /usr.sbin/rdate | |
parent | pledge "stdio rpath". (diff) | |
download | wireguard-openbsd-6a6f1b391b11875979a1f4222092611c791615d3.tar.xz wireguard-openbsd-6a6f1b391b11875979a1f4222092611c791615d3.zip |
rdate is a classic "run as root, talk to internet for a while doing
crazy packet parsing, then do something requiring privilege at the
end" program. Simplistic pledge would be "stdio rpath wpath inet dns
settime", which is not very useful. Imagine if it was exploited? It
could still change your time backwards or write to your passwd file -
game over. However the pledge "categorization" is educational, and
quickly leads to a priv-sep solution of sorts.
Create a pipe and fork. child pledges "stdio inet dns", and talks the
time protocols, then writes error message + timeinfo to the pipe.
parent pledges "stdio rpath wpath settime" and reads error
message/timeinfo from pipe. If error message, spit it out. Otherwise
handle the time, then pledge "stdio rpath", and finally report how the
time was adjusted.
A bit more complicated. Now observe that the pledges help test if
it is right...
(Now that privsep processing is in place, the child could be modified
to chroot + change to a different uid... problem is: which uid..)
ok millert
Diffstat (limited to 'usr.sbin/rdate')
-rw-r--r-- | usr.sbin/rdate/rdate.c | 82 |
1 files changed, 69 insertions, 13 deletions
diff --git a/usr.sbin/rdate/rdate.c b/usr.sbin/rdate/rdate.c index 363fc2a1cb6..5da04a596d6 100644 --- a/usr.sbin/rdate/rdate.c +++ b/usr.sbin/rdate/rdate.c @@ -1,4 +1,4 @@ -/* $OpenBSD: rdate.c,v 1.32 2015/02/09 23:00:14 deraadt Exp $ */ +/* $OpenBSD: rdate.c,v 1.33 2015/10/29 03:16:15 deraadt Exp $ */ /* $NetBSD: rdate.c,v 1.4 1996/03/16 12:37:45 pk Exp $ */ /* @@ -40,6 +40,7 @@ #include <sys/socket.h> #include <sys/time.h> +#include <sys/wait.h> #include <stdio.h> #include <stdlib.h> @@ -62,6 +63,12 @@ void ntp_client(const char *, int, struct timeval *, struct timeval *, int); extern char *__progname; __dead void usage(void); +struct { + char message[256]; + struct timeval new; + struct timeval adjust; +} pdata; + __dead void usage(void) { @@ -76,11 +83,9 @@ main(int argc, char **argv) int slidetime = 0, corrleaps = 0; char *hname; extern int optind; - int c; + int c, p[2], pid; int family = PF_UNSPEC; - struct timeval new, adjust; - while ((c = getopt(argc, argv, "46psanocv")) != -1) { switch (c) { case '4': @@ -127,34 +132,85 @@ main(int argc, char **argv) usage(); hname = argv[optind]; - if (ntp) - ntp_client(hname, family, &new, &adjust, corrleaps); - else - rfc868time_client(hname, family, &new, &adjust, corrleaps); + /* + * Privilege separation increases safety, with a slight reduction + * in precision because the time values have to return over a pipe. + */ + if (pipe(p) == -1) + err(1, "pipe"); + switch ((pid = fork())) { + case -1: + err(1, "fork"); + break; + case 0: + if (pledge("stdio inet dns", NULL) == -1) + err(1, "fork"); + + close(p[0]); /* read side of pipe */ + dup2(p[1], STDIN_FILENO); + if (p[1] != STDIN_FILENO) + close(p[1]); + dup2(STDIN_FILENO, STDOUT_FILENO); + dup2(STDOUT_FILENO, STDERR_FILENO); + setvbuf(stdout, NULL, _IOFBF, 0); + setvbuf(stderr, NULL, _IOFBF, 0); + + if (ntp) + ntp_client(hname, family, &pdata.new, + &pdata.adjust, corrleaps); + else + rfc868time_client(hname, family, &pdata.new, + &pdata.adjust, corrleaps); + + if (write(STDOUT_FILENO, &pdata, sizeof pdata) != sizeof pdata) + exit(1); + exit(0); + } + + if (pledge("stdio rpath wpath settime", NULL) == -1) + err(1, "pledge"); + + close(p[1]); /* write side of pipe */ + if (read(p[0], &pdata, sizeof pdata) < 1) + err(1, "child did not collect time"); + if (waitpid(pid, NULL, 0) == -1) + err(1, "waitpid"); + + /* + * A viable timestamp from the child contains no message. + */ + if (pdata.message[0]) { + pdata.message[sizeof(pdata.message)- 1] = '\0'; + write(STDERR_FILENO, pdata.message, strlen(pdata.message)); + exit(1); + } if (!pr) { if (!slidetime) { logwtmp("|", "date", ""); - if (settimeofday(&new, NULL) == -1) + if (settimeofday(&pdata.new, NULL) == -1) err(1, "Could not set time of day"); logwtmp("{", "date", ""); } else { - if (adjtime(&adjust, NULL) == -1) + if (adjtime(&pdata.adjust, NULL) == -1) err(1, "Could not adjust time of day"); } } + if (pledge("stdio rpath", NULL) == -1) + err(1, "pledge"); + if (!silent) { struct tm *ltm; char buf[80]; - time_t tim = new.tv_sec; + time_t tim = pdata.new.tv_sec; double adjsec; ltm = localtime(&tim); (void) strftime(buf, sizeof buf, "%a %b %e %H:%M:%S %Z %Y\n", ltm); (void) fputs(buf, stdout); - adjsec = adjust.tv_sec + adjust.tv_usec / 1.0e6; + adjsec = pdata.adjust.tv_sec + pdata.adjust.tv_usec / 1.0e6; if (slidetime || verbose) { if (ntp) @@ -164,7 +220,7 @@ main(int argc, char **argv) else (void) fprintf(stdout, "%s: adjust local clock by %lld seconds\n", - __progname, (long long)adjust.tv_sec); + __progname, (long long)pdata.adjust.tv_sec); } } |