I compiled this sample set-root-id program:
#define _GNU_SOURCE
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <errno.h>
void print_ids()
{
uid_t ruid, euid, suid, rgid, egid, sgid;
getresuid(&ruid, &euid, &suid);
printf("\nruid=%d, euid=%d, suid=%d\n", ruid, euid, suid);
getresgid(&rgid, &egid, &sgid);
printf("rgid=%d, egid=%d, sgid=%d\n\n", rgid, egid, sgid);
}
void main(int argc, char *argv[])
{
print_ids();
seteuid(1000); printf("seteuid(1000): %s\n", strerror(errno));
print_ids();
seteuid(1001); printf("seteuid(1001): %s\n", strerror(errno));
print_ids();
seteuid(0); printf("seteuid(0): %s\n", strerror(errno));
print_ids();
}
Call to seteuid(0) works, but dislays an error message: "Operation not permitted":
$ gcc -Wall ./p3.c -o p3
./p3.c:32: warning: return type of ‘main’ is not ‘int’
$ su -c "chown root:root ./p3 ; chmod 4755 ./p3"
Password: ****
$ ls -l ./p3
-rwsr-xr-x 1 root root 7697 2 gen 19.21 ./p3
$ ./p3
ruid=1000, euid=0, suid=0
rgid=1000, egid=1000, sgid=1000
seteuid(1000): Success
ruid=1000, euid=1000, suid=0
rgid=1000, egid=1000, sgid=1000
seteuid(1001): Operation not permitted
ruid=1000, euid=1000, suid=0
rgid=1000, egid=1000, sgid=1000
seteuid(0): Operation not permitted
ruid=1000, euid=0, suid=0
rgid=1000, egid=1000, sgid=1000
$
Why seteuid(0) works but displays the error message? I noticed that omitting call to seteuid(1001) or call to seteuid(1000) no error messages appear.
Thank you
(I work on Debian 6, gcc version 4.4.5)
The old EPERM
is still in errno
from the previous call. Successful system calls do not set errno
to 0; you have to do that yourself.
Usually, you don't need to set errno
to 0. You check the return value of the system call, and then if that indicates failure, you check errno
for more details. errno doesn't tell you that something failed - it tells you how something failed.
There's an exception to this general rule for functions with no distinct error indicator in their return value, like strtoul
, but seteuid
isn't one of those. It has a clean return value. If it returns 0, you shouldn't print an error message at all.
And relying on strerror(0)
to produce a "success message" isn't clean either - in current glibc it says "Success"
but I can still remember when strerror(0)
was "Unknown error 0"
. Maybe somewhere it still is.