From 83fc02bc97917fafc38a6a2b1033f7d750ccd12d Mon Sep 17 00:00:00 2001 From: "Todd C. Miller" Date: Wed, 11 Apr 2012 14:48:08 -0400 Subject: [PATCH] Rototill code to determine the tty. For Linux, we now look up the tty device in /proc/pid/stat instead of trying to open /proc/pid/fd/[0-2]. The sudo_ttyname_dev() function maps the given device number to a string. On BSD, we can use devname(). On Solaris, _ttyname_dev() does what we want. TODO: write /dev/ traversal code for the generic sudo_ttyname_dev(). --- config.h.in | 3 + configure | 28 ++++-- configure.in | 4 +- src/ttyname.c | 234 ++++++++++++++++++++++++++++++++++++-------------- 4 files changed, 192 insertions(+), 77 deletions(-) diff --git a/config.h.in b/config.h.in index 05f14b5fc..0574a4df7 100644 --- a/config.h.in +++ b/config.h.in @@ -666,6 +666,9 @@ /* Define to 1 if you have the `_innetgr' function. */ #undef HAVE__INNETGR +/* Define to 1 if you have the `_ttyname_dev' function. */ +#undef HAVE__TTYNAME_DEV + /* Define to 1 if the compiler supports the C99 __func__ variable. */ #undef HAVE___FUNC__ diff --git a/configure b/configure index 134c19d6f..aa3cd3041 100755 --- a/configure +++ b/configure @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.68 for sudo 1.8.5. +# Generated by GNU Autoconf 2.68 for sudo 1.8.5b8. # # Report bugs to . # @@ -570,8 +570,8 @@ MAKEFLAGS= # Identity of this package. PACKAGE_NAME='sudo' PACKAGE_TARNAME='sudo' -PACKAGE_VERSION='1.8.5' -PACKAGE_STRING='sudo 1.8.5' +PACKAGE_VERSION='1.8.5b8' +PACKAGE_STRING='sudo 1.8.5b8' PACKAGE_BUGREPORT='http://www.sudo.ws/bugs/' PACKAGE_URL='' @@ -1447,7 +1447,7 @@ if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures sudo 1.8.5 to adapt to many kinds of systems. +\`configure' configures sudo 1.8.5b8 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1512,7 +1512,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of sudo 1.8.5:";; + short | recursive ) echo "Configuration of sudo 1.8.5b8:";; esac cat <<\_ACEOF @@ -1730,7 +1730,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -sudo configure 1.8.5 +sudo configure 1.8.5b8 generated by GNU Autoconf 2.68 Copyright (C) 2010 Free Software Foundation, Inc. @@ -2434,7 +2434,7 @@ cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by sudo $as_me 1.8.5, which was +It was created by sudo $as_me 1.8.5b8, which was generated by GNU Autoconf 2.68. Invocation command line was $ $0 $@ @@ -15271,6 +15271,16 @@ cat >>confdefs.h <<_ACEOF #define HAVE_STRUCT_PSINFO_PR_TTYDEV 1 _ACEOF +for ac_func in _ttyname_dev +do : + ac_fn_c_check_func "$LINENO" "_ttyname_dev" "ac_cv_func__ttyname_dev" +if test "x$ac_cv_func__ttyname_dev" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE__TTYNAME_DEV 1 +_ACEOF + +fi +done fi @@ -20608,7 +20618,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by sudo $as_me 1.8.5, which was +This file was extended by sudo $as_me 1.8.5b8, which was generated by GNU Autoconf 2.68. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -20674,7 +20684,7 @@ _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -sudo config.status 1.8.5 +sudo config.status 1.8.5b8 configured by $0, generated by GNU Autoconf 2.68, with options \\"\$ac_cs_config\\" diff --git a/configure.in b/configure.in index bedf548ad..1cbc83664 100644 --- a/configure.in +++ b/configure.in @@ -3,7 +3,7 @@ dnl Process this file with GNU autoconf to produce a configure script. dnl dnl Copyright (c) 1994-1996,1998-2012 Todd C. Miller dnl -AC_INIT([sudo], [1.8.5], [http://www.sudo.ws/bugs/], [sudo]) +AC_INIT([sudo], [1.8.5b8], [http://www.sudo.ws/bugs/], [sudo]) AC_CONFIG_HEADER([config.h pathnames.h]) dnl dnl Note: this must come after AC_INIT @@ -2007,7 +2007,7 @@ AC_HEADER_DIRENT AC_HEADER_TIME AC_HEADER_STDBOOL AC_CHECK_HEADERS(malloc.h netgroup.h paths.h spawn.h utime.h utmpx.h sys/sockio.h sys/bsdtypes.h sys/select.h sys/stropts.h sys/sysmacros.h) -AC_CHECK_HEADERS([procfs.h] [sys/procfs.h], [AC_CHECK_MEMBERS(struct psinfo.pr_ttydev, [], [], [AC_INCLUDES_DEFAULT +AC_CHECK_HEADERS([procfs.h] [sys/procfs.h], [AC_CHECK_MEMBERS(struct psinfo.pr_ttydev, [AC_CHECK_FUNCS(_ttyname_dev)], [], [AC_INCLUDES_DEFAULT #ifdef HAVE_PROCFS_H #include #endif diff --git a/src/ttyname.c b/src/ttyname.c index 395cba823..f7d119082 100644 --- a/src/ttyname.c +++ b/src/ttyname.c @@ -51,6 +51,7 @@ #endif /* HAVE_UNISTD_H */ #include #include +#include #if defined(HAVE_STRUCT_KINFO_PROC_P_TDEV) || defined (HAVE_STRUCT_KINFO_PROC_KP_EPROC_E_TDEV) || defined(HAVE_STRUCT_KINFO_PROC2_P_TDEV) # include #elif defined(HAVE_STRUCT_KINFO_PROC_KI_TDEV) @@ -90,6 +91,86 @@ # define sudo_kp_namelen 4 #endif +#if defined(sudo_kp_tdev) +/* + * Like ttyname() but uses a dev_t instead of an open fd. + * Caller is responsible for freeing the returned string. + * The BSD version uses devname() + */ +static char * +sudo_ttyname_dev(dev_t tdev) +{ + char *dev, *tty = NULL; + debug_decl(sudo_ttyname_dev, SUDO_DEBUG_UTIL) + + /* Some versions of devname() return NULL on failure, others do not. */ + dev = devname(tdev, S_IFCHR); + if (dev != NULL && *dev != '?' && *dev != '#') { + if (*dev != '/') { + /* devname() doesn't use the /dev/ prefix, add one... */ + size_t len = sizeof(_PATH_DEV) + strlen(dev); + tty = emalloc(len); + strlcpy(tty, _PATH_DEV, len); + strlcat(tty, dev, len); + } else { + /* Should not happen but just in case... */ + tty = estrdup(dev); + } + } + debug_return_str(tty); +} +#elif defined(HAVE__TTYNAME_DEV) +extern char *_ttyname_dev(dev_t rdev, char *buffer, size_t buflen); + +/* + * Like ttyname() but uses a dev_t instead of an open fd. + * Caller is responsible for freeing the returned string. + * This version is just a wrapper around _ttyname_dev(). + */ +static char * +sudo_ttyname_dev(dev_t tdev) +{ + char buf[TTYNAME_MAX], *tty; + struct stat sb; + debug_decl(sudo_ttyname_dev, SUDO_DEBUG_UTIL) + + /* Check if it is a pseudo-tty slave, falling back on _ttyname_dev() */ + (void)snprintf(buf, sizeof(buf), "%spts/%u", _PATH_DEV, + (unsigned int)minor(tdev)); + if (stat(buf, &sb) == 0 && sb.st_rdev == tdev) { + tty = buf; + } else { + tty = _ttyname_dev(tdev, buf, sizeof(buf)); + } + + debug_return_str(estrdup(tty)); +} +#else +/* + * Like ttyname() but uses a dev_t instead of an open fd. + * Caller is responsible for freeing the returned string. + * Generic version. + */ +static char * +sudo_ttyname_dev(dev_t tdev) +{ + char buf[PATH_MAX], *tty = NULL; + struct stat sb; + debug_decl(sudo_ttyname_dev, SUDO_DEBUG_UTIL) + + /* First, check if it is a pseudo-tty slave. */ + (void)snprintf(buf, sizeof(buf), "%spts/%u", _PATH_DEV, + (unsigned int)minor(tdev)); + if (stat(buf, &sb) == 0 && sb.st_rdev == tdev) { + tty = buf; + } else { + /* XXX - fallback to scanning /dev/ */ + } + + debug_return_str(estrdup(tty)); +} +#endif + #if defined(sudo_kp_tdev) /* * Return a string from ttyname() containing the tty to which the process is @@ -123,21 +204,13 @@ get_process_ttyname(void) rc = sysctl(mib, sudo_kp_namelen, ki_proc, &size, NULL, 0); } while (rc == -1 && errno == ENOMEM); if (rc != -1) { - char *dev = devname(ki_proc->sudo_kp_tdev, S_IFCHR); - /* Some versions of devname() return NULL, others do not. */ - if (dev == NULL || *dev == '?' || *dev == '#') { - sudo_debug_printf(SUDO_DEBUG_WARN, - "unable to map device number %u to name", - ki_proc->sudo_kp_tdev); - } else if (*dev != '/') { - /* devname() doesn't use the /dev/ prefix, add one... */ - size_t len = sizeof(_PATH_DEV) + strlen(dev); - tty = emalloc(len); - strlcpy(tty, _PATH_DEV, len); - strlcat(tty, dev, len); - } else { - /* Should not happen but just in case... */ - tty = estrdup(dev); + if (ki_proc->sudo_kp_tdev != (dev_t)-1) { + tty = sudo_ttyname_dev(ki_proc->sudo_kp_tdev); + if (tty == NULL) { + sudo_debug_printf(SUDO_DEBUG_WARN, + "unable to map device number %u to name", + ki_proc->sudo_kp_tdev); + } } } else { sudo_debug_printf(SUDO_DEBUG_WARN, @@ -157,14 +230,11 @@ get_process_ttyname(void) debug_return_str(tty); } #elif defined(HAVE_STRUCT_PSINFO_PR_TTYDEV) -# ifndef PRNODEV -# define PRNODEV ((dev_t)-1) -# endif /* * Return a string from ttyname() containing the tty to which the process is * attached or NULL if there is no tty associated with the process (or its - * parent). First tries std{in,out,err} then falls back to our /proc entry, - * or our parent's if that doesn't work. + * parent). First tries /proc/pid/psinfo, then /proc/ppid/psinfo. + * Falls back on ttyname of std{in,out,err} if that fails. */ char * get_process_ttyname(void) @@ -176,68 +246,100 @@ get_process_ttyname(void) int i, fd; debug_decl(get_process_ttyname, SUDO_DEBUG_UTIL) - if ((tty = ttyname(STDIN_FILENO)) == NULL && - (tty = ttyname(STDOUT_FILENO)) == NULL && - (tty = ttyname(STDERR_FILENO)) == NULL) { - /* - * No tty hooked up to std{in,out,err}, check /proc. - * We try to map pr_ttydev in psinfo to /dev/pts/N - */ - for (i = 0; tty == NULL && i < 2; i++) { - snprintf(path, sizeof(path), "/proc/%u/psinfo", - i ? (unsigned int)getppid() : (unsigned int)getpid()); - if ((fd = open(path, O_RDONLY, 0)) == -1) - continue; - nread = read(fd, &psinfo, sizeof(psinfo)); - close(fd); - if (nread != (ssize_t)sizeof(psinfo) || psinfo.pr_ttydev == PRNODEV) - continue; - (void)snprintf(path, sizeof(path), "%spts/%u", _PATH_DEV, - (unsigned int)minor(psinfo.pr_ttydev)); - if (stat(path, &sb) == 0 && sb.st_rdev == psinfo.pr_ttydev) { - fd = open(path, O_RDONLY|O_NOCTTY|O_NONBLOCK, 0); - if (fd != -1) { - tty = ttyname(fd); - close(fd); - } - } + /* Try to determine the tty from pr_ttydev in /proc/pid/psinfo. */ + for (i = 0; tty == NULL && i < 2; i++) { + (void)snprintf(path, sizeof(path), "/proc/%u/psinfo", + i ? (unsigned int)getppid() : (unsigned int)getpid()); + if ((fd = open(path, O_RDONLY, 0)) == -1) + continue; + nread = read(fd, &psinfo, sizeof(psinfo)); + close(fd); + if (nread == (ssize_t)sizeof(psinfo) && psinfo.pr_ttydev != (dev_t)-1) { + tty = sudo_ttyname_dev(psinfo.pr_ttydev); } } - debug_return_str(estrdup(tty)); + /* If all else fails, fall back on ttyname(). */ + if (tty == NULL) { + if ((tty = ttyname(STDIN_FILENO)) != NULL || + (tty = ttyname(STDOUT_FILENO)) != NULL || + (tty = ttyname(STDERR_FILENO)) != NULL) + tty = estrdup(tty); + } + + debug_return_str(tty); } -#else +#elif defined(__linux__) /* * Return a string from ttyname() containing the tty to which the process is * attached or NULL if there is no tty associated with the process (or its - * parent). First tries std{in,out,err} then falls back to our parent's /proc - * entry. + * parent). First tries field 7 in /proc/pid/stat, then /proc/ppid/stat. + * Falls back on ttyname of std{in,out,err} if that fails. */ char * get_process_ttyname(void) { - char path[PATH_MAX], *tty = NULL; - struct stat sb; - pid_t ppid; - int i, fd; + char *line = NULL, *tty = NULL; + size_t linesize = 0; + ssize_t len; + int i; debug_decl(get_process_ttyname, SUDO_DEBUG_UTIL) - if ((tty = ttyname(STDIN_FILENO)) == NULL && - (tty = ttyname(STDOUT_FILENO)) == NULL && - (tty = ttyname(STDERR_FILENO)) == NULL) { - /* No tty for child, check the parent via /proc. */ - ppid = getppid(); - for (i = STDIN_FILENO; i <= STDERR_FILENO && tty == NULL; i++) { - snprintf(path, sizeof(path), "/proc/%u/fd/%d", - (unsigned int)ppid, i); - fd = open(path, O_RDONLY|O_NOCTTY|O_NONBLOCK, 0); - if (fd != -1) { - tty = ttyname(fd); - close(fd); + /* Try to determine the tty from pr_ttydev in /proc/pid/psinfo. */ + for (i = 0; tty == NULL && i < 2; i++) { + FILE *fp; + char path[PATH_MAX]; + (void)snprintf(path, sizeof(path), "/proc/%u/stat", + i ? (unsigned int)getppid() : (unsigned int)getpid()); + if ((fp = fopen(path, "r")) == NULL) + continue; + len = getline(&line, &linesize, fp); + fclose(fp); + if (len != -1) { + /* Field 7 is the tty dev (0 if no tty) */ + char *cp = line; + int field = 1; + while (*cp != '\0') { + if (*cp++ == ' ') { + if (++field == 7) { + dev_t tdev = (dev_t)atoi(cp); + if (tdev > 0) + tty = sudo_ttyname_dev(tdev); + break; + } + } } } } + efree(line); + + /* If all else fails, fall back on ttyname(). */ + if (tty == NULL) { + if ((tty = ttyname(STDIN_FILENO)) != NULL || + (tty = ttyname(STDOUT_FILENO)) != NULL || + (tty = ttyname(STDERR_FILENO)) != NULL) + tty = estrdup(tty); + } + + debug_return_str(tty); +} +#else +/* + * Return a string from ttyname() containing the tty to which the process is + * attached or NULL if there is no tty associated with the process. + * parent). + */ +char * +get_process_ttyname(void) +{ + char *tty; + debug_decl(get_process_ttyname, SUDO_DEBUG_UTIL) + + if ((tty = ttyname(STDIN_FILENO)) == NULL) { + if ((tty = ttyname(STDOUT_FILENO)) == NULL) + tty = ttyname(STDERR_FILENO); + } debug_return_str(estrdup(tty)); } -#endif /* !sudo_kp_tdev && !HAVE_STRUCT_PSINFO_PR_TTYDEV */ +#endif -- 2.40.0