summaryrefslogtreecommitdiffstats
path: root/usr.sbin/rdate
diff options
context:
space:
mode:
authorderaadt <deraadt@openbsd.org>2015-10-29 03:16:15 +0000
committerderaadt <deraadt@openbsd.org>2015-10-29 03:16:15 +0000
commit6a6f1b391b11875979a1f4222092611c791615d3 (patch)
treee37b9c30ffb090ef8b9e170446b010ed8292133e /usr.sbin/rdate
parentpledge "stdio rpath". (diff)
downloadwireguard-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.c82
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);
}
}