Xref: utzoo comp.unix.wizards:25533 alt.security:2512 Path: utzoo!utgpu!news-server.csri.toronto.edu!cs.utexas.edu!swrinde!elroy.jpl.nasa.gov!ncar!hsdndev!cmcl2!kramden.acf.nyu.edu!brnstnd From: brnstnd@kramden.acf.nyu.edu (Dan Bernstein) Newsgroups: comp.unix.wizards,alt.security Subject: Re: BSD tty security, part 3: How to Fix It Message-ID: <21553:May1020:06:0791@kramden.acf.nyu.edu> Date: 10 May 91 20:06:07 GMT References: <19249@rpp386.cactus.org> <28949:May620:55:5391@kramden.acf.nyu.edu> <19253@rpp386.cactus.org> Organization: IR Lines: 135 Since John expressed some doubts, enclosed here is an informal but reasonably detailed proof of the security of my proposed solution. Also here is a justification of each of the required steps in my solution. Someone who reads through this should understand why each step is necessary and why in combination they are sufficient; if there's any misunderstanding, send me e-mail, and I'll post a clarification. In article <19253@rpp386.cactus.org> jfh@rpp386.cactus.org (John F Haugh II) writes: > No, the obvious solution is to provide for file access revocation. What > do you do to assure (because, remember, we have to provide assurances > that our solution really works - handwaving doesn't cut it) that there > isn't some process on the other side of the "privileged program" that > is reading our hardware tty? Huh? Under suggestion #24, no user process will ever open hardwired /dev/tty* or /dev/modem* or whatever you want to call those devices. Provided that the normal UNIX security mechanisms are in place, and provided that /dev/modem* always have their permission bits properly set, the system guarantees that no user program can ever open those files. Are you doubting the concept of file permissions? > Your suggestions are the grossest hack I've > seen to date for solving this problem, and it doesn't even provide > any assurance that it has been solved, Wtf are you talking about? As I said at first, my solution provides strong guarantees that the problem has been solved. Witness a proof of security: 1. All /dev/pty* are initially group- and world-inaccessible, and owned by user pty, as required by the solution. Hence, at the outset, only programs running as root or pty may open those files. Provided that none of those programs ever changes the mode of /dev/pty*, they will continue to be group- and world-inaccessible. 2. A file may only be open in a process if it is (1) opened directly by that process, (2) passed in from another process, e.g., via fork(). Provided that none of those root/pty programs ever passes an open /dev/ptyxx descriptor to another process, and given #1, we conclude that no process can have /dev/pty* open unless it is a root/pty program. (This means, more precisely, that it has at some point had root or pty permissions. Even more precisely, by ``process'' I distinguish between processes with the same pid and process state but separated by an exec(), and by ``passing'' I mean to include leaving the descriptor open through an exec().) 3. Provided that none of those root/pty programs ever passes an open /dev/ptyxx descriptor to another process, and because /dev/ptyxx may only be opened once, there always exists at most one process with /dev/ptyxx open. 4. I claim that if /dev/ptyxx is not open in any process, then /dev/ttyxx is not (group- or world-) accessible, and is owned by pty, provided that no root/pty process changes an inaccessible /dev/ttyxx to an accessible one (or one with a different owner) without having /dev/ptyxx open. Proof: The claim is true at first, as all /dev/tty* are initially inaccessible as required by the solution. By #2, /dev/ptyxx may only be open in a root/pty process. So by hypothesis we must only check that when such a process closes /dev/ptyxx, /dev/ttyxx is not accessible. But, as required by my solution, such a process must change /dev/ttyxx to be owner pty mode 600 before it closes /dev/ptyxx. Hence the claim is always true. 5. If /dev/ptyxx is not open in any process, then /dev/ttyxx is not open in any process except possibly a root/pty process. Proof: The claim is true at first. As in #4, /dev/ptyxx may only be open in a root/pty process. So we must only check that when a root/pty process closes /dev/ptyxx, /dev/ttyxx is not open except possibly in a root/pty process (which, in fact, can only be itself). But, as required by my solution, the process must open a separate open file description to /dev/ttyxx which it does not pass to any other process. Furthermore, also as required by my solution, before closing /dev/ptyxx, and after changing /dev/ttyxx to be owner pty mode 600, the process must be told by TIOCOPENCT that the only open file description to /dev/ttyxx is that separate descriptor, which again it has not passed to any other process. Hence the claim is true. That's the basic idea. From these guarantees you can draw further conclusions. If, for example, telnetd opens /dev/ptyxx and /dev/ttyxx and then passes the descriptors to login, then by #5 we see that no other process can have had /dev/ttyxx open from when /dev/ptyxx was last open, and by #4 we see that no other process can have opened /dev/ttyxx during the last period in which /dev/ptyxx was not open. Hence no other process has /dev/ttyxx open. Now, John, would you like to repeat your statement about assurances? Sure, a stupid superuser (or a buggy root/pty program) can invalidate any of the assumptions mentioned above: for example, he can change /dev/ptyxx and /dev/ttyxx to mode 666 owner shmoe, and (guess what?) it's insecure. There's no way to solve problems like this without adding mandatory access controls. At the top of this article I promised to explain why each of the required steps in my solution was necessary. Well, steps 3, 4, and 9 are used in guarantee #5, and steps 5, 6, and 10 are used in guarantees #1 and #4. I don't see any way to eliminate those steps without breaking at least one of the guarantees; conversely, those steps together with normal UNIX security do make the proofs work. Steps 1, 2, 7, and 8 fix a separate problem. The above guarantees only address the issue of having /dev/ttyxx open directly. Unfortunately, today's UNIX systems have alternative ways of affecting the device through a different mechanism than opening it: for example, /dev/tty. Those steps eliminate the current /dev/tty and reduce that alternative access mechanism to the original mechanism, namely file descriptors. I should note that there are many other alternative access mechanisms (e.g., process groups) that in some systems introduce their own sets of security problems, but the only universal mechanism allowing complete data corruption is /dev/tty. (Having two ptys with the same minor number would be just as dangerous, but I have never seen anyone do that, and it would certainly break the usual pty allocation code.) Do I need to explain step 11, namely protecting /etc/utmp on Suns? This is what I call a SCINUP---a Security Compromise Introduced in the Name of User Power. I suppose it's like the example of a superuser making all the tty files world-writable, but /etc/utmp is so easy to fix that I felt compelled to mention it. Finally, step 12 fixes an old SCINUP, namely having users leave their ttys world-usable in order to allow user-to-user communication. This is also only somewhat related to the basic problems of tty access. I leave it to everyone else to seek out and destroy further SCINUPS embedded in common programs. There. Is there anyone left who doesn't understand why a particular step is necessary, or who doesn't believe that my solution guarantees rather strong security? (Again, if I need to clarify anything above, send me some e-mail.) Those of you who've been shouting religious stupidities about how you absolutely need to see break code to be convinced that my fixes work---can you see the difference now between a proof of security by logic and a ``proof'' of security by testing? (I will address this point in detail in a coming message.) ---Dan