为进程提供额外功能的最常见方法是将文件系统功能分配给其二进制文件。
例如,如果您希望执行/sbin/yourprog 的进程具有CAP_CHOWN 能力,则将该能力添加到该文件的允许和有效集合中:sudo setcap cap_chown=ep /sbin/yourprog。
setcap 实用程序由 libcap2-bin 软件包提供,默认安装在大多数 Linux 发行版上。
还可以为原始流程提供能力,并让该流程根据需要操纵其有效的能力集。例如,Wireshark 的dumpcap 通常在有效、允许和可继承集中安装 CAP_NET_ADMIN 和 CAP_NET_RAW 文件系统功能。
我不喜欢将任何文件系统功能添加到可继承集的想法。当功能不在可继承集中时,执行另一个二进制文件会导致内核放弃这些功能(假设 KEECAPPS 为零;有关详细信息,请参阅prctl(PR_SET_KEEPCAPS) 和man 7 capabilities)。
例如,如果您仅授予/sbin/yourprog CAP_CHOWN 能力并且仅在允许的集合(sudo setcap cap_chown=p /sbin/yourprog)中,则 CAP_CHOWN 能力将不会自动生效,并且如果进程执行其他一些操作,它将被丢弃二进制。要使用 CAP_CHOWN 功能,线程可以在所需操作期间将功能添加到其有效集,然后通过prctl() 调用将其从有效集中删除(但将其保留在允许集中)。请注意,libcap cap_get_proc()/cap_set_proc() 接口会将更改应用于进程中的所有线程,这可能不是您想要的。
对于临时授予能力,可以使用工作子进程。这对于复杂的过程是有意义的,因为它允许将特权操作委托/分离到单独的二进制文件。一个子进程被分叉,通过 Unix 域流或通过 socketpair() 创建的数据报套接字连接到父进程,并执行授予 it 必要功能的辅助二进制文件。然后它使用 Unix 域流套接字来验证身份(进程 ID、用户 ID、组 ID,并通过进程 ID,套接字的另一端正在执行的可执行文件)。不使用管道的原因是需要一个 Unix 域流套接字或数据报套接字对套接字才能使用SO_PEERCRED 套接字选项向内核查询套接字另一端的身份。
存在需要预测和阻止的已知攻击模式。最常见的攻击模式是导致父进程在分叉并执行特权子进程后立即执行受损的二进制文件,时间恰到好处,因此有能力的子进程相信另一端是其正确的父进程执行正确的二进制文件,但实际上控制已被转移到一个完全不同的、被泄露的或不可信的二进制文件中。
关于如何安全地做到这一点的详细信息是一个软件工程问题,而不是一个编程问题,但是使用socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, fdpair) 并验证套接字对等点是父进程仍然不止一次在开始时执行预期的二进制文件,是需要的关键步骤。
我能想到的最简单的例子是仅在允许的集合中使用 prctl() 和 CAP_NET_BIND_SERVICE 文件系统功能,以便其他非特权进程可以使用特权端口(1-1024,最好是定义/列出的系统范围子集在 /etc 下某处的根或管理员拥有的配置文件中)以提供网络服务。如果服务将在被告知时关闭并重新打开其侦听套接字(可能通过 SIGUSR1 信号),则不能简单地在开始时创建一次然后删除侦听套接字。它非常适合 “保留在允许的集合中,但只添加到实际需要它的线程的有效集合,然后立即删除它” 模式。
对于 CAP_CHOWN,示例程序可能会通过文件系统功能将其获取到其有效和允许的集合中,但使用受信任的配置文件(仅限 root/admin 可修改)来列出允许根据真实情况进行的所有权更改运行进程的用户和组身份。考虑一个专用的“sudo”风格的“chown”实用程序,旨在让组织允许团队领导在其团队成员之间转移文件所有权,但不使用 sudo。)