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().
This commit is contained in:
Todd C. Miller
2012-04-11 14:48:08 -04:00
parent 271f3e2054
commit 83fc02bc97
4 changed files with 192 additions and 77 deletions

View File

@@ -666,6 +666,9 @@
/* Define to 1 if you have the `_innetgr' function. */ /* Define to 1 if you have the `_innetgr' function. */
#undef HAVE__INNETGR #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. */ /* Define to 1 if the compiler supports the C99 __func__ variable. */
#undef HAVE___FUNC__ #undef HAVE___FUNC__

28
configure vendored
View File

@@ -1,6 +1,6 @@
#! /bin/sh #! /bin/sh
# Guess values for system-dependent variables and create Makefiles. # 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 <http://www.sudo.ws/bugs/>. # Report bugs to <http://www.sudo.ws/bugs/>.
# #
@@ -570,8 +570,8 @@ MAKEFLAGS=
# Identity of this package. # Identity of this package.
PACKAGE_NAME='sudo' PACKAGE_NAME='sudo'
PACKAGE_TARNAME='sudo' PACKAGE_TARNAME='sudo'
PACKAGE_VERSION='1.8.5' PACKAGE_VERSION='1.8.5b8'
PACKAGE_STRING='sudo 1.8.5' PACKAGE_STRING='sudo 1.8.5b8'
PACKAGE_BUGREPORT='http://www.sudo.ws/bugs/' PACKAGE_BUGREPORT='http://www.sudo.ws/bugs/'
PACKAGE_URL='' 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. # 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. # This message is too long to be a string in the A/UX 3.1 sh.
cat <<_ACEOF 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]... Usage: $0 [OPTION]... [VAR=VALUE]...
@@ -1512,7 +1512,7 @@ fi
if test -n "$ac_init_help"; then if test -n "$ac_init_help"; then
case $ac_init_help in case $ac_init_help in
short | recursive ) echo "Configuration of sudo 1.8.5:";; short | recursive ) echo "Configuration of sudo 1.8.5b8:";;
esac esac
cat <<\_ACEOF cat <<\_ACEOF
@@ -1730,7 +1730,7 @@ fi
test -n "$ac_init_help" && exit $ac_status test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then if $ac_init_version; then
cat <<\_ACEOF cat <<\_ACEOF
sudo configure 1.8.5 sudo configure 1.8.5b8
generated by GNU Autoconf 2.68 generated by GNU Autoconf 2.68
Copyright (C) 2010 Free Software Foundation, Inc. Copyright (C) 2010 Free Software Foundation, Inc.
@@ -2434,7 +2434,7 @@ cat >config.log <<_ACEOF
This file contains any messages produced by compilers while This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake. 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 generated by GNU Autoconf 2.68. Invocation command line was
$ $0 $@ $ $0 $@
@@ -15271,6 +15271,16 @@ cat >>confdefs.h <<_ACEOF
#define HAVE_STRUCT_PSINFO_PR_TTYDEV 1 #define HAVE_STRUCT_PSINFO_PR_TTYDEV 1
_ACEOF _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 fi
@@ -20608,7 +20618,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
# report actual input values of CONFIG_FILES etc. instead of their # report actual input values of CONFIG_FILES etc. instead of their
# values after options handling. # values after options handling.
ac_log=" 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 generated by GNU Autoconf 2.68. Invocation command line was
CONFIG_FILES = $CONFIG_FILES CONFIG_FILES = $CONFIG_FILES
@@ -20674,7 +20684,7 @@ _ACEOF
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
ac_cs_version="\\ ac_cs_version="\\
sudo config.status 1.8.5 sudo config.status 1.8.5b8
configured by $0, generated by GNU Autoconf 2.68, configured by $0, generated by GNU Autoconf 2.68,
with options \\"\$ac_cs_config\\" with options \\"\$ac_cs_config\\"

View File

