实际用户ID,有效用户ID,保存-设置-用户ID,文件的所有者ID

实际用户ID:有的文章中将其称为真实用户ID,这个ID就是我们登陆unix系统时的身份ID。
有效用户ID:定义了操作者的权限。有效用户ID是进程的属性,决定了该进程对文件的访问权限。

 

文件的访问权限包括读写和执行。判断某个进程对文件有何权限时,内核会将非超级用户进程的有效ID与文件的所有者ID进行比较,当然,也可能需要比较有效组ID,这关系到具体的权限测试方法,先不在这里说明。而超级用户创建的进程是允许访问整个文件系统的。它的有效ID等于0。不过,这里还有一点需要说明的是,仅仅有合适的有效ID,还不一定就能获得所有或者部分权限。你需要得到被访问文件的允许,这就是文件访问权限位(用户读、用户写、组读等)的责任了。

 

这里又牵涉到一个“ID”,即文件的所有者ID。文件的所有者ID是什么呢?创建文件是由某用户的进程实现的吧?所以在创建新文件的时候,就将该进程的有效ID作为该文件的所有者ID了。APUE里面有时又将文件的所有者ID称为“文件的用户ID”。


一般情况下,进程的有效用户ID就被设成执行该进程的实际用户ID。比如,用户usr01执行了进程process,process的有效用户ID就设成了用户usr01的ID(实际用户ID)。但是有的时候,一个进程可能要去执行其他用户创建的文件。这时,该进程的有效ID和该文件的所有者ID是不同的(记住文件的所有者Id就是最初创建它的进程有效ID哦)。但是如果文件设置了“设置用户ID位”或者“设置组ID位”,那么该进程在执行该文件的时候,就会将进程的有效ID临时更改为文件的所有者ID。

 

设置用户ID:设置用户ID是由exec函数复制有效用户ID得来的。所以说设置用户ID是进程有效ID的副本。为什么要保留进程有效ID的副本呢?刚才讲到文件有设置用户ID位时,内核会将执行进程的有效ID临时更改为文件的所有者ID。执行完该文件后总要恢复成原来的有效用户ID吧?所以事先保留个副本啦!

 

关于对应的组ID,基本类似了,不再赘述。

---------------------------------------------------------------------------------------------




如果当前进程具有超级权限则:
调用setuid(uid)时,该进程可以将自己的:实际用户id,有效用户id,保存的set-user-id设置成其参数uid的值。既是说,特权用户进程可以将自己改变成任意进程。

如果当前进程是非特权用户则:
调用setuid(uid)时,如果uid是其实际用户id或保存的set-user-id,就把有效用户id设置成uid的值;如果uid不等于这两个值则将errno设置成EPERM(可供用perror()使用),并出错返回。既是说,非特权用户不能随意改变自己,这里涉及到保存的set-user-id,所以后面再详细说。

一个系统启动后,用户从login登录后,会产生一个用户进程,该进程和所有进程一样有七个id值:实际用户id,有效用户id,保存的set-user-id,实际组id,有效组id,添加组id,保存的set-group-id,这里只说uid的问题,这3个uid来自我们登录名。(这中间的创建过程我想明白再说)至此我们有了一个用户shell进程,当执行程序时,通常由fork+exec族函数来做。由用户进程fork出来的子进程继承父进程的实际uid,和有效uid值(还要继承一些东西,在此先屏蔽掉),子进程再调用exec族,调用时:如果文件的set-user-id没有置位(st_mode中),那这个子进程的实际uid,有效uid不变,保存的set-user-id从有效用户id复制。如果文件的set-user-id置位,那这个子进程的实际uid不变,有效uid设置为程序文件的用户id,保存的set-user-id从有效用户id复制。
文件的set-user-id位应该只能由所有者置位,特权用户可以置位所有文件。


