与进程有关的的ID有以下几个或更多:
实际/有效用户id 实际/有效组id 设置用户id(SUID) 设置组id(SGID)
下面我们一一来说明。
我们先来说实际用户ID和实际组ID
实际用户ID:
当我们登陆linux时。我们需要输入用户名和密码。但对于系统来说他识别的其实
是用户id。那么我们记的用户名和系统识别的用户id就有一个映射关系。
比如我运行我 用 ll 命令看文件是有这样的输出
-rw-rw-r-- 1 feng feng 228 May 10 05:36 test_sock.c
我们看到ll 输出的的第三列和第四列显示的拥有者和所属组都是feng.事实上ll获得的是文件拥有者的ID,之所以输出的是feng字符
是为了人们好识别。我们可以查看 /etc/passwd中feng所对应的条目
得到如下:
feng:x:1000:1000:feng,,,:/home/feng:/bin/bash
这里第三第四个(冒号是分隔符)显示的是 feng 的用户id和组id。我们看到它们都是1000,其实ll命令获得的就是1000,但是为了易
于识别就将它映射为字符(你的登录名),但其实系统是不认识feng的 他只认识1000id这个id
不信,我们测试一下,我们将/etc/passwd中 feng条目中的第三列(用户id)由1000改成别的
feng:x:1015:1000:feng,,,:/home/feng:/bin/bash 我们把uid改成了1015
然后我们用ll命令输出如下:
-rw-rw-r-- 1 1000 feng 228 May 10 05:36 test_sock.c
与第一次的ll相比,我们发现第三列显示的不在是 feng 而是1000了。因为feng与1000的对应该系被我们改掉了,所以1000找不到对应的账号名字,就显示数字。
这说明,文件记录的其实是都是数字。
(怎么有种跑题的感觉0.0,算了当做多介绍一点吧)。
说了那么多,一般 实际用户id 就是你当前的的登录名对应在/etc/passwd中对应的那个id,一般情况下他是不变的,所以一般情况下你用
shell命令创建文件,或是写一个程序,用这个程序来创建的文件的所有者就是这个 实际用户id。
那什么是特殊情况呢,在程序中,如果我调用了setuid,而且符合一些规则(比如超级用户特权),那么我们是可以在程序运行过程中
修改实际UID的。(这里我们不讨论这个非一般的情况),但是在终端中我们不能用shell命令改变uid
实际组ID:
实际组ID,一般情况下就是上面我们查看 /etc/passwd 中feng条目对应的第四列。他指的就是feng 的组id。不过不像实际用户id可以在程序运行中改变,他只能在shell中用命令改变
这里又有麻烦了··那什么又是特殊情况呢,我们先来看第一个一般的特殊情况,就拿feng这个用户来说,一般他的组id是1000(/etc/passwd 中feng条目对应的第四列),我们也说这是初始用户组(就像变量的初始化一样,需要有个值),他的组名我们也看到
和用户名字一样都是feng。但是 我们知道linux下一般一个用户是可以属于多个组的,就像我们可以加入多个在学校社团一样,但是
任一时刻我们只能在一个组中处于活跃状态(这个我也有点表达不好了,就是这个意思啦),我们目前处于活跃状态的那个组就是我们的实际
组id(比如你虽然参加了很多个社团,但是同一时刻如果你在a社团,就不能在b社团,你在b社团就不能又在a社团,除非你有分身术)
好像有点绕,我们整理下,就是 一个用户可以属于多个组,但这只是说明你拥有进入这些组的权利了,但某一时刻,我们只能是在一个组中,这个组的id就是我们说的实际组id
那么,我们既然有可以属于多个组,那么我的实际id选哪个呢。选的就是你所属组中的第一个组的id
我们可以用groups命令查看feng用户属于那些组:
feng@ubuntu:~/learn_linux_c_second/chapter_4$ groups
feng adm cdrom sudo dip plugdev lpadmin sambashare
我们看到 第一个组是 feng(id为1000)那么当前的实际组id 就是feng 这个组对应的id,我们可以测试下
//getpid()返回的是实际组id
printf("gid=%d\n",getgid());
输出:
gid=1000
(etc/passwd中 feng这个zu的id为1000)
newgrps命令可以改变当前的用户的组。我们来改变一下:
feng@ubuntu:~/learn_linux_c_second/chapter_4$ newgrp adm
feng@ubuntu:~/learn_linux_c_second/chapter_4$ groups
adm feng cdrom sudo dip plugdev lpadmin sambashare
注意这时候当前用户(feng)的组已经改变了(为所属组中的第一个 这里是adm)
我们在测试上面的程序:
//getpid()返回的是实际组id
printf("gid=%d\n",getgid());
输出:
gid=4
我们看到这时候这时候的输出也改变了(我的机子上adm组所对应的组id为4)。
好了 总结一下:实际用户id 就是你登录时的用户名对应的id ,他一般是不能改变的。特殊情况下可以通过在程序中调用setuid并符合一些规则时可以改变(我们不详述)(几乎不需要去改变他),但是在终端中不能通过shell命令来改变
实际组id,默认情况下就是你的 /etc/passwd 对应的的第四列中的那个id,但是可以通过shell命令在你所属的种选择别的来作为当前的实际组ID,但是在程序运行中不能改变。
有效用户id和有效组id:
这两个id 决定了我们的文件访问权限。我们创建修改或删除,执行一个文件时,内核需要对访问权限进行测试。而测试的基础就是以 有效用户id 和有效组id 为基础的。
而当我们当执行一个程序文件时,通常进程的有效用户id 通常就是实际用户id ,有效组id 通常就是实际组id。 那既然都和实际的相等,我们还要这两个id干什么呢?
我们可以这样理解。实际用户id和实际组id 是你当前登录的账户的性质。 当你运行一个程序的时候,在程序中如果有对文件的访问。那么内核就需要对这个进程是否有权限访问做测试。而内核又是以这两个有效
id为基准进行测试的。也就是说 这两个 有效用户id和有效组id 是进程的性质。只是 再我们执行一个程序时,他们的值通常就是对应的实际id。(通常情况下)
那什么情况下 他们什么时候他们和实际对应的实际id 不相等呢。 这就是我们下面要说的 设置用户id和设置组id
设置用户ID(SUID)和设置组ID(SGID):
设置用户ID:
举个大家估计看过无数遍的例子:
我们每个用户都可以修改自己的密码,这可以在shell中用passed命令来修改密码。新密码会被写入/etc/shadow 中(改文件中记录的是各用户对应的密码)
我们来查看一下这两个文件
feng@ubuntu:~/learn_linux_c_second/chapter_4$ ll /etc/passwd /etc/shadow
-rw-r--r-- 1 root root 2255 May 13 09:26 /etc/passwd
-rw-r----- 1 root shadow 1476 May 13 06:30 /etc/shadow
我们从输出中看到 passwd拥有者的权限为 rws 这个s 就说明 设置用户id 位被开启了(注意这里是拥有者权限中的s位)。这有说明作用呢?
我们注意到shadow这个文件的权限是只有root(超级用户)可以写入,那么问题就来了,
一般用户要想修改密码,新密码必须是要被写入/etc/shadow中的,可是这个文件只有root能够写入数据。怎么解决这个矛盾呢
这就是passwd中 中的 拥有者中的s位(设置用户位)作用了,(注意SUID仅对于二进制程序有效),他可以让具有该程序执行权限的
进程在运行该程序时获得该程序所有者权限(仅在执行该程序过程中获得),即他的有效用户id 会变成 改文件所有者的id。这样内核测试权限是发现有效用户id是root,那么
现在这个进程就具有了超级用户的权限。
那么我们就明白了,虽然我们一般用户是不具备权限去写数据到passwd中的。但是passwd这个程序所有者权限被设置了S标志位,并且我们也具有改程序的执行权限。那么
在我们执行这个程序的时候 我们的 有效用户id 会变成改程序的所有者id(这里是变成root),这样我们就拥有能够修改shadow中的数据的权限了,也就可以修改我们的密码了。
我们来写一个简单的测试程序
我们有一个test程序
-rwsrwxr-x 1 root root 7357 May 13 10:46 test*
他是root创建的程序,设置了SUID位。
他的作用时输出 实际用户id,实际组id 有效用户id 有效组id
程序很简单:
5 int main(void){
6
7
8 printf("uid=%d,euid=%d\n",getuid(),geteuid());
9
10 printf("euid=%d,egid=%d\n",getgid(),getegid());
11 exit(0);
12 }
我们还有另一个程序
test_suid 程序
-rwxrwxr-x 1 feng feng 7430 May 13 22:35 test_suid*
他是 用户feng 创建的 他的作用也是输出 上面的四个id然后调用test程序,来测试有效id和实际id是否改变,内容如下:
6 int main(void){
7 printf("uid=%d,gid=%d\n",getuid(),getgid());
8 printf("euid=%d.egid=%d\n",geteuid(),getegid());
9
10 if(execl("./test","./test",(char *)0)==-1){
11 perror("execl error");
12 exit(1);
13 }
14 exit(0);
15 }
我们运行一下test_suid程序 ,输出如下:
feng@ubuntu:~/learn_linux_c_second/chapter_4$ ./test_suid
uid=1000,gid=1000
euid=1000.egid=1000
uid=1000,euid=0
euid=1000,egid=1000
我们来分析一下结果。我们当前用户是 feng 他的实际用户id和实际组id 都是1000(上面说过)。当我们运行test_suid这个普通程序时
他的 有效用户id和有效组id 都和实际对应的id 一样 (euid=1000,egid=1000).
程序输出两行后 执行 ececl。调用了 test程序。test程序是一个设置了SUID位的程序。所以当我们运行他时。程序的有效用户id就会被设置为root(id为0)。而其他id不变
设置组ID位:
SGID与SUID不同,他不仅可以针对文件(2进制程序),也可以针对目录来设置。
对于文件来说:
SGID置位时(注意这里是所属组中权限的s位),可以让具有该程序执行权限的进程在运行改程序过程中具有该程序所属组的权限(仅在程序运行过程中)即在执行过程中有效组id会变为该程序的组id
这基本和SUID位的作用时一样的,就是在执行时将 有效组id 设置为改程序的所属组id。
我们将上面的那个test程序修改下 让他的SGID位也被设置。
-rwsrwsr-x 1 root root 7357 May 13 10:46 test*
我们再运行一下 test_suid程序输出如下
feng@ubuntu:~/learn_linux_c_second/chapter_4$ ./test_suid
uid=1000,gid=1000
euid=1000.egid=1000
uid=1000,euid=0
euid=1000,egid=0
我们看到这是 有效用户id和有效组id 都变成root(id 为0)了。
对于目录来说:
如果SGID置位,那么某个目录下面创建的文件的 所属组 为该目录的所属组,如果该位未被设置,那么文件的所属组为创建文件的进程的有效组id。
举个简单例子说明下:
我现在切换到root用户 :
我们先将chapter_4目录设置SGID位:
drwxrwsr-x 2 feng feng 4096 May 13 22:52 chapter_4/
然后在 chapter_4目录下用touch命令创建一个文件test_file_1
-rw-r--r-- 1 root feng 0 May 13 22:52 test_file_1
我们看到 test_file_1文件的所属组不是 root的所属组(root的所属组也是root)。而是chapter_4的所属组 feng
我们现在去掉chapter_4的SGID 位
drwxrwxr-x 2 feng feng 4096 May 13 22:52 chapter_4/
然后我们再在该目录下创建一个文件 test_file_2
-rw-r--r-- 1 root root 0 May 13 23:08 test_file_2
我们发现 test_file_2 文件的所属组是root了。
最后我们来总结一下:
实际用户Id 和实际组id 是 用户的性质。他是当前登录用户的信息。
有效用户id 和 有效组 id 是进程拥有的性质。放该进程访问一些资源时,他们是内核用来测试该进程是否具有权限的依据。通常情况下他们和 用户实际id 和实际组id 一样
设置用户id 对于2进制程序SGID置位时,可以让具有该程序执行权限的进程在运行改程序过程中具有该程序所属组的权限,即在执行过程中有效组id会变为该程序的组id. 对于目录,如果SGID置位,那么某个目录下面创建的文件的 所属组 为该目录的所属组,如果该位未被设置,那么文件的所属组为创建文件的进程的有效组id。
阅读(1887) | 评论(0) | 转发(0) |