我正在开发一个以root身份运行的守护程序,但是需要与用户调用API,我检查了API代码,它使用getuid()
来获取用户。
如果root用户按setuid()
删除权限,则无法将其还原为root。如果调用seteuid(),API仍将以用户uid=0
执行某些操作。
我认为在访问子进程中的API和setuid之前的fork应该可以工作,但是即使COW,如果多次调用API也会花费很多。除了使用进程池之外,是否可以解决问题?
答案 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,以及那时候没办法了。)
看下一个例子:
#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。