correct way to run setuid programs in C

cateof picture cateof · Feb 15, 2012 · Viewed 23.1k times · Source

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?

Answer

Joshua picture Joshua · Feb 15, 2012

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.