为什么许多 Linux 发行版使用 setuid 而不是功能?

Why many Linux distros use setuid instead of capabilities?

capabilities(7) 是不将所有 root 权限授予进程的好方法,可以使用 AFAIK 代替 setuid(2)。根据 this 和许多其他人的说法,

"Unfortunately, still many binaries have the setuid bit set, while they should be replaced with capabilities instead."

作为一个简单的例子,在 Ubuntu、

$ ls -l `which ping`
-rwsr-xr-x 1 root root 44168 May  8  2014 /bin/ping

如您所知,在文件上设置 suid/guid,会将有效用户 ID 更改为 root。因此,如果启用 suid 的程序存在缺陷,则非特权用户可以突破并成为 root 用户的等价物。

我的问题是为什么许多 Linux 发行版仍然使用 setuid 方法,而设置 capabilities 可以代替安全问题?

这可能无法给出为什么某些地方的某些人决定以这种或那种方式做出决定的原因,但某些审计工具和界面可能还不知道功能。

一个例子是 proc_connector netlink interface and the programs based on it (like forkstat):有一个进程更改其凭据的事件,但没有更改其功能的事件。


FWIW,you 在类 Debian 发行版中可能无法获得例如 net_raw+ep ping(8) 而不是 setuid 的原因是因为取决于 libcap2-bin 软件包中的 setcap(8) 实用程序,在您安装 ping 之前 已经存在 。来自 iputils-ping.postinst:

    if command -v setcap > /dev/null; then
        if setcap cap_net_raw+ep /bin/ping; then
            chmod u-s /bin/ping
        else
            echo "Setcap failed on /bin/ping, falling back to setuid" >&2
            chmod u+s /bin/ping
        fi
    else
        echo "Setcap is not installed, falling back to setuid" >&2
        chmod u+s /bin/ping
    fi

另请注意,ping 本身将放弃所有 setuid 权限并在启动时切换到使用 Linux 上的功能,因此您对它的担忧可能有点夸张。来自 ping.c:

