I have a process that gets started by a damon running as root, now I want to "downgrade" this process's privileges to those of your average user. Is this possible? If yes how?
PS: Running unix on a mac
I have a process that gets started by a damon running as root, now I want to "downgrade" this process's privileges to those of your average user. Is this possible? If yes how?
PS: Running unix on a mac
sudo tcpdump -Z
uses initgroups(3), setgid(2) and setuid(2) to drop the root privileges of its own process.
# code taken from:
# http://www.opensource.apple.com/source/tcpdump/tcpdump-32/tcpdump/tcpdump.c
/* Drop root privileges and chroot if necessary */
static void
droproot(const char *username, const char *chroot_dir)
{
...
if (initgroups(pw->pw_name, pw->pw_gid) != 0 ||
setgid(pw->pw_gid) != 0 || setuid(pw->pw_uid) != 0) {
fprintf(stderr, "tcpdump: Couldn't change to '%.32s' uid=%lu gid=%lu: %s\n",
username,
(unsigned long)pw->pw_uid,
(unsigned long)pw->pw_gid,
pcap_strerror(errno));
exit(1);
}
...
}
The process itself has to call setuid(2). You should also investigate running it inside chroot(8) if you aren't already. As far as I know, there is no way for root to change the uid of another process.
If the reason you are running it as root is to bind ports, I'd suggest running it as a normal user on a higher port and using ipfw(8) on OS X to forward port 80/443/etc to the higher port:
http://support.crashplanpro.com/doku.php/recipe/forward_port_443_to_pro_server_on_mac_osx
setuid()
alone is absolutely not enough.
– Nicholas Wilson
Feb 22 '13 at 22:35
You can run commands as other users using su
:
su USERNAME -c COMMAND
Will run COMMAND
with privileges dropped to USER
.
Note that, by default, su
will use the target user's shell interpreter to run the command. By contrast, the default behaviour of sudo
is to treat the COMMAND
as a standalone program, that is run the current environment. Of course these default behaviours can be changed with various switches and environment variables.
su
doesn't work if USERNAME has no shell defined (or /bin/false
) whereas sudo does work.
– Aif
Jan 16 '13 at 21:24
su
will reflect that. However, one can always override that using the -s
switch. Note that the purpose of su
is to mimic a given user's behaviour - which is normally influenced by his/her shell. By contrast, sudo
would (by default) ignore target user's shell setting.
– rozcietrzewiacz
Jan 17 '13 at 00:37
To drop privileges, you need a non-root user to drop to. Then it's just a matter of switching to that user:
#define UNPRIV_UID 48
#define UNPRIV_GID 48
if (getuid() == 0) { // we are root
// setting UID/GID requires root privileges, so if you don't set
// the GID first, you won't be able to do it at all.
if (setgid(UNPRIV_GID)!=0) die("Failed to set nonroot GID");
if (setuid(UNPRIV_UID)!=0) die("Failed to set nonroot UID");
}
ASSERT(getuid() != 0);
Note that this is done within the program itself, rather than in a wrapper script. Many programs require root privileges for some specific purpose (e.g. to bind to a low-numbered port), but don't need root after that. So these programs will start as root, but then drop privileges once they're no longer needed.
If you don't need root privileges at all, then just don't run it as root. E.g.:
# Change this:
myprog -C /my/config/file
# To this:
sudo -u someuser myprog -C /my/config/file
# Or this
su someuser -c "myprog -C /my/config/file"
setuid
function only sets the effective UID, not the real UID. You should use setreuid
if you don't want the process to be able to get the privileges back. (And the code above doesn't deal with supplementary group privileges either. It's suitable only for launching mostly-trusted code.)
– David Schwartz
Sep 23 '11 at 10:35
setuid()
does set real and saved userids; you may be thinking of seteuid()
. Not all systems have setreuid()
, so it can't be used quite everywhere. The exact semantics of setuid()
are compliced, but if you have euid 0, you will be able to drop all traditional user-id privileges with setuid()
. The biggest omission in this answer is that initgroups
or setgroups
must be called as well as setgid
and setuid
, and that more thorough assertions should be done at the end.
– Nicholas Wilson
Feb 22 '13 at 22:41
If you're executing a different executable, i.e. you're calling execve
or another of the exec
familiy of function, perhaps indirectly through a function like system
or popen
, and the child process should run without privileges from the start, then the simplest way is to get a shell involved, and call su
. Here's an overview of how the code might look like in Perl, showing the quoting needed for :
$shell_command = quotemeta($path_to_executable) . " --option";
$shell_command =~ s/'/'\\''/; # protect single quotes for the use as argument to su
$su_command = sprintf("su -c '%s' %s", $shell_command, quotemeta($user_name));
open(PIPE, "$su_command |") or die;
If the child process needs to start as root but drop privileges later, see the code in this answer, which illustrates how to downgrade privileges in a process.
initgroups
,setgid
,setuid
(last!) is precisely the right paradigm on unix, and should always be followed. In addition, a responsible "droproot" function checks that its uid and gid really have been set, even if all three primary functions returned success. – Nicholas Wilson Feb 22 '13 at 22:43