You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Linux下如何丢弃附属组?ug+s程序权限处理疑问

如何在Linux中丢弃附属组(针对setuid/setgid程序)

首先,你碰到的EPERM错误完全在预期之内——默认情况下,非root进程调用setgroups()确实需要CAP_SETGID权限,直接调用肯定会碰壁。不过别担心,针对你这种带ug+s权限、归属非root用户及组的程序,有两种可靠的实现方式:

方法一:给程序添加CAP_SETGID文件能力

这是最直接的方案,能让你的程序无需root身份就能调用setgroups(0, NULL)清空所有附属组。

具体步骤:

  1. 在程序代码中加入清空附属组的逻辑:
#include <stdio.h>
#include <sys/types.h>
#include <grp.h>
#include <errno.h>

int main() {
    // 清空所有附属组
    if (setgroups(0, NULL) == -1) {
        perror("Failed to drop supplementary groups");
        return 1;
    }

    // 这里写你的业务逻辑
    printf("All supplementary groups dropped successfully!\n");
    return 0;
}
  1. 编译程序后,用setcap命令给它添加CAP_SETGID能力:
sudo setcap cap_setgid=ep /path/to/your/executable

这样,任何用户执行这个程序时,进程都会临时拥有CAP_SETGID权限,就能顺利调用setgroups()清空附属组了。

方法二:切换有效GID后重置附属组(无需文件能力)

如果不想依赖文件能力,你可以利用进程权限降级的特性:先把有效GID切换回执行用户的真实GID,再将附属组列表设置为仅包含真实GID(这一步不需要特殊权限)。

具体步骤:

在程序中加入以下逻辑:

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <grp.h>
#include <errno.h>

int main() {
    gid_t real_gid = getgid();

    // 将有效GID切换回执行用户的真实GID
    if (setgid(real_gid) == -1) {
        perror("Failed to switch effective GID");
        return 1;
    }

    // 将附属组列表设置为仅包含真实GID(等价于丢弃所有其他附属组)
    gid_t groups[] = {real_gid};
    if (setgroups(1, groups) == -1) {
        perror("Failed to reset supplementary groups");
        return 1;
    }

    // 业务逻辑部分
    printf("Supplementary groups reset to only real GID\n");
    return 0;
}

这个方法的原理是:当进程的有效GID等于真实GID时,setgroups()允许将附属组列表设置为仅包含真实GID,不需要CAP_SETGID权限。虽然不能完全清空附属组列表,但已经达到了“丢弃原本所有附属组”的目的。

为什么直接调用setgroups会失败?

Linux的权限模型中,setgroups()是特权操作,默认只有root或持有CAP_SETGID能力的进程才能调用——除非你只是把附属组列表设置为仅包含进程的真实GID。而你的ug+s程序执行时,有效GID是程序所属组(不是执行用户的真实GID),所以直接调用setgroups()不符合豁免条件,就会返回EPERM。

内容的提问来源于stack exchange,提问作者Omnifarious

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 09:19:59