如何以编程方式获得 root 权限?
Posted
技术标签:
【中文标题】如何以编程方式获得 root 权限?【英文标题】:How to programmatically gain root privileges? 【发布时间】:2011-01-29 20:05:58 【问题描述】:我正在编写一些软件(在 C++ 中,用于 Linux/Mac OSX),它以非特权用户身份运行,但在某些时候需要 root 特权(以创建新的虚拟设备)。
以 root 身份运行此程序不是一个选项(主要是出于安全问题),我需要知道“真实”用户的身份 (uid)。
有没有办法模仿“sudo”命令行为(询问用户密码)来临时获得 root 权限并执行特定任务?如果是这样,我会使用哪些功能?
非常感谢您的帮助!
【问题讨论】:
【参考方案1】:在 OS X 上,您可以使用 AuthorizationExecuteWithPrivileges
函数。 Authorization Services Tasks 上的页面对此(及相关)功能进行了详细讨论。
下面是一段 C++ 代码,用于以管理员权限执行程序:
static bool execute(const std::string &program, const std::vector<std::string> &arguments)
AuthorizationRef ref;
if (AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment, kAuthorizationFlagDefaults, &ref) != errAuthorizationSuccess)
return false;
AuthorizationItem item =
kAuthorizationRightExecute, 0, 0, 0
;
AuthorizationRights rights = 1, &item ;
const AuthorizationFlags flags = kAuthorizationFlagDefaults
| kAuthorizationFlagInteractionAllowed
| kAuthorizationFlagPreAuthorize
| kAuthorizationFlagExtendRights;
if (AuthorizationCopyRights(ref, &rights, kAuthorizationEmptyEnvironment, flags, 0) != errAuthorizationSuccess)
AuthorizationFree(ref, kAuthorizationFlagDestroyRights);
return false;
std::vector<char*> args;
for (std::vector<std::string>::const_iterator it = arguments.begin(); it != arguments.end(); ++it)
args.push_back(it->c_str());
args.push_back(0);
OSStatus status = AuthorizationExecuteWithPrivileges(ref, program.c_str(), kAuthorizationFlagDefaults, &args[0], 0);
AuthorizationFree(ref, kAuthorizationFlagDestroyRights);
return status == errAuthorizationSuccess;
【讨论】:
我们在哪里调用这个函数?什么是 arguments 参数?我试图在 main 函数的开头调用,但它崩溃了。参数为空。我的问题是在 /var/log 中创建一个文件。但是需要许可。我该如何处理这个问题? 值得一提的是这段代码需要的所有库,不是吗?【参考方案2】:如果您每次都需要 root 权限,最好的办法是以 root 身份启动您的程序,然后使用 setuid 和 setgid 删除它们(在子进程中)。这就是 apache 在需要绑定到受限端口 80 时所做的。
如果获得root权限是例外而不是规则并且程序交互运行,另一种方法是编写程序add_interface并执行
sudo add_interface args
让 sudo 为您处理身份验证。您可能想要使用图形前端而不是 sudo,例如 gksu、gksudo、kdesu 或 kdesudo。我不会尝试自己实现安全密码输入;这可能是一个棘手的问题,您可能会留下巨大的安全漏洞和功能问题(您支持指纹读取器吗?)。
另一种选择是polkit,以前称为 PolicyKit。
【讨论】:
【参考方案3】:原答案
您可能会考虑可执行文件本身的 setuid 开关。***上有一个article,它甚至可以非常有效地向您展示geteuid()
和getuid()
之间的区别,前者用于找出您“模仿”谁,而后者用于找出您“是谁”。 sudo 进程,例如,geteuid 应该返回 0(root)并 getuid 您的用户 id,但是,它的子进程确实以 root 身份运行(您可以使用sudo id -u -r
验证这一点)。
我认为没有一种方法可以轻松地以编程方式获得 root 访问权限 - 毕竟,应用最小权限原则,您为什么需要这样做?通常的做法是仅以提升的权限运行代码的有限部分。许多守护进程等也被设置在现代系统下,以他们自己的用户身份运行,拥有他们需要的大部分权限。只有非常特定的操作(挂载等)才真正需要 root 权限。
2013 年更新
我最初的答案是正确的(尽管我 2013 年的自己可能比 2010 年的自己做得更好),但是如果您正在设计一个需要 root 访问权限的应用程序,您可能需要考虑到底需要哪种 root 访问权限并考虑使用POSIX Capabilities(man page)。这些与在 L4 等人中实现的 capability-based security 不同。 POSIX 功能允许您的应用程序被授予 root 权限的子集。例如CAP_SYS_MODULE
将允许您插入内核模块,但不授予您其他根权限。这在发行版中使用,例如Fedora has a feature to completely remove setuid binaries 具有不分青红皂白的 root 访问权限。
这很重要,因为作为程序员,您的代码显然是完美的!但是,您所依赖的库(唉,要是您自己写了就好了!)可能存在漏洞。使用功能,您可以限制此漏洞的使用,并使您和您的公司免受与安全相关的审查。这让每个人都更快乐。
【讨论】:
执行此操作时,您必须非常注意安全性,因为您的应用现在是特权升级漏洞的目标 当你这样做的时候,尽快做那些需要root权限的事情,然后马上下拉给真正的用户。Under sudo, for example, geteuid should return 0 (root) and getuid your user's id.
这是错误的。请参阅我关于这个问题的问题:***.com/questions/10272784/…
@chacham15 啊,是的。 Sudo 本身应该能够看到这些东西,但继承的进程不会。他们只继承有效的用户 ID 作为他们的真实用户 ID。我会编辑这个,因为它不清楚。
当我在 Mac OS X (Mountain Lion) 下执行此操作时,我得到“带有捆绑 ID (null) 的应用程序正在运行 setugid(),这是不允许的。”当我尝试启动程序时。我能做什么?【参考方案4】:
通常这是通过制作二进制 suid-root 来完成的。
一种管理此问题以便难以攻击您的程序的方法是最小化以 root 身份运行的代码,如下所示:
int privileged_server(int argc, char **argv);
int unprivileged_client(int argc, char **argv, int comlink);
int main(int argc, char **argv)
int sockets[2];
pid_t child;
socketpair(AF_INET, SOCK_STREAM, 0); /* or is it AF_UNIX? */
child = fork();
if (child < 0)
perror("fork");
exit(3);
elseif (child == 0)
close(sockets[0]);
dup2(sockets[1], 0);
close(sockets[1]);
dup2(0, 1);
dup2(0, 2); /* or not */
_exit(privileged_server(argc, argv));
else
close(sockets[1]);
int rtn;
setuid(getuid());
rtn = unprivileged_client(argc, argv, sockets[0]);
wait(child);
return rtn;
现在非特权代码通过 fd comlink(这是一个连接的套接字)与特权代码对话。相应的特权代码使用 stdin/stdout 作为其 comlink 的结尾。
特权代码需要验证它需要执行的每个操作的安全性,但由于与非特权代码相比,此代码很小,这应该相当容易。
【讨论】:
你的第二个区块我想你的意思是child > 0
其实我认为应该是else if (child == 0)
-- else
block wait
对于child,所以child需要是中间的else if
block。
如果你想通过套接字传递文件描述符,你可能需要AF_UNIX
。
我故意设计成不传递文件描述符。【参考方案5】:
您可以尝试通过后台 shell 启动命令来创建虚拟设备(包括 sudo)。在您自己的对话框中询问用户密码,并在 sudo 要求时将其输入 shell。还有其他解决方案,例如使用 gksu,但不能保证在每台机器上都可用。
您不会以 root 身份运行整个程序,而只是其中的一小部分需要 root。您应该为此生成一个单独的进程,sudo 可能会对您有所帮助。
【讨论】:
感谢您的回答。不幸的是,该程序并不总是需要 root 权限。有时您可能只是想以某种方式使用它,因此它不需要执行该特定任务。在这种情况下,不需要询问密码。我找到了谈论 setuid() 的线程。我将对此进行调查。 sudo 是否保证在每台机器上都可用?我见过没有它的安装。 不,fedora 上默认没有配置它。我还亲自阻止不属于wheel
组的用户使用 sudo 或 su 作为传统 unix 行为的扩展。
您当然只需要通过 sudo 运行需要 root 权限的代码部分。当您不需要 root 权限时,不要询问密码。通过 sudo 运行整个程序不是我的答案,“命令”仅指需要 root 权限的命令,而不是整个应用程序。
运行过程中Ubuntu软件中心不是交互获取root权限的吗?显然是通过 polkit。【参考方案6】:
您可能想看看这些 API:
setuid, seteuid, setgid, setegid, ...
它们在 Linux 系统的标头 <unistd.h>
中定义(对 MAC 了解不多,但您应该也有类似的标头)。
我可以看到的一个问题是进程必须具有足够的权限才能更改其用户/组 ID。否则调用上述函数将导致错误,errorno
设置为EPERM
。
我建议您以root
用户身份运行程序,一开始就将有效用户ID(使用seteuid
)更改为低权限用户。然后,每当您需要提升权限时,提示输入密码,然后再次使用 seteuid
以恢复为 root
用户。
【讨论】:
【参考方案7】:您无法获得 root 权限,您必须从他们开始并根据需要降低您的权限。执行此操作的常用方法是安装设置了“setuid”位的程序:这将使用文件所有者的有效用户 ID 运行程序。如果你在sudo
上运行ls -l
,你会看到它是这样安装的:
-rwsr-xr-x 2 root root 123504 2010-02-25 18:22 /usr/bin/sudo
当您的程序以 root 权限运行时,您可以调用 setuid(2)
系统调用将您的有效用户 ID 更改为某个非特权用户。我相信(但尚未尝试过)您可以在 setuid 位打开的情况下以 root 身份安装您的程序,立即降低权限,然后根据需要恢复权限(但是,一旦您降低权限,您可能不会可以恢复)。
更好的解决方案是拆分需要以 root 身份运行的程序部分,并在打开 setuid 位的情况下安装它。当然,您需要采取合理的预防措施,以免在您的主程序之外调用它。
【讨论】:
以上是关于如何以编程方式获得 root 权限?的主要内容,如果未能解决你的问题,请参考以下文章
如何以编程方式使用 sudo? IE。将 sudo 集成到我的 GUI 中