Linux是否有一些类似于setuid的C接口,它允许程序使用例如{...}切换到不同的用户。用户名/密码? setuid的问题在于它只能由超级用户使用。
我正在运行一个简单的Web服务,该服务要求作为登录用户执行作业。因此主进程以root身份运行,并在用户登录后分叉并调用setuid以切换到相应的uid。但是,我对以root运行的主进程不太熟悉。我宁愿让它作为另一个用户运行,并且有一些机制可以切换到类似于su
的另一个用户(但是没有启动新进程)。
答案 0 :(得分:9)
首先,非超级用户绝对可以使用setuid()
。从技术上讲,Linux中您需要的只是CAP_SETUID
(和/或CAP_SETGID
)capability来切换到任何用户。其次,setuid()
和setgid()
可以更改真实(执行流程的用户),有效( setuid / setgid二进制文件的所有者)之间的流程标识),并保存了身份。
然而,这些都与您的情况无关。
存在一个相对简单但非常强大的解决方案:拥有一个setuid root帮助程序,由服务守护程序在创建任何线程之前进行分叉和执行,并使用Unix域套接字对在帮助程序和服务之间进行通信,当要执行用户二进制文件时,服务将其凭据和管道端点文件描述符传递给帮助程序。帮助程序将安全地检查所有内容,如果一切正常,它将分叉并执行所需的用户帮助程序,指定的管道端点连接到标准输入,标准输出和标准错误。
尽早启动帮助程序的服务过程如下:
创建一个Unix域socket pair,用于服务和帮助器之间的特权通信。
叉。
在子节点中,关闭所有多余的文件描述符,仅保留套接字对的一端。将标准输入,输出和错误重定向到/dev/null
。
在父级中,关闭套接字对的子端。
在子项中,执行特权助手二进制文件。
父母发送一条简单的消息,可能一封消息根本没有任何数据,但是ancillary message包含其凭据。
帮助程序等待来自服务的初始消息。 收到它后,它会检查凭据。如果凭证未通过审核,则会立即退出。
辅助消息中的凭证定义了始发过程' UID
,GID
和PID
。虽然该过程需要填写这些内容,但内核会验证它们是否属实。帮助程序当然会验证UID
和GID
是否符合预期(对应于服务应该运行的帐户),但诀窍是获取文件的统计信息{{1} } symlink指向。这是发送凭据的进程的真正可执行文件。您应该验证它与已安装的系统服务守护程序(由root:root拥有,在系统二进制目录中)相同。
到目前为止,有一种非常简单的攻击可能会破坏安全性。恶意用户可以创建自己的程序,正确地分叉和执行帮助程序二进制文件,使用其真实凭据发送初始消息 - 但在帮助程序有机会检查凭据实际引用的内容之前,将其替换为正确的系统二进制文件。 !
这一攻击被三个进一步的步骤轻易击败:
帮助程序生成一个(加密安全的)伪随机数,比如1024位,并将其发送回父级。
父母发回号码,但又在辅助消息中添加了凭据。
帮助程序验证/proc/PID/exe
,UID
和GID
是否未更改,PID
仍然指向正确的服务守护程序二进制文件。 (我只是重复完整的检查。)
在步骤8,帮助程序已经确定套接字的另一端正在执行它应该执行的二进制文件。发送一个它必须发回的随机cookie,意味着另一端不能有#34;填充"预先插入消息的套接字。当然,这假设攻击者不能事先猜测伪随机数。如果您要小心,可以从/proc/PID/exe
读取合适的cookie,但请记住它是一种有限的资源(如果内核没有足够的随机性,可能会阻止)。我个人刚刚从/dev/random
读取了1024位(128字节),然后使用它。
此时,帮助程序已确定套接字对的另一端是您的服务守护程序,并且帮助程序可以信任控制消息,只要它可以信任服务守护程序即可。 (我假设这是服务守护程序产生用户进程的唯一机制;否则您需要在每个进一步的消息中重新传递凭据,并且每次在帮助程序中重新检查它们。)
每当服务守护程序希望执行用户二进制文件时,
创建必要的管道(一个用于向用户二进制文件输入标准输入,一个用于从用户二进制文件中取回标准输出)
向包含
的帮助程序发送消息每当帮助者收到这样的消息时,它就会分叉。在子代中,它使用辅助消息中的文件描述符替换标准输入和输出,使用/dev/urandom
和setresgid()
和/或setresuid()
更改标识,将工作目录更改为适当的位置,并执行用户二进制文件。父助手进程关闭辅助消息中的文件描述符,并等待下一条消息。
如果帮助程序在没有来自套接字的输入时退出,则当服务退出时它将自动退出。
如果有足够的兴趣,我可以提供一些示例代码。有很多细节要做,所以代码写起来有点乏味。但是,正确写入,它比例如更安全。 Apache SuEXEC。
答案 1 :(得分:4)
不,没有办法只使用用户名和密码更改UID。 (内核无法以任何方式识别“密码”的概念 - 它只存在于用户空间中。)要从一个非根UID切换到另一个,必须将root作为中间步骤,通常由{{{ 1}} - 使用setuid二进制文件。
您的情况中的另一个选择可能是让主服务器作为非特权用户运行,并让它与以root身份运行的后端进程通信。