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, const char *, int, 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,17 +56,20 @@ * 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, const char *line, int tmout, int fd) { struct iovec localiov[TTYMSG_IOV_MAX]; ssize_t left, wret; - int cnt, fd; + int cnt; char device[MAXNAMLEN] = _PATH_DEV; static char errbuf[1024]; char *p; - int forked; + char rel_path[1024]; + int fd2; + int pd; + cap_rights_t rights; - forked = 0; + strcpy(rel_path,"./"); if (iovcnt > (int)(sizeof(localiov) / sizeof(localiov[0]))) return ("too many iov's (change code in wall/ttymsg.c)"); @@ -82,19 +88,20 @@ * 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); - } + strcat(rel_path, p); + fd2 = openat(fd, rel_path, O_WRONLY|O_NONBLOCK, O_RDONLY); + if (fd2 < 0) + err(1, "openat(%s)", rel_path); + + 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) { @@ -118,22 +125,15 @@ if (errno == EWOULDBLOCK) { int cpid; - if (forked) { - (void) close(fd); - _exit(1); - } - cpid = fork(); + cpid = pdfork(&pd, 0); if (cpid < 0) { (void) snprintf(errbuf, sizeof(errbuf), "fork: %s", strerror(errno)); - (void) close(fd); 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 */ @@ -148,16 +148,10 @@ */ if (errno == ENODEV || errno == EIO) break; - (void) close(fd); - 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,27 @@ static char *mbuf; static int -ttystat(char *line) +ttystat(char *line,int fd) { struct stat sb; char ttybuf[MAXPATHLEN]; + int fd2; + char *p; + char device[MAXNAMLEN] = _PATH_DEV; + char rel_path[1024]; + + strcpy(rel_path,"./"); + 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; + strcat(rel_path, p); (void)snprintf(ttybuf, sizeof(ttybuf), "%s%s", _PATH_DEV, line); - if (stat(ttybuf, &sb) == 0) { + fd2 = openat(fd,rel_path,O_RDONLY); + if (fstat(fd2, &sb) == 0) { return (0); } else return (-1); @@ -92,9 +109,19 @@ 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(); + 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 +161,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 +185,10 @@ if (ingroup == 0) continue; } - if ((p = ttymsg(&iov, 1, utmp->ut_line, 60*5)) != NULL) + if ((p = ttymsg(&iov, 1, utmp->ut_line, 60*5, devfd)) != NULL) warnx("%s", p); } + (void)close(devfd); exit(0); } @@ -187,12 +215,18 @@ 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_limit_stdio() < 0||caph_rights_limit(fd, &rights) == -1) + err(1, "unable to limit capability rights"); + if (!nobanner) { tty = ttyname(STDERR_FILENO); if (tty == NULL) @@ -231,6 +265,10 @@ if (setegid(egid) != 0) err(1, "setegid failed"); } + + if (caph_enter() < 0) + err(1, "unable to enter capability mode"); + cnt = 0; while (fgetws(lbuf, sizeof(lbuf)/sizeof(wchar_t), stdin)) { for (p = lbuf; (ch = *p) != L'\0'; ++p, ++cnt) {