7

I have a process with permissions 4750. Two users exist in my Linux system. The root user and the appz user. The process inherits the permissions of a process manager that runs as "appz" user.

I have two basic routines:

void do_root (void)
{
        int status;
        status = seteuid (euid);
        if (status < 0) { 
        exit (status);
        }    
}

/* undo root permissions */
void undo_root (void)
{
int status;
        status = seteuid (ruid);
        if (status < 0) { 
                exit (status);
        }
        status = setuid(ruid);
        if (status < 0) { 
                exit (status);
        }
}

My flow is the following:

int main() {
 undo_root();
 do some stuff;
 do_root();
 bind( port 80); //needs root perm
 undo_root();
 while(1) {

    accept commads()
    if ( commands needs root user access)
    {
       do_root();
       execute();
       undo_root();

    }

 }

As you can see I want to execute some commands as root. I am trying to drop permissions temporarily and if the tasks needs root access I wrap the command between a do_root and undo_root call.

However it seems that my program is not working.

What is the canonical way to do it?

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
cateof
  • 6,608
  • 25
  • 79
  • 153
  • Rather than just exiting when seteuid fails, call perror() and your program will tell you why it is failing. – William Pursell Feb 15 '12 at 16:45
  • 4
    Once you drop root permissions, you cannot get them back!! – Anya Shenanigans Feb 15 '12 at 16:47
  • Technically, it is the file holding the program that has 4750 permission, not the process. You don't directly say that the permissions are `root:group:4750` - is that a safe inference? – Jonathan Leffler Feb 15 '12 at 16:48
  • @JonathanLeffler yes. The file has 4750 perm. – cateof Feb 15 '12 at 16:49
  • 4
    @Petesh: You are wrong. You can. –  Feb 15 '12 at 16:50
  • 1
    To further clarify Petesh's remark - getting root permissions back requires more complexity (you basically have to switch the euid and real uid, and keep one of them 0 at all times) - your "undo_root" function seems to be a "permanently drop root" function [sometimes used in programs that only need root once during startup, or between fork and exec] – Random832 Feb 15 '12 at 16:52
  • @Random832, what code do I need? – cateof Feb 15 '12 at 17:25
  • 1
    Please be aware that dropping root temporarily is utterly useless for security purposes. It's only useful if you want to temporarily access the filesystem as a different user. For security you must fully and permanently drop root. – R.. GitHub STOP HELPING ICE Feb 15 '12 at 19:30

3 Answers3

7

The old-school way is to in both do_root and undo_root to use setreuid() to swap ruid and euid:

setreuid(geteuid(), getuid());

This is perfectly acceptable if the program is small enough to do a complete security audit.

The new-school way is far more complex and involves fork()ing off a child that accepts directives for what to do as root and then doing setuid(getuid()) to drop root permanently in the parent.. The child is responsible for validating all directives it receives. For a large enough program, this drops the amount of code that must be security audited, and allows the user to manage the process with job control or kill it, etc.

Joshua
  • 40,822
  • 8
  • 72
  • 132
5

There is a paper 'Setuid Demystified' by Hao Chen, David Wagner, and Drew Dean. It was presented at USENIX 2002. It describes how setuid() and transitions work in great detail (correct as of 2002). It is well worth reading (several times - I must be a year or two overdue on a re-read of it).

Fundamentally, as Petesh noted in a comment, when a process with EUID 0 does setuid(nuid) with nuid != 0, there is no way back to root (EUID 0) privileges. And, indeed, it is vital that it is so. Otherwise, when you login, the root process that logs you in could not limit you to your own privileges - you'd be able to get back to root. Saved UID complicates things, but I don't believe it affects the one-way trap of EUID 0 doing setuid().

Community
  • 1
  • 1
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
2

The setuid man page says the following:

... a set-user-ID-root program wishing to temporarily drop root privileges, assume the identity of a non-root user, and then regain root privileges afterwards cannot use setuid()

Meaning that you cannot use setuid(). You have to use seteuid() and, possibly, setreuid(). See Setuid Program Example for more details.

  • My program is based exactly to this GNU page. Instead of do_setuid, I initially did do_root. – cateof Feb 15 '12 at 16:55
  • @cateof: I see you use `setuid` function in the code you posted. That is a one way ticket. –  Feb 15 '12 at 17:07