简体   繁体   English

删除root权限

[英]Dropping root privileges

I have a daemon which gets started as root (so it can bind to low ports). 我有一个以root身份启动的守护进程(因此它可以绑定到低端口)。 After initialisation I'd very much like to have it drop root privileges for safety reasons. 在初始化之后,我非常希望出于安全原因让它删除root权限。

Can anyone point me at a known correct piece of code in C which will do this? 任何人都可以指出我在C中已知的正确代码片段会这样做吗?

I've read the man pages, I've looked at various implementations of this in different applications, and they're all different, and some of them are really complex. 我已经阅读了手册页,我已经在不同的应用程序中查看了它的各种实现,它们都是不同的,其中一些非常复杂。 This is security-related code, and I really don't want to reinvent the same mistakes that other people are making. 这是与安全相关的代码,我真的不想重新发明其他人犯的错误。 What I'm looking for is a best practice, known good, portable library function that I can use in the knowledge that it's going to get it right. 我正在寻找的是一个最佳实践,已知良好的,可移植的库函数,我可以使用它知道它将正确。 Does such a thing exist? 这样的事情存在吗?

For reference: I'm starting as root; 供参考:我是从根本上开始的; I need to change to run under a different uid and gid; 我需要改变以在不同的uid和gid下运行; I need to have the supplementary groups set up correctly; 我需要正确设置补充组; I don't need to change back to root privileges afterwards. 之后我不需要改回root权限。

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() : 鉴于useridgroupid包含要删除的用户和组的ID,并假设有效ID也是root,这可以通过调用setuid()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. 如果你是偏执狂,你可以尝试恢复你的root权限,这应该失败。 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. 此外,如果您仍然偏执,您可能也想要seteuid()setegid() ,但它不应该是必要的,因为如果进程由root拥有,setuid()和setgid()已经设置了所有ID。

The supplementary group list is a problem, because there is no POSIX function to set supplementary groups (there is getgroups() , but no setgroups()). 补充组列表是一个问题,因为没有POSIX函数来设置补充组(有getgroups() ,但没有setgroups())。 There is a BSD and Linux extension setgroups() that you can use, it this concerns you. 您可以使用BSD和Linux扩展setgroups() ,这会让您感到担忧。

You should also chdir("/") or to any other directory, so that the process doesn't remain in a root-owned directory. 您还应该chdir("/")或任何其他目录,以便该进程不会保留在root拥有的目录中。

Since your question is about Unix in general, this is the very general approach. 由于你的问题一般是关于Unix的,所以这是非常通用的方法。 Note that in Linux this is no longer the preferred approach. 请注意,在Linux中,这不再是首选方法。 In current Linux versions, you should set the CAP_NET_BIND_SERVICE capability on the executable, and run it as a normal user. 在当前的Linux版本中,您应该在可执行文件上设置CAP_NET_BIND_SERVICE功能 ,并以普通用户身份运行它。 No root access is needed. 无需root访问权限。

You're looking for this article: 你正在寻找这篇文章:

POS36-C. 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;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM