|
@@ -0,0 +1,264 @@
|
|
|
+Downloaded from upstream: https://www.sudo.ws/repos/sudo/raw-rev/b5460cbbb11b
|
|
|
+
|
|
|
+# HG changeset patch
|
|
|
+# User Todd C. Miller <Todd.Miller@courtesan.com>
|
|
|
+# Date 1496089973 21600
|
|
|
+# Node ID b5460cbbb11bbf9d92ffcc6798a686cf4125efd3
|
|
|
+# Parent c303e6eecc7841e2f891d70613e80fcf27fa6e86
|
|
|
+Fix for CVE-2017-1000367, parsing of /proc/pid/stat on Linux when
|
|
|
+the process name contains spaces. Since the user has control over
|
|
|
+the command name this could be used by a user with sudo access to
|
|
|
+overwrite an arbitrary file.
|
|
|
+Thanks to Qualys for investigating and reporting this bug.
|
|
|
+
|
|
|
+Also stop performing a breadth-first traversal of /dev when looking
|
|
|
+for the device. Only the directories specified in search_devs[]
|
|
|
+are checked.
|
|
|
+
|
|
|
+Signed-off-by: Peter Korsgaard <peter@korsgaard.com>
|
|
|
+diff -r c303e6eecc78 -r b5460cbbb11b src/ttyname.c
|
|
|
+--- a/src/ttyname.c Tue May 23 13:26:54 2017 -0600
|
|
|
++++ b/src/ttyname.c Mon May 29 14:32:53 2017 -0600
|
|
|
+@@ -1,5 +1,5 @@
|
|
|
+ /*
|
|
|
+- * Copyright (c) 2012-2016 Todd C. Miller <Todd.Miller@courtesan.com>
|
|
|
++ * Copyright (c) 2012-2017 Todd C. Miller <Todd.Miller@courtesan.com>
|
|
|
+ *
|
|
|
+ * Permission to use, copy, modify, and distribute this software for any
|
|
|
+ * purpose with or without fee is hereby granted, provided that the above
|
|
|
+@@ -145,20 +145,22 @@
|
|
|
+ }
|
|
|
+ #elif defined(HAVE_STRUCT_PSINFO_PR_TTYDEV) || defined(HAVE_PSTAT_GETPROC) || defined(__linux__)
|
|
|
+ /*
|
|
|
+- * Devices to search before doing a breadth-first scan.
|
|
|
++ * Device nodes and directories to search before searching all of /dev
|
|
|
+ */
|
|
|
+ static char *search_devs[] = {
|
|
|
+ "/dev/console",
|
|
|
+- "/dev/wscons",
|
|
|
+- "/dev/pts/",
|
|
|
+- "/dev/vt/",
|
|
|
+- "/dev/term/",
|
|
|
+- "/dev/zcons/",
|
|
|
++ "/dev/pts/", /* POSIX pty */
|
|
|
++ "/dev/vt/", /* Solaris virtual console */
|
|
|
++ "/dev/term/", /* Solaris serial ports */
|
|
|
++ "/dev/zcons/", /* Solaris zone console */
|
|
|
++ "/dev/pty/", /* HP-UX old-style pty */
|
|
|
+ NULL
|
|
|
+ };
|
|
|
+
|
|
|
++/*
|
|
|
++ * Device nodes to ignore when searching all of /dev
|
|
|
++ */
|
|
|
+ static char *ignore_devs[] = {
|
|
|
+- "/dev/fd/",
|
|
|
+ "/dev/stdin",
|
|
|
+ "/dev/stdout",
|
|
|
+ "/dev/stderr",
|
|
|
+@@ -166,16 +168,18 @@
|
|
|
+ };
|
|
|
+
|
|
|
+ /*
|
|
|
+- * Do a breadth-first scan of dir looking for the specified device.
|
|
|
++ * Do a scan of a directory looking for the specified device.
|
|
|
++ * Does not descend into subdirectories.
|
|
|
+ * Returns name on success and NULL on failure, setting errno.
|
|
|
+ */
|
|
|
+ static char *
|
|
|
+-sudo_ttyname_scan(const char *dir, dev_t rdev, bool builtin, char *name, size_t namelen)
|
|
|
++sudo_ttyname_scan(const char *dir, dev_t rdev, char *name, size_t namelen)
|
|
|
+ {
|
|
|
+- size_t sdlen, num_subdirs = 0, max_subdirs = 0;
|
|
|
+- char pathbuf[PATH_MAX], **subdirs = NULL;
|
|
|
++ size_t sdlen;
|
|
|
++ char pathbuf[PATH_MAX];
|
|
|
+ char *ret = NULL;
|
|
|
+ struct dirent *dp;
|
|
|
++ struct stat sb;
|
|
|
+ unsigned int i;
|
|
|
+ DIR *d = NULL;
|
|
|
+ debug_decl(sudo_ttyname_scan, SUDO_DEBUG_UTIL)
|
|
|
+@@ -187,6 +191,18 @@
|
|
|
+ if ((d = opendir(dir)) == NULL)
|
|
|
+ goto done;
|
|
|
+
|
|
|
++ if (fstat(dirfd(d), &sb) == -1) {
|
|
|
++ sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
|
|
|
++ "unable to fstat %s", dir);
|
|
|
++ goto done;
|
|
|
++ }
|
|
|
++ if ((sb.st_mode & S_IWOTH) != 0) {
|
|
|
++ sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
|
|
|
++ "ignoring world-writable directory %s", dir);
|
|
|
++ errno = ENOENT;
|
|
|
++ goto done;
|
|
|
++ }
|
|
|
++
|
|
|
+ sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
|
|
|
+ "scanning for dev %u in %s", (unsigned int)rdev, dir);
|
|
|
+
|
|
|
+@@ -224,18 +240,6 @@
|
|
|
+ }
|
|
|
+ if (ignore_devs[i] != NULL)
|
|
|
+ continue;
|
|
|
+- if (!builtin) {
|
|
|
+- /* Skip entries in search_devs; we already checked them. */
|
|
|
+- for (i = 0; search_devs[i] != NULL; i++) {
|
|
|
+- len = strlen(search_devs[i]);
|
|
|
+- if (search_devs[i][len - 1] == '/')
|
|
|
+- len--;
|
|
|
+- if (d_len == len && strncmp(pathbuf, search_devs[i], len) == 0)
|
|
|
+- break;
|
|
|
+- }
|
|
|
+- if (search_devs[i] != NULL)
|
|
|
+- continue;
|
|
|
+- }
|
|
|
+ # if defined(HAVE_STRUCT_DIRENT_D_TYPE) && defined(DTTOIF)
|
|
|
+ /*
|
|
|
+ * Avoid excessive stat() calls by checking dp->d_type.
|
|
|
+@@ -248,39 +252,14 @@
|
|
|
+ if (stat(pathbuf, &sb) == -1)
|
|
|
+ continue;
|
|
|
+ break;
|
|
|
+- case DT_DIR:
|
|
|
+- /* Directory, no need to stat() it. */
|
|
|
+- sb.st_mode = DTTOIF(dp->d_type);
|
|
|
+- sb.st_rdev = 0; /* quiet ccc-analyzer false positive */
|
|
|
+- break;
|
|
|
+ default:
|
|
|
+- /* Not a character device, link or directory, skip it. */
|
|
|
++ /* Not a character device or link, skip it. */
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ # else
|
|
|
+ if (stat(pathbuf, &sb) == -1)
|
|
|
+ continue;
|
|
|
+ # endif
|
|
|
+- if (S_ISDIR(sb.st_mode)) {
|
|
|
+- if (!builtin) {
|
|
|
+- /* Add to list of subdirs to search. */
|
|
|
+- if (num_subdirs + 1 > max_subdirs) {
|
|
|
+- char **new_subdirs;
|
|
|
+-
|
|
|
+- new_subdirs = reallocarray(subdirs, max_subdirs + 64,
|
|
|
+- sizeof(char *));
|
|
|
+- if (new_subdirs == NULL)
|
|
|
+- goto done;
|
|
|
+- subdirs = new_subdirs;
|
|
|
+- max_subdirs += 64;
|
|
|
+- }
|
|
|
+- subdirs[num_subdirs] = strdup(pathbuf);
|
|
|
+- if (subdirs[num_subdirs] == NULL)
|
|
|
+- goto done;
|
|
|
+- num_subdirs++;
|
|
|
+- }
|
|
|
+- continue;
|
|
|
+- }
|
|
|
+ if (S_ISCHR(sb.st_mode) && sb.st_rdev == rdev) {
|
|
|
+ sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
|
|
|
+ "resolved dev %u as %s", (unsigned int)rdev, pathbuf);
|
|
|
+@@ -296,16 +275,9 @@
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+- /* Search subdirs if we didn't find it in the root level. */
|
|
|
+- for (i = 0; ret == NULL && i < num_subdirs; i++)
|
|
|
+- ret = sudo_ttyname_scan(subdirs[i], rdev, false, name, namelen);
|
|
|
+-
|
|
|
+ done:
|
|
|
+ if (d != NULL)
|
|
|
+ closedir(d);
|
|
|
+- for (i = 0; i < num_subdirs; i++)
|
|
|
+- free(subdirs[i]);
|
|
|
+- free(subdirs);
|
|
|
+ debug_return_str(ret);
|
|
|
+ }
|
|
|
+
|
|
|
+@@ -324,7 +296,7 @@
|
|
|
+ debug_decl(sudo_ttyname_dev, SUDO_DEBUG_UTIL)
|
|
|
+
|
|
|
+ /*
|
|
|
+- * First check search_devs for common tty devices.
|
|
|
++ * First check search_devs[] for common tty devices.
|
|
|
+ */
|
|
|
+ for (sd = search_devs; (devname = *sd) != NULL; sd++) {
|
|
|
+ len = strlen(devname);
|
|
|
+@@ -349,7 +321,7 @@
|
|
|
+ "comparing dev %u to %s: no", (unsigned int)rdev, buf);
|
|
|
+ } else {
|
|
|
+ /* Traverse directory */
|
|
|
+- ret = sudo_ttyname_scan(devname, rdev, true, name, namelen);
|
|
|
++ ret = sudo_ttyname_scan(devname, rdev, name, namelen);
|
|
|
+ if (ret != NULL || errno == ENOMEM)
|
|
|
+ goto done;
|
|
|
+ }
|
|
|
+@@ -367,9 +339,9 @@
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+- * Not found? Do a breadth-first traversal of /dev/.
|
|
|
++ * Not found? Check all device nodes in /dev.
|
|
|
+ */
|
|
|
+- ret = sudo_ttyname_scan(_PATH_DEV, rdev, false, name, namelen);
|
|
|
++ ret = sudo_ttyname_scan(_PATH_DEV, rdev, name, namelen);
|
|
|
+
|
|
|
+ done:
|
|
|
+ debug_return_str(ret);
|
|
|
+@@ -493,28 +465,35 @@
|
|
|
+ len = getline(&line, &linesize, fp);
|
|
|
+ fclose(fp);
|
|
|
+ if (len != -1) {
|
|
|
+- /* Field 7 is the tty dev (0 if no tty) */
|
|
|
+- char *cp = line;
|
|
|
+- char *ep = line;
|
|
|
+- const char *errstr;
|
|
|
+- int field = 0;
|
|
|
+- while (*++ep != '\0') {
|
|
|
+- if (*ep == ' ') {
|
|
|
+- *ep = '\0';
|
|
|
+- if (++field == 7) {
|
|
|
+- dev_t tdev = strtonum(cp, INT_MIN, INT_MAX, &errstr);
|
|
|
+- if (errstr) {
|
|
|
+- sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
|
|
|
+- "%s: tty device %s: %s", path, cp, errstr);
|
|
|
++ /*
|
|
|
++ * Field 7 is the tty dev (0 if no tty).
|
|
|
++ * Since the process name at field 2 "(comm)" may include spaces,
|
|
|
++ * start at the last ')' found.
|
|
|
++ */
|
|
|
++ char *cp = strrchr(line, ')');
|
|
|
++ if (cp != NULL) {
|
|
|
++ char *ep = cp;
|
|
|
++ const char *errstr;
|
|
|
++ int field = 1;
|
|
|
++
|
|
|
++ while (*++ep != '\0') {
|
|
|
++ if (*ep == ' ') {
|
|
|
++ *ep = '\0';
|
|
|
++ if (++field == 7) {
|
|
|
++ dev_t tdev = strtonum(cp, INT_MIN, INT_MAX, &errstr);
|
|
|
++ if (errstr) {
|
|
|
++ sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
|
|
|
++ "%s: tty device %s: %s", path, cp, errstr);
|
|
|
++ }
|
|
|
++ if (tdev > 0) {
|
|
|
++ errno = serrno;
|
|
|
++ ret = sudo_ttyname_dev(tdev, name, namelen);
|
|
|
++ goto done;
|
|
|
++ }
|
|
|
++ break;
|
|
|
+ }
|
|
|
+- if (tdev > 0) {
|
|
|
+- errno = serrno;
|
|
|
+- ret = sudo_ttyname_dev(tdev, name, namelen);
|
|
|
+- goto done;
|
|
|
+- }
|
|
|
+- break;
|
|
|
++ cp = ep + 1;
|
|
|
+ }
|
|
|
+- cp = ep + 1;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|