hedrick@geneva.rutgers.edu (03/31/89)
[[ This has been reported to sunbugs. --wnl ]] The problem is that setuid(getuid()) and setgid(getgid()) do not work for large uids and gids, i.e. 32K and larger. In 4.3, the uid and gid in <user.h> are declared unsigned short. Thus no sign-extension problems occur. In SunOS, they are declared signed short. getuid and getgid sign-extend the returned value. Unfortunately, setuid and setgid (actually setreuid and setregid) contain explicit checks that reject arguments with non-zero left half. Thus setuid won't accept the value returned by getuid, etc. Note that you can't just do setuid(getuid() & 0xffff); After verifying that the left half is zero, setuid then checks to see whether the value matches either the real or effective uid. It sign-extends the current values. So if you set the left half to zero, you pass the explicit left-half check and then fail the comparision. No possible value will pass both checks. Unfortunately, setuid(getuid()) is used by many setuid programs before doing something like sprouting a shell. It is intended to put things back to an unprivileged state. Because there's no way this can fail, it's unusual for programs to check the value returned by setuid. To the net effect is that a setuid program is likely to sprout a root shell for users whose uid's are 32K and larger. We actually ran into the problem with mail, where we got a shell whose gid was mail instead of the user's own gid. (We run mail setgid to mail, and do setgid(getgid()) in appropriate places.) I haven't examined the system to see exactly where the resulting holes are. It is even possible that no standard part of SunOS 4.0 relies on that code (though I'd be very surprised). But it's still a potentially serious security problem. Unfortunately the only fixes I can think of are - fix the kernel - make sure you don't have any uid's or gid's >= 32K So non-source sites with lots of users may be in big trouble. (We use large gid's because our gid's have meaning to our accounting system. We don't really have 32K of them. We just need all 16 bits to encode all the things we want to encode. We do have uid's >= 32K because of the way we allocate uid's. Because of NFS, we give users the same uid on all systems. AT one point we did this by assigning each department a range of uids they could assign. We assigned the whole 64K address space. We now have a better way of assigning uid's, but we are stuck with uid's > 32K.) I believe SunOS 3.2 is OK. setre[ug]id uses code much closer to 4.3. It still sign-extends the uid and gid, but it does so consistently.