1. 前言
Linux系统中,文件权限有三大类:文件所属用户权限、文件所属组权限、其他用户权限。对于每一类,又有3种具体权限:读权限、写权限、执行权限(对于文件夹,是打开权)。
使用”ll”命令,可以看到一个文件前面有如下的信息:
上图中,用彩色标注的9个字节就是本文将要分析的文件权限符号。其他部分本文暂不讨论。
※ “文件所属组”不含“文件所属用户”。“其他用户”是指除“文件所属用户”和“文件所属组”内用户之外的用户。
2. 修改文件权限的方法
Linux系统中,可以使用chmod命令修改文件权限。
※ chmod命令的详细用法可以通过man命令查看,本文通过一些例子进行简单介绍。
1) 指定权限值
权限值使用4位8进制数表示。“千位”是特殊位(参考3.特殊权限),“百位”对应“文件所属用户”,“十位”对应“文件所属组”,“个位”对应“其他用户”。
例:
1-1) 仅为文件所属用户指定读写和执行权
#chmod 700 a.txt
1-2) 仅为文件所属组指定读写和执行权
#chmod 70 a.txt
1-3) 仅为其他用户指定读写和执行权
#chmod 7 a.txt
1-4) 为所有用户指定读写和执行权
2) 指定权限符号
2-1) 为文件所属用户(u)指定(+)读写和执行权
#chmod u+rwx a.txt
2-2) 为文件所属组(g)指定读写和执行权
#chmod g+rwx a.txt
2-3) 为其他用户(o)指定读写和执行权
#chmod o+rwx a.txt
2-4) 为所有用户(a)指定读写和执行权
#chmod a+rwx a.txt
上面的例子中的权限值和权限符号的具体含义如表2-1所示。
表2-1 权限值、权限符号、权限的关系
权限值 | 权限符号 | 用户权限 | ||
读 | 写 | 执行 | ||
0 | --- | × | × | × |
1 | --x | × | × | × |
2 | -w- | × | ○ | × |
3 | -wx | × | ○ | × |
4 | r-- | ○ | × | × |
5 | r-x | ○ | × | ○ |
6 | rw- | ○ | ○ | × |
7 | rwx | ○ | ○ | ○ |
※ 执行权依赖于读权。执行权对应1,写权对应2,读权对应4。多个权限同时存在时,权限值按照2进制的位与规则计算。
■ root用户的权限
对于普通用户而言,只有当其所对应的3个字节权限符号(或者1位8进制数)指定了某种权限时,它才能对该文件进行相应的操作。
而root用户则不同,只要9个字节权限符号中任意位置指定了某种权限,它就能对该文件进行相应操作。并且,root用户无条件对任意文件具有读权限。
例如,下面这样的文件,root用户具有读写和执行权。
#ll b.txt
3. 特殊权限
1) 数值形式
权限值的“千位”是特殊权限位。其含义如下。
1000:只对文件夹有意义。设置该权限值后,只有root用户或者“文件所属用户”能对该文件夹下的文件进行删除或重命名。
2000:设置该权限后,文件执行时,EGID变成“文件所属组”GID,而不是当前用户所在组的GID
4000:设置该权限后,文件执行时,EUID变成“文件所属用户”UID,而不是当前用户的UID
■ EGID和EUID的作用
EGID和EUID可以理解为潜在的GID和UID,因为可以通过以下的系统调用,将EGID和EUID转化为GID和UID:
setgid(getegid());
setuid(geteuid());
2) 符号形式
表3-1 特殊权限的权限值、权限符号
权限值 | 权限符号 | 指定方法 | 说明 |
4000 | --S------ | chmod u+s | 无执行权限时s显示成大写 |
2000 | -----S--- | chmod g+s | 同上 |
1000 | --------T | chmod o+t | 无执行权限时t显示成大写 |
4100 | --s------ | chmod u+sx | 有执行权限时s显示成小写 |
2010 | -----s--- | chmod g+sx | 同上 |
1001 | --------t | chmod o+tx | 有执行权限时t显示成小写 |
3) 特殊权限的作用
3-1) t权限用于防止文件夹下用户A的文件被用户B删除。像/tmp这样的公共目录一般会使用t权限。
3-2) s权限对系统安全有威胁,因为一个普通用户可以通过设定了s权限的程序将自己提升为root用户,并且不需要通过密码认证。
下面给出一个从普通用户user1变为root的例子。
1) 程序rootProc向root.txt输出一句话。其中,root.txt只有root用户才能改写:
#ll root.txt
程序rootProc的代码如下。
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
int main(void) {
printf(
" UID GID \n"
"Real %d Real %d \n"
"Effective %d Effective %d \n",
getuid (), getgid (),
geteuid(), getegid()
);
setuid(geteuid());
setgid(getegid());
system("echo I’m root. > root.txt");
return 0;
}
2) 没有设定s权限时,程序rootProc的信息如下:
#ll rootProc
此时,user1(UID/GID=501)执行rootProc将输出如下信息,写root.txt时失败:
UID GID
Real 501 Real 501 // UID/GID
Effective 501 Effective 501 // EUID/EGID
sh: root.txt: Permission denied
可见,没有设定s权限时,程序启动后EUID/EGID和UID/GID是相同的,都是user1的UID/GID,因此,通过setuid()/setgid()方法并不能改变UID/GID,无法提升程序的权限。
※ EUID不是0(root)的场合,如果调用setuid(0),试图直接获得root权限,是无法成功的。
3) 设定s权限(chmod ug+s)后,程序rootProc的信息如下:
#ll rootProc
此时,user1执行rootProc将输出如下信息,写root.txt时成功:
UID GID
Real 501 Real 505
Effective 0 Effective 0
设定s权限后,程序启动后的EUID/EGID不再是user1的UID/GID,而变成了root的UID/GID。因此,通过setuid()/setgid()方法可以改变UID/GID,从而使程序能够获得root用户的权限。
※ 上例表明,如果可执行程序的“文件所属用户”是root并且被设定了s权限,普通用户可以从这个程序中获得root用户的权限,并且不需要密码认证。而即使该程序中没有执行setuid()/setgid()操作,黑客也可能通过缓冲区溢出等手段使该程序执行setuid()/setgid()操作,从而使程序获得root权限。因此,为root所属的文件设定s权限时,需要考虑程序本身是否足够安全。