When a setgid program call 'ttyname', and running in a real console (not an xterm or a ssh connection), the function returns 'NULL' instead of the tty path. It work correctly when running in an xterm or when the program is not setgid. This 'bug' was found when trying to run 'screen' in the console, and being hit by a 'Must be connected to terminal.' (ouch) ... Reproducible: Always Steps to Reproduce: 1. $ cat ttyname.c #include <stdio.h> #include <unistd.h> int main() { printf("ttyname(0) = %s\n", ttyname(0)); return 0; } $ gcc -o ttyname ttyname.c $ ls -l ttyname -rwxr-xr-x 1 user users 5317 Jun 18 21:26 ttyname $ ./ttyname ttyname(0) = /dev/vc/6 2. $ su -c 'chown root:utmp ttyname && chmod 2755 ttyname' $ ls -l ttyname -rwxr-sr-x 1 root utmp 5317 Jun 18 21:26 ttyname $ ./ttyname ttyname(0) = null Actual Results: As you can see, when the 'ttyname' demo program is made setgid, running it on the linux console make it fail. It is still working when run in a terminal emulator such as xterm. Expected Results: The output should not have changed between the two invocation of the program, since the program should still have enough rights to access '/proc/<pid>/fd' as the effective uid is not changed by the 'setgid' bit ... Or am I wrong doing this last assumption ? If I'm wrong, the 'screen' ebuild must be changed to either make 'screen' be a root setuid program, or to remove the setgid flag ... Portage 2.0.48-r1 (default-x86-1.4, gcc-3.2.2, glibc-2.3.1-r4) ================================================================= System uname: 2.4.20-gentoo-r2 i686 Intel(R) Pentium(R) 4 CPU 2.40GHz GENTOO_MIRRORS="http://gentoo.oregonstate.edu http://distro.ibiblio.org/pub/Linux/distributions/gentoo" CONFIG_PROTECT="/etc /var/qmail/control /usr/share/config /usr/kde/2/share/config /usr/kde/3/share/config /usr/X11R6/lib/X11/xkb" CONFIG_PROTECT_MASK="/etc/gconf /etc/env.d" PORTDIR="/usr/portage" DISTDIR="/home/ftp/distfiles" PKGDIR="/usr/portage/packages" PORTAGE_TMPDIR="/tmp" PORTDIR_OVERLAY="/home/keiichi/portage" USE="x86 slang mozilla cdr crypt pam berkdb gdbm nls readline ncurses ssl ipv6 doc alsa oggvorbis zlib opengl xv dga truetype png jpeg tiff sdl radeon mmx sse -tcpd -perl -python -X -gtk -gnome -esd -java" COMPILER="gcc3" CHOST="i686-pc-linux-gnu" CFLAGS="-pipe -O3 -march=pentium3 -mcpu=pentium4 -mfpmath=sse -ffast-math -falign-jumps=5 -falign-loops=5 -falign-functions=64" CXXFLAGS="-pipe -O3 -march=pentium3 -mcpu=pentium4 -mfpmath=sse -ffast-math -falign-jumps=5 -falign-loops=5 -falign-functions=64" ACCEPT_KEYWORDS="x86" MAKEOPTS="-j2" AUTOCLEAN="yes" SYNC="rsync://rsync.europe.gentoo.org/gentoo-portage" FEATURES="sandbox ccache userpriv usersandbox notitles" $ ls -la /proc/$$ -r--r--r-- 1 user users 0 Jun 18 22:29 cmdline lrwxrwxrwx 1 user users 0 Jun 18 22:29 cwd -> /home/user -r-------- 1 user users 0 Jun 18 22:29 environ lrwxrwxrwx 1 user users 0 Jun 18 22:29 exe -> /bin/bash dr-x------ 2 user users 0 Jun 18 22:29 fd -r--r--r-- 1 user users 0 Jun 18 22:29 maps -rw------- 1 user users 0 Jun 18 22:29 mem -r--r--r-- 1 user users 0 Jun 18 22:29 mounts lrwxrwxrwx 1 user users 0 Jun 18 22:29 root -> / -r--r--r-- 1 user users 0 Jun 18 22:29 stat -r--r--r-- 1 user users 0 Jun 18 22:29 statm -r--r--r-- 1 user users 0 Jun 18 22:29 status
from screen ebuild: pkg_postinst() { chmod 0775 /var/run/screen einfo "Some dangerous key bindings have been removed or changed to more safe values." einfo "For more info, please check /etc/screenrc" echo einfo "screen is not installed as setuid root, which effectively disables multi-user" einfo "mode. To enable it, run:" einfo "" einfo "\tchmod u+s /usr/bin/screen" einfo "\tchmod g-w /var/run/screen" }
I know that screen is intentionnaly not installed setuid root. But I think the bug is not in screen but in glibc. Otherwise why would my test program have such an aberrant behaviour : - from the linux console: $ ./ttyname ttyname(0)=(null) - from an xterm: $ ./ttyname ttyname(0)=/dev/pts/1 - from the linux console: $ ssh localhost user@localhost's password: Last login: Thu Jun 19 08:22:21 2003 from localhost $ ./ttyname ttyname=/dev/pts/2 For information, ttyname is owned by root:utmp, setgid. In all case, the errno value is 'zero', even though the 'ttyname' manpage state that when ttyname return NULL, errno is set to either EBADF or ENOTTY ...
az: any thoughts ?
After reading ttyname code, and some tests, it appear that if there is a bug, it is in the kernel. Or it is me that is wrong thinking that a program that is setgid still have at least the same right as the user. The following code try to mimic the ttyname implementation on linux (somewhat simplified by exec-ing 'ls', and getting its error description), that read the link '/proc/self/fd/<number>' or fall back on using the '/dev/pts' filesystem. $ cat ttyname.c #include <unistd.h> #include <sys/types.h> int main() { char *const envp[] = { NULL }; char *const argv[] = { "/bin/ls", "-la", "/proc/self/fd", NULL }; printf(" uid=%d, gid=%d\n", getuid(), getgid()); printf("euid=%d, egid=%d\n", geteuid(), get egid()); return execve("/bin/ls", argv, envp); } $ gcc -o ttyname ttyname.c Using this simple program, we get the following result: - from the linux console, not setgid: $ ls -l ttyname -rwxr-xr-x 1 user users 5683 Jun 21 01:38 ttyname $ ./ttyname uid=1000, gid=100 euid=1000, egid=100 total 0 dr-x------ 2 user users 0 Jun 21 01:48 . dr-xr-xr-x 3 user users 0 Jun 21 01:48 .. lrwx------ 1 user users 64 Jun 21 01:48 0 -> /dev/vc/6 lrwx------ 1 user users 64 Jun 21 01:48 1 -> /dev/vc/6 lrwx------ 1 user users 64 Jun 21 01:48 2 -> /dev/vc/6 lr-x------ 1 user users 64 Jun 21 01:48 3 -> /proc/5738/fd - from the linux console, setgid: $ ls -l ttyname -rwxr-sr-x 1 root utmp 5683 Jun 21 01:38 ttyname $ ./ttyname uid=1000, gid=100 euid=1000, egid=406 /bin/ls: /proc/self/fd: Permission denied When called from an xterm (or another terminal emulator using /dev/pts), the libc try to get the name from the '/dev/pts' virtual filesystem after failing from the '/proc' filesystem. So there doesn't seem to be a bug in the glibc (sorry), but I don't understand why the 'setgid' program can't read the /proc/self/fd directory ? I though that a setgid program has the righ of the user + the right of the group, as if he is temporarily a member of this group. After all, the '/proc/self/fd' has the '-r-x------' right, that is the user (which is unchanged) is able to read it. Weird ! It'll need some more investigation to see if it only appear on gentoo, or if this is the intended behaviour of the setgid bit ...
Looks like a devfsd misconfiguration. What does `ls -l /dev/tty6' say? In suid or sgid processes (or rather, if uid != euid OR gid != egid), the kernel sets permissions on `/proc/self/fd/' to "0500/root.root", which means it's unreadable if euid is anything but 0. ttyname()s fallback strategy is to fstat() the fd passed to it and to stat() everything in /dev and /dev/pts until it finds a device file pointing to the same inode (/dev/tty6 if called from /dev/vc/6).
Since MKOLDCOMPAT is disabled in my devfsd.conf, ls -l /dev/tty6 tell me: ls: /dev/tty6: No such file or directory Otherwise, the /dev/vc/6 file has the mode 0600/user.users, but even when changing it to mode 0666/user.users and restoring MKOLDCOMPAT in devfsd.conf doesn't solve the ttyname problem (still return null). I must be missing something ... But I guess the bug can be cloed, since it seems to be a local misconfiguration than anything else. Anyway, thanks for the precision on 'setgid/setuid' behaviour in linux kernel.
its all in the details ;)