说一下正常setuid函数的作用:
引用APUE上的例子(经过简化改遍):有一个文件A所有者为root,我们需要对它进行操作,但该文件只允许root操作。所以系统又提供一个程序用来操作这个文件,该程序通过给定的几个受限操作来限制用户,并且所有者为root,且set-user-id置位。
1),那我们执行次程序后进程中:
实际用户id=普通
有效用户id=root
保存的set-user-id=root
2),程序去操作文件A,因为程序有效用户id是root,所以可以操作。
3),然后程序执行setuid(getuid()),此时进程:
实际用户id=普通
有效用户id=普通
保存的set-user-id=root
此时进程就不再有特权。
4),当执行完一些操作后又要操作文件A,则程序执行setuid(rootid),这里的rootid是程序保存的rootid,可以通过复制进程的保存的set-user-id得来。此时进程:
实际用户id=普通
有效用户id=root
保存的set-user-id=root
5),现在程序又可以操作文件A了
综述:有效用户id在需要时变成特权id,不需要时通过setuid(getuid())变回实际用户id,失去特权,这样特权uid只用在最需要的时候,可以一定程度增强安全性。可以想象如果文件A是一个锁,经历这5部刚好是:加锁--操作--解锁的过程(实际APUE上也是将文件A说成是一个锁,我将它简化了)。事实上用户对密码的修改也是这样做的。


再说一下setuid的非正常作用:
一个普通用户如果想写一个程序,里面包括了setuid(uid)函数,并将其set-user-id置位,那么即使执行自己这个程序,他也扩展不了权限,因为执行程序时,用户进程的几个uid都是自己,执行程序后,实际用户id还是被置为自己,到程序中的setuid(uid)时setuid()的uid只能来自该进程的实际uid和保存的set-user-id,这个函数的这个特性就限制了当前非特权进程不能通过自己来越权。但是如果有一个程序,它的所有者是root,且有缓冲区溢出问题,且文件的set-user-id位被root置位,那么普通用户执行这样的程序时,实际用户id被设置成root,当前进程的三个uid分别为:实际用户id为普通用户,有效用户id为root,保存的set-user-id为root,此时如果通过改变进程的流,去执行一个程序,那么它将以root的身份执行这个程序,因为很多程序是通过检查有效用户id(不是实际用户id)识别用户身份。


THE USER ID OF A PROCESS 


内核会给每个进程关联两个和进程ID无关的用户ID,一个是真实用户ID,还有一个是有效用户ID或者称为 setuid(set user ID)。真实用户ID用于标识由谁为正在运行的进程负责。有效用户ID用于为新创建的文件分配所有权、检查文件访问许可,还用于通过kill系统调用向其它进程发送信号时的许可检查。内核允许一个进程以调用exec一个setuid程序或者显式执行setuid系统调用的方式改变它的有效用户ID。 

所谓setuid程序是指一个设置了许可模式字段中的setuid bit的可执行文件。当一个进程exec一个setuid程序的时候,内核会把进程表以及u区中的有效用户ID设置成该文件所有者的ID。为了区分这两个字段,我们把进程表中的那个字段称作保存用户ID。可以通过一个例子来演示这两个字段的区别。 

setuid系统调用的语法是 setuid(uid) ,其中,uid是新的用户ID,该系统调用的结果取决于有效用户ID的当前值。如果调用进程的有效用户ID是超级用户,内核会把进程表以及u区中的真实和有效用户ID都设置成uid。如果调用进程的有效用户ID不是超级用户,仅当uid等于真实用户ID或保存用户ID时,内核才会把u区中的有效用户ID设置成uid。否则,该系统调用将返回错误。一般来说,一个进程会在fork系统调用期间从父进程那儿继承它的真实和有效用户ID,这些数值即使经过exec系统调用也会保持不变。 

存储在u区中的有效用户ID是最近一次setuid系统调用或是exec一个setuid程序的结果;只有它会被用于文件访问许可。进程表中的保存用户ID使得一个进程可以通过执行setuid系统调用把有效用户ID设置成它的值,以此来恢复最初的有效用户ID。 

setuid程序的例子:login,mkdir。




引用:Uresh Vahalia 的《UNIX Internals:The New Frontiers》一书中对这个问题的论述。。。  

