如何从root暂时删除权限?

时间:2016-08-29 09:26:51

标签: c linux

我正在开发一个以root身份运行的守护程序,但是需要与用户调用API,我检查了API代码,它使用getuid()来获取用户。

如果root用户按setuid()删除权限,则无法将其还原为root。如果调用seteuid(),API仍将以用户uid=0执行某些操作。

我认为在访问子进程中的API和setuid之前的fork应该可以工作,但是即使COW,如果多次调用API也会花费很多。除了使用进程池之外,是否可以解决问题?

4 个答案:

答案 0 :(得分:7)

是的!创建一个进程以使用适当的UID调用API,并通过管道,UNIX域套接字或(共享内存 1 与程序的其余部分进行通信。< / p>

我的意思是,只分叉一次,让特权用户继续运行另一个进程。然后根据需要在需要时创建两者之间的通信。此外,您可能需要考虑使用dbus,因为它还与systemd完美集成,而在现代Linux上,您希望您的守护进程与两者完美交互。

注意:我绝不是这方面的专家,但这是一个很简单的想法,对我来说似乎很清楚。您不需要为每次调用API创建一个进程。这是XY problem的一个很好的例子,你要解决的真正问题与多次避免fork()无关,因为这样做的想法是错误的解决方案。您只需要fork()一次,删除权限并在没有权限的情况下留在那里,如果/需要,可以与父进程通信。

1 任何适合您的IPC机制。

答案 1 :(得分:1)

您可以将以前的有效uid存储在流程的已保存的UID 中:

uid_t real = getuid();
uid_t privileged = geteuid();
setresuid(real, real, privileged);
do_API_call(); // API's getuid() call now returns real
setresuid(real, privileged, -1); // allowed, since saved==privileged

还有相应的setresgid来使用已保存的GID。

请注意,此答案特定于Linux(根据问题标签)。 HP-UX和某些BSD系统上存在类似的调用,但我还没有检查语义是否相同。

实际上,在进一步阅读时setreuid()应该足够了(和POSIX一致)。 setuid()说:

  

如果调用者的有效UID是root(更确切地说:如果调用者具有CAP_SETUID能力),则真正的UID和保存的set-user-ID也是          集。

  

如果用户是root用户或程序是set-user-ID-root,则必须特别小心。 setuid()函数检查调用者的有效用户ID,如果是超级用户,则所有与进程相关的用户ID都是          设为uid。发生这种情况后,程序无法重新获得root权限。

但是setreuid()没有这样的陈述。

答案 2 :(得分:1)

只需致电seteuid(2)即可完成合适的非特权内容。 seteuid(2)允许在真实的(或已保存的)用户ID(启动suid程序的用户ID或在您的情况下为root)和 suid 用户ID(suid程序所属的用户ID),因此之后重新获得特权用户ID应该没有问题(因为保存的用户ID是root,再次切换到它没有任何问题再次)。

如果您使用setuid(2)更改uid,则会更改所有(有效,已保存和真实的uid),这仅允许root用户(或程序setuid root,以及那时候没办法了。)

看下一个例子:

文件pru49015.c:

#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>

int main(int argc, char **argv)
{
    int opt, suid = getuid(), /* this is the saved uid */
        uid = 0;
    while ((opt = getopt(argc, argv, "i:")) != EOF) {
        switch (opt) {
        case 'i': uid = atoi(optarg); break;
        }
    }
    /* execute this program with root privileges, like setuid root, for example */
    printf("real uid=%d; effective uid=%d\n", getuid(), geteuid());
    seteuid(uid);  /* change to the non-privileged id configured */
    printf("real uid=%d; effective uid=%d\n", getuid(), geteuid());
    seteuid(suid); /* return back to saved uid */
    printf("real uid=%d; effective uid=%d\n", getuid(), geteuid());
}

您将获得如下输出:

$ pru49015 -i 37
real uid=502; effective uid=0
real uid=502; effective uid=37
real uid=502; effective uid=502

用作setuid-root程序时

如果您以root身份使用它,您将获得以下输出:

$ sudo pru$$ -i 37
real uid=0; effective uid=0
real uid=0; effective uid=37
real uid=0; effective uid=0

机制是允许你在setuid程序之间切换用户(让我们称之为保存的用户ID )和程序运行setuid的用户(被叫< em>有效用户ID 或 suid 用户)可以根据需要多次使用。

答案 3 :(得分:-1)

来自here

  

通常,当执行进程时,有效,实际和已保存的用户和组ID都分别设置为进程父进程的真实用户和组ID。但是,当在可执行文件上设置setuid位时,有效和保存的用户ID将设置为拥有该文件的用户ID。