rayan@ai.toronto.edu (Rayan Zachariassen) (01/20/88)
I'm trying to simulate the effect (within a root process) of the BSD construct: setreuid(0, uid); ... setreuid(0, 0); The important thing being that the '...' is run with effective uid 'uid', and that both real and effective uid are restored to 0 afterwards. Now, the SysV manual for setuid() says that setuid(uid); ... setuid(0); is possible iff 0 is the "saved set_user id" and refers to exec(2). Of course, exec(2) doesn't mention this concept. What does "saved set_user id" mean? That the suid bit is set on the executable and this is the owner of the binary, or that it is the uid that actually did the exec'ing (uid of parent)? So, we tried it under SysVr2v4 (no, this is not crypt output...) on a Honeywell box, the token System V machine here. Apparently, the documented behaviour refers to neither of the two possibilities hypothesized. If anyone knows how to achieve the desired result under some System V (without using fork()), pray tell. Incidentally, the '...' is essentially an fopen call. Can't use access(); for one thing it is junk, for another it tests on the real uid anyway, which brings us back to the problem at hand. You can also tell me it can't be done, in which case this program will allow astute persons to deduce the contents of files they can't read, and aren't allowed to access. :-( How does one write secure code under System V without using zillions of forks? (that's rhetorical). Incidentally, I was reminded that the SVID mumbles something about setuid() making use of the original uid/gid's of the process. Might this mean that (some versions of) System V and the SVID are different in these details? If so, what's the scoop? I dimly recall a discussion here on the subject. rayan ps: to make this educational, here's a method using one too many fork()'s: if ((pid = fork()) == 0) { /* child */ (void) setuid(uid); if (open(file, 0) < 0) _exit(errno) _exit(0); } else if (pid < 0) { sorry, we can't check permissions right now... return; /* assume bad guy */ } (void) wait(&status); if (status > 0) return; /* permission denied */ ...open file, etc... but this is really really really silly.
slb@hudson.acc.virginia.edu (Sandy Bryant) (01/21/88)
In article <1988Jan19.192854.3411@jarvis.csri.toronto.edu> rayan@ai.toronto.edu (Rayan Zachariassen) writes:
..I'm trying to simulate the effect (within a root process) of the BSD construct:
..
.. setreuid(0, uid);
.. ...
.. setreuid(0, 0);
..
..The important thing being that the '...' is run with effective uid 'uid',
..and that both real and effective uid are restored to 0 afterwards.
..
..Now, the SysV manual for setuid() says that
..
.. setuid(uid);
.. ...
.. setuid(0);
..
..is possible iff 0 is the "saved set_user id" and refers to exec(2).
..Of course, exec(2) doesn't mention this concept. What does "saved set_user id"
..mean?
There is a field in the user struct that saves the effective uid
for a setuid program at exec. This usually allows you to switch
your effective uid back and forth between your real uid and the original
effective uid at will. Problem is that whenever *root* calls setuid(),
everything - real uid, effective uid, AND the saved set_user id field in
the user struct are all set to the new uid, so you can never go back to
being root again. To do otherwise creates a security hole, I guess.
If you can use some other "priviledged" id besides root to own those
files, you can do what you want.
This was true in SVR2, SVR2.1, and SVR3 on the AT&T 3B's, and I assume
it is standard SV behavior. The man page does day that when root calls
setuid, both effective and real uid are set.
sandy
sandy
kre@munnari.oz (Robert Elz) (01/23/88)
In article <172@hudson.acc.virginia.edu>, slb@hudson.acc.virginia.edu (Sandy Bryant) writes: > Problem is that whenever *root* calls setuid(), > everything - real uid, effective uid, AND the saved set_user id field in > the user struct are all set to the new uid, so you can never go back to > being root again. To do otherwise creates a security hole, I guess. Not really. If a root process really wants to do something as another user, and then come back, it can easily enough in any case ... if (fork() == 0) { setuid(someome); do_whatever(); exit(0); } wait(&status); (plus some error checking) will do it every time. Allowing root to just switch uid's and then switch back cannot possibly create any bigger "security hole". Once you're root, security is gone anyway. I suspect that the reason for the limitation is just pragmatics, combined with a liberal dose of stupidity. The Sys V developers at AT&T saw the need (correctly) for this facility, (that is, real and effective uid switching) .. its been added to unix ever since Waterloo (and probably others) did it in V6. However, they seem to have a real phobia about adding anything new, so rather than add a new system call, which could have worked sanely, without annoying limitations, they just added the internal, hidden, saved set_user value, and allowed the old setuid() sys call to use that whenever it wanted. Now we get to the potential security hole ... From time immemorial (since Jan 1 00:00:00 1970 GMT ?) setuid processes have always believed that "setuid(getuid())" is a safe and permanent way to remove any special privileges they may have had, after which its safe to assume that no special checking on path names, etc, is needed. If a process that was setuid root did this, and the definition of setuid(getuid()) was changed to allow a later setuid(0), then a clever user might be able to break through all kinds of new security holes, that didn't used to exist. This is known as "breaking working programs", and is, naturally, something to be avoided. Thus the restriction: if a process that is setuid root does setuid(x), where x != 0, then all of the uid's are changed, and the security hole is closed. And for anyone silly enough to believe that the setuid bit could ever have been intended to be used with user id's other than zero, well, tough! This problem is even worse if the saved set_user id is retained through exec (which my V.2 man pages very cleverly neglect to say anything about, my V.3 manuals aren't accessible at the minute, so I can't check those). The moral: when you want to add something new, add something new, don't modify something that exists already, and pretend that everything is magically going to be compatible. kre
gamiddleton@watmath.waterloo.edu (Guy Middleton) (01/29/88)
In article <1962@munnari.oz> kre@munnari.oz (Robert Elz) writes: > The Sys V developers at AT&T saw the need (correctly) for this facility, > (that is, real and effective uid switching) .. its been added to unix > ever since Waterloo (and probably others) did it in V6. > > However, they seem to have a real phobia about adding anything new, > so rather than add a new system call, which could have worked sanely, > without annoying limitations, they just added the internal, hidden, > saved set_user value, and allowed the old setuid() sys call to use > that whenever it wanted. As a point of information, here is the way it worked at Waterloo: Instead of two uids (real and effetive), there were three: real, effective, and effective-at-exec-time. This third uid (called the euid) was not directly settable, and was never (well, almost never) examined. Two new syscalls were provided -- schizo(), which would set the effective uid to either of the other two. become(), which would set the effective uid to an arbitrary value. It would only work if the effective-at-exec uid was zero [this was the only place the euid was looked at]. setuid() did the usual thing, except it left the euid unaffected. We don't use this any more. The BSD setreuid() makes it unnecessary. -Guy Middleton, University of Waterloo Institute for Computer Research gamiddleton@math.waterloo.edu, watmath!gamiddleton