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

发布于 2024-08-26 15:30:09 字数 231 浏览 7 评论 0原文

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

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

有没有办法模仿“sudo”命令行为(询问用户密码)来临时获得 root 权限并执行特定任务?如果是这样,我会使用哪些功能?

非常感谢您的帮助!

I am writing some software (in C++, for Linux/Mac OSX) which runs as a non-privileged user but needs root privileges at some point (to create a new virtual device).

Running this program as root is not a option (mainly for security issues) and I need to know the identity (uid) of the "real" user.

Is there a way to mimic the "sudo" command behavior (ask for user password) to temporarily gain root privileges and perform the particular task ? If so, which functions would I use ?

Thank you very much for your help !

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

鸵鸟症 2024-09-02 15:30:09

如果您每次都需要 root 权限,最好的办法是以 root 身份启动您的程序并使用 setuidsetgid。这就是 apache 需要绑定到受限端口 80 时所做的事情。

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

sudo add_interface args

,让 sudo 为您处理身份验证。您可能希望使用 gksu、gksudo、kdesu 或 kdesudo 等图形前端来代替 sudo。我不会尝试自己实现安全密码输入;这可能是一个棘手的问题,您可能会留下巨大的安全漏洞和功能问题(您支持指纹识别器吗?)。

另一种替代方案是 polkit,以前称为 PolicyKit。

If you need root privileges every time, the best thing is to start your program as root and drop them (in a subprocess) with setuid and setgid. That's what apache does when it needs to bind to the restricted port 80.

If gaining root rights is the exception instead of the rule and the program is run interactively, another way is to write a program add_interface and execute

sudo add_interface args

and let sudo handle authentication for you. Instead of sudo, you may want to use a graphical frontend like gksu, gksudo, kdesu, or kdesudo. I wouldn't try to implement secure password input myself; it can be a tricky problem and you'll probably leave gaping security holes and functionality problems (Do you support fingerprint readers?).

Another alternative is polkit, previously called PolicyKit.

如日中天 2024-09-02 15:30:09

原始答案

您可能会考虑在可执行文件本身上设置 setuid 开关。维基百科有一篇关于它的文章,甚至向您展示了 geteuid() 和 getuid() 非常有效,前者是为了找出你在“模仿”谁,后者是为了找出你“是谁”。例如,sudo 进程 geteuid 应返回 0(root)并 getuid 您的用户 ID,但是,其子进程确实以 root 身份运行(您可以使用 sudo id -u -r 来验证这一点) )。

我不认为有一种方法可以轻松地以编程方式获得 root 访问权限 - 毕竟,应用最小权限原则,为什么需要这样做呢?常见的做法是仅使用提升的权限运行有限的代码部分。许多守护进程等也在现代系统下设置为以自己的用户身份运行,并拥有他们所需的大部分权限。只有非常特定的操作(安装等)才真正需要 root 权限。

2013 年更新

我原来的答案是成立的(尽管我 2013 年的自己可能比 2010 年的自己做得更好),但是如果您正在设计一个需要 root 访问权限的应用程序,您可能需要考虑到底是什么需要某种根访问权限并考虑使用 POSIX 功能 (手册页)。这些与 L4 等中实现的基于能力的安全性不同。 POSIX 功能允许您的应用程序被授予 root 权限的子集。例如CAP_SYS_MODULE将允许您插入内核模块,但不给您其他根权力。这在发行版中使用,例如 Fedora 有一个功能可以完全删除 setuid 二进制文件,并具有不加区别的 root 访问权限。

这很重要,因为作为一名程序员,您的代码显然是完美的!但是,您所依赖的库(唉,如果您编写了它们就好了!)可能存在漏洞。使用功能,您可以限制此漏洞的使用,并使您自己和您的公司免受与安全相关的审查。这让每个人都更快乐。

Original answer

You might consider the setuid switch on the executable itself. Wikipedia has an article on it which even shows you the difference between geteuid() and getuid() quite effectively, the former being for finding out who you're "emulating" and the latter for who you "are". The sudo process, for example, geteuid should return 0 (root) and getuid your user's id, however, its sub-processes do truly run as root (you can verify this with sudo id -u -r).

I don't think there's a way to easily programmatically gain root access - after all, applying the principle of least privilege, why would you need to? Common practise is to run only limited parts of code with elevated privileges. A lot of daemons etc are also set up under modern systems to run as their own user with most of the privileges they need. It's only for very specific operations (mounting etc) that root privileges are truly needed.

2013 update

My original answer stands (although my 2013 self might make a better job of it than my 2010 one), but if you are designing an application that requires root access, you may want to consider exactly what sort of root access is needed and consider the use of POSIX Capabilities (man page). These are different to capability-based security as implemented in L4 et al. POSIX capabilities allow your application to be granted a subset of root's powers. For example CAP_SYS_MODULE will allow you to insert kernel modules, but give you no other root powers. This is in use in distributions e.g. Fedora has a feature to completely remove setuid binaries with indiscriminate root access.

