fopen("/etc/sudoers", "r") 总是失败,因为该文件权限为0440且属主root:root,普通用户无读取权限,系统返回eacces;唯一安全替代是调用sudo -n -l。

不能直接读取 /etc/sudoers 文件 —— 它的权限是 0440,且属主为 root:root,普通用户(包括你的 C++ 进程)无权打开。
为什么 fopen("/etc/sudoers", "r") 总是失败?
系统会返回 errno = EACCES(拒绝访问),不是路径错、也不是文件不存在,而是内核强制拦截。即使你用 stat() 检查存在性,fopen() 仍会失败。sudoers 文件设计上就禁止非特权进程直接读取,这是安全基线。
常见错误现象:
-
fopen()返回nullptr,errno为EACCES或EPERM - 试图用
system("cat /etc/sudoers")—— shell 同样无权限,输出为空或报错 - 误以为加了
setuid root就能绕过 —— 现代 Linux 默认禁用该位对脚本/解释器的继承,且 C++ 可执行文件设 setuid 后若含stdin/stdout或动态链接,会被内核直接拒绝加载
替代方案:调用 sudo -l 或 sudo -n -l
这是唯一被官方支持、无需 root 权限、且可从 C++ 安全调用的方式。它不读文件,而是让 sudo 自己解析并返回当前用户被授权的命令列表。
实操建议:
- 用
popen("sudo -n -l 2>/dev/null", "r")避免交互式密码提示(-n表示 non-interactive) - 若用户未配置免密,
popen会立即失败(errno不变,但流中无数据),需检查feof()和读取字节数判断是否真有输出 - 输出格式不稳定(含注释、空行、分组标题),不要正则硬匹配,优先找以
(开头的行(如(ALL) NOPASSWD: /bin/ls) - 注意:某些发行版(如 RHEL/CentOS)默认启用
requiretty,会导致popen中的sudo失败;需临时在/etc/sudoers.d/下添加Defaults:youruser !requiretty(需 root 配置)
如果必须解析 sudoers 语法(如审计工具)
那就必须运行在 root 权限下 —— 没有绕过路径。此时你有两个选择:
- 用
sudo启动你的程序:sudo ./your_program,再用fopen("/etc/sudoers", "r")直接读取(推荐) - 写一个独立的 setuid root 辅助二进制(如
/usr/local/bin/sudoers_reader),由它读取后按固定格式(如 JSON)输出到 stdout,主程序用popen()调用它;注意该辅助程序必须静态链接、不依赖环境变量、且只做读取+输出,避免提权攻击面 - 不要用
libsudoers—— 它是 sudo 源码内部库,未提供稳定 ABI,不公开头文件,也不支持直接链接
真正麻烦的不是“怎么读”,而是“谁来读”和“读完怎么信”。sudoers 是策略源头,任何绕过 sudo 机制的读取行为,都会破坏最小权限原则。多数场景下,sudo -l 的输出已足够支撑权限判断逻辑。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











