C语言 删除 root 权限

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3357737/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 06:01:28  来源:igfitidea点击:

Dropping root privileges

cunixroot

提问by David Given

I have a daemon which gets started as root (so it can bind to low ports). After initialisation I'd very much like to have it drop root privileges for safety reasons.

我有一个以 root 身份启动的守护进程(因此它可以绑定到低端口)。初始化后,出于安全原因,我非常希望它放弃 root 权限。

Can anyone point me at a known correctpiece 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; I need to have the supplementary groups set up correctly; I don't need to change back to root privileges afterwards.

供参考:我以 root 身份开始;我需要更改为在不同的 uid 和 gid 下运行;我需要正确设置补充组;之后我不需要改回 root 权限。

采纳答案by Ben

You're looking for this article:

您正在寻找这篇文章:

POS36-C. Observe correct revocation order while relinquishing privileges

POS36-C。放弃特权时遵守正确的撤销顺序

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

不知道如何最好地将一些信息放在那里而不复制该页面的内容......

回答by Juliano

In order to drop all privileges (user and group), you need to drop the group before the user. Given that useridand groupidcontains 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. If it doesn't fail, you bailout:

如果您是偏执狂,您可以尝试恢复您的 root 权限,这应该会失败。如果它没有失败,你就救助:

 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()). There is a BSD and Linux extension setgroups()that you can use, it this concerns you.

补充组列表是一个问题,因为没有 POSIX 函数来设置补充组(有getgroups(),但没有 setgroups())。您可以使用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("/")或 到任何其他目录,以便该进程不会保留在根目录中。

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_SERVICEcapabilityon the executable, and run it as a normal user. No root access is needed.

由于您的问题通常是关于 Unix 的,因此这是非常通用的方法。请注意,在 Linux 中,这不再是首选方法。在当前的 Linux 版本中,您应该在可执行文件上设置CAP_NET_BIND_SERVICE功能,并以普通用户身份运行它。不需要root访问权限。

回答by Bora M. Alper

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;
}