From 98c01feabde0178c87af811e9d67d175d3063fad Mon Sep 17 00:00:00 2001 From: "Todd C. Miller" Date: Thu, 12 Jan 2012 15:17:30 -0500 Subject: [PATCH] When trying to determine the tty, fall back on /proc/ppid/fd/{0,1,2} if the main process's fds 0-2 are not hooked up to a tty. On BSD, use the KERN_PROC_PID sysctl() instead. --HG-- branch : 1.7 --- config.h.in | 9 ++++ configure | 63 +++++++++++++++++++++++++- configure.in | 23 +++++++++- sudo.c | 125 +++++++++++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 215 insertions(+), 5 deletions(-) diff --git a/config.h.in b/config.h.in index f3413479a..c796855bc 100644 --- a/config.h.in +++ b/config.h.in @@ -516,6 +516,15 @@ /* Define to 1 if the system has the type `struct in6_addr'. */ #undef HAVE_STRUCT_IN6_ADDR +/* Define to 1 if `ki_tdev' is a member of `struct kinfo_proc'. */ +#undef HAVE_STRUCT_KINFO_PROC_KI_TDEV + +/* Define to 1 if `kp_eproc.e_tdev' is a member of `struct kinfo_proc'. */ +#undef HAVE_STRUCT_KINFO_PROC_KP_EPROC_E_TDEV + +/* Define to 1 if `p_tdev' is a member of `struct kinfo_proc'. */ +#undef HAVE_STRUCT_KINFO_PROC_P_TDEV + /* Define to 1 if the system has the type `struct timespec'. */ #undef HAVE_STRUCT_TIMESPEC diff --git a/configure b/configure index 8c19e5c09..64d074f56 100755 --- a/configure +++ b/configure @@ -16145,7 +16145,7 @@ fi done -for ac_func in sysctl getutxid getutid +for ac_func in getutxid getutid do : as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" @@ -16158,6 +16158,67 @@ fi done +for ac_func in sysctl +do : + ac_fn_c_check_func "$LINENO" "sysctl" "ac_cv_func_sysctl" +if test "x$ac_cv_func_sysctl" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_SYSCTL 1 +_ACEOF + ac_fn_c_check_member "$LINENO" "struct kinfo_proc" "ki_tdev" "ac_cv_member_struct_kinfo_proc_ki_tdev" " + #include + #include + #include + +" +if test "x$ac_cv_member_struct_kinfo_proc_ki_tdev" = xyes; then : + +cat >>confdefs.h <<_ACEOF +#define HAVE_STRUCT_KINFO_PROC_KI_TDEV 1 +_ACEOF + + +else + + ac_fn_c_check_member "$LINENO" "struct kinfo_proc" "kp_eproc.e_tdev" "ac_cv_member_struct_kinfo_proc_kp_eproc_e_tdev" " + #include + #include + +" +if test "x$ac_cv_member_struct_kinfo_proc_kp_eproc_e_tdev" = xyes; then : + +cat >>confdefs.h <<_ACEOF +#define HAVE_STRUCT_KINFO_PROC_KP_EPROC_E_TDEV 1 +_ACEOF + + +else + + ac_fn_c_check_member "$LINENO" "struct kinfo_proc" "p_tdev" "ac_cv_member_struct_kinfo_proc_p_tdev" " + #include + #include + +" +if test "x$ac_cv_member_struct_kinfo_proc_p_tdev" = xyes; then : + +cat >>confdefs.h <<_ACEOF +#define HAVE_STRUCT_KINFO_PROC_P_TDEV 1 +_ACEOF + + +fi + + +fi + + +fi + + +fi +done + + for ac_func in openpty do : ac_fn_c_check_func "$LINENO" "openpty" "ac_cv_func_openpty" diff --git a/configure.in b/configure.in index ccce9ba9c..1fff13250 100644 --- a/configure.in +++ b/configure.in @@ -2022,7 +2022,28 @@ AC_CHECK_FUNCS(setsid, [], [ AC_FUNC_SETPGRP ]) -AC_CHECK_FUNCS(sysctl getutxid getutid, [break]) +AC_CHECK_FUNCS(getutxid getutid, [break]) + +AC_CHECK_FUNCS(sysctl, [AC_CHECK_MEMBERS([struct kinfo_proc.ki_tdev], + [], + [ + AC_CHECK_MEMBERS([struct kinfo_proc.kp_eproc.e_tdev], [], [ + AC_CHECK_MEMBERS([struct kinfo_proc.p_tdev], [], [], [ + #include + #include + ]) + ], + [ + #include + #include + ]) + ], + [ + #include + #include + #include + ]) +]) AC_CHECK_FUNCS(openpty, [AC_CHECK_HEADERS(libutil.h util.h pty.h, [break])], [ AC_CHECK_LIB(util, openpty, [ diff --git a/sudo.c b/sudo.c index 287f551af..bdd64df44 100644 --- a/sudo.c +++ b/sudo.c @@ -91,6 +91,14 @@ #ifdef HAVE_MBR_CHECK_MEMBERSHIP # include #endif +#if defined(HAVE_STRUCT_KINFO_PROC_P_TDEV) || defined (HAVE_STRUCT_KINFO_PROC_KP_EPROC_E_TDEV) +# include +#else +# if defined(HAVE_STRUCT_KINFO_PROC_KI_TDEV) +# include +# include +# endif +#endif #include "sudo.h" #include "lbuf.h" @@ -574,6 +582,118 @@ main(argc, argv, envp) exit(0); /* not reached */ } +/* + * How to access the tty device number in struct kinfo_proc. + */ +#if defined(HAVE_STRUCT_KINFO_PROC_KP_EPROC_E_TDEV) +# define sudo_kp_tdev kp_eproc.e_tdev +# define sudo_kp_namelen 4 +#else +# if defined(HAVE_STRUCT_KINFO_PROC_KI_TDEV) +# define sudo_kp_tdev ki_tdev +# define sudo_kp_namelen 4 +# else +# if defined(HAVE_STRUCT_KINFO_PROC_P_TDEV) +# define sudo_kp_tdev p_tdev +# define sudo_kp_namelen 6 +# endif +# endif +#endif + +#ifdef sudo_kp_tdev +/* + * 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 sysctl using the current pid, then the parent's pid. + * Falls back on ttyname of std{in,out,err} if that fails. + */ +static char * +get_process_tty(void) +{ + char *tty = NULL; + struct kinfo_proc *ki_proc = NULL; + size_t size = sizeof(*ki_proc); + int i, mib[6], rc; + + /* + * Lookup tty for this process and, failing that, our parent. + * Even if we redirect std{in,out,err} the kernel should still know. + */ + for (i = 0; tty == NULL && i < 2; i++) { + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_PID; + mib[3] = i ? (int)getppid() : (int)getpid(); + mib[4] = sizeof(*ki_proc); + mib[5] = 1; + do { + size += size / 10; + ki_proc = erealloc(ki_proc, size); + 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 != '#') { + 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); + } + } + } + efree(ki_proc); + } + + /* 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); + } + + return 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 (or its + * parent). First tries std{in,out,err} then falls back to the parent's /proc + * entry. We could try following the parent all the way to pid 1 but + * /proc/%d/status is system-specific (text on Linux, a struct on Solaris). + */ +static char * +get_process_tty(void) +{ + char path[PATH_MAX], *tty = NULL; + pid_t ppid; + int i, fd; + + 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/%d/fd/%d", ppid, i); + fd = open(path, O_RDONLY|O_NOCTTY, 0); + if (fd != -1) { + tty = ttyname(fd); + close(fd); + } + } + } + + return estrdup(tty); +} +#endif /* sudo_kp_tdev */ + /* * Initialize timezone, set umask, fill in ``sudo_user'' struct and * load the ``interfaces'' array. @@ -619,9 +739,8 @@ init_vars(envp) } } - if ((p = ttyname(STDIN_FILENO)) || (p = ttyname(STDOUT_FILENO)) || - (p = ttyname(STDERR_FILENO))) { - user_tty = user_ttypath = estrdup(p); + if ((p = get_process_tty()) != NULL) { + user_tty = user_ttypath = p; if (strncmp(user_tty, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0) user_tty += sizeof(_PATH_DEV) - 1; } else -- 2.40.0