1

I have an executable binary which was compiled from a C source file

The executable has the setuid permission on

I noticed that, if the owner of the executable is root, I can use

setuid(geteuid());

when compiling the file to set the real UID of my the process running the executable to be root. Then, anyone who runs the executable can run it as root.

However, I noticed that only happens when the owner of the executable is root. It did not work when I tried to give test_user ownership of the executable (and fixing permissions to contain setuid again). After reading these documentation pages (1, 2, 3) and reading this post, I noticed that setuid(new_euid) is meant to change the effective UID instead of the real UID of the process running the exectuable. It just happens that, under particular circumstances (effective UID is root), setuid(new_euid) also sets the real UID and saved UID of the process running the executable to new_euid.

I solved the issue by using setreuid instead of setuid, as follows:

setreuid(geteuid(), geteuid());

Which allowed me to set the real UID of the process to be the effective UID (owner of the executable) and reset effective UID to it's value (redundant).

I understand that setuid() will work under certain conditions, but is not less confusing and more appropriate to just use setreuid(), setresuid(), or seteuid() when changing real UID, saved UID, or effective UID is desired since they always work?

Moreover: I understand that seteuid() appears to be doing the same as setuid() with the difference explained here (effective UID is root). This is supposed to not allow root priviledged programs regain priviledges after dropping them (because all 3 UIDs would be changed to the same value using setuid())? So should I just use setuid() for root priviledged programs even when it is not as clear compared to setresuid() for example?

I see that setuid() can be secure since it doesn't allow root priviledged programs regain priviledges once droped, but that behavior can be implemented using the other mentioned functions with less confussion.

Another thing getuid() returns the real UID of the process while setuid() is meant for modifying effective UID (unless priviledged), which is also confusing.

1 Answer 1

0

There's probably some hacky history behind all that. As you say, setreuid() is clearer, and since it's specified in the standard, I would use it. Followed by piously checking the return value, and verifying with getuid() and geteuid() afterwards.

setresuid() isn't in POSIX, so it might not be as widely available (FreeBSD and OpenBSD seem to have it, though). If the implementations of setreuid() match the documentation, you don't need to explicitly set the saved UID, since setreuid() should set it for you:

If the real user ID is being set (ruid is not -1), or the effective user ID is being set to a value not equal to the real user ID, then the saved set-user-ID of the current process shall be set equal to the new effective user ID.

Then again, avoiding setuid programs completely in favor of e.g. a separately running privileged process and communicating with it through a socket might be a good idea if possible. There are a pile of things that get inherited from a process to its children, and with setuid, the less privileged one gets to set them for the more privileged one.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .