Linux 作为一种网络操作系统,允许多个用户使用。为了保护用户的个人文件不被其他用户侵犯,Linux 提供了文件权限的机制。这种机制使得一个文件或目录归一个特定的用户所有,这个用户有权对他所拥有的文件或目录进行存取或其他操作,也可以设置其他用户对这些文件或目录的操作权限。
Linux 还用到了用户组的概念。每个用户在建立用户目录时都被放到至少一个用户组中(当然,系统管理员可以将用户编进多个用户组中)。用户组通常是根据使用计算机的用户的种类来划分的。例如,普通用户通常属于usrs 组。另外,还有几个系统定义的组(如bin 和admin ),系统使用这些组来控制对资源的访问。
普通文件的权限有三部分:读、写和执行。分别用“r ” 、“w ” 、“x ” 来表示。目录也有这三种表示方式,分别表示:列出目录内容、在目录中建立或删除文件、进入和退出目录。以一个例子来解释文件权限的概念。
我们用带-l 选项的ls 命令来显示包括文件权限的长格式信息。
/ $ ls -l /home/user1/file1
-rw- r--r-- 1 user1 usrs 490 JUN 28 21:50 file1
在这个文件的长格式信息中,第一列代表的就是文件的类型和权限。其中,第一个字母表示文件的类型,“- ” 表明这是一个普通文件。接下来九个字符每三个一组依次代表文件的所有者、所有者所在用户组和组外其他用户对该文件的访问权限,代表文件权限的三个字符依次是读、写和执行权限,当用户没有相应的权限时, 系统在该权限对应的位置上用“- ” 表示。所以,本例表示 用户user1 自己对该文件有读和写的权限,但没有执行权限,而user1 所在usrs 组的其他用户和组外的用户对该文件只有读权限,没有写和执行权限。
有时候,我们会看到权限的执行位 上出现了“s ” ,而不是“x ” 。这涉及到进程 中的概念。一个进程,除了进程标识号(PID )外,还有四个标识号表示进程的权限。它们是:
· real user ID 实际用户标识号
· real group ID 实际用户组标识号
· effective user ID 有效用户标识号
· effective group ID 有效用户组标识号
实际用户标识号就是运行该进程的用户的UID ,实际用户组标识号就是运行该进程的用户的GID 。一般情况下,有效用户标识号、有效用户组标识号分别和实际用户标识号、实际用户组标识号相同。但如果设置setuid 位和setgid 位,则这两个标识号在对文件进行操作时自动转换成该文件所有者和所有组的标识号。setuid 位和setgid 位设置 与否,就是看文件权限位 上是否有“s ” ,如果用户的执行权限位 为“s ” ,则设置了setuid 位,如果用户组的执行权限位 为“s ” ,则设置了setgid 位。
这两个标识位 的设置正确与否关系到整个系统的安全,所以非同小可,我们以两个例子来说明。
/bin/passwd 就是一个设置了setuid 位的文件。当/bin/passwd 被执行时,它要在/etc 目录下找一个也叫passwd 的文件,该文件是个文本文件,里面记录了全部用户的数据,如口令、用户标识号等。该文件显然不能让超级用户以外的人修改,所以该文件的存取权限是rw-r--r-- 。但是,普通用户在更改它自己的口令时,必须改动这个文件,解决这个问题的方法便是设置setuid 的位,使指令在被执行时有和root 相同的权利,这样就可以修改这个文件了。
setuid 位的设立是有风险的!如果某个普通进程执行一个设置了setuid 位,且所有者是“root ” 的文件,立刻具有了超级用户的权利,万一该程序有BUG ,就留下了被人入侵系统的可能。因此,把文件按性质分类,再用设置setgid 位来达到分享的目标是一个不错的办法。
Linux 中有一个叫/etc/kmem 的字符设备文件,目的为存储一些核心程序要用到的数据。当用户注册到系统时所输入的口令都存在其中,所以这个文件不能给普通用户读写,以免给企图侵入系统者可乘之机,但将使用权限设为“rw------- ” 也不行,这样一般用户无法用ps 等显示系统状态的指令(这类显示系统状态的指令必须要读取kmem 的内容)。如果将存取权限设置成“rw-r----- ” ,再把组标识号改成与kmem 一样,最后再设置setgid 位,这样,无论谁执行ps ,该进程的组标识号都变得和ps 一样,从而就能读取kmem 的数据了。
作为一个系统管理员,能用setgid 来代替setuid 就尽量用setgid ,因为它的风险小得多!
还有一个不引人注意的现象,就是在其他用户的执行权限位 上可能出现“t ” ,而不是“x ” ,这表示该文件被装入内存后,将一直保留在内存中,好象 dos 下TSR 程序,这样可以减少程序的装入时间,增加程序的反应速度。因此,系统管理员可以对一些使用频率较高的工具程序设置该位。
创建一个新文件时,需要提供文件的访问权限位,但是,新文件的权限位 并不就是根据这个数设置的,还必须参考另一个值:文件权限的掩码,这是一个9 位的二进制数,对应于9 个权限位,如果这个数某一位上置1 ,则新创建文件相应权限位 被强制置0 ,而不管所提供的权限位 是怎样的。这种机制使的一个新文件在创建时被限制为一个合适的权限。可以用umask 命令看到这个值,它以3 位八进制数显示,默认为022 ,即000010010 ,所以一个新文件创建时,除了文件拥有者外,其它用户是没有写权限的。在VFS 的fs_struct 结构中有一个umask 域,存储的就是这个文件权限的掩码。
文件建立后,文件拥有者可以用chmod 改变访问权限。每一组权限位 的设置可以用一个八进制数表示,这样,用三个八进制数就可以表示9 个权限位 了。例如chmod 755 /home/user1/file1 , 则该文件的权限表示为 wxr-xr-xr 。如果要设置setuid 、setgid 位,则需要用到第四个八进制数,该数为4 ,则setuid 位被设置;该数为2 ,则setgid 位被设置,该数为1 ,则是其他用户的执行位 被设置成“t ” 。
Ext2 索引节点中的i_mode 就是用来存储文件的属性、用户标识号、用户组标识号和访问权限等信息的,这是一个16 位的字段,其中0 到8 位用来表示文件的访问权限,第9 位用来控制文件是否驻留内存,10 、11 位即setgid 、setuid 位,12 到15 位的组合用来表示文件类型。如表9.1 所示:
表9.1 imode 字段各位的含义
位 | 符号常量 | 含义 | |
12~15 位的组合 | 1100 | S_IFSOCK | 套接字 |
1010 | S_IFLNK | 符号链接文件 | |
1000 | S_IFREG | 普通文件 | |
0110 | S_IFBLK | 块设备 文件 | |
0100 | S_IFDIR | 目录 | |
0010 | S_IFCHR | 字符设备文件 | |
0001 | S_IFIFO | 命名管道 | |
11 | S_ISUID | 用户 | |
10 | S_ISGID | 用户组 | |
9 | S_ISVTX | 文件是否驻留内存 | |
0~8 | rwx-rwx-rwx | 文件的访问权限 |
这个表中的符号常量都是在include/Linux/stat.h 中定义的。