Re: PTY feature? or bug?

Theodore Ts'o (tytso@mit.edu)
Wed, 24 Apr 1996 22:40:22 -0400


From: inr-linux-kernel@ms2.inr.ac.ru (really kuznet@ms2.inr.ac.ru)
Date: 24 Apr 1996 22:27:13 +0400

Apparently, you forgot:

current->state = TASK_INTERRUPTIBLE;

Yup, I forgot that line. Mea culpa; thanks for catching that.

I'd like to repeat my question.

Are you sure, that it should wait for master open?

NetBSD-current does wait for the master open. So does SunOS. I suspect
all BSD-derived systems do wait for the master to open. (and remember
that pty's aren't really defined by any other standard than what BSD
does, since it was the OS that introduced this innovation).

As a point in favor of your argument, Solaris does returns EPERM if you
try opening the slave pty if the master is not open.

All the applications, that I've seen (rlogind, telnetd, emacs, xterm),
open master and only then open slave.

The real problem is that we need to have a standardized way of
allocating pty's in libc. Every application does thing differently, so
I'm not prepared to bet that all applications really do open the master
first. Ideally we may want some kernel support as well, so that the pty
can get chowned to the user --- which is necessary if you want real
security on the pty anyway.

I've just made exciting experiment.

ttyp0 is not allocated.

I start cat /dev/ttyp0
It is running (or sleeps after my fix).

Then I start emacs and make M-x shell.
It is funny: all the input goes to cat!
(Unfortunately, emacs is not allowed to make vhangup)

This is a bug in emacs and in script; they both allocate pty's
insecurely. I think this problem has been brought up before on various
emacs lists, but remember RMS's MIT AI-lab heritage; he doesn't really
care about security (he tells everyone his password is "rms" --- why
should he worry about security?).

Emacs *should* call vhangup; for example, the code which allocates a pty
in term gets it much closer to being right. What term does works under
Linux, but it's not completely portable. And in any case, both emacs
and term leave the pty chowned as root, and world readable, so an
attacker can just grab the pty anyway.

If you're going to do things securely, the pty allocation routine needs
to have a setuid helper program that chowns and sets the
permission bits appropriately on the slave pty. Then after the fork,
the slave pty should be opened as follows:

<lose controlling ctty --- setsid(), ioctl(TIOCNOTTY), etc.>
fd = open(<slave>, O_RDWR);
/* slave is now controlling tty */

vhangup(); /* This forces a hangup on the controlling tty */

fd2 = open(<slave>, O_RDWR);
close(fd);

dup2(fd2, 0);
dup2(fd2, 1);
dup2(fd2, 2);

close(fd2);

This method should be portable on most systems that support vhangup.
Opening the second slave pty before closing the first is important on
some systems. Unfortunately, very few applications actually get this
right.

Ideally, I think pty allocation needs some kernel support, because when
the pty is allocated, it should immediately be chown'ed to the right
user and have its permission bits set to 0600. Unfortunately, if your
application isn't setuid root, or has a setuid helper program, it can't
do this. With kernel support, you can also make sure that the pty is
chown'ed back to being owned by root when the pty is released.

Bottom line? For right now, we're following BSD. If we want to make
changes to plug some security holes, let's plug *all* of the security
holes, which will probably mean redesigning how all applications obtain
their pseudo-ttys.

- Ted