Not understanding setuid - c

I created a VERY simple script:
//#escalate.c - a setuid utility so that we can call shutdown
//# and other things safely without needing root access. We
//# do need to:
//# gcc escalate.c -o escalate.out
//# sudo chown root:root escalate.out
//# sudo chmod 4755 escalate.out
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <errno.h>
int main()
{
int status;
status = setuid( 0 ); // you can set it at run time also
system("date > /tmp/date.fil");
return errno;
}
On Raspian it generates the file in /tmp, owned by the root and returns 0 as expected.
On Ubuntu 22 it created the file owned by ME and the return status is 1. What am I missing about setuid(0); ?
I tried creating, modifying the permissions and ownership etc. On Raspian it works like a charm, on Ubuntu it does not.
==================
OK - solved it myself. On Ubuntu I was running with an encrypted home and so it was mounted with nosuid set.

the problem was that the file system was mounted nosuid

Related

Problems with readlink and set uid

I'm having trouble getting setuid to work as hoped (chmod 3755).
I'm trying to implement, using C language, my own version of psgrep (or pgrep) for a very specific need. In order to accomplish this, I need to get the value of the link at /proc/[pid]/exe for every listed [pid]
To demonstrate the issue, I made this tiny program: (proof.c)
#define _GNU_SOURCE
#include <unistd.h>
#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[]) {
char procFilename[] = "/proc/1/exe" ;
char pointsTo[80] ;
int rc ;
memset(pointsTo, 0x00, sizeof(pointsTo)) ;
rc = readlink(procFilename, pointsTo, sizeof(pointsTo)) ;
if ( rc < 0 ) {
perror("Trying to read /proc/1/exe link") ;
}
else {
printf("%s points to %s.\n", procFilename, pointsTo) ;
}
}
In the same folder, I exeucte:
gcc -o proof proof.c
sudo chown root:root proof
sudo chmod 3755 proof
Now, here are the execution results:
> ./proof
Trying to read /proc/1/exe link: Permission denied
> sudo ./proof
/proc/1/exe points to /usr/lib/systemd/systemd.
> ls -l proof
-rwxr-sr-t 1 root root 16888 Feb 16 14:06 proof
As I understand it, the chmod and chown combination should have gotten me past the permission error, but obviously I am missing something.
When I searched stackoverflow for "sticky-bit problems" there were no good matches (though a surprising number of people expect setuid to work with scripts). One problem about 2.6 kernel didn't seem to fit either. Anyway, what might I be missing?

syscall(SYS_getuid) returns different result from getuid()

I would like to use syscalls to get the id of the current user. I tried it like this:
#include <unistd.h>
#include <sys/syscall.h>
#include <stdio.h>
int main(int argc, char *argv[])
{
int uid = syscall(SYS_getuid);
printf("%d\n", uid);
return 0;
}
I executed it with root, but it prints -1 instead of 0.
If I replace it with int uid = syscall(SYS_getuid);, then it correctly returns 0. What do I wrong? How to get the current user id using syscall?
I run it on i686/ubuntu docker image, because I have to create 32bit executables.
Minimal reproducible example:
Dockerfile
FROM i686/ubuntu
RUN apt-get update
RUN apt-get install --assume-yes --no-install-recommends --quiet \
gcc libc6-dev
RUN apt-get clean all
main.c
#include <unistd.h>
#include <sys/syscall.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
int main(int argc, char *argv[])
{
int uid = syscall(SYS_getuid);//getuid();//
if(uid == -1)
printf("Error: %s\n", strerror(errno));
printf("%d\n", uid);
return 0;
}
Run ( On x64 Windows10 ):
docker build --platform linux/386 -t my-gcc .
docker run --platform linux/386 --rm -v ${pwd}:/usr/local/src/:rw my-gcc gcc -m32 -xc /usr/local/src/main.c -o /usr/local/src/main
docker run --platform linux/386 --rm -v ${pwd}:/usr/local/src/:rw my-gcc /usr/local/src/main
The result is:
Error: Function not implemented
-1
Per getuid(2):
The original Linux getuid() and geteuid() system calls supported only 16-bit user IDs. Subsequently, Linux 2.4 added getuid32() and geteuid32(), supporting 32-bit IDs. The glibc getuid() and geteuid() wrapper functions transparently deal with the variations across kernel versions.
Apparently you are running your program on a kernel that has the old getuid system call compiled out, and only getuid32 is available on x86-32. If you run fgrep CONFIG_UID16 "/boot/config-$(uname -r)", you will be able to see if your running kernel supports the 16-bit syscall. If this command prints anything other than CONFIG_UID16=y, it means the old system call is unavailable.
If you invoke SYS_getuid32 instead, it should work fine. Note that SYS_getuid32 may fail to be available on other architectures.

Problem with new System call I'm trying to make (Ubuntu 18.04.1 LTS Kernel:4.17.4)

