The setuid
function from unistd.h
successfully changes the real userid (in a program with the proper permissions and ownership) from a normal user to the root user. However, when using the same function in a program owned by a normal user, and with the same permissions, the function call returns 0
for success but in fact does not succeed in changing the real userid from one normal user to another normal user.
What is the reason behind this behavior, and what is the best (most portable, most secure) work-around?
What I tried
Here is try.c
:
#include <unistd.h> /* for the get/set uid functions */
#include <stdio.h> /* for printf and perror */
int main() {
int newuid = geteuid(); /* The goal is to set the real UID to this value. */
printf("Original UID is %d, EUID is %d\n", getuid(), geteuid());
if (setuid(newuid)) perror("setuid reported failure");
printf("UID after setuid is %d\n", getuid());
return 0;
}
I compiled with gcc
and changed the permissions to 4711
, i.e., -rws--x--x
. The owner of this program is username bob
, UID 32983
.
When user alice
, UID 1000
, runs the try
program, she sees
Original UID is 1000, EUID is 32893
UID after setuid is 1000
In other words, the setuid
function call reported success (returned 0), but the real userid did not in fact change.
If I copy this executable to a root-owned directory such as /usr/local/bin
, change the ownership to root
and keep the permissions at 4711
, then it works as expected when alice
runs it:
Original UID is 1000, EUID is 0
UID after setuid is 0
So it works in this case!
I have tried this and gotten the same behavior on Ubuntu and Debian; kernel versions 3.2.0-4-amd64, 3.5.0-36-generic, and 3.14-1-amd64; and glibc versions 2.13, 2.15, and 2.19.
Why I want to do this
Most linux programs only use the effective UID to determine permissions, so this shouldn't matter. But some, notably bash
, will ignore the effective UID and revert to the real UID. Of course there are some great reasons for that, but writing a C wrapper for a SUID bash script seems to be a standard work-around. (I am properly sanitizing the environment and have a really good reason to do this; namely, my own laziness.)
In any case, I am very curious why this would work to setuid to root
but not to any normal user.