Linux 在 C 中运行 setuid 程序的正确方法

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/9297415/
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-08-06 04:36:03  来源:igfitidea点击:

correct way to run setuid programs in C

clinuxsecuritysetuid

提问by cateof

I have a process with permissions 4750. Two users exist in my Linux system. The root user and the appz user. The process inherits the permissions of a process manager that runs as "appz" user.

我有一个权限为 4750 的进程。我的 Linux 系统中存在两个用户。root 用户和 appz 用户。该进程继承了以“appz”用户身份运行的进程管理器的权限。

I have two basic routines:

我有两个基本的例程:

void do_root (void)
{
        int status;
        status = seteuid (euid);
        if (status < 0) { 
        exit (status);
        }    
}

/* undo root permissions */
void undo_root (void)
{
int status;
        status = seteuid (ruid);
        if (status < 0) { 
                exit (status);
        }
        status = setuid(ruid);
        if (status < 0) { 
                exit (status);
        }
}

My flow is the following:

我的流程如下:

int main() {
 undo_root();
 do some stuff;
 do_root();
 bind( port 80); //needs root perm
 undo_root();
 while(1) {

    accept commads()
    if ( commands needs root user access)
    {
       do_root();
       execute();
       undo_root();

    }

 }

As you can see I want to execute some commands as root. I am trying to drop permissions temporarily and if the tasks needs root access I wrap the command between a do_root and undo_root call.

如您所见,我想以 root 身份执行一些命令。我正在尝试暂时删除权限,如果任务需要 root 访问权限,我会将命令包装在 do_root 和 undo_root 调用之间。

However it seems that my program is not working.

但是,我的程序似乎不起作用。

What is the canonical way to do it?

什么是规范的方式来做到这一点?

采纳答案by Joshua

The old-school way is to in both do_root and undo_root to use setreuid() to swap ruid and euid:

老派的方法是在 do_root 和 undo_root 中使用 setreuid() 来交换 ruid 和 euid:

setreuid(geteuid(), getuid());

This is perfectly acceptable if the program is small enough to do a complete security audit.

如果程序小到足以进行完整的安全审计,这是完全可以接受的。

The new-school way is far more complex and involves fork()ing off a child that accepts directives for what to do as root and then doing setuid(getuid()) to drop root permanently in the parent.. The child is responsible for validating all directives it receives. For a large enough program, this drops the amount of code that must be security audited, and allows the user to manage the process with job control or kill it, etc.

新学校的方式要复杂得多,它涉及 fork() 关闭一个接受指令的孩子,然后执行 setuid(getuid()) 以永久地将 root 置于父级中。 孩子负责验证它收到的所有指令。对于足够大的程序,这会减少必须进行安全审计的代码量,并允许用户通过作业控制或终止等来管理进程。

回答by cateof

The setuidman page says the following:

setuid手册页说的情况如下:

... a set-user-ID-root program wishing to temporarily drop root privileges, assume the identity of a non-root user, and then regain root privileges afterwards cannot use setuid()

... 一个 set-user-ID-root 程序希望暂时放弃 root 权限,假设一个非 root 用户的身份,然后重新获得 root 权限,不能使用 setuid()

Meaning that you cannot use setuid(). You have to use seteuid()and, possibly, setreuid(). See Setuid Program Examplefor more details.

这意味着您不能使用setuid(). 您必须使用,seteuid()并且可能使用setreuid(). 有关更多详细信息,请参阅Setuid 程序示例

回答by Jonathan Leffler

There is a paper 'Setuid Demystified' by Hao Chen, David Wagner, and Drew Dean. It was presented at USENIX 2002. It describes how setuid()and transitions work in great detail (correct as of 2002). It is well worth reading (several times - I must be a year or two overdue on a re-read of it).

Hao Chen、David Wagner 和 Drew Dean 发表了一篇论文“ Setuid Demystified”。它是在 USENIX 2002 上提出的。它setuid()非常详细地描述了转换的工作方式(截至 2002 年是正确的)。这本书非常值得一读(好几次——我必须要迟一两年才能重新阅读它)。

Fundamentally, as Peteshnoted in a comment, when a process with EUID 0 does setuid(nuid)with nuid != 0, there is no way back to root(EUID 0) privileges. And, indeed, it is vital that it is so. Otherwise, when you login, the rootprocess that logs you in could not limit you to your own privileges - you'd be able to get back to root. Saved UID complicates things, but I don't believe it affects the one-way trap of EUID 0 doing setuid().

从根本上说,正如Petesh在评论中指出的那样,当具有 EUID 0 的进程使用setuid(nuid)nuid != 0,就无法恢复到root(EUID 0) 特权。而且,确实如此,这一点至关重要。否则,当您登录时,您登录的root过程无法将您限制为自己的权限 - 您将能够返回root. 保存的 UID 使事情复杂化,但我不相信它会影响 EUID 0 的单向陷阱执行setuid().