p27 

2.3.3 User Credentials 

UID和GID这样的标识符会影响文件的所有权和访问许可,以及向其它进程发送信号的能力。这些属性统称为凭证。 

每个进程都有两对ID ——真实的和有效的。当一个用户登录的时候,login程序会把两对ID设置成密码数据库(/etc/passwd文件,或某些如Sun Microsystems的NIS之类的分布式机制)中指定的UID和GID。当一个进程fork的时候,子进程将从父进程那儿继承它的凭证。 

有效UID和有效GID印象文件的创建和访问。在创建文件的时候,内核将文件的所有者属性设置成创建进程的有效UID和有效GID。在访问文件的时候,内核使用进程的有效UID和GID来判断它是否能够访问该文件。真实UID和真实GID标识进程的真实所有者,会影响到发送信号的权限。对于一个没有超级用户权限的进程来说,仅当它的真实或有效UID于另一个进程的真实UID匹配时它才能向那个进程发送信号。 

有三个系统调用可以改变凭证。如果一个进程调用exec执行一个安装为suid模式的程序,内核将把进程的有效UID修改成文件的所有者。同样,如果该程序安装为sgid模式,内核则会去修改调用进程的有效GID。UNIX提供这个特性是想赋予用户特殊的权限以完成一些特定的任务。 

一个用户还可以通过调用setuid或setgid来改变它的凭证。超级用户可以通过这些系统调用改变真实的和有效的UID以及GID。普通用户则只能通过这些调用来把它们的有效UID或GID改回到真实的数值。 

System V和BSD UNIX在处理凭证方面存在着一些差别。SVR3还维护了一个saved UID和saved GID,分别是在调用 exec之前的有效UID和GID的数值。setuid和setgid系统调用还可以把有效ID恢复为保存的数值。4.3BSD不支持这一特性,它允许一个用户属于一个辅组(supplemental group)的集合(使用setgroups系统调用)。用户创建的文件将属于它的主组(primary group),而用户则既可以访问属于主组的文件,也可以访问属于辅组的文件。 

SVR4整合了上述所有特性。它支持附组,也会在exec的时候维护saved UID和GID。 

setuid程序的例子:passwd。




引用:Marshall Kirk McKusick, George V. Neville-Neil 的《The Design and Implementation of the FreeBSD Operating System》一书中对这个问题的论述。。。  

3.7 User, Group, and Other Identifiers 

每个FreeBSD进程的状态里都有一个UID和一组GID。一个进程的文件系统访问特权就是由它的UID和GIDs来定义的。通常,这些标识符都是新进程创建的时候从父进程那儿自动继承过来的。只有超级用户才能修改一个进程的真实UID或真实GID。这个方案在各种特权之间进行了严格的区分,确保除了超级用户之外的其它任何用户都无法获得特权。 

每个文件都有三组许可bit,分别用于所有者、组以及其它用户的读、写或执行许可。这些许可bit将按如下顺序进行检查: 
    1、如果文件的UID和进程的UID相同,则仅应用所有者的许可,不再检查组和其它用户的许可。 
    2、如果UID不匹配,但文件的GID和进程的众多GID之一匹配,则仅引用组的许可,不再检查所有者和其它用户的许可。 
    3、仅当进程UID和GID与文件的UID和GID都不匹配时,才会去检查其它用户的许可。如果这些许可不允许所请求的操作,该操作就会失败。 

一个进程的UID和GIDs是从它的父进程那儿继承来的。当一个用户登录的时候,login程序会在执行exec系统调用运行用户的登录shell之前设置好UID和GIDs,因此,后续的所有进程都会继承到恰当的标识符。 

我们经常会想赋予一个用户有限的额外特权。......为了解决这个问题,内核允许程序在运行过程中创建被赋予特权的程序。以不同的UID运行的程序被称为setuid程序,以一个额外的组特权运行的程序被称为setgid程序。当运行一个setuid程序的时候,进程的许可将被扩展以包括与程序相关联的UID的许可。该程序的UID就被称为进程的有效UID,而进程最初的UID则被称为真实UID。同样,执行一个setgid程序会把进程的许可扩展为程序的GID的许可,相应的也有有效GID和真实GID的定义。 

