I am developing a daemon running as root, but need to call an API with the user, I checked the API codes, it uses getuid()
to get the user.
If root user drops privilege by setuid()
, it can't be restored to root. If calling seteuid(), the API will still do something as user uid=0
.
I think fork before accessing API and setuid in the child process should work, but even if COW , it will cost much if calling API many times. Is it possible to solve the problem except using process pool?
Yes! Create a single process to call the API with the appropriate UID and communicate with the rest of the program through a Pipe, a UNIX domain socket or (shared memory)1.
I mean, fork only once and keep the privileged user running another process. Then create communication between the two if needed and as needed. Also, you might want to consider using dbus since it also integrates perfectly with systemd and on modern linux you want your daemon to interact nicely with both.
Note: I am by no means an expert on the subject, but this is a simple idea that seems clear to me. You don't need to create a process for every call to the API. This is a good example of the XY problem, the real problem that you want to solve, has nothing to do with avoiding to fork()
multiple times because the idea of doing that is the wrong solution. You only need to fork()
once, drop privileges and stay there without privileges, communicating with the parent process if/as needed.
1Any IPC mechanism that works for you.
Just call seteuid(2)
to do the appropiate unprivileged stuff. seteuid(2)
allows to switch between the real(or saved) user id (the one that launches the suid program or root
in your case) and the suid user id (the one the suid program belongs to) so there should be no problem to regain privileged user id afterwards (as the saved user id is root
, you don't have any issue to switch to it again and again).
If you change uids with setuid(2)
you'll change all (effective, saved and real uids) and this is only allowed to the root user (or a program setuid root, and there's no way back then).
Look at the next example:
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
int main(int argc, char **argv)
{
int opt, suid = getuid(), /* this is the saved uid */
uid = 0;
while ((opt = getopt(argc, argv, "i:")) != EOF) {
switch (opt) {
case 'i': uid = atoi(optarg); break;
}
}
/* execute this program with root privileges, like setuid root, for example */
printf("real uid=%d; effective uid=%d\n", getuid(), geteuid());
seteuid(uid); /* change to the non-privileged id configured */
printf("real uid=%d; effective uid=%d\n", getuid(), geteuid());
seteuid(suid); /* return back to saved uid */
printf("real uid=%d; effective uid=%d\n", getuid(), geteuid());
}
You will get an output like this:
$ pru49015 -i 37
real uid=502; effective uid=0
real uid=502; effective uid=37
real uid=502; effective uid=502
when used as a setuid-root program
If you use it as root, you'll get the following output:
$ sudo pru$$ -i 37
real uid=0; effective uid=0
real uid=0; effective uid=37
real uid=0; effective uid=0
The mechanism is that you are allowed on a setuid- program to switch between the user you are (let's call it the saved user id) and the user the program runs setuid to (the called effective user id or suid user) as many times as you want.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With