From: Todd C. Miller Date: Tue, 30 May 2017 16:44:11 +0000 (-0600) Subject: Add a new "devsearch" Path setting to sudo.conf for configuring the X-Git-Tag: SUDO_1_8_21^2~64 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=cc71b99849c148986a144c02aa14d6c34f979536;p=sudo Add a new "devsearch" Path setting to sudo.conf for configuring the /dev paths to traverse instead of hard-coding a list in ttyname.c The default value can be set at configure time. --- diff --git a/INSTALL b/INSTALL index c674ac71a..e1d3ba658 100644 --- a/INSTALL +++ b/INSTALL @@ -368,6 +368,12 @@ Operating system-specific options: Enable the creation of an Ubuntu-style admin flag file the first time sudo is run. + --enable-devsearch=PATH + Set a system-specific search path of directories to look in + for device nodes. Sudo uses this when mapping the process's + tty device number to a device name. The default value is: + /dev/pts:/dev/vt:/dev/term:/dev/zcons:/dev/pty:/dev + --with-bsm-audit Enable support for sudo BSM audit logs on systems that support it. This includes recent versions of FreeBSD, Mac OS X and Solaris. diff --git a/configure b/configure old mode 100644 new mode 100755 index ae9d434c9..35f771853 --- a/configure +++ b/configure @@ -722,6 +722,7 @@ timeout vardir rundir iolog_dir +devsearch FILEDIGEST exampledir TMPFILES_D @@ -959,6 +960,7 @@ enable_rpath enable_static_sudoers enable_shared_libutil enable_tmpfiles_d +enable_devsearch with_selinux enable_gss_krb5_ccache_name enable_shared @@ -1641,6 +1643,8 @@ Optional Features: --disable-shared-libutil Disable use of the libsudo_util shared library. --enable-tmpfiles.d=DIR Set the path to the systemd tmpfiles.d directory. + --enable-devsearch=PATH The colon-delimited path to search for device nodes + when determing the tty name. --enable-gss-krb5-ccache-name Use GSS-API to set the Kerberos V cred cache name --enable-shared[=PKGS] build shared libraries [default=yes] @@ -3051,6 +3055,7 @@ $as_echo "$as_me: Configuring Sudo version $PACKAGE_VERSION" >&6;} + # @@ -3098,6 +3103,7 @@ pam_session=on pam_login_service=sudo PLUGINDIR=/usr/local/libexec/sudo FILEDIGEST=filedigest.lo +devsearch="/dev/pts:/dev/vt:/dev/term:/dev/zcons:/dev/pty:/dev" # # End initial values for man page substitution # @@ -6664,6 +6670,23 @@ else fi +# Check whether --enable-devsearch was given. +if test "${enable_devsearch+set}" = set; then : + enableval=$enable_devsearch; case $enableval in + yes) # use default value + ;; + no) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Ignoring attempt to disable the device search path" >&5 +$as_echo "$as_me: WARNING: Ignoring attempt to disable the device search path" >&2;} + ;; + *) devsearch="$enableval" +esac +fi + +cat >>confdefs.h < +.\" Copyright (c) 2010-2017 Todd C. Miller .\" .\" Permission to use, copy, modify, and distribute this software for any .\" purpose with or without fee is hereby granted, provided that the above @@ -16,7 +16,7 @@ .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.TH "SUDO.CONF" "5" "October 15, 2016" "Sudo @PACKAGE_VERSION@" "File Formats Manual" +.TH "SUDO.CONF" "5" "May 30, 2017" "Sudo @PACKAGE_VERSION@" "File Formats Manual" .nh .if n .ad l .SH "NAME" @@ -238,6 +238,21 @@ may be overridden by the \fRSUDO_ASKPASS\fR environment variable. .TP 10n +devsearch +.br +An ordered, colon-separated search path of directories to look in for +device nodes. +This is used when mapping the process's tty device number to a device name. +Sudo will +\fInot\fR +recurse into subdirectories. +If terminal devices may be located in a subdirectory of +\fI/dev\fR, +that path must be explicitly listed in +\fIdevsearch\fR. +The default value is: +\fR/dev/pts:/dev/vt:/dev/term:/dev/zcons:/dev/pty:/dev\fR +.TP 10n noexec The fully-qualified path to a shared library containing wrappers for the diff --git a/doc/sudo.conf.mdoc.in b/doc/sudo.conf.mdoc.in index f10d31cd3..013e3c03e 100644 --- a/doc/sudo.conf.mdoc.in +++ b/doc/sudo.conf.mdoc.in @@ -1,5 +1,5 @@ .\" -.\" Copyright (c) 2010-2016 Todd C. Miller +.\" Copyright (c) 2010-2017 Todd C. Miller .\" .\" Permission to use, copy, modify, and distribute this software for any .\" purpose with or without fee is hereby granted, provided that the above @@ -14,7 +14,7 @@ .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.Dd October 15, 2016 +.Dd May 30, 2017 .Dt SUDO.CONF @mansectform@ .Os Sudo @PACKAGE_VERSION@ .Sh NAME @@ -215,6 +215,19 @@ The value of may be overridden by the .Ev SUDO_ASKPASS environment variable. +.It devsearch +An ordered, colon-separated search path of directories to look in for +device nodes. +This is used when mapping the process's tty device number to a device name. +Sudo will +.Em not +recurse into subdirectories. +If terminal devices may be located in a subdirectory of +.Pa /dev , +that path must be explicitly listed in +.Em devsearch . +The default value is: +.Li /dev/pts:/dev/vt:/dev/term:/dev/zcons:/dev/pty:/dev .It noexec The fully-qualified path to a shared library containing wrappers for the diff --git a/include/sudo_conf.h b/include/sudo_conf.h index 14fe55f16..227c7149f 100644 --- a/include/sudo_conf.h +++ b/include/sudo_conf.h @@ -59,6 +59,7 @@ __dso_public const char *sudo_conf_askpass_path_v1(void); __dso_public const char *sudo_conf_sesh_path_v1(void); __dso_public const char *sudo_conf_noexec_path_v1(void); __dso_public const char *sudo_conf_plugin_dir_path_v1(void); +__dso_public const char *sudo_conf_devsearch_path_v1(void); __dso_public struct sudo_conf_debug_list *sudo_conf_debugging_v1(void); __dso_public struct sudo_conf_debug_file_list *sudo_conf_debug_files_v1(const char *progname); __dso_public struct plugin_info_list *sudo_conf_plugins_v1(void); @@ -71,6 +72,7 @@ __dso_public void sudo_conf_clear_paths_v1(void); #define sudo_conf_sesh_path() sudo_conf_sesh_path_v1() #define sudo_conf_noexec_path() sudo_conf_noexec_path_v1() #define sudo_conf_plugin_dir_path() sudo_conf_plugin_dir_path_v1() +#define sudo_conf_devsearch_path() sudo_conf_devsearch_path_v1() #define sudo_conf_debugging() sudo_conf_debugging_v1() #define sudo_conf_debug_files(_a) sudo_conf_debug_files_v1((_a)) #define sudo_conf_plugins() sudo_conf_plugins_v1() diff --git a/lib/util/sudo_conf.c b/lib/util/sudo_conf.c index 0b88badac..6b2418237 100644 --- a/lib/util/sudo_conf.c +++ b/lib/util/sudo_conf.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2016 Todd C. Miller + * Copyright (c) 2009-2017 Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -99,6 +99,7 @@ static struct sudo_conf_table sudo_conf_var_table[] = { #define SUDO_CONF_PATH_SESH 1 #define SUDO_CONF_PATH_NOEXEC 2 #define SUDO_CONF_PATH_PLUGIN_DIR 3 +#define SUDO_CONF_PATH_DEVSEARCH 4 static struct sudo_conf_data { bool disable_coredump; @@ -107,7 +108,7 @@ static struct sudo_conf_data { int max_groups; struct sudo_conf_debug_list debugging; struct plugin_info_list plugins; - struct sudo_conf_path_table path_table[5]; + struct sudo_conf_path_table path_table[6]; } sudo_conf_data = { true, true, @@ -120,6 +121,7 @@ static struct sudo_conf_data { { "sesh", sizeof("sesh") - 1, false, _PATH_SUDO_SESH }, { "noexec", sizeof("noexec") - 1, false, _PATH_SUDO_NOEXEC }, { "plugin_dir", sizeof("plugin_dir") - 1, false, _PATH_SUDO_PLUGIN_DIR }, + { "devsearch", sizeof("devsearch") - 1, false, _PATH_SUDO_DEVSEARCH }, { NULL } } }; @@ -451,6 +453,12 @@ sudo_conf_plugin_dir_path_v1(void) return sudo_conf_data.path_table[SUDO_CONF_PATH_PLUGIN_DIR].pval; } +const char * +sudo_conf_devsearch_path_v1(void) +{ + return sudo_conf_data.path_table[SUDO_CONF_PATH_DEVSEARCH].pval; +} + int sudo_conf_group_source_v1(void) { diff --git a/lib/util/util.exp.in b/lib/util/util.exp.in index 1582a18f7..3ad4d6e2f 100644 --- a/lib/util/util.exp.in +++ b/lib/util/util.exp.in @@ -7,6 +7,7 @@ sudo_conf_disable_coredump_v1 sudo_conf_group_source_v1 sudo_conf_max_groups_v1 sudo_conf_noexec_path_v1 +sudo_conf_devsearch_path_v1 sudo_conf_plugin_dir_path_v1 sudo_conf_plugins_v1 sudo_conf_probe_interfaces_v1 diff --git a/pathnames.h.in b/pathnames.h.in index 9ce3d9b07..7979f1129 100644 --- a/pathnames.h.in +++ b/pathnames.h.in @@ -123,6 +123,10 @@ # undef _PATH_SUDO_PLUGIN_DIR #endif /* _PATH_SUDO_PLUGIN_DIR */ +#ifndef _PATH_SUDO_DEVSEARCH +# undef _PATH_SUDO_DEVSEARCH +#endif /* _PATH_SUDO_DEVSEARCH */ + #ifndef _PATH_VI # undef _PATH_VI #endif /* _PATH_VI */ diff --git a/src/ttyname.c b/src/ttyname.c index 355c88606..f21d4411c 100644 --- a/src/ttyname.c +++ b/src/ttyname.c @@ -62,12 +62,6 @@ #include "sudo.h" -#if defined(HAVE_STRUCT_DIRENT_D_NAMLEN) && HAVE_STRUCT_DIRENT_D_NAMLEN -# define NAMLEN(dirent) (dirent)->d_namlen -#else -# define NAMLEN(dirent) strlen((dirent)->d_name) -#endif - /* * How to access the tty device number in struct kinfo_proc. */ @@ -145,22 +139,9 @@ sudo_ttyname_dev(dev_t tdev, char *name, size_t namelen) } #elif defined(HAVE_STRUCT_PSINFO_PR_TTYDEV) || defined(HAVE_PSTAT_GETPROC) || defined(__linux__) /* - * Device nodes and directories to search before searching all of /dev - */ -static char *search_devs[] = { - "/dev/console", - "/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 + * Device nodes to ignore. */ -static char *ignore_devs[] = { +static const char *ignore_devs[] = { "/dev/stdin", "/dev/stdout", "/dev/stderr", @@ -207,7 +188,7 @@ sudo_ttyname_scan(const char *dir, dev_t rdev, char *name, size_t namelen) "scanning for dev %u in %s", (unsigned int)rdev, dir); sdlen = strlen(dir); - if (dir[sdlen - 1] == '/') + while (sdlen > 0 && dir[sdlen - 1] == '/') sdlen--; if (sdlen + 1 >= sizeof(pathbuf)) { errno = ERANGE; @@ -215,32 +196,33 @@ sudo_ttyname_scan(const char *dir, dev_t rdev, char *name, size_t namelen) } memcpy(pathbuf, dir, sdlen); pathbuf[sdlen++] = '/'; - pathbuf[sdlen] = '\0'; while ((dp = readdir(d)) != NULL) { struct stat sb; - size_t d_len, len; /* Skip anything starting with "." */ if (dp->d_name[0] == '.') continue; - d_len = NAMLEN(dp); - if (sdlen + d_len >= sizeof(pathbuf)) + pathbuf[sdlen] = '\0'; + if (strlcat(pathbuf, dp->d_name, sizeof(pathbuf)) >= sizeof(pathbuf)) { + sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO, + "%s%s is too big to fit in pathbuf", pathbuf, dp->d_name); continue; - memcpy(&pathbuf[sdlen], dp->d_name, d_len + 1); /* copy NUL too */ - d_len += sdlen; + } + /* Ignore device nodes listed in ignore_devs[]. */ for (i = 0; ignore_devs[i] != NULL; i++) { - len = strlen(ignore_devs[i]); - if (ignore_devs[i][len - 1] == '/') - len--; - if (d_len == len && strncmp(pathbuf, ignore_devs[i], len) == 0) + if (strcmp(pathbuf, ignore_devs[i]) == 0) break; } - if (ignore_devs[i] != NULL) + if (ignore_devs[i] != NULL) { + sudo_debug_printf(SUDO_DEBUG_DEBUG|SUDO_DEBUG_LINENO, + "ignoring %s", pathbuf); continue; -# if defined(HAVE_STRUCT_DIRENT_D_TYPE) && defined(DTTOIF) + } + +# if defined(HAVE_STRUCT_DIRENT_D_TYPE) /* * Avoid excessive stat() calls by checking dp->d_type. */ @@ -248,18 +230,19 @@ sudo_ttyname_scan(const char *dir, dev_t rdev, char *name, size_t namelen) case DT_CHR: case DT_LNK: case DT_UNKNOWN: - /* Could be a character device, stat() it. */ - if (stat(pathbuf, &sb) == -1) - continue; break; default: /* Not a character device or link, skip it. */ + sudo_debug_printf(SUDO_DEBUG_DEBUG|SUDO_DEBUG_LINENO, + "skipping non-device %s", pathbuf); continue; } -# else - if (stat(pathbuf, &sb) == -1) - continue; # endif + if (stat(pathbuf, &sb) == -1) { + sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO, + "unable to stat %s", pathbuf); + 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); @@ -281,68 +264,89 @@ done: debug_return_str(ret); } +static char * +sudo_dev_check(dev_t rdev, const char *devname, char *buf, size_t buflen) +{ + struct stat sb; + debug_decl(sudo_dev_check, SUDO_DEBUG_UTIL) + + if (stat(devname, &sb) == 0) { + if (S_ISCHR(sb.st_mode) && sb.st_rdev == rdev) { + sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO, + "comparing dev %u to %s: match!", + (unsigned int)rdev, devname); + if (strlcpy(buf, devname, buflen) < buflen) + debug_return_str(buf); + sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO, + "unable to store %s, have %zu, need %zu", + devname, buflen, strlen(devname) + 1); + errno = ERANGE; + } + } + sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO, + "comparing dev %u to %s: no", (unsigned int)rdev, devname); + debug_return_str(NULL); +} + /* * Like ttyname() but uses a dev_t instead of an open fd. * Returns name on success and NULL on failure, setting errno. * Generic version. */ static char * -sudo_ttyname_dev(dev_t rdev, char *name, size_t namelen) +sudo_ttyname_dev(dev_t rdev, char *buf, size_t buflen) { - char buf[PATH_MAX], **sd, *devname; - char *ret = NULL; - struct stat sb; + const char *devsearch, *devsearch_end; + char path[PATH_MAX], *ret; + const char *cp, *ep; size_t len; debug_decl(sudo_ttyname_dev, SUDO_DEBUG_UTIL) /* - * First check search_devs[] for common tty devices. + * First, check /dev/console. + */ + ret = sudo_dev_check(rdev, "/dev/console", buf, buflen); + if (ret != NULL) + goto done; + + /* + * Then check the device search path. */ - for (sd = search_devs; (devname = *sd) != NULL; sd++) { - len = strlen(devname); - if (devname[len - 1] == '/') { - if (strcmp(devname, "/dev/pts/") == 0) { - /* Special case /dev/pts */ - (void)snprintf(buf, sizeof(buf), "%spts/%u", _PATH_DEV, + devsearch = sudo_conf_devsearch_path(); + devsearch_end = devsearch + strlen(devsearch); + for (cp = sudo_strsplit(devsearch, devsearch_end, ":", &ep); + cp != NULL; cp = sudo_strsplit(NULL, devsearch_end, ":", &ep)) { + + len = (size_t)(ep - cp); + if (len >= sizeof(path)) { + sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO, + "devsearch entry %.*s too long", (int)len, cp); + continue; + } + memcpy(path, cp, len); + path[len] = '\0'; + + if (strcmp(path, "/dev/pts") == 0) { + /* Special case /dev/pts */ + len = (size_t)snprintf(path, sizeof(path), "/dev/pts/%u", + (unsigned int)minor(rdev)); + if (len >= sizeof(path)) { + sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO, + "devsearch entry /dev/pts/%u too long", (unsigned int)minor(rdev)); - if (stat(buf, &sb) == 0) { - if (S_ISCHR(sb.st_mode) && sb.st_rdev == rdev) { - sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO, - "comparing dev %u to %s: match!", - (unsigned int)rdev, buf); - if (strlcpy(name, buf, namelen) < namelen) - ret = name; - else - errno = ERANGE; - goto done; - } - } - sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO, - "comparing dev %u to %s: no", (unsigned int)rdev, buf); - } else { - /* Traverse directory */ - ret = sudo_ttyname_scan(devname, rdev, name, namelen); - if (ret != NULL || errno == ENOMEM) - goto done; + continue; } + ret = sudo_dev_check(rdev, path, buf, buflen); + if (ret != NULL) + goto done; } else { - if (stat(devname, &sb) == 0) { - if (S_ISCHR(sb.st_mode) && sb.st_rdev == rdev) { - if (strlcpy(name, devname, namelen) < namelen) - ret = name; - else - errno = ERANGE; - goto done; - } - } + /* Scan path, looking for rdev. */ + ret = sudo_ttyname_scan(path, rdev, buf, buflen); + if (ret != NULL || errno == ENOMEM) + goto done; } } - /* - * Not found? Check all device nodes in /dev. - */ - ret = sudo_ttyname_scan(_PATH_DEV, rdev, name, namelen); - done: debug_return_str(ret); }