diff --git a/usr.bin/wall/ttymsg.h b/usr.bin/wall/ttymsg.h --- a/usr.bin/wall/ttymsg.h +++ b/usr.bin/wall/ttymsg.h @@ -1,4 +1,4 @@ #define TTYMSG_IOV_MAX 32 -const char *ttymsg(struct iovec *, int, const char *, int); +const char *ttymsg(struct iovec *, int, int, const char *, int); diff --git a/usr.bin/wall/ttymsg.c b/usr.bin/wall/ttymsg.c --- a/usr.bin/wall/ttymsg.c +++ b/usr.bin/wall/ttymsg.c @@ -30,10 +30,13 @@ */ - +#include #include #include + +#include #include +#include #include #include #include @@ -53,15 +56,15 @@ * ignored (exclusive-use, lack of permission, etc.). */ const char * -ttymsg(struct iovec *iov, int iovcnt, const char *line, int tmout) +ttymsg(struct iovec *iov, int iovcnt, int dfd, const char *line, int tmout) { struct iovec localiov[TTYMSG_IOV_MAX]; ssize_t left, wret; - int cnt, fd; char device[MAXNAMLEN] = _PATH_DEV; static char errbuf[1024]; char *p; - int forked; + int fd2, pd, cnt, forked; + cap_rights_t rights; forked = 0; if (iovcnt > (int)(sizeof(localiov) / sizeof(localiov[0]))) @@ -82,19 +85,19 @@ * open will fail on slip lines or exclusive-use lines * if not running as root; not an error. */ - if ((fd = open(device, O_WRONLY|O_NONBLOCK, 0)) < 0) { - if (errno == EBUSY || errno == EACCES) - return (NULL); - (void) snprintf(errbuf, sizeof(errbuf), "%s: %s", device, - strerror(errno)); - return (errbuf); - } + fd2 = openat(dfd, p, O_RDWR|O_NONBLOCK); + if (fd2 < 0) + err(1, "openat(%s)", p); + + cap_rights_init(&rights, CAP_WRITE, CAP_FSTAT, CAP_FSTATAT); + if (caph_rights_limit(fd2, &rights) == -1) + err(1, "unable to limit capability rights"); for (cnt = 0, left = 0; cnt < iovcnt; ++cnt) left += iov[cnt].iov_len; for (;;) { - wret = writev(fd, iov, iovcnt); + wret = writev(fd2, iov, iovcnt); if (wret >= left) break; if (wret >= 0) { @@ -119,27 +122,25 @@ int cpid; if (forked) { - (void) close(fd); - _exit(1); - } - cpid = fork(); + (void) close(dfd); + _exit(1); + } + cpid = pdfork(&pd, 0); if (cpid < 0) { (void) snprintf(errbuf, sizeof(errbuf), "fork: %s", strerror(errno)); - (void) close(fd); + (void) close(dfd); return (errbuf); } if (cpid) { /* parent */ - (void) close(fd); return (NULL); } - forked++; /* wait at most tmout seconds */ (void) signal(SIGALRM, SIG_DFL); (void) signal(SIGTERM, SIG_DFL); /* XXX */ (void) sigsetmask(0); (void) alarm((u_int)tmout); - (void) fcntl(fd, F_SETFL, 0); /* clear O_NONBLOCK */ + (void) fcntl(dfd, F_SETFL, 0); /* clear O_NONBLOCK */ continue; } /* @@ -148,16 +149,13 @@ */ if (errno == ENODEV || errno == EIO) break; - (void) close(fd); - if (forked) - _exit(1); + (void) close(dfd); + if (forked) + _exit(1); (void) snprintf(errbuf, sizeof(errbuf), "%s: %s", device, strerror(errno)); return (errbuf); } - (void) close(fd); - if (forked) - _exit(0); return (NULL); } diff --git a/usr.bin/wall/wall.c b/usr.bin/wall/wall.c --- a/usr.bin/wall/wall.c +++ b/usr.bin/wall/wall.c @@ -36,9 +36,12 @@ #include #include +#include #include +#include #include +#include #include #include #include @@ -68,13 +71,23 @@ static char *mbuf; static int -ttystat(char *line) +ttystat(char *line, int dfd) { struct stat sb; char ttybuf[MAXPATHLEN]; + int fd2; + char *p; + char device[MAXNAMLEN] = _PATH_DEV; + strlcat(device, line, sizeof(device)); + p = device + sizeof(_PATH_DEV) - 1; + if (strncmp(p, "pts/", 4) == 0) + p += 4; + if (strchr(p, '/') != NULL) + return -1; (void)snprintf(ttybuf, sizeof(ttybuf), "%s%s", _PATH_DEV, line); - if (stat(ttybuf, &sb) == 0) { + fd2 = openat(dfd, p, O_RDONLY); + if (fstat(fd2, &sb) == 0) { return (0); } else return (-1); @@ -92,9 +105,20 @@ char **np; const char *p; struct passwd *pw; + int devfd; (void)setlocale(LC_CTYPE, ""); + /* + * Cache NLS data, for strerror, for err(3), before entering capability + * mode. + */ + caph_cache_catpages(); + caph_cache_tzdata(); + setutxent(); + devfd = open("/dev/pts/", O_RDONLY|O_NONBLOCK,0); + if (devfd < 0) + err(1, "open(/dev)"); while ((ch = getopt(argc, argv, "g:n")) != -1) switch (ch) { case 'n': @@ -134,7 +158,7 @@ while ((utmp = getutxent()) != NULL) { if (utmp->ut_type != USER_PROCESS) continue; - if (ttystat(utmp->ut_line) != 0) + if (ttystat(utmp->ut_line, devfd) != 0) continue; if (grouplist) { ingroup = 0; @@ -158,9 +182,10 @@ if (ingroup == 0) continue; } - if ((p = ttymsg(&iov, 1, utmp->ut_line, 60*5)) != NULL) + if ((p = ttymsg(&iov, 1, devfd, utmp->ut_line, 60*5)) != NULL) warnx("%s", p); } + (void)close(devfd); exit(0); } @@ -187,12 +212,29 @@ const char *tty; const char *whom; gid_t egid; + cap_rights_t rights; (void)snprintf(tmpname, sizeof(tmpname), "%s/wall.XXXXXX", _PATH_TMP); if ((fd = mkstemp(tmpname)) == -1 || !(fp = fdopen(fd, "r+"))) err(1, "can't open temporary file"); (void)unlink(tmpname); + cap_rights_init(&rights, CAP_FSTAT, CAP_IOCTL, CAP_READ, + CAP_SEEK, CAP_WRITE); + if (caph_rights_limit(fd, &rights) == -1) + err(1, "unable to limit capability rights"); + if (fname) { + egid = getegid(); + setegid(getgid()); + if (freopen(fname, "r", stdin) == NULL) + err(1, "can't read %s", fname); + if (setegid(egid) != 0) + err(1, "setegid failed"); + } + + if (caph_enter() < 0) + err(1, "unable to enter capability mode"); + if (!nobanner) { tty = ttyname(STDERR_FILENO); if (tty == NULL) @@ -223,14 +265,6 @@ } (void)fwprintf(fp, L"%79s\r\n", " "); - if (fname) { - egid = getegid(); - setegid(getgid()); - if (freopen(fname, "r", stdin) == NULL) - err(1, "can't read %s", fname); - if (setegid(egid) != 0) - err(1, "setegid failed"); - } cnt = 0; while (fgetws(lbuf, sizeof(lbuf)/sizeof(wchar_t), stdin)) { for (p = lbuf; (ch = *p) != L'\0'; ++p, ++cnt) {