分类: LINUX
2014-12-03 17:05:28
第一遍拜读《UNIX环境高级编程》时,没有深究细节,目的只是对全书有一个整体的了解,以便划分出知识点,逐个击破。在专研第四章时,对里面提到的进程的实际用户ID、实际组ID、有效用户ID、有效组ID、保存设置-用户-ID、保存设置-组-ID这六个概念似懂非懂,这里以专题的形式将自己的学习经历整理成文,一方面加深自己的理解和记忆,另一方面希望帮助和我有相同疑惑的同行朋友少走弯路。
《UNIX环境高级编程》第四章 文件和目录
《Linux与UNIX Shell编程指南》第一章 文件安全与权限
为了让大部分读者都能轻松的阅读和理解,这里结合《UNIX环境高级编程》加上我个人的验证和理解再重复一下相关的概念。
为了讲清楚实际用户ID、实际组ID、有效用户ID、有效组ID、保存设置-用户-ID、保存设置-组-ID这六个概念,这里需要引入两个新的概念,就是:设置-用户-ID和设置-组-ID。
关于设置-用户-ID和设置-组-ID,Linux与UNIX Shell编程指南中如是说:设置-用户-ID意味着如果某个用户对属于自己的文件设置了这种权限(chmod u+x file),那么其它用户在执行这一文件时也会具有其属主的权限。设置-组-ID意味着如果某个用户对属于自己的文件设置了这种权限(chmod g+x file),那么其它用户在执行这文件本时也会具有其同组用户的权限。
可能看到这里,你对上面的“具有其属主的权限”和“具有其同组用户的权限”这两句话还是不太理解,别急,耐心接着往下看,你会找到答案的。
实际用户ID、实际组ID、有效用户ID、有效组ID、保存设置-用户-ID、保存设置-组-ID这六个概念是针对进程而言的,而设置-用户-ID、设置-组-ID这两个概念是正对文件而言的。
以root身份创建两个用户,一个user1,一个user2,用户user1属于组user1,用户user2属于组user2。用户user2编写了一段名为proc.c的程序,编译为名为proc的二进制文件。用户user1执行用户user2编译生成的proc二进制文件。
二进制文件proce的功能如下:
1、 调用getuid()、getgid()、geteuid()、getegid()打印进程的实际用户ID、实际组ID、有效用户ID、有效组ID。
2、 向用户user2创建的文本文件user2.txt中写入一个字符串。
UNIX环境高级编程如是说:实际用户I D和实际组I D标识我们究竟是谁。这两个字段在登录时取自口令文件中的登录项。
我如是说:从前提中可以看到,二进制文件proc由用户user2创建,由用户user1执行。从后面的实例可以看到,进程的有效用户ID和有效组ID和二进制文件由谁创建没有关系,和二进制文件由谁执行有关系,这也符合UNIX环境高级高级编程的解释。
二进制文件proc的一个功能是打开用户user2创建的一个文本文件user2.txt,并写入一个字符串。用户user2可以正常写入,用户user1写入时错误为:Permission denied。
UNIX环境高级编程如是说:有效用户I D,有效组I D决定了我们的文件访问权。进程的有效用户I D通常就是实际用户I D,有效组I D通常是实际组I D。这解释了为什么用户user1执行proc时会报错,因为进程proc由用户user1执行,通常进程proc对文件的访问权限与user1对文件的访问权限一样,也就是proc的有效用户ID等于proc的实际用户ID等于用户user1的ID,明显用户user1没有对user2.txt的写权限。
我如是说:UNIX环境高级编程中只说通常“进程的有效用户ID就是实际用户ID”,也就是说存在例外的情况,以user2身份,执行“chmod u+s proc”,用户user1再执行二进制文件就不会有错误输出,可以正常写入内容。
读到这里再来理解前面提到的两句话“具有其属主的权限”和“具有其同组用户的权限”,本质就是进程的有效用户ID变成其二进制文件属主的用户ID,组ID也一样。
UNIX环境高级编程用了不少的篇幅来阐述这两个概念,说明这个概念不是很好理解。但也不要被吓住,只要理解了setuid这个系统调用,这个问题也就清楚了,理解一个系统调用,对于程序员的你来讲,应该手到擒来了吧。
在proc.c中加入如下代码片段:
502是user1的用户ID,503是user2的用户ID,504不知道是什么,执行结果显示第三个setuid调用失败了,这是为什么?
UNIX环境高级编程如是说:
1.若进程具有超级用户特权,则s e t u i d函数将实际用户I D、有效用户I D,以及保存的设
置-用户-I D设置为u i d。
2.若进程没有超级用户特权,但是u i d等于实际用户I D或保存的设置-用户- I D,则s e t u i d只将有效用户I D设置为u i d。不改变实际用户I D和保存的设置-用户- I D。
3.如果上面两个条件都不满足,则e r r n o设置为E P E R M,并返回出错。
如果是超级用户当然是拥有所有权限,只要是有效的用户ID,调用setuid正如1中描述的那样。这里的user1和user2都不是超级用户,这里符合第二条规则,“保存设置-用户-ID”理解为“保存”+“设置用户ID”,在setuid调用的时候,把入参和实际用户ID和保存设置-用户-ID比较,如果不和二者中的其中一个相等,则接口调用失败。
这样做有什么用?
用户user1执行完需要user2才有权限的任务后,恢复进程的有效用户ID为user1的用户ID,等需要执行user2才有权限的任务时,又可以将进程的有效用户ID设置为user2的userID,这样互相切换,而不会导致setuid调用失败。
以root身份创建用户:
useradd –m user1
useradd –m user2
以user2身份创建文件:
touch proc.c
touch user2.txt
编辑proc.c,写入如下内容:
编译proc.c为proc:
gcc -g -o proc proc.c
用户user2执行proc:
用户user1执行proc:
用户user2执行如下命令:
chmod u+s proc
chmod g+s proc
用户user1再次执行proc: