BSD tty security, part 3: How to Fix It

Dan Bernstein brnstnd at kramden.acf.nyu.edu
Sat May 4 07:05:01 AEST 1991


In article <18953 at sdcc6.ucsd.edu> muller at sdcc10.ucsd.edu (Keith Muller) writes:
> You missed the point. Your method boils down to avoiding using ptys (and only
> works for ptys it will not work for real hardware ttys) that have background
> jobs still using them.

(It will work for hardwired ttys if you make the outlined changes to
getty's initialization routine. But without a secure break key [as you
can implement with my suggestion #24], hardwired ttys are completely
insecure anyway.)

Yes. Isn't it nice that the system also avoids using pipes that have
background jobs still using them?

> Better solutions are gained when alternative solutions are
> investigated.

I agree that it is worthwhile to investigate alternative solutions. But
I really, really, really don't want to see someone call something a
``fix'' when it does not solve the problem on the vast majority of
available systems. That's what you've been doing.

I agree that vhangup(), revoke(), etc. would make a more useful (though
more complex) security model, if they worked (which they don't in most
current systems). That's why I've been pushing for an enforce() syscall.
The problem is that to make vhangup() actually work as documented, you
have to reorganize every file access in the system to go through the
mechanism that vhangup() controls. Similarly for revoke() and enforce().
This is a worthwhile long-term project, not something you can reasonably
require in a minimal fix.

> ->Remember your article title was BSD tty security (not sunos pty security).<-

Yes. The majority of BSD-derived multiuser systems ``out there'' are not
SunOS machines.

> On very active system with lots of background (or hung jobs) you eventually
> run out of ptys. Without your {sleep, TIOCOPENCT ... loop} the service denial
> hole is still present. This is something that is fairly important in
> many environments.

Again, I address this in the comments after step 12, as well as in step
7. For most environments it is not a problem, as there are typically ten
times more ptys than background jobs. For the environments where this is
not true, you will have to pay attention to my other suggestions.

> My point is there a
> an alternative to pty allocation that is less complex (the revoke() 
> suggestion encased in "exclusive use" ioctls is a lot simplier than the
> complex setup and cleanup you are suggesting.

Less complex? Less complex? Do you believe what you're saying?

Would it be ``less complex'' if the system had a static pool of pipes?
You pick a pipe, revoke() any current users of it, and then use it for
yourself?

Would it be ``less complex'' if the system had a static pool of pids?
You pick a process id, fork(), revoke() any other processes with the
same pid, and then use it for yourself?

Would it be ``less complex'' if the system had a static pool of files?
You pick an inode, revoke() any other users of that inode, and then use
it for yourself?

Sorry, but you're not being realistic. UNIX pipe management is simple
because UNIX allocates pipes dynamically. UNIX pid management is simple
because UNIX allocates pids dynamically. UNIX file management is simple
because UNIX allocates files dynamically. Do you see anyone worrying
about the security of pipes?

That's why my solution effectively implements dynamically allocated
ptys. ``Dynamically allocated'' doesn't imply infinitely many---you can
run out of pipes or processes or files if you try hard enough---but it
makes management and security much, much simpler.

> It is easily stuck in
> a library routine also and is a lot less code).

How about we stop arguing, you write up a complete description at the
same level of detail as mine, and then we see which solution is simpler
and requires less code.

> Tracking down all those references to a pty (or tty) is the SAME for
> both your TIOCOPENCT and the revoke().