These are the steps I followed.
1) I was running on 4.15.0 kernel so I updated to more recent kernel.
wget https://www.kernel.org/pub/linux/kernel/v4.x/linux-4.17.4.tar.xz
2)Extracted the kernel source code using
sudo tar -xvf linux-4.17.4.tar.xz -C/usr/src/
3) In cd /usr/src/linux-4.17.4/ created a new directory called it
sub
Then created
sub.c
inside
sub
directory.
In
sub.c
I wrote the code to subtract y from x if (y>x) or else return 0; Here x is Integer and y is double.
#include <linux/kernel.h>
asmlinkage int sys_sub(int x,double y)
{
printk("working...");
if(y>x){
return ((int)y-x);}
else
return 0;
}
4) created a Makefile in the same sub directory and added obj-y := sub.o
5) In
/usr/src/linux-4.17.4
opened Makefile and modified the core-y line to
core-y += kernel/ mm/ fs/ ipc/ security/ crypto/ block/ sub/
6) Then in
cd arch/x86/entry/syscalls/
I opened
gedit syscall_64.tbl
and as 548th system call I entered
548 64 hello sys_sub
7) In
cd include/linux/
I opened
gedit syscalls.h
and added
asmlinkage int sys_sub(int x,double y);
as the last line just before endif
8) I made sure ext4 is chosen in
sudo make menuconfig
9) I compiled the kernel using
sudo make modules_install install
10) performed
shutdown -r now
11) Checked
uname -r
to make sure that I'm running
4.17.4
indeed which I was.
12) I created a C program to check the system call
#include <stdio.h>
#include <linux/kernel.h>
#include <sys/syscall.h>
#include <unistd.h>
int main()
{
int res = syscall(548,10,44);
printf("System call sys_sub returned %d ", res);
return 0;
}
But it is only returning
System call sys_sub returned 0
and
dmesg
shows Hello World for some reason. Please help me. What am I doing wrong?
Edit:
I made necessary changes in my code as per the comments I read.
Now my system call code looks like this:
#include <linux/kernel.h>
asmlinkage long sys_sub(int a,int b)
{
printk("System call is working...\n");
printk("Inputs are %d and %d",a,b);
if(b>a)
{
int c= b-a;
printk("Answer is %d",c);
return c;
}
printk("Answer is 0");
return 0;
}
I added some print statements to make sure that the system calls are correctly invoked.
I recompiled the kernel and ran again and now I'm getting
dmesg
output as
System call is working... Inputs are 1114685272 and 1114685272 Answer
is 0
Seems like the kernel is getting random junk values instead of the parameters that I'm passing which makes it to always fail the if loop. The random values for both parameters seems to always be the same!
I dont know where I'm going wrong now.
In one internet article it is mentioned to compile whole kernel and then modules_install. Also return of system call has to be long instead of int. unistd.h file needs some changes for new system call.
The article is for 2.6 kernel but can be referred at http://www.tldp.org/HOWTO/html_single/Implement-Sys-Call-Linux-2.6-i386/

Set owner of the new created file in C

I have a program write.c, which creates a new file. I compiled that through root user and set the sticky bit for setuid using chmod u+s write.
Now, if a user2 executes this program. A new file is created with the root as owner, why ? The owner of the file should be user2.
For that, I changed the uid using setuid() and seteuid() to user2. And then created the file. But this also creates the file with root as owner. I want to create the file as user2 as owner.
Post an mcve. What you describe works just fine on my system. This:
#!/bin/sh -e
cat > main.c <<EOF
#define _GNU_SOURCE
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char** argv)
{
int fd;
uid_t ruid,euid,suid;
struct stat sb;
getresuid(&ruid,&euid,&suid);
printf("ruid=%ld euid=%ld suid=%ld\n", (long)ruid,(long)euid,(long)suid);
if(0>(fd = open(argv[1], O_CREAT|O_RDWR, 0660))){
perror(0);
exit(1);
}
fstat(fd,&sb);
printf("owner=%ld\n", (long)sb.st_uid);
close(fd);
seteuid(ruid);
getresuid(&ruid,&euid,&suid);
printf("ruid=%ld euid=%ld suid=%ld\n", (long)ruid,(long)euid,(long)suid);
if(0>(fd = open(argv[2], O_CREAT|O_RDWR, 0660))){
perror(0);
exit(1);
}
fstat(fd,&sb);
printf("owner=%ld\n", (long)sb.st_uid);
close(fd);
}
EOF
gcc main.c
sudo chown root a.out
sudo chmod u+s a.out
rm -f roots mine
./a.out roots mine
gets me:
ruid=1008 euid=0 suid=0
owner=0
ruid=1008 euid=1008 suid=0
owner=1008
i.e., the seteuid call succesfully resets my uid and the second file
is no longer owner by root.

unsuccessfully call external shell script as non-root user

I want A user to do some jobs as B user. It's OK if B is root, but non-root user failed. Here are basic codes:
root.c:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
setuid( 0 );
system( "/tmp/script_of_root.sh" );
return 0;
}
script_of_root.sh:
#!/bin/sh
echo $RANDOM >> /tmp/file_of_root
playback:
$ cd /tmp
$ cc root.c -o root
$ su -
# chown root.root /tmp/root
# chmod 4755 /tmp/root
# exit
$ ./root
After executing "./root", file "/tmp/file_of_root" will be updated. But if I apply the same thing to a non-root user, it doesn't work. Codes:
foobar.c:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
setuid( 1001 ); // uid of user "foobar"
system( "/tmp/script_of_foobar.sh" );
return 0;
}
script_of_foobar.sh:
#!/bin/sh
echo $RANDOM >> /tmp/file_of_foobar
playback:
$ cd /tmp
$ cc foobar.c -o foobar
$ su -
# chown foobar.users /tmp/foobar
# chmod 4755 /tmp/foobar
# exit
$ ./foobar
If I run "./foobar" as other normal user(not "foobar" itself), it's gonna be error:
/tmp/script_of_foobar.sh: line 2: file_of_foobar: Permission denied
I am totally confused. Why the second scenario not working?
Best regards.
The setuid call in foobar.c will only succeed if you are root, or your effective UID is 1001.
So. If you're not root, setuid(1001) will fail, and you won't have the required rights to overwrite the file owned by "foobar".
From the output it looks like your non-root user is able to execute script_of_foobar.sh, but unable to write to /tmp/file_of_foobar. Are the permissions on /tmp possibly off? I believe it should be set to 1777.

Resources