据我了解,不同的用户ID如下(从进程的角度):
- 真实用户ID:拥有该进程的用户ID
- 有效用户ID:决定当前允许做什么和不允许做什么的用户ID
- 保存的用户ID:基本上是原来的有效用户ID,必要时能够返回到原来的有效用户ID
现在我有两个问题:
在程序开始时将有效用户 ID 保存在变量中是否会导致不需要保存的用户 ID?
如何在 C 程序中检索保存的用户 ID?我找不到任何执行此操作的函数。
答案1
在程序开始时将有效用户 ID 保存在变量中是否会使保存的用户 ID 变得不必要?
这不是用户空间程序记住什么的问题,而是内核允许它使用什么权限的问题。为了实现用户之间的分离,系统必须控制进程可以使用哪些用户 ID。否则任何进程都可能要求成为 root。
如何在 C 程序中检索保存的用户 ID?我找不到任何执行此操作的函数。
使用标准函数则不能(只有 和getuid()
)geteuid()
。至少Linux 有getresuid()
不过,它会返回所有三个用户 ID。
无论如何,通常你不需要阅读它。它允许在 setuid 程序的情况下在真实用户 ID 和有效用户 ID 之间进行切换,因此它作为有效用户 ID 的副本启动。
在setuid程序中,真实的用户ID是运行该程序的用户的ID,有效和保存的用户ID是拥有该程序的用户的ID。有效用户 ID 对于权限检查至关重要,因此如果进程想要暂时放弃权限,它会在实际用户 ID 和保存的用户 ID 之间更改有效用户 ID。
内核以什么方式使用保存的用户 ID 来检查进程是否可以更改其用户 ID?这是否意味着当进程尝试更改其有效用户 ID 时,内核会检查保存的用户 ID 以确保允许该进程这样做?
是的。 Linux 手册页提到setuid()
了这一点,但它有些隐藏:
ERRORS
EPERM The user is not privileged and uid does not match the real
UID or saved set-user-ID of the calling process.
换句话说,您只能将(有效)用户 ID 设置为真实或保存的 ID 之一。
的手册页setreuid()
对此更清楚:
Unprivileged processes may only set the effective user ID to the real
user ID, the effective user ID, or the saved set-user-ID.
答案2
如果保存的uid是一个变量。
您不能使用常规变量,因为您无权将其复制到有效或真实 uid。内核不能允许您从常规变量进行复制,因为这将允许您成为您想要的任何人(好的,但前提是该值已经在其中一个uid
s 中)。
您是正确的,有效 uid 用于检查系统调用的权限。例外是setuid
/ setgid
(另请参阅功能)。
权限setuid
是:你可以移动ID(从一个uid到另一个uid),但不能引入新的(除非你有CAP_SETUID
:root会有这个)。
因此作为普通用户。您将从一两个 uid 开始(真实且有效,但它们可能都是相同的)。如果它们不同,那么您可能希望将真实的复制到有效的。如果你这样做的话,那么你就会只剩下一个,而且无法挽回。内核不会让您从常规变量加载它。因此,您将有效的内容复制到保存中。对于其余的执行,您可以保存原始有效的内容,并且真实地保存。现在,您可以通过将其复制到 effective-uid 来使其有效。一段时间后,您可能会选择放弃一个。例如,通过将 real 复制到其他两个。现在已经没有回头路了。现在获得特权的唯一方法是使用exec
setuid 二进制文件。 (或者具有新功能的二进制文件,但暂时不用担心)。
要获得 suid,您可以使用getresuid
--http://man7.org/linux/man-pages/man2/getresuid.2.html
实际上,只需使用常规 C 变量即可。但请注意,如果 uid 值尚未包含在其中一个 uid 中,则不允许您添加它。
/*setup UIDs*/
uid_t real_uid = getuid(void);
uid_t original_effective_uid = geteuid(void);
int err = setresuid(real_uid, original_effective_uid, original_effective_uid); /*this line not needed as done by kernel for setuid binary*/
#ifdef see_effect_of_no_suid
int err = setresuid(real_uid, original_effective_uid, real_uid);
#endif
while (true) {
err=seteuid(original_effective_uid);
/*do stuff as original_effective uid*/
err=seteuid(real_uid);
/*do stuff as real uid*/
}