capabilities比setuid更安全,因其将权限细分为CAP_SYS_TIME等独立单元,仅需赋予必要权限;需用sudo setcap cap_sys_time+ep ./setTime.bin为二进制文件设置能力,并确保挂载选项未禁用capabilities。

为什么 setuid 不是首选,而 capabilities 更安全
直接给二进制加 setuid 会让它全程以 root 身份运行,哪怕只调用一次 settimeofday() —— 这等于把整把钥匙交出去。Capabilities 把权限拆成小块,比如只要 CAP_SYS_TIME 就能改系统时间,不需要 CAP_NET_ADMIN 或 CAP_CHOWN。内核在执行特权操作前只检查对应 capability 是否启用,不看 UID,所以普通用户进程也能做特定高权事。
怎么给目标二进制文件设置 capability(不是给 C++ 程序本身)
关键点:capability 是绑定在**可执行文件的 inode 属性上**的,不是进程启动时动态加的。你必须对最终要 execl 的那个二进制(比如 setTime.bin)设置,而不是对父进程或 shell 脚本。
- 先确认目标文件没有 setuid 位:
ls -l ./setTime.bin,输出中不应出现s(如-rwsr-xr-x) - 用
setcap添加能力:sudo setcap cap_sys_time+ep ./setTime.bin。其中+ep表示“effective + permitted”,确保能力在 exec 时生效 - 验证是否成功:
getcap ./setTime.bin应输出./setTime.bin = cap_sys_time+ep - 注意:如果该文件被复制、重命名或通过
cp拷贝,capability 会丢失;必须用cp --preserve=capabilities或重新setcap
父进程 fork + exec 时 capability 怎么传递
子进程继承父进程的 capability 集合,但 execve(execl 底层调用)会根据被加载文件的 capability 属性重置 effective 和 permitted 集。也就是说:即使父进程没任何 capability,只要 setTime.bin 自身带 cap_sys_time+ep,exec 后子进程就能用 settimeofday()。
- 不需要在 C++ 里调用
prctl(PR_SET_CAPBILITY)或cap_set_proc() - 也不需要提前
seteuid(0)或切换用户再切回来——那反而可能清空 capability - 唯一要求:父进程对目标二进制有执行权限,且该二进制已用
setcap正确标记 - 若你在
execl前调用了setuid()切换到普通用户,没问题;capability 是文件属性,不依赖当前 UID
常见失败原因和调试方法
运行时报 Operation not permitted,八成是 capability 没生效,而不是代码写错。
- 检查是否漏了
+ep:只有+p(permitted)不够,必须+ep才能让 effective 位在 exec 后置位 - 检查文件是否在挂载时禁用了 capability:
mount | grep "noatime"看是否有nosuid, noexec或noatime——noatime没影响,但nosuid会同时禁用 capability - 检查 SELinux 是否拦截:
ausearch -m avc -ts recent | grep setTime,若看到avc: denied ... capability=...,需调整策略或临时设为 permissive - 用
strace -e trace=settimeofday,execve ./setTime.bin看系统调用返回值,确认是EPERM还是其他错误
Capability 是文件级元数据,不是运行时状态;一旦设错,光改代码没用,得回头检查 getcap 输出和挂载选项。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











