Using the setuid bit in Linux - c

I have this C file:
#include <stdio.h>
#include <unistd.h>
int main(void)
{
printf("%s\n", getlogin());
printf("%i\n", getuid());
}
I compile it, set the UID and GID both to root and set the setuid bit, so that it looks like this:
-rwsrwsr-x 1 root root 8735 Apr 8 19:51 a.out
However when I call $ ./a.out I still get:
user
1000
What am I doing wrong?

The real user ID is still the user that called the program, but the effective user ID is root. In a setuid program, they are not the same.
To get the effective user ID, call geteuid(). You can also use cuserid() to get the name associated with the effective user ID.

Your program has got only the permission to change its uid. To actually switch to root you have to call setuid(0) in it.
Please have a look here

Related

setuid(###) followed by getuid() fails in C program with permissions bit set

I'm trying something very simple with setuid followed by getuid in a C program that is not working unless the user is the owner of the c program. I am setting the permissions bit using chmod 7777 in addition to thesetuid. Here is the C program...
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main()
{
int x;
printf("Setting uid to 1111\n");
x=setuid(1111);
printf("setuid returned... %d\n",x);
printf("Testing state of uid using getuid()\n");
printf("uid: %d\n",getuid());
return 0;
}
Given that 1111 is the uid of the owner of the C program (returned by id -u).
after gcc, chmod 7777 on the binary produced.
Testing as the owner works fine...
% suid
Setting uid to 1111
setuid returned... 0
Testing state of uid using getuid()
uid: 1112
and to check the permissions on the binary...
ls -l suid
-rwsrwsrwx 1 katman design 6852 Mar 1 17:56 suid
Then I become another user (same group BTW) and run the "suid" executable again...
% suid
Setting uid to 1111
setuid returned... 0
Testing state of uid using getuid()
uid: 57849
Why isn't setuid working given that the owner granted permission to do this?
Also...
Q: If I can get this to work, I have heard that one can do a system() call to run a cshell script from a C program like this and the shell script should run as the owner of the C program. The uid is "passed down" to children. True or False?
Q: If the cshell script runs a perl script which in turn runs system calls, etc... will all these offspring shells run as the owner of the parent C binary that started started the whole cascade? IOW, is the uid "inherited" by children?

setuid not working, linux rhel6, simple example

This is a repost after being referred to "Calling a script from a setuid root C program - script does not run as root" for a solution
My problem is different in that I do not want to run the C program (and c-shell script called inside) as root. Rather, I want to run as the owner of the c program file.
Also, I tried with "setuid(0)" as this was the solution in the referenced post. This is reflected in the edited code below. Same results.
Also, I opened permissions up all the way this time with "chmod 7775" (just in case it was a permissions problem)
Here's the original note with edits to reflect the change to "setuid(0)"
I'm having a problem implementing an simple example that would demonstrate how setuid can be made to run a binary with the uid of the file owner of the binary. What I would eventually like to do is run a c-shell script using this technique.
I read that this will not work for shell scripts but also heard of a work-around using a C program to run a system() call that'll run the c-shell script ( e.g. system("source my.csh") ). I wrote a C program that attempts this, plus simply reports the current uid. This is what I tried...
From my current shell, I did a "su katman" to become the user of the binary I want to run as user katman.
I created a C program try.c. ...
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main()
{
setuid(0);
printf("In try.c ... sourcing katwhoami.csh...\n");
system( "source /home/me/su_experiments/katwhoami.csh");
printf("In try.c ... using straight system call...\n");
system("whoami");
return 0;
}
I "setuid(0)" as recommended by a reference to a different note. But earlier, I tried setting it to the uid of the C program's owner as obtained with "id -u".
The katwhoami.csh shell script is simply...
date
echo "In katwhoami.csh, I am "`whoami`
echo "In katwhoami.csh, I am "$USER
exit
Then I compiled the C program and set the bit...
% gcc -o try try.c
% chmod 7775 try
% ls -l try
-rwsrwsr-x 1 katman design 6784 Mar 1 11:59 try
And then I test it...
% try
In try.c ... sourcing katwhoami.csh...
Thu Mar 1 12:28:28 EST 2018
In katwhoami.csh, I am ktadmin
In katwhoami.csh, I am ktadmin
In try.c ... using straight system call...
ktadmin
...which is what I expected.
I exit to get back to the shell I started from, hoping that if I run try there, it'll tell me I'm "katman"....
% exit
% whoami
daveg
% try
In try.c ... sourcing katwhoami.csh...
Thu Mar 1 12:30:04 EST 2018
In katwhoami.csh, I am daveg
In katwhoami.csh, I am daveg
In try.c ... using straight system call...
daveg
... which is not what I was hoping for :-(
As you can probably tell, I'm new at using this.
Any help would be appreciated !
Update....
I tried a new sample program, a.c...
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main()
{
FILE *fh;
setuid(1234);
system("whoami");
fh=fopen("test.file","w");
fprintf(fh,"Here I am\n");
fclose(fh);
return 0;
}
I compiled and set the bit
gcc -o a a.c
chmod 4775 a
Then I exited the "su" and ran as a user that is NOT the owner of the binary. Same result as before with regard to reported uid (the current uid), BUT, the owner of the file that the C program created ion this version was the owner of the C program !!! So that worked.
Is there something fishy about the "whoami" command? system() call ?
If I do a "system("source some.csh") does it create a new shell which assumes the original uid (not the owner of the C binary) ? Something like that ?
I really need the new uid to "stick" as far as child processes.

Why is my setuid function not working?

I've the following simple code to check my getuid function:
uidActual=getuid();
printf ("User id is [%d]\n", uidActual);
error=setuid(197623);
printf("[%d]",error);
uidActual=getuid();
printf ("\n User id is [%d]\n", uidActual);
But it always returns a -1 as error, so the uid doesn't change.
The 197623 in setuid seems right as I've, apart from other things, the following in my mkpasswd command:
user1: 197609:197609[...]
user2: 197623:197121[...]
Where 197609 and 197623 must be the id for the user as in fact I start the application with user 1 and I obtain its id properly displaying at the beginning and the end: "User id is 197609".
I've set all permissions for everyone on the created executable and I've even run the executable as a root in cygwin with cygstart --action=runas ./a.exe and it still doesn't work.
Funny thing is that the setgid (for changing group) function works perfectly with setgid(197121), even without special permissions or running. So I'm out of ideas of why this function always returns an error.
Any idea on what is wrong on my code that could be causing the problem?
Thanks for your attention.
the posted code must be run by a user with appropriate privileges. Perhaps by:
sudo ./a.exe
here is an excerpt from the (linux) man page for setuid()
"EPERM The user is not privileged (Linux: does not have the CAP_SETUID capability) and uid does not match the real UID or saved set-user-ID of the calling process."

setgid Operation not permitted

I have a C program that calls setgid() with the group id of the group "agrp", and it is saying "Operation not permitted" when I try to run it.
The program has the following ls -la listing:
-r-xr-s--x 1 root agrp 7508 Nov 18 18:48 setgidprogram
What I want, is setgidprogram to be able to access a file that has the owner otheruser and the group agrp, and permissions set to u+rw,g+rw (User and group read/writeable.)
What am I doing wrong? Does setgidprogram HAVE to have the setuid bit set also? (When I tried it, it worked.)
I am running Fedora 19, and I have SELinux disabled.
EDIT
Here is some example code:
wrap.c:
#include <stdio.h>
#include <errno.h>
#include <sys/types.h>
#include <unistd.h>
#include <grp.h>
int main(void)
{
struct group *grp = getgrnam("agrp");
printf("%d\n",grp->gr_gid);
if(setgid(grp->gr_gid) != 0)
{
printf("%s.\n", strerror(errno));
return 1;
}
execl("/tmp/whoami_script.sh", NULL);
printf("%s.\n", strerror(errno));
return 0;
}
/tmp/whoami_script.sh:
#!/usr/bin/bash
id
$ ls -la /tmp/whoami_script.sh wrap
-r-xr-xr-x 1 root agrp 19 Nov 18 19:53 /tmp/whoami_script.sh
$ ./wrap
1234
uid=1000(auser) gid=1000(auser) groups=1000(auser),0(root),10(wheel)
---x--s--x 1 root agrp 7500 Nov 18 19:55 wrap
Is this enough information now?
The original version of the question showed 6550 permission on the file.
If you're not either user root or in group agrp, you need to be able to use the public execute permissions on the program — which are missing. Since it is a binary, you don't need read permission. To fix it:
# chmod o+x setgidprogram
(The # denotes 'as root or via sudo', or equivalent mechanisms.) As it stands, only people who already have the relevant privileges can use the program.
If the program is installed SGID agrp, there is no need for the program to try to do setgid(agrp_gid) internally. The effective GID will be the GID belonging to agrp and the program will be able to access files as any other member of agrp could.
That said, normally you can do a no-op successfully. For example, this code works fine:
#include <stdio.h>
#include <unistd.h>
#include "stderr.h"
int main(int argc, char **argv)
{
err_setarg0(argv[argc-argc]);
gid_t gid = getegid();
if (setgid(gid) != 0)
err_syserr("Failed to setgid(%d)\n", (int)gid);
puts("OK");
return 0;
}
(You just have to accept that the err_*() function do error reporting; the argc-argc trick avoids a warning/error from the compiler about otherwise unused argument argc.)
If you make the program SUID root, then the SGID property doesn't matter much; the program will run with EUID root and that means it can do (almost) anything. If it is SUID root, you should probably be resetting the EUID to the real UID:
setuid(getuid());
before invoking the other program. Otherwise, you're invoking the other program as root, which is likely to be dangerous.
Dissecting POSIX
In his answer, BenjiWiebe states:
The problem was I was only setting my effective GID, not my real GID. Therefore, when I exec'd, the child process was started with the EGID set to the RGID. So, in my code, I used setregid() which worked fine.
Yuck; which system does that? Linux trying to be protective? It is not the way things worked classically on Unix, that's for sure. However, the POSIX standard seems to have wriggle room in the verbiage (for execvp()):
If the ST_NOSUID bit is set for the file system containing the new process image file, then the effective user ID, effective group ID, saved set-user-ID, and saved set-group-ID are unchanged in the new process image. Otherwise, if the set-user-ID mode bit of the new process image file is set, the effective user ID of the new process image shall be set to the user ID of the new process image file. Similarly, if the set-group-ID mode bit of the new process image file is set, the effective group ID of the new process image shall be set to the group ID of the new process image file. The real user ID, real group ID, and supplementary group IDs of the new process image shall remain the same as those of the calling process image. The effective user ID and effective group ID of the new process image shall be saved (as the saved set-user-ID and the saved set-group-ID) for use by setuid().
If I'm parsing that right, then we have a number of scenarios:
ST_NOSUID is set.
ST_NOSUID is not set, but SUID or SGID bit is set on the executable.
ST_NOSUID is not set, but SUID or SGID biy is not set on the executable.
In case 1, it is fairly clearly stated that the EUID and EGID of the exec'd process are the same as in the original process (and if the EUID and RUID are different in the original process, they will be different in the child).
In case 2, if the SUID bit is set on the executable, the EUID will be set to the SUID. Likewise if the SGID bit is set on the executable, the EGID will be set to the SGID. It is not specified what happens if the SUID bit is set, the SGID bit is not set, and the original process has different values for EGID and RGID; nor, conversely, is it specified what happens if the SGID bit is set, the SUID bit is not set, and the original process has different values for EUID and RUID.
Case 3, where neither the SUID nor SGID bit is set on the executable, also seems to be unspecified behaviour.
Classically on Unix systems, the EUID and RUID could be different, and the difference would be inherited across multiple (fork() and) exec() operations if the executable does not override the EUID or EGID with its own SUID or SGID bits. However, it is not clear that the POSIX standard mandates or prohibits this; it seems to be unspecified behaviour. The rationale section provides no guidance on the intentions.
If my reading is correct, then I find it amusing that the ST_NOSUID bit means that if a program is launched by a process that is running SUID, then the program on the 'no SUID' file system will be run with different real and effective UID (RUID and EUID), which seems counter-intuitive. It doesn't matter what the SUID and SGID bits on the executable are set to (so the bits on the executable are ignored), but the inherited values of EUID and RUID are maintained.
This code finally worked:
#include <stdio.h>
#include <errno.h>
#include <sys/types.h>
#include <unistd.h>
#include <grp.h>
int main(void)
{
gid_t g = getegid();
if(setregid(g, g) != 0)
{
printf("Error setting GID: %s.\n", strerror(errno));
}
execl("/tmp/whoami_script.sh", "/tmp/whoami_script.sh", NULL);
printf("Error: %s.\n", strerror(errno));
return 0;
}
The problem was I was only setting my effective GID, not my real GID. Therefore, when I exec'd, the child process was started with the EGID set to the RGID. So, in my code, I used setregid which worked fine.

Check if user is root in C?

How can I verify if the user is root?
Usually it's a mistake to test if the user is root. POSIX does not even require a root user, but leaves it to the implementation to determine how permissions work. Code such as:
if (i_am_root) do_privileged_op(); else print_error();
will really annoy users with advanced privilege models where root is not necessary to perform the necessary privileged operations. I remember back in the early days of cd burning on Linux, I had to hack all over the cdrecord source to remove all the useless checks to see if it was running as root, when it worked just fine with permission to read /dev/sga.
Instead, you should always attempt the privileged operation you need to perform, and check for EPERM or similar if it fails to notify the user that they have insufficient privileges (and perhaps should retry running as root).
The one case where it's useful to check for root is checking if your program was invoked "suid-root". A reasonable test would be:
uid_t uid=getuid(), euid=geteuid();
if (uid<0 || uid!=euid) {
/* We might have elevated privileges beyond that of the user who invoked
* the program, due to suid bit. Be very careful about trusting any data! */
} else {
/* Anything goes. */
}
Note that I allowed for the possibility (far-fetched, but best to be paranoid) that either of the calls to get uid/euid could fail, and that in the failure case we should assume we're suid and a malicious user has somehow caused the syscalls to fail in an attempt to hide that we're suid.
getuid or geteuid, depending on what you really mean. In either case, 0 means root.
#include <stdio.h>
#include <unistd.h>
if (geteuid() != 0) {
fprintf(stderr, "App needs root\n");
exit(1);
}
The point made by R is valid. You should consider trial and error, or another approach that does not explicitly require root.
better to use getuid or geteuid but it is in zconf.h header file and you must enter that like bellow :
#include <zconf.h>
#include <stdio.h>
int main()
{
int a;
a=getuid();
//or you can use a=geteuid();
//euid is effective user id and uid is user id
// both euid and uid are zero when you are root user
if (a==0){
printf("you are root user");
//so you can do what`enter code here`ever `enter code here` you want as root user
}
else
printf("please run the script as root user !");
return 0;
}

Resources