Dropping root privileges Dropping root privileges unix unix

Dropping root privileges


In order to drop all privileges (user and group), you need to drop the group before the user. Given that userid and groupid contains the IDs of the user and the group you want to drop to, and assuming that the effective IDs are also root, this is accomplished by calling setuid() and setgid():

if (getuid() == 0) {    /* process is running as root, drop privileges */    if (setgid(groupid) != 0)        fatal("setgid: Unable to drop group privileges: %s", strerror(errno));    if (setuid(userid) != 0)        fatal("setuid: Unable to drop user privileges: %S", strerror(errno));}

If you are paranoid, you can try to get your root privileges back, which should fail. If it doesn't fail, you bailout:

 if (setuid(0) != -1)     fatal("ERROR: Managed to regain root privileges?");

Also, if you are still paranoid, you may want to seteuid() and setegid() too, but it shouldn't be necessary, since setuid() and setgid() already set all the IDs if the process is owned by root.

The supplementary group list is a problem, because there is no POSIX function to set supplementary groups (there is getgroups(), but no setgroups()). There is a BSD and Linux extension setgroups() that you can use, it this concerns you.

You should also chdir("/") or to any other directory, so that the process doesn't remain in a root-owned directory.

Since your question is about Unix in general, this is the very general approach. Note that in Linux this is no longer the preferred approach. In current Linux versions, you should set the CAP_NET_BIND_SERVICE capability on the executable, and run it as a normal user. No root access is needed.


You're looking for this article:

POS36-C. Observe correct revocation order while relinquishing privileges

Not sure how to best put some information there without duplicating the content of that page ...


This was what I could do best:

#define _GNU_SOURCE  // for secure_getenv()int drop_root_privileges(void) {  // returns 0 on success and -1 on failure    gid_t gid;    uid_t uid;    // no need to "drop" the privileges that you don't have in the first place!    if (getuid() != 0) {        return 0;    }    // when your program is invoked with sudo, getuid() will return 0 and you    // won't be able to drop your privileges    if ((uid = getuid()) == 0) {        const char *sudo_uid = secure_getenv("SUDO_UID");        if (sudo_uid == NULL) {            printf("environment variable `SUDO_UID` not found\n");            return -1;        }        errno = 0;        uid = (uid_t) strtoll(sudo_uid, NULL, 10);        if (errno != 0) {            perror("under-/over-flow in converting `SUDO_UID` to integer");            return -1;        }    }    // again, in case your program is invoked using sudo    if ((gid = getgid()) == 0) {        const char *sudo_gid = secure_getenv("SUDO_GID");        if (sudo_gid == NULL) {            printf("environment variable `SUDO_GID` not found\n");            return -1;        }        errno = 0;        gid = (gid_t) strtoll(sudo_gid, NULL, 10);        if (errno != 0) {            perror("under-/over-flow in converting `SUDO_GID` to integer");            return -1;        }    }    if (setgid(gid) != 0) {        perror("setgid");        return -1;    }    if (setuid(uid) != 0) {        perror("setgid");        return -1;        }    // change your directory to somewhere else, just in case if you are in a    // root-owned one (e.g. /root)    if (chdir("/") != 0) {        perror("chdir");        return -1;    }    // check if we successfully dropped the root privileges    if (setuid(0) == 0 || seteuid(0) == 0) {        printf("could not drop root privileges!\n");        return -1;    }    return 0;}