# 进程的用户/组id
如果进程的三个用户/组ID的概念都已经了解过但还是无法理解的,可以直接看最下面的案例分析。
# 进程的三个用户/组ID
- real id
- effective id
- saved id
由于分析用户uid和组gid比较类似,方便起见,下面只分析了用户uid的情况,使用统称如real user id和real group id统一写作real id。
# real id
real user id
是真正执行process起来的用户user
进程是由哪个用户调用的,或者是由哪个父进程发起的,就是进程的real id。
# effective id
effective id
的作用主要是决定文件的访问权限。这个id的情况比较复杂。
- 一般情况下,没有设置
set-user-ID
位时,effective id
会等于real id
。 - 当
set-user-ID
bit = ON时,effective id
等于该文件的owner id
。
关于
owner id
:在系统中每个文件都有一个owner和group owner,这一般是通过stat结构中的st_uid来标记的。
关于set-user-id
:在文件的mode里面有个叫做set-user-id的位,在设置了该位后,程序运行的时候effective-user-id等于该文件的owner id。作用就是其他用户可以像owner一样运行该程序。
# saved id
saved id
仅在effective id
发生改变时由exec系统调用保存。
当进程没有root权限的时候,进程在设置effective id
时需要将设置的id和real id
或者saved id
进行比较,并且只能设置为其中的一个。
也就是说,saved id
的主要作用是在进程切换effective id
时的可选项。
没有任何的api可以获取到saved id
,这个id完全由内核管理,仅在系统调用setuid()
时起作用。
# 案例1
系统中有两个用户A和B
B创建一个可执行程序proc,并且设置set-user-id
bit
A执行proc时,proc进程的用户id如下:
- real id = A
- effective id = B
- saved id = B
因为proc由A执行,因此real id = A。proc的
set-user-id
被置位,因此effective id = owner id = B。而saved id = effective id。
此时进程执行对B具有的权限相应的操作。当程序将effective id
设置回A。此时用户id如下:
- real id = A
- effective id = A
- saved id = B
因为A是real id,因此改动成功。
# 案例2
用户A创建了一个main程序,此时main的owner id = A。
当以A用户的身份直接运行main时:
- real id = A
- effective id = A
- saved id = A
当以root用户执行main时:
- real id = 0
- effective id = 0
- saved id = 0
因为没有设置
set-user-id
位,因此effective id总是与real id一致。
使用chown B
命令修改owner id = B。 继续以A的身份运行main:
- real id = A
- effective id = A
- saved id = A
原因和上面一样,虽然文件的owner已经改成B了,但是没设置
set-user-id
位,因此effective id还是等于real id
使用chmod u+s
命令设置set-user-id
位为ON。 继续以A的身份运行main:
- real id = A
- effective id = B
- saved id = B
因为
set-user-id
位打开,effective id = owner id,由于前面使用了chown
命令将owner id改为了B,因此effective id = B。
而上面所有的saved id都与effective id一致。这个id的设置完全由内核控制,程序员无法获取到该id的信息。
# setuid()系统调用
setuid(uid)首先请求内核将本进程的[real id],[effective id]和[saved id]都设置成函数指定的uid, 若权限不够则请求只将effective uid设置成uid, 再不行则调用失败。
分两种情况讨论:
- 进程当前的effective id = 0(root):
setuid(uid)
会同时把real id、effective id、saved id
设置为uid - 进程当前的effective id = 普通用户:
setuid(uid)
只有当uid为real id或saved id中的一个才能调用成功,结果就是把effective id设置成两者之一。若参数uid是其他值,则setuid()调用将返回错误。
因此,非root用户是不可能通过setuid()或者seteuid()取得其他权限(包括root权限)的,它只能恢复原来的权限。只能通过exec一个设置了set-user-id
位的可执行程序,来取得其他(程序文件所有者)权限(包括root权限)。
关于sudo
sudo的实现里有setuid函数,通过setuid()将进程的real UID和effective UID都设置为了0,因此整个过程中进程都是有root权限的。
# seteuid()系统调用
主要讨论与setuid()的区别。
- setuid(uid)首先请求内核将本进程的[real id],[effective id]和[saved id]都设置成函数指定的uid, 若权限不够则请求只将effective uid设置成uid, 再不行则调用失败.
- seteuid(uid)仅请求内核将本进程的[effective id]设置成函数指定的uid.
对于非root用户来说,setuid()和seteuid()的行为是完全一样的。
对于root用户来说,setuid()可以设置real id、effective id、saved id三个id,而seteuid()只会设置effective id,并且与setuid()一样,只能将其设置为real id或saved id其中一个,因为非root用户是不可能通过setuid()或者seteuid()取得其他权限(包括root权限)的,它只能恢复原来的权限。
参考:https://blog.csdn.net/todd911/article/details/7738069
修改时间:2022.03.29