如何以编程方式获得root权限?

我正在编写一些以非特权用户身份运行的软件(在C ++中,用于Linux / Mac OSX),但某些时候需要root权限(以创build新的虚拟设备)。

以root身份运行这个程序不是一个选项(主要是为了安全问题),我需要知道“真实”用户的身份(uid)。

有没有办法模仿“sudo”命令行为(请求用户密码)暂时获得root权限并执行特定任务? 如果是这样,我将使用哪些function?

非常感谢您的帮助 !

原始答案

您可能会考虑可执行文件本身的setuid开关。 维基百科有一篇关于它的文章 ,它甚至能够非常有效地向你展示geteuid()getuid()之间的区别,前者是为了找出你在“模仿”谁,在后者是为了“你是谁”。 sudo进程,例如,geteuid应该返回0(root),并得到你的用户的id,但是,它的subprocess确实以root身份运行(你可以用sudo id -u -r来validation)。

我不认为有一种方法可以轻松地以编程方式获得root权限 – 毕竟,应用最小权限的原则,您为什么需要? 通常的做法是只用有限的权限运行有限的部分代码。 许多守护进程等也在现代系统下build立起来,以他们所需要的大部分特权运行。 只有非常具体的操作(挂载等),真正需要root权限。

2013更新

我原来的答案(虽然我的2013年自我可能比我的2010年更好),但是如果你正在devise一个需要root权限的应用程序,你可能需要考虑究竟需要什么样的root权限,并考虑使用POSIXfunction (手册页) 。 这些与L4等人所执行的基于能力的安全不同。 POSIXfunction允许您的应用程序被授予根的权力的一个子集。 例如CAP_SYS_MODULE将允许你插入内核模块,但是不会给你其他的根权限。 这是在分发中使用,例如Fedora有一个function,可以彻底删除setuid的二进制文件 ,不分青红皂的root访问权限。

这很重要,因为作为程序员,你的代码显然是完美的! 但是,你所依赖的图书馆(感叹,如果只是你写的),可能存在漏洞。 使用function,您可以限制使用此漏洞,并保护您和您的公司免受安全相关的审查。 这让每个人都更快乐。

如果您每次都需要root权限,最好的办法是以root用户身份启动程序,并使用setuid和setgid将其删除(在subprocess中 )。 这就是Apache需要绑定到受限制端口80时所做的。

如果获得根权限是exception而不是规则,程序是交互式运行的,另一种方法是编写程序add_interface并执行

 sudo add_interface args 

让sudo为你处理authentication。 你可以使用像gksu,gksudo,kdesu或者kdesudo这样的graphics前端来代替sudo。 我不会尝试自己实现安全的密码input; 这可能是一个棘手的问题,你可能会留下大胆的安全漏洞和function问题(你是否支持指纹识别器?)。

另一种select是polkit ,以前称为PolicyKit。

你不能获得root权限,你必须从他们开始,并根据需要减less你的权限。 这样做的通常方法是安装带有“setuid”位的程序:这将使用文件所有者的有效用户标识运行程序。 如果你在sudo上运行ls -l ,你会发现它是以这种方式安装的:

 -rwsr-xr-x 2 root root 123504 2010-02-25 18:22 /usr/bin/sudo 

当您的程序以root用户权限运行时,您可以调用setuid(2)系统调用来将您的有效用户标识更改为某个非特权用户。 我相信(但没有尝试过),你可以安装你的程序作为root用setuid位上,立即减less权限,然后恢复特权根据需要(但是,有可能,一旦你降低你的权限,你不会能够恢复它)。

一个更好的解决scheme是打破你需要以root身份运行的程序,并在打开setuid位的情况下安装它。 当然,您需要采取合理的预防措施,使其不能在您的主程序之外调用。

通常这是通过使你的二进制suid根。

pipe理这种方法的一种方法是,对程序的攻击是很难的,就是尽量减less以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作为其链接的结尾。

特权代码需要validation每个操作所需执行的安全性,但是与非特权代码相比,这个代码很小,这应该是相当容易的。

你可能想看看这些API:

 setuid, seteuid, setgid, setegid, ... 

它们在Linux系统的头文件<unistd.h>中定义(对MAC不太了解,但是你也应该有类似的头文件)。

我可以看到的一个问题是,该进程必须具有足够的权限来更改其用户/组ID。 否则,调用上述函数会导致errorno设置为EPERM错误。

我build议你以root用户的身份运行你的程序,在开始的时候改变有效的用户ID(使用seteuid )给一个seteuid用户。 然后,无论何时您需要提升权限,提示input密码,然后再使用seteuid恢复为root用户。

在OS X上,您可以使用AuthorizationExecuteWithPrivilegesfunction。 “ 授权服务任务 ”页面对此(及相关)function进行了详细讨论。

这里有一些C ++代码来执行一个具有pipe理员权限的程序:

 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; } 

您可以尝试启动命令以通过后台shell创build虚拟设备(包括sudo)。 在sudo要求的时候,请在你自己的对话框中input用户密码,并将其input到shell中。 还有其他的解决scheme,如使用gksu,但不能保证在每台机器上都可用。

你不要以root身份运行你的整个程序,而只是需要根的一小部分。 你应该为此产生一个单独的进程,sudo可能对你有帮助。