以root权限调用c++函数,而不以root身份执行整个程序
Calling a C++ function with root privileges, without executing the whole program as root
目前我正在玩蓝牙LE和iBeacon设备。我写了一个服务器,不断寻找附近的信标。
我的服务器遵循以下示例(Link)
不幸调用了函数:
hci_le_set_scan_parameters()
需要root权限。
因为我不想用根权限运行整个服务器,我想问是否有可能只调用这个函数的根权限?
我知道在执行程序时要求sudo总是至少有问题,我找不到任何其他扫描iBeacons的可能性。如果还有其他的可能性,我也很高兴听到。
感谢您的帮助和亲切的问候
nPLus
根据POSIX, UID/GID是进程属性。进程内的所有代码都使用当前为整个进程设置的UID/GID执行。
您可以以root身份启动服务器,然后立即放弃root权限。然后,您可以在执行函数时使用seteuid(2)
临时获得根权限。
参见此回答
您也可以只获得选定的capabilities(7)
代替(临时或永久)。
线程安全注意
在Linux上,UID/GID是每个线程的属性,可以为单个线程设置它们,参见seteuid()
手册页中的NOTES
部分和这篇文章。
如果可以将特权部分移动到单独的进程中,我强烈建议这样做。父进程将至少构造一个Unix Domain套接字对,一端为自己保留,另一端作为子进程的标准输入或输出。
使用Unix域套接字对的原因是这样的套接字对不仅是双向的,而且还支持识别另一端的进程,并将打开的文件描述符从一个进程传递到另一个进程。
例如,如果您的主进程需要超级用户访问权限来读取文件,可能在特定目录中,或者在其他可识别的地方,您可以将这些文件的打开移动到单独的辅助程序中。通过使用Unix域套接字对在两者之间进行通信,辅助程序可以使用getsockopt(ufd、SOL_SOCKET、SO_PEERCRED、&ucred、&ucred_size)来获取对等凭证:进程ID、有效用户ID和有效组ID。对伪文件/proc/PID/exe
(其中PID
是一个正十进制的进程ID)使用readlink(),您可以获得另一端当前正在运行的可执行文件。如果目标文件/设备可以打开,那么助手可以将打开的文件描述符传递回父进程。(Linux中的访问检查仅在打开文件描述符时完成。只有当描述符被打开为只读或套接字读端被关闭时,读访问才会被阻塞,而只有当描述符被打开为只读或套接字写端被关闭时,写访问才会被阻塞。)我建议传递一个int
作为数据,如果成功,则传递0
作为辅助消息,否则传递errno
错误代码(没有辅助数据)。
然而,重要的是要考虑这些帮助可能被利用的方式。限制在一个特定的目录,或者可能有一个系统范围的配置文件,指定允许的路径glob模式(并且不是每个人都可以写),并使用例如fnmatch()来检查传递的路径是否列出,都是很好的方法。
辅助进程可以通过setuid
或通过Linux文件系统功能获得特权。例如,只给帮助器CAP_DAC_OVERRIDE
功能可以让它绕过文件读、写和执行检查。在Debian衍生版本中,用于操作文件系统功能的命令行工具setcap
位于libcap2-bin包中。
如果不能将特权部分移动到单独的进程中,可以使用Linux、bsd和HP-UX系统中支持的接口:setresuid(),它可以在单个调用中设置真实、有效和保存用户id。(对于真实的、有效的和保存的组id,有一个相应的setresgid()调用,但是当使用那个组id时,记住补充组列表不会被修改;需要显式调用setgroups()或initgroups()来修改补充组列表。也有文件系统用户ID和文件系统组ID,但是C库将设置它们,以便在设置有效用户和/或组ID时匹配有效ID。
如果进程以超级用户权限启动,那么有效用户ID将为零。如果您先使用getresuid(&ruid, &euid, &suid)
和getresgid(&rgid, &egid, &sgid)
,您可以使用setresgid(rgid, rgid, rgid)
来确保只保留真正的组身份,并通过调用setresuid(ruid, ruid, 0)
暂时放弃超级用户权限。要重新获得超级用户权限,请使用setresuid(0, ruid, 0)
;要永久删除超级用户权限,请使用setresuid(ruid, ruid, ruid)
。
这是有效的,因为允许进程在真实、有效和保存的身份之间切换。有效的是管理资源的访问权限。
有一种方法可以将特权限制在进程内的一个专用线程,但它是黑客和脆弱的,我不推荐它。
为了将特权限制在单个线程内,您可以在SYS_setresuid
/SYS_setresuid32
、SYS_setresgid
/SYS_setresgid32
、SYS_getresuid
/SYS_getresuid32
、SYS_getresgid
/SYS_getresgid32
、SYS_setfsuid
/SYS_setfsuid32
和SYS_setfsgid
/SYS_setfsgid32
系统调用周围创建自定义包装器。(让包装器调用32位版本,如果它返回-ENOSYS,则退回到16位版本。)
在Linux中,用户和组标识实际上是每个线程,而不是每个进程。所使用的标准C库将使用例如实时POSIX信号和一个内部处理程序来通知其他线程切换标识,作为操作这些标识的库函数的一部分。
在您的进程早期,创建一个特权线程,它将保留root(0)作为保存的用户身份,但否则将真实身份复制到有效和保存的身份。对于主要流程,将真实身份复制到有效和保存的身份。当特权线程需要执行某些操作时,它首先将有效用户标识设置为root,然后执行该操作,然后将有效用户标识重置为真实用户标识。通过这种方式,特权部分被限制在这个线程中,并且只在必要时应用于部分,因此大多数常见的信号等漏洞将没有机会工作,除非它们恰好发生在这样一个特权部分。
这样做的缺点是,它是必要的,没有任何身份改变的C库函数(setuid(), seteuid(), setgid(), setegid(), setfsuid(), setfsgid(), setreuid(), setregid(), setregid(), setregid(), setregid(), setregid(), setregid(), setresgid())必须由进程中的任何代码使用。因为在Linux C库函数是弱的,你可以通过用你自己的版本替换它们来确保:你自己定义这些函数,用正确的名字(如所示和两个下划线)和参数。
在所有的方法中,我相信通过Unix域套接字对进行身份验证的单独进程是最明智的。它是最容易做到健壮的,并且至少可以在POSIX和BSD系统之间移植。
- 在执行其他功能的同时播放动画(LED矩阵和Arduino/ESP8266)
- C++,系统无法执行指定的程序
- 使用C++中的模板和运算符重载执行矩阵运算
- 创建一个函数以在输入为负数或零时输出字符串.第一次执行用户定义的函数
- 执行函数时导致崩溃的变量
- 无论条件是否为true,if总是在c++中执行
- 当函数模板参数是具有默认参数的类模板时,函数模板参数的推导如何执行
- 在C++中对T*类型执行std::move的意外行为
- 使用QProcess执行命令,并将结果存储在QStringList中
- 如何在没有信号的情况下从C++执行QML插槽
- 如何确认我的constexpr表达式实际上已经在编译时执行
- C++17中的并行执行策略
- QML按钮点击功能执行顺序
- 程序在执行程序的其余部分之前退出
- 为什么catch中的代码没有被执行
- 在运行时使用root执行程序
- 以root权限调用c++函数,而不以root身份执行整个程序
- 从C++代码中执行root拥有的脚本
- 我需要root权限才能在Android下执行本机应用程序
- 尝试执行"cc1"时 gcc 错误:execvp:使用非 root 用户运行时没有这样的文件或目录