In all Unix systems, a UID, or "user ID number", is an integer (actually an unsigned short on older systems) that identifies particular user. Every running process has at least two UID numbers associated with it, the real UID number, which identifies the user who launched the process, and the effective UID number, which is used to determine what resources the process can access. Normally these are the same, but if a program with a set-uid bit set is run, then while the real UID remains that of the user who ran it, the effective UID is that of the user who owns the file.
POSIX and System V Unixes have a third UID number associated with every process, the saved UID number. This is a the effective UID number that the process had when it started. It is saved so that that UID can be taken back after it is given up. If the unistd.h header file defines _POSIX_SAVED_IDS, or if sysconf(_SC_SAVED_IDS) is true, then you have saved UIDs. However, not all Unixes have unistd.h or sysconf().
The effective UID is used when checking whether the process can open a file. If the process creates a file, the ownership is determined by the effective UID.
It is always possible for a process running as root to signal any process. For non-root processes the sending process's UIDs must match up with the receiving process's uid, but if the manual pages can be trusted, the exact rules vary. A signal can be set if:
Sending Process's... matches Recieving Process's... OS effective real or saved RedHat Linux real or effective real or effective FreeBSD, OpenBSD real or effective real or saved SunOS 4.1, Solaris, SuSE Linux/i386 7.3 effective effective old BSD, NextStep, Slackware Linux 3.1, ULTRIX 4.2
Some of this variation may be manual bugs. Some man pages aren't even internally consistant on this. It makes sense that recieving signals should depend on the real UID, so if a user runs an set-uid-root program, they can still send it signals, even though it is running as root. OpenBSD restricts the types of signals that can be sent to set-uid processes.
Linux also has a filesystem UID, which is normally the same as the effective user id. Whenever the effective UID is changed, the filesystem UID changes to the same value. The Linux man page says applications like NFS servers set the filesystem UID to a different value than the effective UID so they can read/write files as a user, without that user being able to send signals to the server process. This doesn't entirely make sense, because whether or not you can recieve signals on Linux depends on the real and saved UIDs, not the effective UID.
The functions for setting UID values vary widely between versions of Unix. In an attempt to clarify the situation, I'm going to try to fit the versions into an historical context. However, one should be cautioned that this historical context is mainly inferred from reading a lot of manual pages. I'm sure it is a huge oversimplification of the actual sequence of events, and may bare little resemblence to it.
setuid(uid) | Set both real and effective UID to uid |
setruid(uid) | Set real UID to uid |
seteuid(uid) | Set effective UID to uid |
setreuid(ruid,euid) | Set real UID to ruid and effective UID to euid |
These calls succeed only if either (1) the caller's effective userid is root, or (2) the uid's requested match either the caller's current real or effective UID numbers.
Of course, you can also change your effective UID by calling execve() on a file with a set-uid bit set.
Systems without saved UIDs are pretty much extinct. (NextStep was the last one I saw).
The idea was presumably that non-privileged processes should never be changing their real UID. Using the setuid() call, they could permanently surrender their set-uid priveleges. Using the seteuid() they could temporarily surrender them, or take them back. Oddly, there is no obvious way for a root process to set it's real and effective UIDs to different values (except execve()). This may be why attempts to depreciate setreuid() never quite stuck. While setruid() disappeared, setreuid() kept hanging around, often with stern instructions not to use it because it would be eliminated any decade now. In 2001 POSIX brought it back.
The 2001 version of the POSIX made further changes, including restoration of the prodigal setreuid() to respectibility.
I need to put some more work into this to figure out exactly what the differences between the 1990 and 2001 POSIX versions are.
If you are not root however, it sets only the effective UID, not the real or saved UID. You can set it to your effective, real or saved UID. In other words, it behaves identically to seteuid().
The FreeBSD manual seems to say that root can only use this to set to the real UID, not the saved UID. But that's too dumb to be true, isn't it?
Where it still exists, it is usually, but not always, extended so that you can also set your real UID to your saved UID.
If the caller is root, and the effective UID is set to something other than it's real UID or its saved UID, then it's saved UID is changed also. It's real UID is never changed.
Versions of Linux before 1.1.37 have saved uids, but you can't use seteuid() to set the effective uid to them.
What exactly happens to the saved UID when this is used seems to vary a lot. I think the new standard is to set the saved UID to the new value of the effective UID if either the real uid is changed (to anything) or the effective UID is changed to something other than the real UID. In older versions the saved uid was only changed to the new effective UID if the effective UID is changed from root to something other than the real or saved UID.
Versions of Linux before 1.1.37 have saved uids, but you can't use setreuid() to set the effective uid to them.
In AIX, setreuid(a,b) (where a!=b) is equivalent to seteuid(b) and setreuid(b,b) is equivalent to setuid(b). In other words, you can't set the real UID without setting the effective UID.
Otherwise, you can set them to your real, effective, or saved UID in any permutation.
This was evidentally introduced to cut the Gordian Knot of setuid() functions. No standard supports this, but it's been increasingly popular. It appears have originated in HPUX and exists in Linux after 2.1.44 and in OpenBSD.
Linux only.
The simplest way to do this is to make the applog program an set-uid-root program, and permit the log file so only root can write it. To get the user id to put in the log entry header, we'll call getuid() which returns our real UID. We never need to call any set*uid() functions. This works fine, but any security hole in this program becomes a root exploit, which is highly undesirable.
So a better solution is to create a new user specially for this application, maybe with user name logger. We permit the log file so only logger can write to it, and we make the program so it runs set-uid-logger. Now if a security hole appears in the program, then only the logger account is compromised, a much smaller problem.
However, a new problem appears. Suppose user ralph runs the program, submitting a file that is not read permitted to others. Since the process is running with the effective UID of logger, it will not be able to read ralph's file. Luckily all Unixes provide ways to toggle between your two identities - we can have the program first open the log file while it is logger then switch it's effective UID to ralph to open his file. In more complex applications, the program may have to regularly toggle two identities back and forth. This is quite common.
In systems without saved UIDs, you do this swapping with setreuid(). In systems with saved UIDs, you can do this swapping with seteuid(). You don't want to use setuid() in this case, because it wouldn't work right if the original user was root. If the original real UID was root, then the first setuid() changes all UIDs to logger making it impossible to switch back again.
Some other interesting cases come up if we start as root. It is often useful for root programs to become other users, to ensure that nothing is done that couldn't be done by that user. Normally seteuid() is used for this. If the root process user wants to become first user andy and then user beth, then you can't just do:
seteuid(uid_of_andy); : seteuid(uid_of_beth);The second call won't work, because although your real and saved UIDs are still root, your effective UID isn't. So you have to do:
seteuid(uid_of_andy); : seteuid(0); seteuid(uid_of_beth);That is, you need to become effectively root again before you can become a new user. The fact that priviledged status depends only on the effective UID and not the real or saved UID is kind of odd, given that you can easily set your effective UID to your real or saved UID.
If you are root and want to set your effective UID to andy and your real UID to beth, then you have a problem. Your choices are (1) call the (no longer) depreciated setreuid() functions, (2) call the non-portable setresuid() function, or (3) do a setuid(uid_of_beth) and then use execve to run a program which is set-uid to andy. I guess this is why they un-depreciated setreuid().