系统可以通过setuid和setgid程序来提供对文件或服务的受控访问。当然,这样的程序必须仔细编写,以保证它们只具有一些有限的功能。 

UID和GIDs是作为每个进程的状态的一部分来维护的。由于历史原因,GIDs被实现成了一个显著的GID(即有效GID)和一个GIDs的辅助数组,不过在逻辑上则被看作是一组GIDs。在FreeBSD中,那个显著的GID就是GIDs数组中的第一个条目。辅助数组的大小是固定的(FreeBSD中是16),不过可以通过重新编译内核来修改这个数值。 

FreeBSD是通过把运行setgid程序的进程的辅组数组中的第0个元素设置成文件的属组来实现setgid功能的。之后就可以像普通进程那样对许可进行检查了。由于存在额外的组,setgid程序就能够比一个运行没有特殊权限的程序的用户进程访问更多的文件。为了避免在运行一个setgid 程序的时候丢失与第0个数组元素中的组相关联的特权,login程序会在初始化用户的辅组数组的时候将第0个数组元素复制到第一个数组元素中。因此,当运行的setgid程序修改第0个元素的时候,用户不会丢失任何特权,因为曾经保存在第0个数组元素中的组仍然可以从第一个数组元素中得到。 

setuid功能是通过把进程的有效UID从用户的数值修改为被运行的程序的数值来实现的。和setgid一样,保护机制此时将毫不变样地允许访问,同时也不会意识到程序正在运行setuid。由于一个进程在同一时刻只能有一个UID,在运行setuid的时候就可能会丢失某些特权。在加载新的有效UID的时候,之前的真实UID将会继续作为真实UID。不过真实UID是不会用于任何确认检查的。 

一个setuid进程在运行过程中可能会想临时取消它的特殊权限。比如,它可能只在运行开始和结束的时候需要访问某个受限文件的特殊权限。在其余的运行时间中,它应当只具有真实用户的权限。在BSD的早期版本中,特权的回收是通过对真实的和有效的UID进行切换来完成的。由于只有有效UID被用于访问控制,这个方法既提供了所需的语义,又提供了一个隐藏特殊权限的地方。这个方法的缺点是很容易就混淆了真实的和有效的UID。 

在FreeBSD中,使用了一个额外的标识符,即saved UID来记录setuid程序的身份。当一个程序被exec之后,它的有效UID会被拷贝到saved UID中。下表中的第1行表示了一个没有特权的程序,它的真实、有效以及saved UID都是真实用户的数值。第2行正在运行中的 setuid程序,它的有效UID被设置成了具有相应特权的UID,而这个特权UID也会被拷贝到saved UID中。 

Actions affecting the real, effective, and saved UIDs. 
_________________________________________________________________ 
Action            Real    Effective    Saved 

1.exec-normal     R       R            R 
2.exec-setuid     R       S            S 
3.seteuid(R)      R       R            S 
4.seteuid(S)      R       S            S 
5.seteuid(R)      R       R            S 
6.exec-normal     R       R            R 

Key:R-real user identifier; S-special-privilege user identifier 
_________________________________________________________________ 


seteuid系统调用只会修改有效UID,而不会影响真实的或saved UID。seteuid系统调用被允许将有效UID修改为真实的或 saved UID的数值。表中的第3行和第4行表示了一个setuid程序在一直保持正确的真实UID的同时是如何放弃和重新取回它的特殊权限的。第5 行和第6行表示了一个setuid程序可以运行一个子进程而不赋予它特殊权限。首先,它会把它的有效UID设置成真实UID。然后,当exec那个子进程的时候,有效UID就会被拷贝到saved UID中,从此就会失去对特权UID的所有访问。 

与此类似,也有一个saved GID机制,允许进程在真实GID和最初的有效GID之间进行切换。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值