This matters because as a programmer, your code is obviously perfect! But, the libraries on which you depend (sigh, if only you'd written them!) might have vulnerabilities in them. Using capabilities, you can limit the use of this exploit, and save yourself and your company from security-related scrutiny. This makes everyone happier.

天煞孤星 2024-09-02 15:30:09

您无法获得 root 权限,您必须从 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 更改为某个非特权用户。我相信(但没有尝试过)您可以以 root 身份安装程序并启用 setuid 位,立即降低权限,然后根据需要恢复权限(但是,一旦降低权限,您就不会再恢复权限)能够恢复)。

更好的解决方案是分解出需要以 root 身份运行的程序部分,并在打开 setuid 位的情况下安装它。当然,您需要采取合理的预防措施,确保它不能在主程序之外调用。

You can't gain root privileges, you must start out with them and reduce your privileges as needed. The usual way that you do this is to install the program with the "setuid" bit set: this runs the program with the effective userid of the file owner. If you run ls -l on sudo, you'll see that it is installed that way:

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

While your program is running with root privileges, you can call the setuid(2) system call to change your effective userid to some non-privileged user. I believe (but haven't tried this) that you could install your program as root with the setuid bit on, immediately reduce privilege, and then restore privilege as needed (it's possible, however, that once you lower your privilege you won't be able to restore it).

A better solution is to break out the piece of your program that needs to run as root, and install it with the setuid bit turned on. You will, of course, need to take reasonable precautions that it can't be invoked outside of your master program.

坏尐絯 2024-09-02 15:30:09

通常这是通过创建二进制文件 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 作为通信链接的结尾。

特权代码需要验证它需要执行的每个操作的安全性,但由于此代码与非特权代码相比很小,因此这应该相当容易。

Normally this is done by making your binary suid-root.

One way of managing this so that attacks against your program are hard is to minimize the code that runs as root like so:

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

Now the unprivileged code talks to the privileged code via the fd comlink (which is a connected socket). The corresponding privileged code uses stdin/stdout as its end of the comlink.

The privileged code needs to verify the security of every operation it needs to do but as this code is small compared to the unprivileged code this should be reasonably easy.

攀登最高峰 2024-09-02 15:30:09

你可能想看一下这些 API:

setuid, seteuid, setgid, setegid, ...

它们在 Linux 系统中的头文件 中定义(对 MAC 不太了解,但你应该有一个类似的头文件)那里也有)。

我发现的一个问题是该进程必须具有足够的权限才能更改其用户/组 ID。否则,调用上述函数将导致错误,并将 errorno 设置为 EPERM

我建议您以 root 用户身份运行程序,并在一开始就将有效用户 ID(使用 seteuid)更改为低权限用户。然后,每当您需要提升权限时,都会提示输入密码,然后再次使用 seteuid 恢复为 root 用户。

You might want to take a look at these APIs:

setuid, seteuid, setgid, setegid, ...

They're defined in the header <unistd.h> in Linux systems (don't know much about MAC, but you should have a similar header there too).

One problem that I can see is that the process must have sufficient privileges to change its user-/group- IDs. Otherwise calls to the above functions will result in an error with errorno set to EPERM.

I would suggest that you run your program as the root user, change effective user ID (using seteuid) to an underprivileged user at the very beginning. Then, whenever you need to elevate permissions, prompt for a password then use seteuid again to revert to the root user.

往昔成烟 2024-09-02 15:30:09

在 OS X 上,您可以使用 AuthorizationExecuteWithPrivileges 函数。 授权服务任务页面有对此(和相关)功能的一些详细讨论。

下面是一些使用管理员权限执行程序的 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;
}

On OS X, you can use the AuthorizationExecuteWithPrivileges function. The page on Authorization Services Tasks has some elaborate discussion of this (and related) functions.

Here's a bit of C++ code to execute a program with administrator privileges:

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;
}
白衬杉格子梦 2024-09-02 15:30:09

您可以尝试通过后台 shell 启动命令来创建虚拟设备(包括 sudo)。在您自己的对话框中询问用户密码,并在 sudo 询问时将其通过管道传输到 shell。还有其他解决方案,例如使用 gksu,但不能保证这些解决方案在每台计算机上都可用。

您不必以 root 身份运行整个程序,而只运行需要 root 权限的一小部分。您应该为此生成一个单独的进程,并且 sudo 可能会对您有所帮助。

You can try launching the command to create the virtual device (including sudo) through a background shell. Ask for the users password in a dialog of your own and pipe that into the shell when sudo asks for it. There are other solutions like using gksu, but those are not guaranteed to be available on every machine.

You don't run your entire program as root, but only the small part of it that needs root. You should spawn a separate process for that and sudo may be of assistance to you.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文