How to programmatically gain root privileges?
Asked Answered
R

7

34

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 !

Rebba answered 20/3, 2010 at 16:12 Comment(0)
I
15

Original answer

You might consider the setuid switch on the executable itself. Wikipedia has an article on it which even shows you the difference between geteuid() and getuid() quite effectively, the former being for finding out who you're "emulating" and the latter for who you "are". The sudo process, for example, geteuid should return 0 (root) and getuid your user's id, however, its sub-processes do truly run as root (you can verify this with sudo id -u -r).

I don't think there's a way to easily programmatically gain root access - after all, applying the principle of least privilege, why would you need to? Common practise is to run only limited parts of code with elevated privileges. A lot of daemons etc are also set up under modern systems to run as their own user with most of the privileges they need. It's only for very specific operations (mounting etc) that root privileges are truly needed.

2013 update

My original answer stands (although my 2013 self might make a better job of it than my 2010 one), but if you are designing an application that requires root access, you may want to consider exactly what sort of root access is needed and consider the use of POSIX Capabilities (man page). These are different to capability-based security as implemented in L4 et al. POSIX capabilities allow your application to be granted a subset of root's powers. For example CAP_SYS_MODULE will allow you to insert kernel modules, but give you no other root powers. This is in use in distributions e.g. Fedora has a feature to completely remove setuid binaries with indiscriminate root access.

This matters because as a programmer, your code is obviously perfect! But, the libraries on which you depend (sigh, if only you'd written them!) might have vulnerabilities in them. Using capabilities, you can limit the use of this exploit, and save yourself and your company from security-related scrutiny. This makes everyone happier.

Imprecate answered 20/3, 2010 at 16:25 Comment(7)
When you do this, you have to be very careful about security, because your app is now a target for Escalation of Privilege exploitsPhilippines
When you do this, do the stuff that needs root privileges as soon as possible and drop down to the real user right after that.Posehn
Under sudo, for example, geteuid should return 0 (root) and getuid your user's id. This is false. See my question regarding exactly this issue: #10273284Blim
@Blim ah yes. Sudo itself should be able to see those things, but inherited processes won't. They only inherit the effective user id as their real user id. I'll edit this, as it isn't clear.Imprecate
When I do this under Mac OS X (Mountain Lion) I get "The application with bundle ID (null) is running setugid(), which is not allowed." when I try to start the program. What can I do?Peculation
@Peculation I'm not familiar with OSX, however, recently there has been a focus on removing setuid/setgid as these are automatically exploit targets for the bad guys, so it could be that this is a feature of OSX. Have you tried searching SO? If that turned up nothing, do ask your own question. I'd be interested in the answer!Imprecate
the other option is to set the device ownership group using udev rules, and ensuring your user is a member of that group. E.g. the plugdev or disk groupsNonattendance
I
21

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.

Isagoge answered 20/3, 2010 at 16:21 Comment(0)
I
15

Original answer

You might consider the setuid switch on the executable itself. Wikipedia has an article on it which even shows you the difference between geteuid() and getuid() quite effectively, the former being for finding out who you're "emulating" and the latter for who you "are". The sudo process, for example, geteuid should return 0 (root) and getuid your user's id, however, its sub-processes do truly run as root (you can verify this with sudo id -u -r).

I don't think there's a way to easily programmatically gain root access - after all, applying the principle of least privilege, why would you need to? Common practise is to run only limited parts of code with elevated privileges. A lot of daemons etc are also set up under modern systems to run as their own user with most of the privileges they need. It's only for very specific operations (mounting etc) that root privileges are truly needed.

2013 update

My original answer stands (although my 2013 self might make a better job of it than my 2010 one), but if you are designing an application that requires root access, you may want to consider exactly what sort of root access is needed and consider the use of POSIX Capabilities (man page). These are different to capability-based security as implemented in L4 et al. POSIX capabilities allow your application to be granted a subset of root's powers. For example CAP_SYS_MODULE will allow you to insert kernel modules, but give you no other root powers. This is in use in distributions e.g. Fedora has a feature to completely remove setuid binaries with indiscriminate root access.

This matters because as a programmer, your code is obviously perfect! But, the libraries on which you depend (sigh, if only you'd written them!) might have vulnerabilities in them. Using capabilities, you can limit the use of this exploit, and save yourself and your company from security-related scrutiny. This makes everyone happier.

Imprecate answered 20/3, 2010 at 16:25 Comment(7)
When you do this, you have to be very careful about security, because your app is now a target for Escalation of Privilege exploitsPhilippines
When you do this, do the stuff that needs root privileges as soon as possible and drop down to the real user right after that.Posehn
Under sudo, for example, geteuid should return 0 (root) and getuid your user's id. This is false. See my question regarding exactly this issue: #10273284Blim
@Blim ah yes. Sudo itself should be able to see those things, but inherited processes won't. They only inherit the effective user id as their real user id. I'll edit this, as it isn't clear.Imprecate
When I do this under Mac OS X (Mountain Lion) I get "The application with bundle ID (null) is running setugid(), which is not allowed." when I try to start the program. What can I do?Peculation
@Peculation I'm not familiar with OSX, however, recently there has been a focus on removing setuid/setgid as these are automatically exploit targets for the bad guys, so it could be that this is a feature of OSX. Have you tried searching SO? If that turned up nothing, do ask your own question. I'd be interested in the answer!Imprecate
the other option is to set the device ownership group using udev rules, and ensuring your user is a member of that group. E.g. the plugdev or disk groupsNonattendance
H
9

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.

Hardfeatured answered 20/3, 2010 at 16:26 Comment(0)
E
4

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.

Exclude answered 20/3, 2010 at 16:29 Comment(4)
your second block i think you mean child > 0Cris
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.Forestall
You probably want AF_UNIX if you want to pass file descriptors across the socket.Bertrand
I deliberately design so that I don't pass file descriptors around.Exclude
C
2

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.

Cahn answered 20/3, 2010 at 16:26 Comment(0)
L
2

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;
}
Logistic answered 10/2, 2016 at 13:26 Comment(2)
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?Classmate
It'd be worthy to mention all the libraries this code needs, wouldn't it?Mathias
L
1

You can try launching the command to create the virtual device (including sudo) through a background shell. Ask for the users password in a dialog of your own and pipe that into the shell when sudo asks for it. There are other solutions like using gksu, but those are not guaranteed to be available on every machine.

You don't run your entire program as root, but only the small part of it that needs root. You should spawn a separate process for that and sudo may be of assistance to you.

Longdistance answered 20/3, 2010 at 16:16 Comment(5)
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.Rebba
Is sudo guaranteed to be available on every machine? I've seen installations without it.Untruthful
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.Imprecate
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.Longdistance
Doesn't the Ubuntu Software-Center interactively gain root privileges during operation? Apparently via polkit.Mustachio

© 2022 - 2024 — McMap. All rights reserved.