How to programmatically gain root privileges?

45,185

Solution 1

If you need root privileges every time, the best thing is to start your program as root and drop them (in a subprocess) with setuid and setgid. That's what apache does when it needs to bind to the restricted port 80.

If gaining root rights is the exception instead of the rule and the program is run interactively, another way is to write a program add_interface and execute

sudo add_interface args

and let sudo handle authentication for you. Instead of sudo, you may want to use a graphical frontend like gksu, gksudo, kdesu, or kdesudo. I wouldn't try to implement secure password input myself; it can be a tricky problem and you'll probably leave gaping security holes and functionality problems (Do you support fingerprint readers?).

Another alternative is polkit, previously called PolicyKit.

Solution 2

You can't gain root privileges, you must start out with them and reduce your privileges as needed. The usual way that you do this is to install the program with the "setuid" bit set: this runs the program with the effective userid of the file owner. If you run ls -l on sudo, you'll see that it is installed that way:

-rwsr-xr-x 2 root root 123504 2010-02-25 18:22 /usr/bin/sudo

While your program is running with root privileges, you can call the setuid(2) system call to change your effective userid to some non-privileged user. I believe (but haven't tried this) that you could install your program as root with the setuid bit on, immediately reduce privilege, and then restore privilege as needed (it's possible, however, that once you lower your privilege you won't be able to restore it).

A better solution is to break out the piece of your program that needs to run as root, and install it with the setuid bit turned on. You will, of course, need to take reasonable precautions that it can't be invoked outside of your master program.

Solution 3

Normally this is done by making your binary suid-root.

One way of managing this so that attacks against your program are hard is to minimize the code that runs as root like so:

int privileged_server(int argc, char **argv);
int unprivileged_client(int argc, char **argv, int comlink);


int main(int argc, char **argv) {
    int sockets[2];
    pid_t child;
    socketpair(AF_INET, SOCK_STREAM, 0);  /* or is it AF_UNIX? */

    child = fork();
    if (child < 0) {
        perror("fork");
        exit(3);
    } elseif (child == 0) {
        close(sockets[0]);
        dup2(sockets[1], 0);
        close(sockets[1]);
        dup2(0, 1);
        dup2(0, 2); /* or not */
        _exit(privileged_server(argc, argv));
    } else {
        close(sockets[1]);
        int rtn;
        setuid(getuid());
        rtn = unprivileged_client(argc, argv, sockets[0]);
        wait(child);
        return rtn;
    }
}

Now the unprivileged code talks to the privileged code via the fd comlink (which is a connected socket). The corresponding privileged code uses stdin/stdout as its end of the comlink.

The privileged code needs to verify the security of every operation it needs to do but as this code is small compared to the unprivileged code this should be reasonably easy.

Solution 4

On OS X, you can use the AuthorizationExecuteWithPrivileges function. The page on Authorization Services Tasks has some elaborate discussion of this (and related) functions.

Here's a bit of C++ code to execute a program with administrator privileges:

static bool execute(const std::string &program, const std::vector<std::string> &arguments)
{
    AuthorizationRef ref;
    if (AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment, kAuthorizationFlagDefaults, &ref) != errAuthorizationSuccess) {
        return false;
    }

    AuthorizationItem item = {
        kAuthorizationRightExecute, 0, 0, 0
    };
    AuthorizationRights rights = { 1, &item };
    const AuthorizationFlags flags = kAuthorizationFlagDefaults
                                   | kAuthorizationFlagInteractionAllowed
                                   | kAuthorizationFlagPreAuthorize
                                   | kAuthorizationFlagExtendRights;

    if (AuthorizationCopyRights(ref, &rights, kAuthorizationEmptyEnvironment, flags, 0) != errAuthorizationSuccess) {
        AuthorizationFree(ref, kAuthorizationFlagDestroyRights);
        return false;
    }

    std::vector<char*> args;
    for (std::vector<std::string>::const_iterator it = arguments.begin(); it != arguments.end(); ++it) {
        args.push_back(it->c_str());
    }
    args.push_back(0);

    OSStatus status = AuthorizationExecuteWithPrivileges(ref, program.c_str(), kAuthorizationFlagDefaults, &args[0], 0);

    AuthorizationFree(ref, kAuthorizationFlagDestroyRights);
    return status == errAuthorizationSuccess;
}

Solution 5

You might want to take a look at these APIs:

setuid, seteuid, setgid, setegid, ...

They're defined in the header <unistd.h> in Linux systems (don't know much about MAC, but you should have a similar header there too).

One problem that I can see is that the process must have sufficient privileges to change its user-/group- IDs. Otherwise calls to the above functions will result in an error with errorno set to EPERM.

I would suggest that you run your program as the root user, change effective user ID (using seteuid) to an underprivileged user at the very beginning. Then, whenever you need to elevate permissions, prompt for a password then use seteuid again to revert to the root user.

Share:
45,185
ereOn
Author by

ereOn

I love Golang, networking and security. If you need an open-source, free, multi-platform, VPN solution, you may want to check http://www.freelan.org which is basically how I spend my free time.

Updated on February 11, 2020

Comments

  • ereOn
    ereOn about 4 years

    I am writing some software (in C++, for Linux/Mac OSX) which runs as a non-privileged user but needs root privileges at some point (to create a new virtual device).

    Running this program as root is not a option (mainly for security issues) and I need to know the identity (uid) of the "real" user.

    Is there a way to mimic the "sudo" command behavior (ask for user password) to temporarily gain root privileges and perform the particular task ? If so, which functions would I use ?

    Thank you very much for your help !

  • ereOn
    ereOn about 14 years
    Thank you for your answer. Unfortunately, the program doesn't always need root privileges. Sometimes you might just want to use it in a way so it doesn't need to do that particular task. In such a case, asking for the password is not needed. I've found threads that talk about setuid(). I'm going to investigate this.
  • ziggystar
    ziggystar about 14 years
    Is sudo guaranteed to be available on every machine? I've seen installations without it.
  • Admin
    Admin about 14 years
    Nope, it's not configured on fedora by default. I also personally block users not in the group wheel from using sudo or su as an extension of traditional unix behaviour.
  • Gerco Dries
    Gerco Dries about 14 years
    You only run the part of your code that needs root privs through sudo of-course. When you don't need root privs, don't ask for the password. Running the entire program through sudo was NOT my answer, 'the command' refers ONLY to the command that needs root privs, not the entire application.
  • Matt Joiner
    Matt Joiner almost 14 years
    your second block i think you mean child > 0
  • user470379
    user470379 over 13 years
    Actually I believe it should be else if (child == 0) -- the else block wait s for the child, so the child needs to be the middle else if block.
  • Toby Speight
    Toby Speight over 7 years
    You probably want AF_UNIX if you want to pass file descriptors across the socket.
  • vpalmu
    vpalmu over 7 years
    I deliberately design so that I don't pass file descriptors around.
  • CMCDragonkai
    CMCDragonkai over 7 years
    Doesn't the Ubuntu Software-Center interactively gain root privileges during operation? Apparently via polkit.
  • gkhanacer
    gkhanacer over 6 years
    Where we call this function? And what would be arguments parameter? I tried to call at the beginning of main function, but it crashed. arguments was null. My problem is creating a file at /var/log. But permission is necessary. How can I deal with this problem?
  • Konstantin
    Konstantin over 6 years
    It'd be worthy to mention all the libraries this code needs, wouldn't it?