http://hi.baidu.com/051156/blog/item/9813f7460452c6016a63e554.htmlhttp://hi.baidu.com/051156/blog/item/9813f7460452c6016a63e554.htmlhttp://sunlit.javaeye.com/blog/400898
Unix中,每个进程都包含三个ID:real user ID,effective user ID,saved set user ID。
real user ID表示的是实际上进程的执行者是谁。
effective user ID主要用于校验该进程在执行时所获得的文件访问权限,也就是说当进程需要访问文件,系统检查权限时实际上检查的该进程的"effective user ID",或者是effective user ID 告诉系统,该进程在对资源访问时,具有等同于哪个用户的权限。
saved set-user-ID 仅在effective user ID发生改变时保存。(这个看了后面的例子就明白了^_^)
一般情况下,real user ID就是进程的effective user ID,但是当要运行的可执行程序设置了"set-user-ID"位之后,进程的effective user ID变成该文件的属主用户id,同时该进程的"saved set-user-ID"变成此时进程的"effective user ID",也就是该可执行程序的属主用户ID,该进程在执行一些与文件访问权限相关的操作时系统检查的是进程的effective user ID.
为什么需要一个"saved set-user-ID"?因为当进程没有超级用户权限的时候,进程在设置"effective user ID"时需要将需要设置的ID和该进程的"real user ID"或者"saved set-user-ID"进行比较.
APUE2中进行的解释是:
1)If the process has superuser privileges, the setuid function sets the real user ID, effective user ID, and saved set-user-ID to uid.
2)If the process does not have superuser privileges, but uid equals either the real user ID or the saved set-user-ID, setuid sets only the effective user ID to uid. The real user ID and the saved set-user-ID are not changed.
3)If neither of these two conditions is true, errno is set to EPERM, and 1 is returned
也就是说:
1)当用户具有超级用户权限的时候,setuid 函数设置的id对三者都起效.
2)否则,仅当该id为real user ID 或者saved set-user-ID时,该id对effective user ID起效.
3)否则,setuid函数调用失败.
也就是说,这个saved set-user-ID更多的作用是在进程切换自己的effective user ID起作用.
需要特别提醒的是:并没有任何的API可以获取到进程的saved set-user-ID,它仅仅是系统在调用setuid函数时进行比较而起作用的.
APUE2中关于此事的原话如下:
Note that we can obtain only the current value of the real user ID and the effective user ID with the functions getuid and geteuid from Section 8.2. We can't obtain the current value of the saved set-user-ID.
举一个例子说明问题,假设这样的一种情况,系统中有两个用户A,B,还有一个由B创建的可执行程序proc,该可执行程序的set-
user-id位已经进行了设置.
当A用户执行程序proc时,
程序的real user ID = A的用户ID,effective user ID = B的用户ID, saved set-user-ID=B的用户ID.
假如在该进程结束了对某些限制只能由用户B访问的文件操作后,程序将effective user ID设置回A,也就是说此时:
程序的real user ID = A的用户ID,effective user ID = A的用户ID, saved set-user-ID=B的用户ID.
这个改动之所以能成功,原因在于上面列举出的情况2):该ID为进程的real user ID.
最后,假设由于种种原因进程需要再次切换effective user ID为B,可是因为不能通过API获取进程的saved set-user-ID(该值为B的用户ID),所以只能通过两种途径获得(可能还有别的途径):
a)在设置effective user ID变回A之前保存effective user ID,它的值为B的用户ID.
b)调用函数getpwnam( "B"),在返回的struct passwd *指针中成员pw_uid存放的就是用户B的ID.
这样,这个调用setuid(B的用户ID)就会成功,原因也在于上面说的情况2):该ID与进程的saved set-user-ID相同.