Linux下如何丢弃附属组?ug+s程序权限处理疑问
如何在Linux中丢弃附属组(针对setuid/setgid程序)
首先,你碰到的EPERM错误完全在预期之内——默认情况下,非root进程调用setgroups()确实需要CAP_SETGID权限,直接调用肯定会碰壁。不过别担心,针对你这种带ug+s权限、归属非root用户及组的程序,有两种可靠的实现方式:
方法一:给程序添加CAP_SETGID文件能力
这是最直接的方案,能让你的程序无需root身份就能调用setgroups(0, NULL)清空所有附属组。
具体步骤:
- 在程序代码中加入清空附属组的逻辑:
#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; }
- 编译程序后,用
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
相关产品推荐
相关产品推荐

