A command name may also contain newline characters so read

/proc/self/stat until EOF.  It is not legal for /proc/self/stat to
contain embedded NUL bytes so treat the file as corrupt if we see
any.  With help from Qualys.

This is not exploitable due to the /dev traversal changes in sudo
1.8.20p1 (thanks Solar!).
This commit is contained in:
Todd C. Miller
2017-05-31 09:14:31 -06:00
parent 15901c9487
commit c13ebffbce

View File

@@ -456,25 +456,37 @@ char *
get_process_ttyname(char *name, size_t namelen) get_process_ttyname(char *name, size_t namelen)
{ {
const char path[] = "/proc/self/stat"; const char path[] = "/proc/self/stat";
char *line = NULL; char *cp, buf[1024];
char *ret = NULL; char *ret = NULL;
size_t linesize = 0;
int serrno = errno; int serrno = errno;
ssize_t len; ssize_t nread;
FILE *fp; int fd;
debug_decl(get_process_ttyname, SUDO_DEBUG_UTIL) debug_decl(get_process_ttyname, SUDO_DEBUG_UTIL)
/* Try to determine the tty from tty_nr in /proc/self/stat. */ /*
if ((fp = fopen(path, "r")) != NULL) { * Try to determine the tty from tty_nr in /proc/self/stat.
len = getline(&line, &linesize, fp); * Ignore /proc/self/stat if it contains embedded NUL bytes.
fclose(fp); */
if (len != -1) { if ((fd = open(path, O_RDONLY | O_NOFOLLOW)) != -1) {
cp = buf;
while ((nread = read(fd, cp, buf + sizeof(buf) - cp)) != 0) {
if (nread == -1) {
if (errno == EAGAIN || errno == EINTR)
continue;
break;
}
cp += nread;
if (cp >= buf + sizeof(buf))
break;
}
if (nread == 0 && memchr(buf, '\0', cp - buf) == NULL) {
/* /*
* Field 7 is the tty dev (0 if no tty). * Field 7 is the tty dev (0 if no tty).
* Since the process name at field 2 "(comm)" may include spaces, * Since the process name at field 2 "(comm)" may include
* start at the last ')' found. * whitespace (including newlines), start at the last ')' found.
*/ */
char *cp = strrchr(line, ')'); *cp = '\0';
cp = strrchr(buf, ')');
if (cp != NULL) { if (cp != NULL) {
char *ep = cp; char *ep = cp;
const char *errstr; const char *errstr;
@@ -505,7 +517,8 @@ get_process_ttyname(char *name, size_t namelen)
errno = ENOENT; errno = ENOENT;
done: done:
free(line); if (fd != -1)
close(fd);
if (ret == NULL) if (ret == NULL)
sudo_debug_printf(SUDO_DEBUG_WARN|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO, sudo_debug_printf(SUDO_DEBUG_WARN|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
"unable to resolve tty via %s", path); "unable to resolve tty via %s", path);