int
main(int argc, char **argv)
{
        struct addrinfo hints = { .ai_family = AF_UNSPEC, .ai_protocol = IPPROTO
_UDP, .ai_socktype = SOCK_DGRAM, .ai_flags = getaddrinfo_flags };
        struct addrinfo *result, *ai;
        int status;
        int ch;
        socket_st sock4 = { .fd = -1 };
        socket_st sock6 = { .fd = -1 };
        char *target;

        limit_capabilities();

来自ping_common.c

void limit_capabilities(void)
{
        ...
        if (setuid(getuid()) < 0) {
                perror("setuid");
                exit(-1);
        }

考虑一个不同的星球,如果功能补丁被拒绝,提交者必须更加努力地进行邻里改进,并得出以下结论:

  • 文件系统可以挂载 suid、nosuid 或 capsuid。

  • 如果挂载 capsuid,${file} 上的 setuid 位不算数,除非 .${file}。suid_capability 也存在,是 setuid,并且是 0 长度或可解析为某种标准化格式:

    -r-sr-xr-x. 1 carton carton 3812 Dec 27 15:39 t1
    -r-Sr--r--. 1 carton carton    0 Dec 27 15:39 .t1.suid_capability
    
  • 如果文件为空,setuid 位正常工作。如果它有可解析的内容,它就作为部分根功能。

  • 如果需要,.t1.suid_capability 文件可以用以下字段注释:

    • 绑定到匹配的 't1' 文件的散列

    • 绑定到绝对路径,例如,chroot 中的 setuid 文件在您进入 chroot 之前不会变得“热”。

    • 绑定到 public 键或在启动时加载的哈希随机数,以识别已安装的系统,如 uuid。如果私钥或随机数对备份服务器隐藏,传统的密码重置仍然有效,但某些形式的 rootkit 持久性将变得更加困难。它还允许您在子目录中使用其他系统的映像,自动使它们有效地“nosuid”挂载,即使您对挂载选项不遵守纪律并且选择不使用绝对路径功能。

现在回答一些关于这个星球相对于我们自己星球的问题:

  • 与我们的本土星球相比,是否缺少任何功能?

  • 哪个系统更适合 'ls'、'tar'、'rsync'、'cpio'、'find'、'pax' , 'mtree'?使用 gentoo 的 ebuild 沙箱?使用 LXC?

  • 在 NFS 或 9p root 上,哪个系统更适合无盘或来宾系统 运行?

  • 哪个系统与 tripwire 配合使用效果更好?

现在重复这些问题,考虑三个系统而不是两个:

  • 我们的星球:文件系统中的神秘功能元数据

  • 使用文明 Unix 方法的备用星球

  • mosvy 的 'ping' 示例,其中二进制文件在启动后立即删除权限

第三个系统最终开始失去与现状相关的功能:有人可以编写一个假设的功能感知绊线,但在 'ping' 示例中效果不佳。有没有人这样做过? . . . NSA 以外的任何人——将这种复杂性强加给我们的人是否完成了从复杂性中提供(相对较小的)价值的工作?如果那是我们想要解决的功能问题(而不是减少攻击面),是否有更好的方法来获得它,比如 dm-verity,再次讨论复杂性?

现在让我们对 mosvy 的 'ping':

做一个微调
  • crt.o 或一些早期的运行时启动代码读取 sidecar 文件和 /etc/suid_capability_hash_nonce。如果 /etc/suid_capability_hash_nonce 存在但 sidecar 文件不存在,它会删除 suid(在“替代星球”术语中,/etc/suid_capability_hash_nonce 的存在使整个系统“-o capsuid”挂载)。早期的运行时启动代码处理解析 sidecars 并下降到枚举功能,这样逻辑就不必一个接一个地编码到 main.c 中。

老实说,我认为 'ping' 示例更优越,因为程序知道它们需要什么功能,并且只有在程序源代码更改时需求才会更改。通过将它们设置在 drop_privileges() 样式的函数中,需求永远不会不同步。扇出分布将不必争先恐后地更新功能集。

如果您不同意并想要 sidecar/capabilities-style 系统,可以在不篡改内核、挂载选项、启动等任何内容的情况下实现等效的东西:旧的 dracut-nfsroot initrds 将继续工作。这只是风格问题。

这完全是苏格拉底式的说法,除了 dm-verity + 'ping'-style privileges-dropping 之外,功能不提供任何东西。避开没有发挥作用的复杂性并没有什么“不幸”。

每隔几年,CADT 开发人员会发明一些新形式的元数据以塞入文件系统目录:Finder 元数据、POSIX ACL、SELinux 上下文、NFS ACL。接下来他们会怎么想?更新所有文件系统、所有网络协议、所有 fileutils 工具、所有非 Linux 存储操作系统、所有花哨的许可“企业”磁带备份套件需要多长时间?会有人抽出时间来更新所有这些东西吗,还是我们会一瘸一拐地使用不合标准的工具?甚至核心功能也会被有用地记录下来,还是会成为一些烦人的“发行版”的未记录的玩物?该功能是否会在大部分时间都有效,但会阻止系统意外启动并产生先有鸡还是先有蛋的恢复问题?该功能真的会阻止任何攻击吗?

对于所有必须在他们之后进行清理并容忍他们自己的系统中做出不同的、更简单的选择的复杂性,只是为了解除他们的新功能破坏的互操作的人公平吗?

在这种情况下交付的价值特别低,但我们有足够的经验可以将此类提案的价值标准定得很高。我认为将此功能接受到 Linux 并赞扬任何避免它的发行版是错误的。

2008年内核加入文件能力时才真正启用能力支持。所以,那是 13 年,并且正在计算替换 setuid。你的问题很有道理。

如果能力支持在 Linux 中没有以向后兼容的方式实现,它要么在出生时就被拒绝, 要么 事情肯定会改变现在!我怀疑这一切都归结为这样一个事实,即当发现某些代码中的错误可被利用时,当它们的好处只是明显暂时时,没有采用它们的经济动机。

我认为人们已经接受了 setuid 二进制文件似乎可以利用的所有方式,以及人们在另一个漏洞出现时迅速推出的点修复。缓冲区溢出 -> 启动 shell -> root exploit -> 代码修复 -> 重新开始。他们对用户身份 = 特权(即 root)的想法感到满意。当一连串的漏洞利用可以产生所有特权时,这会影响人们如何描述将所有强大的根分解为 independent capabilities 的徒劳性。显然,特权和身份 应该 等同的普遍观念是环境能力甚至存在的原因。

然而,当按预期使用时,能力不是身份=特权。它们是 capable binaries = privilege - 相对于执行任意代码的用户身份,通过这些功能位的 组合 和包含它们的实际程序中的代码。

如果您编写的是编辑文件的代码,那么很明显,它不需要担心被直接滥用以形成原始以太网数据包或加载内核模块。利用该代码中的错误很可能允许对文件进行恶意编辑,但与 setuid 不同的是,它不允许在网络上发送奇怪的数据包。至少不是没有欺骗一些独立的可执行文件的代码通过代理来完成它。

但是,没有人故意编写错误代码,所以我认为您的问题的答案归结为另一个问题:“限制漏洞的可利用程度到底有什么好处?”。

我怀疑如果一些发行版想出如何重组他们的代码库以消除 setuid-root 二进制文件,支持文件功能,随着时间的推移,随着代码漏洞被发现,它会变得更好(当然不会更糟)与坚持使用 setuid-root 的其他发行版相比。但是,在这样的分布出现之前,我不能指责这只是一种观点。