summaryrefslogtreecommitdiff
path: root/usr/src
diff options
context:
space:
mode:
authorJason King <jason.king@joyent.com>2020-04-16 00:29:46 -0500
committerJason King <jason.king@joyent.com>2020-04-18 14:43:50 -0500
commit6a79a30125dbfeba7eb8ef0a9cd3a8206f644043 (patch)
treea865e205f8ae26be9cc35d281dc72d1d6d89236c /usr/src
parent6e2e67256d436ef900becfa771aee283e7e55430 (diff)
downloadillumos-joyent-6a79a30125dbfeba7eb8ef0a9cd3a8206f644043.tar.gz
12523 Buffer overflow in w and whodo
Reviewed by: Peter Tribble <peter.tribble@gmail.com> Reviewed by: Toomas Soome <tsoome@me.com> Reviewed by: Yuri Pankov <ypankov@fastmail.com> Approved by: Robert Mustacchi <rm@fingolfin.org>
Diffstat (limited to 'usr/src')
-rw-r--r--usr/src/cmd/w/w.c334
-rw-r--r--usr/src/cmd/whodo/whodo.c354
2 files changed, 475 insertions, 213 deletions
diff --git a/usr/src/cmd/w/w.c b/usr/src/cmd/w/w.c
index c062190a21..f945415688 100644
--- a/usr/src/cmd/w/w.c
+++ b/usr/src/cmd/w/w.c
@@ -23,6 +23,8 @@
*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ *
+ * Copyright 2020 Joyent, Inc.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
@@ -58,6 +60,7 @@
#include <ctype.h>
#include <fcntl.h>
#include <time.h>
+#include <err.h>
#include <errno.h>
#include <sys/types.h>
#include <utmpx.h>
@@ -69,6 +72,7 @@
#include <sys/loadavg.h>
#include <limits.h>
#include <priv_utils.h>
+#include <sys/sysmacros.h>
/*
* Use the full lengths from utmpx for user and line.
@@ -130,6 +134,10 @@ static void calctotals(struct uproc *);
static void prttime(time_t, int);
static void prtat(time_t *time);
+static int priv_proc_open(const char *, int);
+static int priv_proc_openat(int, const char *, int);
+static boolean_t do_proc_read(int, void *, size_t);
+
static char *prog; /* pointer to invocation name */
static int header = 1; /* true if -h flag: don't print heading */
static int lflag = 1; /* set if -l flag; 0 for -s flag: short form */
@@ -146,6 +154,18 @@ static time_t proctime; /* cpu time of process in doing */
static pid_t curpid, empty;
static int add_times; /* boolean: add the cpu times or not */
+/*
+ * Basic privs we never need and can drop. This is likely not exhaustive,
+ * but should significantly reduce any potential attack surfaces.
+ */
+static const char *drop_privs[] = {
+ PRIV_FILE_WRITE,
+ PRIV_NET_ACCESS,
+ PRIV_PROC_EXEC,
+ PRIV_PROC_FORK,
+ PRIV_FILE_LINK_ANY
+};
+
#if SIGQUIT > SIGINT
#define ACTSIZE SIGQUIT
#else
@@ -163,24 +183,64 @@ main(int argc, char *argv[])
struct psinfo info;
struct sigaction actinfo[ACTSIZE];
struct pstatus statinfo;
- size_t size;
struct stat sbuf;
DIR *dirp;
struct dirent *dp;
- char pname[64];
- char *fname;
+ char pname[PATH_MAX];
int procfd;
+ int dirfd;
char *cp;
int i;
int days, hrs, mins;
int entries;
double loadavg[3];
+ priv_set_t *pset;
+
+ if (__init_suid_priv(PU_CLEARLIMITSET, PRIV_PROC_OWNER, NULL) != 0) {
+ err(EXIT_FAILURE, "failed to enable privilege bracketing");
+ }
+
+ /*
+ * After setting up privilege bracketing, we can further reduce the
+ * privileges in use. The effective set is set to the basic set minus
+ * the privs in drop_privs. The permitted set is the effective set
+ * plus PRIV_PROC_OWNER (i.e. the privilege being bracketed).
+ */
+ pset = priv_allocset();
+ if (pset == NULL)
+ err(EXIT_FAILURE, "priv_allocset failed");
+
+ priv_basicset(pset);
+ for (i = 0; i < ARRAY_SIZE(drop_privs); i++) {
+ if (priv_delset(pset, drop_privs[i]) != 0) {
+ err(EXIT_FAILURE,
+ "failed to remove %s privilege from privilege set",
+ drop_privs[i]);
+ }
+ }
+
+ if (setppriv(PRIV_SET, PRIV_EFFECTIVE, pset) < 0)
+ err(EXIT_FAILURE, "failed setting effective privilege set");
+
+ if (priv_addset(pset, PRIV_PROC_OWNER) != 0) {
+ err(EXIT_FAILURE,
+ "failed to add PRIV_PROC_OWNER privilege to privilege set");
+ }
+
+ if (setppriv(PRIV_SET, PRIV_PERMITTED, pset) < 0)
+ err(EXIT_FAILURE, "failed to set permitted privilege set");
/*
- * This program needs the proc_owner privilege
+ * Unfortunately, when run as root, privilege bracketing is a no-op,
+ * so we have to add PRIV_PROC_OWNER into our effective set for things
+ * to work.
*/
- (void) __init_suid_priv(PU_CLEARLIMITSET, PRIV_PROC_OWNER,
- (char *)NULL);
+ if (getuid() == 0 && setppriv(PRIV_SET, PRIV_EFFECTIVE, pset) < 0) {
+ err(EXIT_FAILURE, "failed to set effective privilege set");
+ }
+
+ priv_freeset(pset);
+ pset = NULL;
(void) setlocale(LC_ALL, "");
#if !defined(TEXT_DOMAIN)
@@ -235,23 +295,17 @@ main(int argc, char *argv[])
/*
* read the UTMPX_FILE (contains information about each logged in user)
*/
- if (stat(UTMPX_FILE, &sbuf) == ERR) {
- (void) fprintf(stderr, gettext("%s: stat error of %s: %s\n"),
- prog, UTMPX_FILE, strerror(errno));
- exit(1);
- }
+ if (stat(UTMPX_FILE, &sbuf) < 0)
+ err(EXIT_FAILURE, gettext("stat error of %s"), UTMPX_FILE);
+
entries = sbuf.st_size / sizeof (struct futmpx);
- size = sizeof (struct utmpx) * entries;
- if ((ut = malloc(size)) == NULL) {
- (void) fprintf(stderr, gettext("%s: malloc error of %s: %s\n"),
- prog, UTMPX_FILE, strerror(errno));
- exit(1);
- }
+ if ((ut = calloc(entries, sizeof (struct utmpx))) == NULL)
+ err(EXIT_FAILURE, gettext("calloc error of %s"), UTMPX_FILE);
(void) utmpxname(UTMPX_FILE);
utmpbegin = ut;
- utmpend = (struct utmpx *)((char *)utmpbegin + size);
+ utmpend = utmpbegin + entries;
setutxent();
while ((ut < utmpend) && ((utp = getutxent()) != NULL))
@@ -317,8 +371,7 @@ main(int argc, char *argv[])
}
if (fflush(stdout) == EOF) {
- perror((gettext("%s: fflush failed\n"), prog));
- exit(1);
+ err(EXIT_FAILURE, "fflush failed");
}
}
@@ -326,68 +379,63 @@ main(int argc, char *argv[])
* loop through /proc, reading info about each process
* and build the parent/child tree
*/
- if (!(dirp = opendir(PROCDIR))) {
- (void) fprintf(stderr, gettext("%s: could not open %s: %s\n"),
- prog, PROCDIR, strerror(errno));
- exit(1);
- }
+ if ((dirp = opendir(PROCDIR)) == NULL)
+ err(EXIT_FAILURE, gettext("could not open %s"), PROCDIR);
while ((dp = readdir(dirp)) != NULL) {
if (dp->d_name[0] == '.')
continue;
-retry:
- (void) sprintf(pname, "%s/%s/", PROCDIR, dp->d_name);
- fname = pname + strlen(pname);
- (void) strcpy(fname, "psinfo");
- if ((procfd = open(pname, O_RDONLY)) < 0)
+
+ if (snprintf(pname, sizeof (pname), "%s/%s", PROCDIR,
+ dp->d_name) > sizeof (pname))
continue;
- if (read(procfd, &info, sizeof (info)) != sizeof (info)) {
- int err = errno;
- (void) close(procfd);
- if (err == EAGAIN)
- goto retry;
- if (err != ENOENT)
- (void) fprintf(stderr, gettext(
- "%s: read() failed on %s: %s \n"),
- prog, pname, strerror(err));
+
+ dirfd = priv_proc_open(pname, O_RDONLY | O_DIRECTORY);
+
+ if (dirfd < 0) {
+ if (errno == ENOENT)
+ continue;
+ warn(gettext("failed to open %s"), pname);
+ continue;
+ }
+
+ procfd = priv_proc_openat(dirfd, "psinfo", O_RDONLY);
+ if (procfd < 0) {
+ (void) close(dirfd);
+ continue;
+ }
+
+ if (!do_proc_read(procfd, &info, sizeof (info))) {
+ warn(gettext("read() failed on %s"), pname);
+ (void) close(dirfd);
continue;
}
(void) close(procfd);
up = findhash(info.pr_pid);
up->p_ttyd = info.pr_ttydev;
- up->p_state = (info.pr_nlwp == 0? ZOMBIE : RUNNING);
+ up->p_state = (info.pr_nlwp == 0 ? ZOMBIE : RUNNING);
up->p_time = 0;
up->p_ctime = 0;
up->p_igintr = 0;
- (void) strncpy(up->p_comm, info.pr_fname,
- sizeof (info.pr_fname));
+ (void) strlcpy(up->p_comm, info.pr_fname,
+ sizeof (up->p_comm));
up->p_args[0] = 0;
if (up->p_state != NONE && up->p_state != ZOMBIE) {
- (void) strcpy(fname, "status");
-
- /* now we need the proc_owner privilege */
- (void) __priv_bracket(PRIV_ON);
-
- procfd = open(pname, O_RDONLY);
-
- /* drop proc_owner privilege after open */
- (void) __priv_bracket(PRIV_OFF);
-
- if (procfd < 0)
+ procfd = priv_proc_openat(dirfd, "status", O_RDONLY);
+ if (procfd < 0) {
+ (void) close(dirfd);
continue;
+ }
+
+ if (!do_proc_read(procfd, &statinfo,
+ sizeof (statinfo))) {
+ warn(gettext("read() failed on %s/status"),
+ pname);
- if (read(procfd, &statinfo, sizeof (statinfo))
- != sizeof (statinfo)) {
- int err = errno;
(void) close(procfd);
- if (err == EAGAIN)
- goto retry;
- if (err != ENOENT)
- (void) fprintf(stderr, gettext(
- "%s: read() failed on %s: %s \n"),
- prog, pname, strerror(err));
+ (void) close(dirfd);
continue;
}
(void) close(procfd);
@@ -397,33 +445,22 @@ retry:
up->p_ctime = statinfo.pr_cutime.tv_sec +
statinfo.pr_cstime.tv_sec;
- (void) strcpy(fname, "sigact");
-
- /* now we need the proc_owner privilege */
- (void) __priv_bracket(PRIV_ON);
-
- procfd = open(pname, O_RDONLY);
-
- /* drop proc_owner privilege after open */
- (void) __priv_bracket(PRIV_OFF);
-
- if (procfd < 0)
+ procfd = priv_proc_openat(dirfd, "sigact", O_RDONLY);
+ if (procfd < 0) {
+ (void) close(dirfd);
continue;
+ }
+
+ if (!do_proc_read(procfd, actinfo, sizeof (actinfo))) {
+ warn(gettext("read() failed on %s/sigact"),
+ pname);
- if (read(procfd, actinfo, sizeof (actinfo))
- != sizeof (actinfo)) {
- int err = errno;
(void) close(procfd);
- if (err == EAGAIN)
- goto retry;
- if (err != ENOENT)
- (void) fprintf(stderr, gettext(
- "%s: read() failed on %s: %s \n"),
- prog, pname, strerror(err));
+ (void) close(dirfd);
continue;
}
(void) close(procfd);
-
+ (void) close(dirfd);
up->p_igintr =
actinfo[SIGINT-1].sa_handler == SIG_IGN &&
actinfo[SIGQUIT-1].sa_handler == SIG_IGN;
@@ -431,15 +468,19 @@ retry:
/*
* Process args.
*/
- up->p_args[0] = 0;
+ up->p_args[0] = '\0';
clnarglist(info.pr_psargs);
- (void) strcat(up->p_args, info.pr_psargs);
+ (void) strlcpy(up->p_args, info.pr_psargs,
+ sizeof (up->p_args));
if (up->p_args[0] == 0 ||
up->p_args[0] == '-' && up->p_args[1] <= ' ' ||
up->p_args[0] == '?') {
- (void) strcat(up->p_args, " (");
- (void) strcat(up->p_args, up->p_comm);
- (void) strcat(up->p_args, ")");
+ (void) strlcat(up->p_args, " (",
+ sizeof (up->p_args));
+ (void) strlcat(up->p_args, up->p_comm,
+ sizeof (up->p_args));
+ (void) strlcat(up->p_args, ")",
+ sizeof (up->p_args));
}
}
@@ -466,7 +507,34 @@ retry:
}
/* revert to non-privileged user after opening */
- (void) __priv_relinquish();
+ __priv_relinquish();
+ if (getuid() == 0) {
+ /*
+ * Since the privilege bracketing functions are effectively
+ * no-ops when running as root, we must explicitly
+ * relinquish PRIV_PROC_OWNER ourselves.
+ */
+ pset = priv_allocset();
+ if (pset == NULL) {
+ err(EXIT_FAILURE,
+ gettext("failed to allocate privilege set"));
+ }
+
+ priv_emptyset(pset);
+
+ if (priv_addset(pset, PRIV_PROC_OWNER) != 0) {
+ err(EXIT_FAILURE, gettext("failed to add "
+ "PRIV_PROC_OWNER to privilege set"));
+ }
+
+ if (setppriv(PRIV_OFF, PRIV_PERMITTED, pset) != 0) {
+ err(EXIT_FAILURE,
+ gettext("failed to set permitted privilege set"));
+ }
+
+ priv_freeset(pset);
+ pset = NULL;
+ }
(void) closedir(dirp);
(void) time(&now); /* get current time */
@@ -509,10 +577,9 @@ retry:
prttime(idle, 8);
showtotals(findhash(ut->ut_pid));
}
- if (fclose(stdout) == EOF) {
- perror((gettext("%s: fclose failed"), prog));
- exit(1);
- }
+ if (fclose(stdout) == EOF)
+ err(EXIT_FAILURE, gettext("fclose failed"));
+
return (0);
}
@@ -579,9 +646,9 @@ calctotals(struct uproc *up)
if (up->p_upid > curpid && (!up->p_igintr || empty)) {
curpid = up->p_upid;
if (lflag)
- (void) strcpy(doing, up->p_args);
+ (void) strlcpy(doing, up->p_args, sizeof (doing));
else
- (void) strcpy(doing, up->p_comm);
+ (void) strlcpy(doing, up->p_comm, sizeof (doing));
}
if (add_times == 1) {
@@ -625,11 +692,9 @@ findhash(pid_t pid)
return (tp);
}
tp = malloc(sizeof (*tp)); /* add new node */
- if (!tp) {
- (void) fprintf(stderr, gettext("%s: out of memory!: %s\n"),
- prog, strerror(errno));
- exit(1);
- }
+ if (tp == NULL)
+ err(EXIT_FAILURE, gettext("out of memory!"));
+
(void) memset(tp, 0, sizeof (*tp));
tp->p_upid = pid;
tp->p_state = NONE;
@@ -662,7 +727,7 @@ prttime(time_t tim, int width)
} else if (tim > 0) {
(void) snprintf(value, sizeof (value), "%d", (int)tim);
} else {
- (void) strcpy(value, "0");
+ (void) strlcpy(value, "0", sizeof (value));
}
width = (width > 2) ? width - 1 : 1;
PRINTF(("%*s ", width, value));
@@ -711,8 +776,8 @@ findidle(char *devname)
time_t lastaction, diff;
char ttyname[64];
- (void) strcpy(ttyname, "/dev/");
- (void) strcat(ttyname, devname);
+ (void) strlcpy(ttyname, "/dev/", sizeof (ttyname));
+ (void) strlcat(ttyname, devname, sizeof (ttyname));
if (stat(ttyname, &stbuf) != -1) {
lastaction = stbuf.st_atime;
diff = now - lastaction;
@@ -744,3 +809,66 @@ clnarglist(char *arglist)
}
}
}
+
+static int
+priv_proc_open(const char *path, int oflag)
+{
+ int fd, errsave = 0;
+
+ if (__priv_bracket(PRIV_ON) != 0)
+ err(EXIT_FAILURE, gettext("privilege bracketing failed"));
+
+ do {
+ fd = open(path, oflag);
+ if (fd < 0)
+ errsave = errno;
+ } while (fd < 0 && errno == EAGAIN);
+
+ if (__priv_bracket(PRIV_OFF) != 0)
+ err(EXIT_FAILURE, gettext("privilege bracketing failed"));
+
+ if (fd < 0)
+ errno = errsave;
+
+ return (fd);
+}
+
+static int
+priv_proc_openat(int dfd, const char *path, int mode)
+{
+ int fd, errsave = 0;
+
+ if (__priv_bracket(PRIV_ON) != 0)
+ err(EXIT_FAILURE, gettext("privilege bracketing failed"));
+
+ do {
+ fd = openat(dfd, path, mode);
+ if (fd < 0)
+ errsave = errno;
+ } while (fd < 0 && errno == EAGAIN);
+
+ if (__priv_bracket(PRIV_OFF) != 0)
+ err(EXIT_FAILURE, gettext("privilege bracketing failed"));
+
+ if (fd < 0)
+ errno = errsave;
+
+ return (fd);
+}
+
+static boolean_t
+do_proc_read(int fd, void *buf, size_t bufsize)
+{
+ ssize_t n;
+
+ do {
+ n = pread(fd, buf, bufsize, 0);
+ if (n == bufsize)
+ return (B_TRUE);
+ /*
+ * Retry on a partial read or EAGAIN, otherwise fail
+ */
+ } while (n >= 0 || errno == EAGAIN);
+
+ return (B_FALSE);
+}
diff --git a/usr/src/cmd/whodo/whodo.c b/usr/src/cmd/whodo/whodo.c
index fe097ace23..98abc0792d 100644
--- a/usr/src/cmd/whodo/whodo.c
+++ b/usr/src/cmd/whodo/whodo.c
@@ -23,6 +23,8 @@
*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ *
+ * Copyright 2020 Joyent, Inc.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
@@ -55,9 +57,11 @@
#include <ctype.h>
#include <fcntl.h>
#include <time.h>
+#include <err.h>
#include <errno.h>
#include <sys/types.h>
#include <utmpx.h>
+#include <sys/sysmacros.h>
#include <sys/utsname.h>
#include <sys/stat.h>
#include <sys/mkdev.h>
@@ -95,8 +99,8 @@
#define ZOMBIE 'z' /* zombie process */
#define VISITED 'v' /* marked node as visited */
-static int ndevs; /* number of configured devices */
-static int maxdev; /* slots for configured devices */
+static uint_t ndevs; /* number of configured devices */
+static uint_t maxdev; /* slots for configured devices */
#define DNINCR 100
static struct devl { /* device list */
char dname[DEVNAMELEN]; /* device name */
@@ -136,6 +140,10 @@ static char *getty(dev_t);
static void prttime(time_t, int);
static void prtat(time_t *);
+static int priv_proc_open(const char *, int);
+static int priv_proc_openat(int, const char *, int);
+static boolean_t do_proc_read(int, void *, size_t);
+
static char *prog;
static int header = 1; /* true if -h flag: don't print heading */
static int lflag = 0; /* true if -l flag: w command format */
@@ -150,6 +158,18 @@ static time_t proctime; /* cpu time of process in doing */
static int empty;
static pid_t curpid;
+/*
+ * Basic privs we never need and can drop. This is likely not exhaustive,
+ * but should significantly reduce any potential attack surfaces.
+ */
+static const char *drop_privs[] = {
+ PRIV_FILE_WRITE,
+ PRIV_NET_ACCESS,
+ PRIV_PROC_EXEC,
+ PRIV_PROC_FORK,
+ PRIV_FILE_LINK_ANY
+};
+
#if SIGQUIT > SIGINT
#define ACTSIZE SIGQUIT
#else
@@ -168,23 +188,62 @@ main(int argc, char *argv[])
struct psinfo info;
struct sigaction actinfo[ACTSIZE];
struct pstatus statinfo;
- size_t size;
struct stat sbuf;
struct utsname uts;
DIR *dirp;
struct dirent *dp;
- char pname[64];
- char *fname;
- int procfd;
+ char pname[PATH_MAX];
+ int procfd, dirfd;
int i;
int days, hrs, mins;
int entries;
+ priv_set_t *pset;
+
+ if (__init_suid_priv(PU_CLEARLIMITSET, PRIV_PROC_OWNER, NULL) != 0) {
+ err(EXIT_FAILURE, "failed to enable privilege bracketing");
+ }
+
+ /*
+ * After setting up privilege bracketing, we can further reduce the
+ * privileges in use. The effective set is set to the basic set minus
+ * the privs in drop_privs. The permitted set is the effective set
+ * plus PRIV_PROC_OWNER (i.e. the privilege being bracketed).
+ */
+ pset = priv_allocset();
+ if (pset == NULL)
+ err(EXIT_FAILURE, "priv_allocset failed");
+
+ priv_basicset(pset);
+ for (i = 0; i < ARRAY_SIZE(drop_privs); i++) {
+ if (priv_delset(pset, drop_privs[i]) != 0) {
+ err(EXIT_FAILURE,
+ "failed to remove %s privilege from privilege set",
+ drop_privs[i]);
+ }
+ }
+
+ if (setppriv(PRIV_SET, PRIV_EFFECTIVE, pset) < 0)
+ err(EXIT_FAILURE, "failed setting effective privilege set");
+
+ if (priv_addset(pset, PRIV_PROC_OWNER) != 0) {
+ err(EXIT_FAILURE,
+ "failed to add PRIV_PROC_OWNER privilege to privilege set");
+ }
+
+ if (setppriv(PRIV_SET, PRIV_PERMITTED, pset) < 0)
+ err(EXIT_FAILURE, "failed to set permitted privilege set");
/*
- * This program needs the proc_owner privilege
+ * Unfortunately, when run as root, privilege bracketing is a no-op,
+ * so we have to add PRIV_PROC_OWNER into our effective set for things
+ * to work.
*/
- (void) __init_suid_priv(PU_CLEARLIMITSET, PRIV_PROC_OWNER,
- (char *)NULL);
+ if (getuid() == 0 && setppriv(PRIV_SET, PRIV_EFFECTIVE, pset) < 0) {
+ err(EXIT_FAILURE, "failed to set effective privilege set");
+ }
+
+ priv_freeset(pset);
+ pset = NULL;
(void) setlocale(LC_ALL, "");
#if !defined(TEXT_DOMAIN)
@@ -229,25 +288,18 @@ main(int argc, char *argv[])
* read the UTMPX_FILE (contains information about
* each logged in user)
*/
- if (stat(UTMPX_FILE, &sbuf) == ERR) {
- (void) fprintf(stderr, gettext("%s: stat error of %s: %s\n"),
- prog, UTMPX_FILE, strerror(errno));
- exit(1);
- }
+ if (stat(UTMPX_FILE, &sbuf) < 0)
+ err(EXIT_FAILURE, gettext("stat error of %s"), UTMPX_FILE);
+
entries = sbuf.st_size / sizeof (struct futmpx);
- size = sizeof (struct utmpx) * entries;
- if ((ut = malloc(size)) == NULL) {
- (void) fprintf(stderr, gettext("%s: malloc error of %s: %s\n"),
- prog, UTMPX_FILE, strerror(errno));
- exit(1);
- }
+ if ((ut = calloc(entries, sizeof (struct utmpx))) == NULL)
+ err(EXIT_FAILURE, gettext("calloc error of %s"), UTMPX_FILE);
(void) utmpxname(UTMPX_FILE);
utmpbegin = ut;
- /* LINTED pointer cast may result in improper alignment */
- utmpend = (struct utmpx *)((char *)utmpbegin + size);
+ utmpend = utmpbegin + entries;
setutxent();
while ((ut < utmpend) && ((utp = getutxent()) != NULL))
@@ -306,31 +358,36 @@ main(int argc, char *argv[])
* loop through /proc, reading info about each process
* and build the parent/child tree
*/
- if (!(dirp = opendir(PROCDIR))) {
- (void) fprintf(stderr, gettext("%s: could not open %s: %s\n"),
- prog, PROCDIR, strerror(errno));
- exit(1);
- }
+ if ((dirp = opendir(PROCDIR)) == NULL)
+ err(EXIT_FAILURE, gettext("could not open %s"), PROCDIR);
while ((dp = readdir(dirp)) != NULL) {
if (dp->d_name[0] == '.')
continue;
-retry:
- (void) snprintf(pname, sizeof (pname),
- "%s/%s/", PROCDIR, dp->d_name);
- fname = pname + strlen(pname);
- (void) strcpy(fname, "psinfo");
- if ((procfd = open(pname, O_RDONLY)) < 0)
+
+ if (snprintf(pname, sizeof (pname), "%s/%s", PROCDIR,
+ dp->d_name) > sizeof (pname))
continue;
- if (read(procfd, &info, sizeof (info)) != sizeof (info)) {
- int err = errno;
- (void) close(procfd);
- if (err == EAGAIN)
- goto retry;
- if (err != ENOENT)
- (void) fprintf(stderr, gettext(
- "%s: read() failed on %s: %s\n"),
- prog, pname, strerror(err));
+
+ dirfd = priv_proc_open(pname, O_RDONLY | O_DIRECTORY);
+
+ if (dirfd < 0) {
+ if (errno == ENOENT)
+ continue;
+ warn(gettext("failed to open %s"), pname);
+ continue;
+ }
+
+
+ procfd = priv_proc_openat(dirfd, "psinfo", O_RDONLY);
+ if (procfd < 0) {
+ (void) close(dirfd);
+ continue;
+ }
+
+ if (!do_proc_read(procfd, &info, sizeof (info))) {
+ warn(gettext("read() failed on %s"), pname);
+ (void) close(dirfd);
continue;
}
(void) close(procfd);
@@ -341,34 +398,24 @@ retry:
up->p_time = 0;
up->p_ctime = 0;
up->p_igintr = 0;
- (void) strncpy(up->p_comm, info.pr_fname,
- sizeof (info.pr_fname));
+ (void) strlcpy(up->p_comm, info.pr_fname,
+ sizeof (up->p_comm));
up->p_args[0] = 0;
if (up->p_state != NONE && up->p_state != ZOMBIE) {
- (void) strcpy(fname, "status");
-
- /* now we need the proc_owner privilege */
- (void) __priv_bracket(PRIV_ON);
-
- procfd = open(pname, O_RDONLY);
-
- /* drop proc_owner privilege after open */
- (void) __priv_bracket(PRIV_OFF);
-
- if (procfd < 0)
+ procfd = priv_proc_openat(dirfd, "status", O_RDONLY);
+ if (procfd < 0) {
+ (void) close(dirfd);
continue;
+ }
+
+ if (!do_proc_read(procfd, &statinfo,
+ sizeof (statinfo))) {
+ warn(gettext("read() failed on %s/status"),
+ pname);
- if (read(procfd, &statinfo, sizeof (statinfo))
- != sizeof (statinfo)) {
- int err = errno;
(void) close(procfd);
- if (err == EAGAIN)
- goto retry;
- if (err != ENOENT)
- (void) fprintf(stderr, gettext(
- "%s: read() failed on %s: %s \n"),
- prog, pname, strerror(err));
+ (void) close(dirfd);
continue;
}
(void) close(procfd);
@@ -378,31 +425,22 @@ retry:
up->p_ctime = statinfo.pr_cutime.tv_sec +
statinfo.pr_cstime.tv_sec;
- (void) strcpy(fname, "sigact");
-
- /* now we need the proc_owner privilege */
- (void) __priv_bracket(PRIV_ON);
-
- procfd = open(pname, O_RDONLY);
+ procfd = priv_proc_openat(dirfd, "sigact", O_RDONLY);
+ if (procfd < 0) {
+ (void) close(dirfd);
+ continue;
+ }
- /* drop proc_owner privilege after open */
- (void) __priv_bracket(PRIV_OFF);
+ if (!do_proc_read(procfd, actinfo, sizeof (actinfo))) {
+ warn(gettext("read() failed on %s/sigact"),
+ pname);
- if (procfd < 0)
- continue;
- if (read(procfd, actinfo, sizeof (actinfo))
- != sizeof (actinfo)) {
- int err = errno;
(void) close(procfd);
- if (err == EAGAIN)
- goto retry;
- if (err != ENOENT)
- (void) fprintf(stderr, gettext(
- "%s: read() failed on %s: %s \n"),
- prog, pname, strerror(err));
+ (void) close(dirfd);
continue;
}
(void) close(procfd);
+ (void) close(dirfd);
up->p_igintr =
actinfo[SIGINT-1].sa_handler == SIG_IGN &&
@@ -415,14 +453,18 @@ retry:
*/
if (lflag) { /* w command needs args */
clnarglist(info.pr_psargs);
- (void) strcpy(up->p_args, info.pr_psargs);
+ (void) strlcpy(up->p_args, info.pr_psargs,
+ sizeof (up->p_args));
if (up->p_args[0] == 0 ||
up->p_args[0] == '-' &&
up->p_args[1] <= ' ' ||
up->p_args[0] == '?') {
- (void) strcat(up->p_args, " (");
- (void) strcat(up->p_args, up->p_comm);
- (void) strcat(up->p_args, ")");
+ (void) strlcat(up->p_args, " (",
+ sizeof (up->p_args));
+ (void) strlcat(up->p_args, up->p_comm,
+ sizeof (up->p_args));
+ (void) strlcat(up->p_args, ")",
+ sizeof (up->p_args));
}
}
@@ -452,7 +494,34 @@ retry:
}
/* revert to non-privileged user */
- (void) __priv_relinquish();
+ __priv_relinquish();
+ if (getuid() == 0) {
+ /*
+ * Since the privilege bracketing functions are effectively
+ * no-ops when running as root, we must explicitly
+ * relinquish PRIV_PROC_OWNER ourselves.
+ */
+ pset = priv_allocset();
+ if (pset == NULL) {
+ err(EXIT_FAILURE,
+ gettext("failed to allocate privilege set"));
+ }
+
+ priv_emptyset(pset);
+
+ if (priv_addset(pset, PRIV_PROC_OWNER) != 0) {
+ err(EXIT_FAILURE, gettext("failed to add "
+ "PRIV_PROC_OWNER to privilege set"));
+ }
+
+ if (setppriv(PRIV_OFF, PRIV_PERMITTED, pset) != 0) {
+ err(EXIT_FAILURE,
+ gettext("failed to set permitted privilege set"));
+ }
+
+ priv_freeset(pset);
+ pset = NULL;
+ }
(void) closedir(dirp);
(void) time(&now); /* get current time */
@@ -553,7 +622,9 @@ showtotals(struct uproc *up)
proctime = 0;
empty = 1;
curpid = -1;
- (void) strcpy(doing, "-"); /* default act: normally never prints */
+
+ /* default act: normally never prints */
+ (void) strlcpy(doing, "-", sizeof (doing));
calctotals(up);
/* print CPU time for all processes & children */
@@ -598,7 +669,7 @@ calctotals(struct uproc *up)
if (up->p_upid > curpid && (!up->p_igintr || empty)) {
curpid = up->p_upid;
- (void) strcpy(doing, up->p_args);
+ (void) strlcpy(doing, up->p_args, sizeof (doing));
}
/* descend for its children */
@@ -610,34 +681,36 @@ calctotals(struct uproc *up)
}
static char *
-devadd(char *name, dev_t ddev)
+devadd(const char *name, dev_t ddev)
{
struct devl *dp;
int leng, start, i;
if (ndevs == maxdev) {
- maxdev += DNINCR;
- dp = realloc(devl, maxdev * sizeof (struct devl));
- if (!dp) {
- (void) fprintf(stderr,
- gettext("%s: out of memory!: %s\n"),
- prog, strerror(errno));
- exit(1);
- }
+ uint_t newdev;
+
+ newdev = maxdev + DNINCR;
+ if (newdev < DNINCR)
+ errx(EXIT_FAILURE, gettext("devadd overflow"));
+
+ dp = recallocarray(devl, maxdev, newdev, sizeof (struct devl));
+ if (dp == NULL)
+ err(EXIT_FAILURE, gettext("out of memory!"));
+ maxdev = newdev;
devl = dp;
}
dp = &devl[ndevs++];
dp->ddev = ddev;
if (name == NULL) {
- (void) strcpy(dp->dname, " ? ");
+ (void) strlcpy(dp->dname, " ? ", sizeof (dp->dname));
return (dp->dname);
}
leng = strlen(name);
if (leng < DEVNAMELEN + 4) {
/* strip off "/dev/" */
- (void) strcpy(dp->dname, &name[5]);
+ (void) strlcpy(dp->dname, &name[5], sizeof (dp->dname));
} else {
/* strip enough off the front to fit */
start = leng - DEVNAMELEN - 1;
@@ -645,9 +718,9 @@ devadd(char *name, dev_t ddev)
for (i = start; i < leng && name[i] != '/'; i++)
;
if (i == leng)
- (void) strncpy(dp->dname, &name[start], DEVNAMELEN);
+ (void) strlcpy(dp->dname, &name[start], DEVNAMELEN);
else
- (void) strncpy(dp->dname, &name[i+1], DEVNAMELEN);
+ (void) strlcpy(dp->dname, &name[i+1], DEVNAMELEN);
}
return (dp->dname);
}
@@ -715,11 +788,9 @@ findhash(pid_t pid)
}
}
tp = malloc(sizeof (*tp)); /* add new node */
- if (!tp) {
- (void) fprintf(stderr, gettext("%s: out of memory!: %s\n"),
- prog, strerror(errno));
- exit(1);
- }
+ if (tp == NULL)
+ err(EXIT_FAILURE, gettext("out of memory!"));
+
(void) memset((char *)tp, 0, sizeof (*tp));
tp->p_upid = pid;
tp->p_state = NONE;
@@ -802,8 +873,8 @@ findidle(char *devname)
time_t lastaction, diff;
char ttyname[64];
- (void) strcpy(ttyname, "/dev/");
- (void) strcat(ttyname, devname);
+ (void) strlcpy(ttyname, "/dev/", sizeof (ttyname));
+ (void) strlcat(ttyname, devname, sizeof (ttyname));
if (stat(ttyname, &stbuf) != -1) {
lastaction = stbuf.st_atime;
diff = now - lastaction;
@@ -835,3 +906,66 @@ clnarglist(char *arglist)
}
}
}
+
+static int
+priv_proc_open(const char *path, int oflag)
+{
+ int fd, errsave = 0;
+
+ if (__priv_bracket(PRIV_ON) != 0)
+ err(EXIT_FAILURE, gettext("privilege bracketing failed"));
+
+ do {
+ fd = open(path, oflag);
+ if (fd < 0)
+ errsave = errno;
+ } while (fd < 0 && errno == EAGAIN);
+
+ if (__priv_bracket(PRIV_OFF) != 0)
+ err(EXIT_FAILURE, gettext("privilege bracketing failed"));
+
+ if (fd < 0)
+ errno = errsave;
+
+ return (fd);
+}
+
+static int
+priv_proc_openat(int dfd, const char *path, int mode)
+{
+ int fd, errsave = 0;
+
+ if (__priv_bracket(PRIV_ON) != 0)
+ err(EXIT_FAILURE, gettext("privilege bracketing failed"));
+
+ do {
+ fd = openat(dfd, path, mode);
+ if (fd < 0)
+ errsave = errno;
+ } while (fd < 0 && errno == EAGAIN);
+
+ if (__priv_bracket(PRIV_OFF) != 0)
+ err(EXIT_FAILURE, gettext("privilege bracketing failed"));
+
+ if (fd < 0)
+ errno = errsave;
+
+ return (fd);
+}
+
+static boolean_t
+do_proc_read(int fd, void *buf, size_t bufsize)
+{
+ ssize_t n;
+
+ do {
+ n = pread(fd, buf, bufsize, 0);
+ if (n == bufsize)
+ return (B_TRUE);
+ /*
+ * Retry on a partial read or EAGAIN, otherwise fail
+ */
+ } while (n >= 0 || errno == EAGAIN);
+
+ return (B_FALSE);
+}