Incorrect. On a Sun, for instance, to handle TIOCOPENCT you just go from
file descriptor to open file description to vnode, then report v->count
of the vnode. Done. Your revoke() requires going through the entire file
table and setting flags here and there. (That's what you said...)

> Clearing and/or setting a flag
> once you have found the references is trival.

And once you have included special treatment of that flag in every
single tty operation. This is a lot of kernel work. Why do you refuse to
write out these changes at a sufficiently low level of detail that
people can implement them? If it's so few lines of code, why don't you
write out the code?

> >Most importantly, your changes don't close the holes on anything but
> >Suns and very recent BSD releases. Should I explain this? All your
> >revoke() does is stop access through the open file table (and it's not
> >entirely clear that it will work any better than vhangup() for that).
> >But p_ttyd (or u_ttyd) doesn't *go* through the open file table. Poof,
> >there goes your security.
> Neither p_ttyd (or u_ttyd) are in 4.3 RENO or earlier (and you did call
> this a BSD tty security fix).

Do you know what you're talking about?

ttyd is standard BSD, and is on the vast majority of BSD-derived
systems. ttyvp only appears on Suns, very recent (i.e. non-production)
BSD releases, and possibly some obscure or extremely recently released
systems that I'm not familiar with.

I repeat my claim: your changes don't close the holes on anything but
Suns and very recent BSD releases (such as Reno). In fact, unless you
know you've changed every single tty operation to check that flag and
behave properly, including when coming out of sleep, your changes are
insecure even on those systems.

> >You also require that a flag be added to the file structure and that
> >every single tty I/O operation be changed to pay attention to that flag
> >and take special action if it is set. So far (not to mention replacing
> >u_ttyd with u_ttyvp or the equivalent) we're talking about some hundreds
> >of extra lines of kernel code---far more than required to implement
> >/dev/fd/3 (to replace /dev/tty) or TIOCOPENCT.
> As stated above access rights are already checked on i/o ops,

But you want people to add an extra check, plus have it handled for
blocking processes. I repeat my claim.

> On 4.3 RENO and earlier there is only u_ttyvp, so that replacement
> is not needed.

This statement is simply incorrect. On BSD 4.3 Reno and LATER there is
u_ttyvp. I believe that they took the name and idea from SunOS, though
I'll have to check this with Marc Teitelbaum.

Damn it, I can't stay polite about this. You obviously aren't familiar
with a wide range of machines---your statements about u_ttyvp prove
that. Your fixes do not work on most machines, but you say they do
because you don't know what you're doing. This is exactly the kind of
sheer idiocy that has let to the current situation: nobody fixes the
problem at its source, but people like you keep claiming that the latest
kludge really does solve the problem, and fewer and fewer people realize
that the problem still exists. I can't tolerate this! You are damaging
security on present and future systems by claiming that an insecure
solution is a solution. Don't you realize how stupid this is?

When the problem is SOLVED, feel free to advocate further complexity in
the security mechanism. In the meantime, don't be an ass. If you're
going to propose a solution, check with more experienced people (like
those at BSD) who can tell you whether it really will work.

> >You require that all tty-handling programs be setuid root. Setuid to
> >some other uid would be much safer---but you can't do that without
> >letting unprivileged users abuse revoke().
> It is as easy (or easier) to make a setuid() program safe as it is to
> preventing a server from crashing before it cleans up (so you state).

That's what they said about Emacs. Ever hear of move-mail?

> Having a pty "allocation" running as non-root causes problems.

Perhaps you did not read my proposal. I require that *non-root*
tty-handling programs be made setuid pty. This means programs like
script. script already doesn't chown ttys; my changes don't affect this
situation at all.

This is the third time you've claimed that my changes break something
when in fact they don't. Please read my proposal before criticizing it!

> Things like mesg etc would
> have to be setuid() pty to work.

Again, please read my proposal before criticizing it! I have addressed
practically every issue you've brought up.

> A single user would be unable to access
> another pty he was assigned by pty.

There are many ways to pass data and descriptors. Furthermore, your
statement about pty is incorrect; please read the pty interface
definition in my pty paper, particularly the definition of -xc.

> >I'm sorry to criticize your changes in such harsh terms, but they remind
> >me too much of the kludge upon kludge that vendors like Convex have
> >tried. You don't stop the problems at their source; you let two
> >independent user programs have a tty simultaneously open; your changes
> >don't even work as advertised on most machines, because you missed a
> >fundamental avenue around your revoke(). Even if your fix were simpler
> >or less fragile than mine, these flaws would condemn it.
> Sorry you are so sensitive about your design

I'm not sensitive about it. I welcome constructive criticism of it. I'd
love to see it made simpler without compromising its security.

But I am extremely sensitive to your claims that you have a fix, when in
fact YOUR CHANGES DO NOT SOLVE THE PROBLEM ON THE VAST MAJORITY OF
SYSTEMS. This is the worst kind of security through obscurity: by
claiming that your changes work, you might delude other people into
believing you, and then they won't implement real fixes WHEN THE SYSTEM
WITH YOUR CHANGES IS JUST AS INSECURE AS IT WAS BEFORE.

Simplicity is a side issue when you can't even make correct fixes.

> (I suspect many people
> consider your proposal a kludge...).

It is a kludge, albeit a reliable one. It's a kludge to fix a kludge.

> In the environment BSD prior to 4.3 RENO revoke() works as
> advertised (no process remains with any access after the revoke()),

You are wrong.

---Dan



More information about the Comp.unix.wizards mailing list