SUID
谈到SUID不得不说进程的安全上下文,那么何为进程的安全上下文?在我看来,进程的安全上下文应该分成上文和下文。
我们先来谈谈进程安全的上文,我们都明白,用户有是权限的,命令或者所有的文件和目录(在linux当中目录实际上也是一个文件哦!)也是有权限的,文件的权限我们可以很直观的通过ls命令看到:
[user1@A ~]$ ls -l /etc/passwd #一个配置文件的权限 -rw-r--r--. 1 root root 2466 9月 9 19:35 /etc/passwd [user1@A ~]$ ls -ld /etc #一个目录的权限 drwxr-xr-x. 141 root root 8192 9月 9 19:36 /etc [user1@A ~]$ ll `which cat` #一个命令或者说是一个二进制程序文件的权限 -rwsr-xr-x. 1 root root 54080 11月 6 2016 /bin/cat
那么用户的权限怎样理解和查看呢?刚开始我虽然接受了用户是有权限的理论,但是实际上并不完全理解,也不知道去哪里查看用户有哪些权限,后来学习的时候长一点儿的时候,忽然想明白了,linux把用户分类、分组实际就是一个权限的管理呀!!我们使用id命令查看一个用户的时候显示的信息其实就是用户的权限。
[user1@A ~]$ id root ; id zhanghe uid=0(root) gid=0(root) 组=0(root) uid=1000(zhanghe) gid=1000(zhanghe) 组=1000(zhanghe),10(wheel)
好了,终于把用户和文件的权限给说完了,可是这仅仅是一小步而已,距离把进程的安全的上文说明白还早呢!!继续吧。我们在上面知道了用户和文件(包括命令的二进制文件、正经文件、目录) 都是有权限的。那么当一个用户使用cat命令访问一个文件的时候,操作系统内部的权限动作是怎样的呢?正面我们就以一个最常用的案例来说明:
[user1@A ~]$ cat /etc/shadow
那么我们就来分析分析一个普通用户cat /etc/shadow时操作系统内部的权限运作:当我们在终端当中的bash当中输入cat /etc/shadow时,bash会对cat /etc/shadow这一段字符串进行分析,首先bash根据空格分析出哪一段是命令部分和参数部分,然后根据PATH环境变量分析出命令的部分是否正确,能在PATH变量当中查找到的就是认为正确的,如果bash没有在PATH变量当中没有找到命令部分的话就返回”command not found……”,就像下面这样:
[user1@A ~]$ dkjg #这是随便输入的 bash: dkjg: command not found...
接下来,user1用户就会根据PATH变量的路径找到cat命令对应的二进制文件位置。cat二进制文件“感受“到有用户来访问自己,就会根据自己inode文件当中记录的权限,首先核对一下来访问自己是不是属主,如果是属主的话就按照属主的权限来运行,如果不是属主那就核对一下来访问自己的这个用户有没有在属组里面,如果在属组里面话,就按照属组的权限进行运行,如果来访问即不是属主,也没有包含在属组里面的话,那么就按照其他人的权限进行运行。无论按照哪个权限,只要是运行起来之后,这个原本静态的程序就变成了动态的进程,并且这个进程也是带有权限的,注意!注意!!注意!!!cat进程所携带的权限与原本的cat二进制程序文件的权限一毛钱的关系也没有,这个动态的、运行当中的进程所携带的权限是将其运行起来的那个用户的权限,也就是说如果是user1用户运行的cat命令,那么cat进程所携带的权限就是user1所携带的权限。
到此,进程的安全上下文当中的上文就写完了。
再补充一点知识之后再讲进程安全上下文当中的下文部分。
如果你命令部分输入正确了,但是参数部分你是胡乱输入的话,那么bash是不会去判断你参数部分是不是输入正确,你可能可以疑惑了,命令部分正确的输入 ,但是参数部分胡乱输入的话也是会报错的呀?没错,这样同样也会报错:“No such file or directory”,但是请你仔细看看,报错并不是bash这个程序,而是你输入的命令报错的,就像下面这样:
[user1@A ~]$ cd oiejg -bash: cd: oiejg: No such file or directory #真正报错是cd这个命令,而不是bash程序。
再说的详细一点,当用户运行cat时,bash会在PATH变量当中找到cat对应二进制文件的绝对路径(/bin/cat),提交给内核,内核会根据其路径找到真正的cat命令在硬件当中的地址,然后把cat从硬件当中调用到内核当中的内核空间,然后再给cat进程分配真正在用户空间的地址,接下来把cat进程放入到内存的的用户空间,所以说真正的IO其实是两个步骤。
好了,下面再说一下进程安全上下文当中的下文部分。
在上文我们讲到了进程携带的权限其实是运行者的权限,所以当user1运行cat /etc/passwd的时,cat进程所携带的权限就是user1用户的权限,然后cat进程再去访问/etc/passwd,同样的,passwd文件也是会根据inode当中记录的权限对来访问的进程进行权限核对,发现user1不是passwd的属主,也没有在root组里面(passwd文件的属主和属组默认都是root),就按照其他人的权限访问(只读权限),所以普通用户user1就成功用cat命令查看到了passwd里面的内容。
终于,终于把进程的安全上下文讲完了,解释起来有点麻烦,但是如果明白了之后,其实挺简单的!进程安全上下文其实是为解释SUID做铺垫的,当您理解了进程的安全上下文之后再去理解SUID也变得非常的简单了,其实一句话就可以了:“所谓的SUID其实就是打破了进程安全上下文当中的上文”。那么是怎样打破的呢?
当user1去执行cat /etc/passwd的时候,cat进程携带的权限其实是user1的权限,但是如果如果给cat赋予了SUID权限的话,那么user1再运行cat命令的时候,一旦运行起来了之后,cat进程所携带的权限就是cat二进制文件的权限,cat自己的权限属主和属组都是root,也就是说cat进程所携带的权限不是user1的权限了,而是root的权限,所以说所谓的SUID就是打破了进程安全上下文当中的上文。其实给一个命令SUID权限是非常危险的,为什么呢?
[root@A ~]# su - user1 -c "cat /etc/shadow" #原本user1没有权限查看/etc/shadow cat: /etc/shadow: 权限不够 [root@A ~]# chmod u+s `which cat` #给cat二进制文件加上SUID权限 [root@A ~]# su - user1 -c "cat /etc/shadow" #然后user1再查看shadow文件,成功了!! bin:*:15980:0:99999:7::: daemon:*:15980:0:99999:7::: adm:*:15980:0:99999:7:::
所以说轻易不要给命令赋予SUID权限,其实系统当中一个现在的有SUID权限的命令就是passwd:
[user1@A ~]$ ll `which passwd` -rwsr-xr-x. 1 root root 30768 11月 24 2015 /usr/bin/passwd
这个命令普通用户在使用的时候也会暂时获取root的权限,可能你又疑惑了,如果普通用户运行passwd命令有root的权限,岂不是普通用户想修改谁的密码就修改谁的密码吗?其实我们用腿想一下就知道了,linux做为一款优秀成熟的操作系统肯定不会出这样重大的漏洞的,那么是怎样解决这个问题的呢?其实在内核代码就已经做了规定,普通用户使用passwd命令的时候是不允许携带任何参数的,只能赤裸裸的使用passwd命令修改自己的密码,就像下面演示的一样,
[user1@A ~]$ passwd root #user1想去修改root的密码 passwd: Only root can specify a user name. #只有根用户才能指定用户名称。
好了,到此,SUID就讲完了,那么来一遍完整的演示:
[root@A ~]# ll `which cat` -rwxr-xr-x. 1 root root 54080 11月 6 2016 /usr/bin/cat [root@A ~]# su - user1 -c "cat /etc/shadow" cat: /etc/shadow: 权限不够 [root@A ~]# chmod u+s `which cat` [root@A ~]# su - user1 -c "cat /etc/shadow" bin:*:17110:0:99999:7::: daemon:*:17110:0:99999:7:::
抽象理解:
进程的安全上下文就像“一把普通菜刀”,这把菜刀有多大的权利取决于谁拿着这把菜刀!皇帝(root)可以杀所有人,甚至可以自杀;普通百姓拿着这把菜刀权利就小的很,只能搞搞与自己相关的东西。
suid就像“尚方宝剑”,这把宝剑有多大的权利取决于这把剑自身!无论认谁拿尚方宝剑都可以“上斩昏君下斩佞臣”。
SGID
SGID可以用于文件也可以用于目录,用于文件时与suid类似,当程序运行于进程时属组是程序文件的属组,但是很少这样用,经常还是用在目录上,当用到目录上时,在目录里面创建的文件或者子目录都会继承目录的属组,仅仅是继承属组。
先演示没有sgid权限的场景:
[root@linuxprobe ~]# mkdir -m 777 /public [root@linuxprobe ~]# ll -d /public/ drwxrwxrwx 2 root root 6 6月 4 10:52 /public/ [root@linuxprobe ~]# su - zhanghe -c "touch /public/zhanghe.txt;ls -ld /public/zhanghe.txt" -rw-rw-r-- 1 zhanghe zhanghe 0 6月 4 10:53 /public/zhanghe.txt #在public目录里面创建文件的属主和属组与public目录没有关系,目录是root:root,而用户创建文件的权限是zhanghe:zhanghe
用了sgid权限之后的场景:
[root@linuxprobe ~]# mkdir -m 777 /test [root@linuxprobe ~]# ll -d /test drwxrwxrwx 2 root root 6 6月 4 10:55 /test [root@linuxprobe ~]# chmod g+s /test;ls -ld /test #加上sgid权限 drwxrwsrwx 2 root root 6 6月 4 10:56 /test [root@linuxprobe ~]# su - zhanghe -c "touch /test/zhanghe.txt;ls -ld /test/zhanghe.txt" -rw-rw-r-- 1 zhanghe root 0 6月 4 10:59 /test/zhanghe.txt
抽象理解:
sgid对目录使用时就是让此目录里创建的文件继承该目录的属组,就是这么简单,根本也用不着抽象,但是这个知识点还挺常用,我们应该讲一下sgid会用到什么场景当中(下文摘自《鸟哥的私房菜》):
有两个用户tom和jerry,两个用户除了属性自己的用户组之外还属于project组,两个用户需要共同拥有对/srv/ahome目录的开发权,且该目录不允许其他人进入查阅,请先用传统的权限实现,然后再用sgid实现。
准备工作:
[root@linuxprobe ~]# groupadd project #创建一个用户组project [root@linuxprobe ~]# useradd -G project tom #把tom用户加入到project里面 [root@linuxprobe ~]# useradd -G project jerry #把jerry用户加入到project里面 [root@linuxprobe ~]# id tom ; id jerry uid=1002(tom) gid=1003(tom) 组=1003(tom),1002(project) uid=1003(jerry) gid=1004(jerry) 组=1004(jerry),1002(project)
实现的方式:
我们只要把/srv/ahome的属组改成project,然后把权限改成770,即可以实现上述要求。
[root@linuxprobe /]# mkdir /srv/ahome [root@linuxprobe /]# chmod 770 /srv/ahome [root@linuxprobe /]# chgrp project /srv/ahome [root@linuxprobe /]# ll /srv/ahome -d drwxrwx--- 2 root project 6 6月 4 14:56 /srv/ahome
这样就好了吗?我们可以试一试
[root@linuxprobe /]# su - tom -c "touch /srv/ahome/tom.txt" [root@linuxprobe /]# su - jerry -c "touch /srv/ahome/jerry.txt" [root@linuxprobe /]# ll /srv/ahome/ -rw-rw-r-- 1 jerry jerry 0 6月 4 15:12 jerry.txt -rw-rw-r-- 1 tom tom 0 6月 4 15:11 tom.txt
通过上面的操作我们可以知道,在/srv/ahome这个目录里面tom的文件对jerry来讲是其他人,也就是说jerry在对tom创建的文件进行操作时,是以其他人的身份进行操作的,只有读的权限,相互之间并不能修改,这是不符合要求,我们想实现他们之间可以相互编辑,我们可以这么做:
[root@linuxprobe /]# rm -rf /srv/ahome/* [root@linuxprobe /]# chmod g+s /srv/ahome/ [root@linuxprobe /]# su - tom -c "touch /srv/ahome/tom.txt" [root@linuxprobe /]# su - jerry -c "touch /srv/ahome/jerry.txt"
我们再看两个文件的权限:
[root@linuxprobe /]# ll /srv/ahome/ -rw-rw-r-- 1 jerry project 0 6月 4 15:19 jerry.txt -rw-rw-r-- 1 tom project 0 6月 4 15:19 tom.txt
当tom再去访问jerry的文件时是以属组的权限去访问的,这样相互之间就有读写权限,可以相互编辑文件。
sticky
我们如果把一个目录的属组改为开发组,开发组里面的成员都对目录有读写执行的权限,开发组有那么多人,如果一个要看另一个不顺眼将其文件删除了怎么办?所以有了sticky权限,当我们对一个目录使用了sticky权限 之后,用户只能删除自己的文件,而不能删除别的文件。
/tmp/和/usr/tmp这两个目录默认有sticky权限。
[root@linuxprobe ~]# ll /tmp/ -d ; ll /usr/tmp/ -d drwxrwxrwt. 79 root root 8192 6月 4 10:52 /tmp/ drwxrwxrwt. 20 root root 4096 6月 4 09:01 /usr/tmp/
那么谁可以删除呢?
l root
l 文件所有者
l 该目录所有者
suid针对文件,以所有者身份执行
sgid针对文件时,以所有组的身份执行;针对目录时,继承目录的属组。
stickty,针对目录,只能删除自己的文件。/tmp和/var/tmp,只有自己可以销毁。