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