@@ -3,7 +3,7 @@ dnl Process this file with GNU autoconf to produce a configure script.
dnl dnl
dnl Copyright (c) 1994-1996,1998-2012 Todd C. Miller <Todd.Miller@courtesan.com> dnl Copyright (c) 1994-1996,1998-2012 Todd C. Miller <Todd.Miller@courtesan.com>
dnl 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]) AC_CONFIG_HEADER([config.h pathnames.h])
dnl dnl
dnl Note: this must come after AC_INIT dnl Note: this must come after AC_INIT
@@ -2007,7 +2007,7 @@ AC_HEADER_DIRENT
AC_HEADER_TIME AC_HEADER_TIME
AC_HEADER_STDBOOL 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(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 #ifdef HAVE_PROCFS_H
#include <procfs.h> #include <procfs.h>
#endif #endif

View File

@@ -51,6 +51,7 @@
#endif /* HAVE_UNISTD_H */ #endif /* HAVE_UNISTD_H */
#include <errno.h> #include <errno.h>
#include <fcntl.h> #include <fcntl.h>
#include <limits.h>
#if defined(HAVE_STRUCT_KINFO_PROC_P_TDEV) || defined (HAVE_STRUCT_KINFO_PROC_KP_EPROC_E_TDEV) || defined(HAVE_STRUCT_KINFO_PROC2_P_TDEV) #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 <sys/sysctl.h> # include <sys/sysctl.h>
#elif defined(HAVE_STRUCT_KINFO_PROC_KI_TDEV) #elif defined(HAVE_STRUCT_KINFO_PROC_KI_TDEV)
@@ -90,6 +91,86 @@
# define sudo_kp_namelen 4 # define sudo_kp_namelen 4
#endif #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) #if defined(sudo_kp_tdev)
/* /*
* Return a string from ttyname() containing the tty to which the process is * 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); rc = sysctl(mib, sudo_kp_namelen, ki_proc, &size, NULL, 0);
} while (rc == -1 && errno == ENOMEM); } while (rc == -1 && errno == ENOMEM);
if (rc != -1) { if (rc != -1) {
char *dev = devname(ki_proc->sudo_kp_tdev, S_IFCHR); if (ki_proc->sudo_kp_tdev != (dev_t)-1) {
/* Some versions of devname() return NULL, others do not. */ tty = sudo_ttyname_dev(ki_proc->sudo_kp_tdev);
if (dev == NULL || *dev == '?' || *dev == '#') { if (tty == NULL) {
sudo_debug_printf(SUDO_DEBUG_WARN, sudo_debug_printf(SUDO_DEBUG_WARN,
"unable to map device number %u to name", "unable to map device number %u to name",
ki_proc->sudo_kp_tdev); 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);
} }
} else { } else {
sudo_debug_printf(SUDO_DEBUG_WARN, sudo_debug_printf(SUDO_DEBUG_WARN,
@@ -157,14 +230,11 @@ get_process_ttyname(void)
debug_return_str(tty); debug_return_str(tty);
} }
#elif defined(HAVE_STRUCT_PSINFO_PR_TTYDEV) #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 * 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 * 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, * parent). First tries /proc/pid/psinfo, then /proc/ppid/psinfo.
* or our parent's if that doesn't work. * Falls back on ttyname of std{in,out,err} if that fails.
*/ */
char * char *
get_process_ttyname(void) get_process_ttyname(void)
@@ -176,68 +246,100 @@ get_process_ttyname(void)
int i, fd; int i, fd;
debug_decl(get_process_ttyname, SUDO_DEBUG_UTIL) debug_decl(get_process_ttyname, SUDO_DEBUG_UTIL)
if ((tty = ttyname(STDIN_FILENO)) == NULL && /* Try to determine the tty from pr_ttydev in /proc/pid/psinfo. */
(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++) { for (i = 0; tty == NULL && i < 2; i++) {
snprintf(path, sizeof(path), "/proc/%u/psinfo", (void)snprintf(path, sizeof(path), "/proc/%u/psinfo",
i ? (unsigned int)getppid() : (unsigned int)getpid()); i ? (unsigned int)getppid() : (unsigned int)getpid());
if ((fd = open(path, O_RDONLY, 0)) == -1) if ((fd = open(path, O_RDONLY, 0)) == -1)
continue; continue;
nread = read(fd, &psinfo, sizeof(psinfo)); nread = read(fd, &psinfo, sizeof(psinfo));
close(fd); close(fd);
if (nread != (ssize_t)sizeof(psinfo) || psinfo.pr_ttydev == PRNODEV) if (nread == (ssize_t)sizeof(psinfo) && psinfo.pr_ttydev != (dev_t)-1) {
continue; tty = sudo_ttyname_dev(psinfo.pr_ttydev);
(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);
}
}
} }
} }
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);
} }
#else
debug_return_str(tty);
}
#elif defined(__linux__)
/* /*
* Return a string from ttyname() containing the tty to which the process is * 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 * 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 * parent). First tries field 7 in /proc/pid/stat, then /proc/ppid/stat.
* entry. * Falls back on ttyname of std{in,out,err} if that fails.
*/ */
char * char *
get_process_ttyname(void) get_process_ttyname(void)
{ {
char path[PATH_MAX], *tty = NULL; char *line = NULL, *tty = NULL;
struct stat sb; size_t linesize = 0;
pid_t ppid; ssize_t len;
int i, fd; int i;
debug_decl(get_process_ttyname, SUDO_DEBUG_UTIL) debug_decl(get_process_ttyname, SUDO_DEBUG_UTIL)
if ((tty = ttyname(STDIN_FILENO)) == NULL && /* Try to determine the tty from pr_ttydev in /proc/pid/psinfo. */
(tty = ttyname(STDOUT_FILENO)) == NULL && for (i = 0; tty == NULL && i < 2; i++) {
(tty = ttyname(STDERR_FILENO)) == NULL) { FILE *fp;
/* No tty for child, check the parent via /proc. */ char path[PATH_MAX];
ppid = getppid(); (void)snprintf(path, sizeof(path), "/proc/%u/stat",
for (i = STDIN_FILENO; i <= STDERR_FILENO && tty == NULL; i++) { i ? (unsigned int)getppid() : (unsigned int)getpid());
snprintf(path, sizeof(path), "/proc/%u/fd/%d", if ((fp = fopen(path, "r")) == NULL)
(unsigned int)ppid, i); continue;
fd = open(path, O_RDONLY|O_NOCTTY|O_NONBLOCK, 0); len = getline(&line, &linesize, fp);
if (fd != -1) { fclose(fp);
tty = ttyname(fd); if (len != -1) {
close(fd); /* 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)); debug_return_str(estrdup(tty));
} }
#endif /* !sudo_kp_tdev && !HAVE_STRUCT_PSINFO_PR_TTYDEV */ #endif