Unix操作系统从一开始就在其文件系统中引入了文件、访问权限等概念,并在此基础上实现了有利于提高文件安全性的机制。从那以后这些概念和机制就一直被继承下来并进一步得到改进和完善。即使在经过了很多年后的今天,而且在计算机系统的安全性已经成为一个突出问题的情况下,这一套机制仍然不失其先进性。尽管还存在一些缺点和需要进一步改进的地方,从总体上说还是瑕不掩瑜。与当今正在广泛使用的其他操作系统相比,可以说Unix的安全性总的来说至少不会差于这些系统;如果考虑到近年来在Unix及linux中已经作出的改进以及不难作出的进一步改进,可以说Unix在安全性方面与任何其他系统相比都不逊色。同时,从当前流行的其他操作系统中,人们不难看出它们受Unix影响的或明或暗的痕迹。
Unix文件系统的访问权限是一种二维结构。就同一个用户来说,对一个文件的访问分成读、写和执行三种形式,因而形成三种不同的权限;而就同一中访问方式来说,则又可因访问者的身份属于文件主、文件主的同组人以及其他用户(称为other)而分别决定允许与否。这样一块就有9中组合,可以用9个二进制位来表示。早期的Unix是在16位结构的PDP-11机器上开发出来的,所以从那时起就一直用一个16位短整型数据结构来表示一个文件的访问模式,而将其中的低9位用于访问权限。当时比较流行的是八进制表示法,所以正好将这9位表示成3个八进制位从高到低分别用于文件主(u)、文件主的同组人(g)以及其他(o),而每个八进制数位中的三个二进制位则从高到低分别用于读、写和执行三种权限。这种表示方法一致沿用至今,例如在命令“chmod 644 file1”中的644就是这样三个8进制位。此外,这种把访问者区分为文件主、同组人以及其他用户,根据访问者的身份而分别决定其访问权限的方案称为“discretionary access control”,简写为DAC。
这个方案的实施分成几个方面。首先,除用户名外,每个用户还授予一个(在系统范围内)唯一的用户号UID,并且总是属于某一而用户组,而每个用户组则唯一的组号gid,这些信息记录在相当于一个小数据库的文件/etc/passwd中。其次,每个文件的索引节点中记录着文件主的uid、gid以及文件访问模式。还有,在每个进程的task_struct结构中相应地设置了uid和gid等字段。每当用户通过登录进入系统并创立第一个shell进程时,就从/etc/passwd中根据用户名查得其uid和gid,并将其设置到该shell进程的task_struct结构中,从此以后便代代相传。最后,也是最重要的是,内核在执行用户进程访问文件的请求的访问权限。(实际上,进程的task_struct结构中还有euid、egid、suid、sgid、fsuid以及fsgid等字段,下面还要解释)。此外,uid为0的用户为超级用户,而超级用户对任何文件都具有与文件主相同的权限。还要注意,用户名与用户号并不是一对一的关系,多个用户,甚至所有用户,都可以对应到同一个用户号。
由于超级用户的进程对任何文件都具有与文件主相同的权限,实际上可以对任何文件为所欲为,这就带来了危险(这里还没有考虑有人非法取得特权用户权限所引起的问题)。所以,有时候需要通过一个进程的用户号和组号来改变(限制)其访问权限。由此引申出了进程的真实用户号、真实组号和当前的有效用户号、有效组号的概念。相应的,在进程的task_struct结构中也增设了euid(表示effective uid)和egid两个字段,并且提供了setuid、seteuid等系统调用。另一方面,在改变有效用户号时往往需要把原来的有效用户号暂时保存起来,以便以后恢复,所以在task_struct结构中又增设可suid(表示saved uid)和sgid两个字段,这样,在task_struct结构中就有了三个用户号和三个组号,即uid、euid、suid以及gid、egid、sgid。后来,在开发和使用网络文件系统NFS的守护神(即服务进程)的过程中认识到,在网络环境下对文件的访问还需要一个不同的用户号,因此有增加一个fsuid和一个fsgid。通常fsuid与euid相同,而fsgid和egid相同,但是在特殊的情况下可以不同。这里要指出,一般而言,只有特权用户以及具有特权用户权限的进程(见下面的所谓set_uid文件和进程)才能通过系统调用来改变其用户号和组号,这些系统调用的结果都是使进程的权限更受限制;在相反的方向上,则最多是恢复到原有的水平,所以一个非特权用户进程是不能通过setuid或seteuid得到特权用户的权限的,这一点跟我们头脑中一个普通用户可以通过shell命令su变成特权用户的印象可能不一致。这里面的原因是su是一个set_uid可执行程序,它的文件主是root,即特权用户,所以当普通用户执行su的过程中就自动具有了特权用户的权限,这正是我们接下去要讨论的。
在前述二维访问权限机制的框架中,让我们考虑一个问题,即一个普通用户怎样才可以改变它自己的口令。我们知道,有关用户的名称、用户号、组号、口令等信息都保存在文件/etc/passwd中。这个文件的主人只能是超级用户,因为只有超级用户才是系统中最核心、权利最大的用户,通常就是系统的管理员。除超级用户外,其他所有的用户对这个文件都不应该由写权限,因为那样的话每个用户都可以通过修改这个文件、将自己的用户号改成0而变成特权用户了。所以,除文件主以外,所有其他用户对/etc/passwd都只能有读而不能有写权限。这显然是合理的,而且只能如此。可是,这样一来,一个普通用户就不能通过运行一个什么程序来改变自己的口令了,因为改口令意味着改变/etc/passwd中的内容。怎样解决这个矛盾呢?早期的Unix采用了一种当时看起来很巧妙的办法,就是在一些特殊用途的可执行文件上加一个标记,使得任何用户在执行这个文件(程序)时就暂时有了与该文件的文件主(通常是超级用户)相同的权限。这样,只要把用来改变口令的程序(/bin/passwd)加上这种标记,普通用户在执行这个程序的时候就能拉大旗作虎皮,暂时有了特权用户的权限,可以改变/etc/passwd的内容了。一旦执行完毕,则又回到原来的权限,又是普通用户了。这样的可执行文件,就成为set_uid文件,而加上这种文件上的标记,则是在文件模式中的一个标志位S_ISUID。与此类似,还有一个标志位S_ISGID,可以理解为对S_ISUID标志位的推广。有时候人们称S_ISUID标志位为s位,因为在用命令 ls -l 列目录时把表示这种文件对文件主的可执行权限的字符x变成了s。在当时,这个办法却是很巧妙、很有效,据说,at&t还为此申请了专利。可是,近年来去发现这种set_uid文件给黑客们带来了可乘之机,简直已经成了Unix(以及linux)在安全性检查的万恶之源,后面我们还会回到这个问题上来。
除这两个标志位以外,早期Unix还为可执行文件定义了一个粘滞(sticky)标志位。对于一些频繁运行的程序,可以把这个标志位设成1,使得内核在这个程序运行完毕后尽可能将其映像保存在内存汇总不予释放,这样下一次需要启动这个程序运行时就不需要再从磁盘装入了。不过,现在的Unix和linux多已采用虚存管理,所以这个标志位现在已经没什么意义了。
前面说过,文件的模式是以一个16位无符号整数表示的,其中9位已经用于对三种不同用户的访问权限,现在又用去了3位。这样还剩下4位,用来表示文件的类型。不过,由于只剩下4位,要为每种文件类型都分配一个标志位已不可能了,所以表示文件类型的这4位是编码的。对文件类型和上述几个标志位的定义在include/linux/stat.h,但是另一个文件include/linux/sysv_fs.h中有几行注释提供了比较详细的说明:
/* The admissible values for i_mode are listed in <linux/stat.h> :
* #define S_IFMT 00170000 mask for type
* #define S_IFREG 0100000 type = regular file
* #define S_IFBLK 0060000 type = block device
* #define S_IFDIR 0040000 type = directory
* #define S_IFCHR 0020000 type = character device
* #define S_IFIFO 0010000 type = named pipe
* #define S_ISUID 0004000 set user id
* #define S_ISGID 0002000 set group id
* #define S_ISVTX 0001000 save swapped text even after use
* Additionally for SystemV:
* #define S_IFLNK 0120000 type = symbolic link
注意,这里的数字均为八进制,其中S_IFMT并不代表一种文件类型,而只是对文件类型的屏蔽位段。对低9位的定义则为:
#define S_IRWXU 00700
#define S_IRUSR 00400
#define S_IWUSR 00200
#define S_IXUSR 00100
#define S_IRWXG 00070
#define S_IRGRP 00040
#define S_IWGRP 00020
#define S_IXGRP 00010
#define S_IRWXO 00007
#define S_IROTH 00004
#define S_IWOTH 00002
#define S_IXOTH 00001
这个16位的文件模式存储在每个文件的索引节点中,而每个进程则在其task_struct结构中有uid、euid等说明其身份的信息。这就是判定一个进程是否有权对某个文件进行某种访问的基础。对访问权限的判定主要是由函数permission完成的,读者在path_walk的代码中已经看到过了,在那里的for循环中对路径中的每一个节点调用这个函数,其代码如下:
int permission(struct inode * inode,int mask)
{
if (inode->i_op && inode->i_op->permission) {
int retval;
lock_kernel();
retval = inode->i_op->permission(inode, mask);
unlock_kernel();
return retval;
}
return vfs_permission(inode, mask);
}
参数mask为代表着所要求的访问方式的标志位,定义如下:
#define MAY_EXEC 1
#define MAY_WRITE 2
#define MAY_READ 4
对于一般的文件系统就分成这么三种方式。网络文件系统NFS的情况特殊,除这三种方式以外还定义了MAY_TRUNC、MAY_LOCK等方式以及这些方式的若干组合,不过NFS不在本书要讨论的范围内。
如果具体的文件系统通过其inode_operations结构中函数指针permission提供了特定的访问权限判定函数,那就把事情交给它了,否则就执行一般的vfs_permission。
就ext2文件系统而言,共有三个inode_operations结构,即ext2_file_inode_operations、ext2_dir_inode_operations以及ext2_fast_symlink_inode_operations,根据具体inode结构所代表的节点性质而在ext2_read_inode中将其i_op指针设置成指向这三者之一。可是,这三个结构中都没有提供专门的permission操作(函数指针permission为NULL),所以执行vfs_permission,其代码如下:
permission=>vfs_permission
/*
* permission()
*
* is used to check for read/write/execute permissions on a file.
* We use "fsuid" for this, letting us set arbitrary permissions
* for filesystem access without changing the "normal" uids which
* are used for other things..
*/
int vfs_permission(struct inode * inode,int mask)
{
int mode = inode->i_mode;
if ((mask & S_IWOTH) && IS_RDONLY(inode) &&
(S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode)))
return -EROFS; /* Nobody gets write access to a read-only fs */
if ((mask & S_IWOTH) && IS_IMMUTABLE(inode))
return -EACCES; /* Nobody gets write access to an immutable file */
if (current->fsuid == inode->i_uid)
mode >>= 6;
else if (in_group_p(inode->i_gid))
mode >>= 3;
if (((mode & mask & S_IRWXO) == mask) || capable(CAP_DAC_OVERRIDE))
return 0;
/* read and search access */
if ((mask == S_IROTH) ||
(S_ISDIR(inode->i_mode) && !(mask & ~(S_IROTH | S_IXOTH))))
if (capable(CAP_DAC_READ_SEARCH))
return 0;
return -EACCES;
}
这里用到的一些红操作分别定义于fs.h和stat.h:
#define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK)
#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
#define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR)
#define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK)
#define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO)
#define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK)
#define IS_RDONLY(inode) ((inode)->i_sb->s_flags & MS_RDONLY)
IS_RDONLY表示节点所在的文件系统,即磁盘设备,是按只读方式安装的。在这样的磁盘上,对常规文件、目录以及符号链接这三种节点都不能写。但是,即使是在按只读方式安装的文件系统中,如果节点所代表的是FIFO文件、socket等等特殊文件,或者设备文件(块设备或字符设备都一样),那就未必是不可写的。为什么呢?因为对这些文件的写访问实际上不会或者不一定写到该节点所在的磁盘上去。
其次IS_IMMUTABLE的定义为:
#define IS_IMMUTABLE(inode) ((inode)->i_flags & S_IMMUTABLE)
在较新的linux(Unix)版本中,除访问权限外又给每个文件加上了一些属性,不可更改即使其中之一。这些属性也像访问权限一样以标志位的形式存储在文件的索引节点中,但是不像访问权限那样区分文件主、文件主的同组用户以及公众,而是另成系统,并且凌驾于访问权限上。而且,一旦设置了这些属性,即使是特权用户也不能在系统还是正常的多用户环境下运行时将这些属性去掉。这样,就算有黑客偷到了特权用户的口令,对这些文件也就无能为力了。如果一个文件被设置成了不可更改,那么即使是超级用户通过chmod把文件的访问模式设置成科协也无济于事。显然,这是因为安全性考虑而作的改进和增强。表示这些属性的标志位定义如下:
/*
* Inode flags
*/
#define EXT2_SECRM_FL 0x00000001 /* Secure deletion */
#define EXT2_UNRM_FL 0x00000002 /* Undelete */
#define EXT2_COMPR_FL 0x00000004 /* Compress file */
#define EXT2_SYNC_FL 0x00000008 /* Synchronous updates */
#define EXT2_IMMUTABLE_FL 0x00000010 /* Immutable file */
#define EXT2_APPEND_FL 0x00000020 /* writes to file may only append */
#define EXT2_NODUMP_FL 0x00000040 /* do not dump file */
#define EXT2_NOATIME_FL 0x00000080 /* do not update atime */
/* Reserved for compression usage... */
#define EXT2_DIRTY_FL 0x00000100
#define EXT2_COMPRBLK_FL 0x00000200 /* One or more compressed clusters */
#define EXT2_NOCOMP_FL 0x00000400 /* Don't compress */
#define EXT2_ECOMPR_FL 0x00000800 /* Compression error */
/* End compression flags --- maybe not all used */
#define EXT2_BTREE_FL 0x00001000 /* btree format dir */
#define EXT2_RESERVED_FL 0x80000000 /* reserved for ext2 lib */
#define EXT2_FL_USER_VISIBLE 0x00001FFF /* User visible flags */
#define EXT2_FL_USER_MODIFIABLE 0x000000FF /* User modifiable flags */
其中有些属性是为其他目的而设的(如压缩),我们在这里只关心与安全性有关的属性。例如,EXT2_APPEND_FL表示对文件的写访问只能添加在文件的末尾,而不能改变文件中已有的内容。读者会问,在打开文件时不是就有个添加模式吗?为什么这里又要来一个添加属性呢?答案很简单,打开文件时的添加模式是用户进程自愿的,而文件的添加属性却是强制的。
所以,只要inode结构中的i_flag里面的S_IMMUTABLE标志位1,那就剥夺了所有用户对这个文件的写访问权(见163行),而与文件所设置的访问权限以及访问者的身份无关。
还有个属性EXT2_NODUMP_FL,意图是使可执行文件在运行中访问内存出错(越界访问等)时不要生成dump文件。从Unix的早期版本开始可执行文件在运行过程中因访问内存侵权而出错时都会把当时的内存映射卸载到一个磁盘文件汇总(名为core,因为早期的计算机采用磁芯存储器),使程序员可以使用调试工具(如gdb等)来重建起发生问题时的场景,这对于软件的维护显然是有好处的。可是,在实践中却发现,这也给怀有恶意的黑客提供了可乘之机。为了在某些情况下不让产生dump文件,在task_struct结构中增设了一个标志位dumpable,在某些情况下(例如通过setuid设置了有效用户号)就将这个标志位清0。同时,又设置了一个系统调用prctl,其用途之一就是将dumpable标志设置成1或0,可是这还是不能解决防止恶意攻击的问题。在一些特殊的应用环境(如银行)中,对一些特殊的可执行程序,需要完全杜绝其产生dump文件的可能性,这就是设置EXT2_NODUMP_FL属性及标志位的意图。此外,对于某些特殊文件,不能像对一般的文件那样每次访问后就要打下时间戳,标志位EXT2_NOATIME_FL就是为此目的而设置的。总之,对于传统的Unix文件系统而言,这些属性(标志位)都是体制外的,所以不能纳入原先的框架中,而其中有一些是为增强文件系统的安全性而设置的。
回到permission的代码中,下面就是访问权限的比对了。这里mode是取自inode结构中的文件访问模式,即前述的16位无符号短整型;mask则为所要求的访问方式,即MAY_EXEC、MAY_WRITE或MAY_READ,实际上只用了最低3位。前面说过,当前进程的fsuid是专用于文件访问目的的有效uid,通常与进程的euid想用,但是在使用网络文件系统时可能会不同。如果当前进程的fsuid与文件主的uid相同,那么要比对的是mode中用于文件主的访问权限,所以把mode右移6位,把用于文件主的三个标志位移动到最低的3位中。如果当前进程的fsuid与文件主的uid不同,那就要检查一下当前右移3位。判定是否同组比判定是否文件主要复杂一些,是通过一个函数in_group_p来完成的,其代码如下:
permission=>vfs_permission=>in_group_p
/*
* Check whether we're fsgid/egid or in the supplemental group..
*/
int in_group_p(gid_t grp)
{
int retval = 1;
if (grp != current->fsgid)
retval = supplemental_group_member(grp);
return retval;
}
如果进程的fsgid与文件主的组号相同,那就成了。可是即使这二者不同也还有可能实际上是相等的,因为一个用户(从而一个进程)可以同时属于若干个组,后面会讲解进程的博客,可以看看。sched.h中对task_struct的定义,在task_struct结构中有个数组groups,其大小为常数NGROUPS,该常数为32。当然,一个用户(进程)未必会那么社会化,所以在task_struct中还有个计数器ngroups。与此相应,还提供了系统调用get_groups和set_groups。(只有得到授权的进程才可以set_groups)。所以,如果fsgid与文件主的组号不同,就要进一步拿这个数组中的其他候补组号跟文件主的组号相比,函数的代码如下:
permission=>vfs_permission=>in_group_p=>supplemental_group_member
static int supplemental_group_member(gid_t grp)
{
int i = current->ngroups;
if (i) {
gid_t *groups = current->groups;
do {
if (*groups == grp)
return 1;
groups++;
i--;
} while (i);
}
return 0;
}
最后,如果当前进程却是不属于文件主的同组人,那就是属于其他用户了。此时mode不需要移位,因为要比对的3位已经在最低的位置上了。
常数S_IRWXO的值为7,所以比对的是此时mode中最低的3位。比对的结果相符时,permission返回0,;要是不符呢?一般而言就失败了。但是还有例外。首先,如果当前进程得到了授权,允许其CAP_DAC_OVERRIDE,即可以凌驾于文件系统的访问权限控制机制DAC之上,则基本上不受其限制。不过,前面159行和163行中检查的两种情况不在内。实际上,IS_IMMUTABLE要有另一种授权(CAP_LINUX_IMMUTABLE)的进程才能设置。所以,这种进程就好像是捧着“尚方宝剑”的钦差大臣,这才是真正意义上的超级用户。可惜超级用户和特权用户这两个词都已经用于uid为0的用户,所以我们在本书中称此类进程为授权进程。等一下我们还要回到这个话题上来。
除了拥有CAP_DAC_OVERRIDE授权的进程之外,还要一种特殊情况,那就是另一种授权CAP_DAC_READ_SEARCH,拥有这种特权的进程可以读任何文件,并且可以搜索任何目录节点,所以,代码中的177行检查所要求的是否读访问或者对目录节点的搜索。这里要提醒读者,搜索目录节点时所要求的访问方式为执行而不是读,所以在path_walk的for循环汇总对每个目录节点调用permission时的参数为MAY_EXEC,而不是MAY_READ。
如前所述,用户进程在一定条件下可以通过系统调用来设置其用户号,有关的系统调用由setuid、setfsuid、seteuid、setreuid。其中setuid是标准的设置用户号调用,内核中与之相应的函数为sys_setuid,代码如下:
/*
* setuid() is implemented like SysV with SAVED_IDS
*
* Note that SAVED_ID's is deficient in that a setuid root program
* like sendmail, for example, cannot set its uid to be a normal
* user and then switch back, because if you're root, setuid() sets
* the saved uid too. If you don't like this, blame the bright people
* in the POSIX committee and/or USG. Note that the BSD-style setreuid()
* will allow a root program to temporarily drop privileges and be able to
* regain them by swapping the real and effective uid.
*/
asmlinkage long sys_setuid(uid_t uid)
{
int old_euid = current->euid;
int old_ruid, old_suid, new_ruid;
old_ruid = new_ruid = current->uid;
old_suid = current->suid;
if (capable(CAP_SETUID)) {
if (uid != old_ruid && set_user(uid) < 0)
return -EAGAIN;
current->suid = uid;
} else if ((uid != current->uid) && (uid != current->suid))
return -EPERM;
current->fsuid = current->euid = uid;
if (old_euid != uid)
current->dumpable = 0;
if (!issecure(SECURE_NO_SETUID_FIXUP)) {
cap_emulate_setxuid(old_ruid, old_euid, old_suid);
}
return 0;
}
一般超级用户(其euid为0)都具有CAP_SETUID授权,此时在569行和573行把当前进程的euid、suid、fsuid都设立成新的uid。注意,这里吧suid也设置成新的uid,实在是个败笔,因为task_struct结构中的suid本意在于save uid,即在暂时改变euid时可以记住原来的euid是什么,以便以后恢复。而这里吧suid也设置成了uid,就失去了它的作用,并且用户号改变的历史也被一笔勾销,以后无法恢复成超级用户了。代码的原作者在函数前面加了注释,也谈到了这个问题。可是,超级用户在调用setuid时把其suid也设置成新的uid,这是在POSIX标准中规定了的,明知道不合理也只能如此。正因为这样,在BSD(以及linux)中另外提供了系统调用seteuid和setreuid来避免这个缺点。相比之下,对于不具备CAP_SETUID授权的进程,则只能设置当前进程的euid和fsuid,但是只有在新的uid就是进程的真实uid或者suid时才能进行。
每当进程改变其euid时,其task_struct结构中的标志位dumpable就被清0,这样进程在访问出错时就不会产生dump文件了、
如果当前进程具有CAP_SETUID授权,并且新的uid又与原来的真实用户号不同,则连进程的真实用户号也要改变,这是通过set_user实现的:
sys_setuid=>set_user
static int set_user(uid_t new_ruid)
{
struct user_struct *new_user, *old_user;
/* What if a process setreuid()'s and this brings the
* new uid over his NPROC rlimit? We can check this now
* cheaply with the new uid cache, so if it matters
* we should be checking for it. -DaveM
*/
new_user = alloc_uid(new_ruid);
if (!new_user)
return -EAGAIN;
old_user = current->user;
atomic_dec(&old_user->processes);
atomic_inc(&new_user->processes);
current->uid = new_ruid;
current->user = new_user;
free_uid(old_user);
return 0;
}
后面的进程相关博客会提到,内核中有个杂凑表uidhash_table,各个进程的task_struct结构按其用户号uid的杂凑值挂入杂凑表的某个队列中。这样,根据给定的uid就可以很快找到所有属于该用户的进程。同时,在task_struct结构中有个指针user,指向一个user_struct数据结构,这个数据结构就好像task_struct与杂凑队列之间的连接件,进程的task_struct结构就是通过它挂入杂凑队列。现在,既然当前进程要改换门庭了,就要从原来的杂凑队列中脱链并将其user_struct结构释放,然后另行分配一个user_struct结构并挂入另一个队列。函数free_uid的代码如下:
sys_setuid=>set_user=>free_uid
void free_uid(struct user_struct *up)
{
if (up && atomic_dec_and_lock(&up->__count, &uidhash_lock)) {
uid_hash_remove(up);
kmem_cache_free(uid_cachep, up);
spin_unlock(&uidhash_lock);
}
}
函数alloc_uid的作用与free_uid正好相反,我们就不列出它的代码了。
我们在前面讲过,超级用户的进程通常是得到某些授权的。相比之下,一般用户则得不到任何授权,它所有的只是文件系统的访问权限机制DAC所赋予的基本权利,而且这些基本权利也有可能得不到兑现,因为文件的属性如IS_IMMUTABLE等是凌驾于DAC之上的。可想而知,当一个超级用户进程改变其uid至某一普通用户时,其授权也要发生一些变化。
对进程的授权是独立于文件系统的访问权限控制之外,并且凌驾于其上的机制。为此目的在task_struct结构中设置了cap_effective、cap_inheritable,和cap_permitted三个字段,其类型为kernel_cap_t,目前实际上是32位无符号整数。每一种授权(capability)都用一个标志位来表示,目前共定义了29中授权,所以32位无符号整数就够用了。这些表示为(和授权)的定义如下:
/**
** POSIX-draft defined capabilities.
**/
/* In a system with the [_POSIX_CHOWN_RESTRICTED] option defined, this
overrides the restriction of changing file ownership and group
ownership. */
#define CAP_CHOWN 0
/* Override all DAC access, including ACL execute access if
[_POSIX_ACL] is defined. Excluding DAC access covered by
CAP_LINUX_IMMUTABLE. */
#define CAP_DAC_OVERRIDE 1
/* Overrides all DAC restrictions regarding read and search on files
and directories, including ACL restrictions if [_POSIX_ACL] is
defined. Excluding DAC access covered by CAP_LINUX_IMMUTABLE. */
#define CAP_DAC_READ_SEARCH 2
/* Overrides all restrictions about allowed operations on files, where
file owner ID must be equal to the user ID, except where CAP_FSETID
is applicable. It doesn't override MAC and DAC restrictions. */
#define CAP_FOWNER 3
/* Overrides the following restrictions that the effective user ID
shall match the file owner ID when setting the S_ISUID and S_ISGID
bits on that file; that the effective group ID (or one of the
supplementary group IDs) shall match the file owner ID when setting
the S_ISGID bit on that file; that the S_ISUID and S_ISGID bits are
cleared on successful return from chown(2) (not implemented). */
#define CAP_FSETID 4
/* Used to decide between falling back on the old suser() or fsuser(). */
#define CAP_FS_MASK 0x1f
/* Overrides the restriction that the real or effective user ID of a
process sending a signal must match the real or effective user ID
of the process receiving the signal. */
#define CAP_KILL 5
/* Allows setgid(2) manipulation */
/* Allows setgroups(2) */
/* Allows forged gids on socket credentials passing. */
#define CAP_SETGID 6
/* Allows set*uid(2) manipulation (including fsuid). */
/* Allows forged pids on socket credentials passing. */
#define CAP_SETUID 7
/**
** Linux-specific capabilities
**/
/* Transfer any capability in your permitted set to any pid,
remove any capability in your permitted set from any pid */
#define CAP_SETPCAP 8
/* Allow modification of S_IMMUTABLE and S_APPEND file attributes */
#define CAP_LINUX_IMMUTABLE 9
/* Allows binding to TCP/UDP sockets below 1024 */
/* Allows binding to ATM VCIs below 32 */
#define CAP_NET_BIND_SERVICE 10
/* Allow broadcasting, listen to multicast */
#define CAP_NET_BROADCAST 11
/* Allow interface configuration */
/* Allow administration of IP firewall, masquerading and accounting */
/* Allow setting debug option on sockets */
/* Allow modification of routing tables */
/* Allow setting arbitrary process / process group ownership on
sockets */
/* Allow binding to any address for transparent proxying */
/* Allow setting TOS (type of service) */
/* Allow setting promiscuous mode */
/* Allow clearing driver statistics */
/* Allow multicasting */
/* Allow read/write of device-specific registers */
/* Allow activation of ATM control sockets */
#define CAP_NET_ADMIN 12
/* Allow use of RAW sockets */
/* Allow use of PACKET sockets */
#define CAP_NET_RAW 13
/* Allow locking of shared memory segments */
/* Allow mlock and mlockall (which doesn't really have anything to do
with IPC) */
#define CAP_IPC_LOCK 14
/* Override IPC ownership checks */
#define CAP_IPC_OWNER 15
/* Insert and remove kernel modules - modify kernel without limit */
/* Modify cap_bset */
#define CAP_SYS_MODULE 16
/* Allow ioperm/iopl access */
/* Allow sending USB messages to any device via /proc/bus/usb */
#define CAP_SYS_RAWIO 17
/* Allow use of chroot() */
#define CAP_SYS_CHROOT 18
/* Allow ptrace() of any process */
#define CAP_SYS_PTRACE 19
/* Allow configuration of process accounting */
#define CAP_SYS_PACCT 20
/* Allow configuration of the secure attention key */
/* Allow administration of the random device */
/* Allow examination and configuration of disk quotas */
/* Allow configuring the kernel's syslog (printk behaviour) */
/* Allow setting the domainname */
/* Allow setting the hostname */
/* Allow calling bdflush() */
/* Allow mount() and umount(), setting up new smb connection */
/* Allow some autofs root ioctls */
/* Allow nfsservctl */
/* Allow VM86_REQUEST_IRQ */
/* Allow to read/write pci config on alpha */
/* Allow irix_prctl on mips (setstacksize) */
/* Allow flushing all cache on m68k (sys_cacheflush) */
/* Allow removing semaphores */
/* Used instead of CAP_CHOWN to "chown" IPC message queues, semaphores
and shared memory */
/* Allow locking/unlocking of shared memory segment */
/* Allow turning swap on/off */
/* Allow forged pids on socket credentials passing */
/* Allow setting readahead and flushing buffers on block devices */
/* Allow setting geometry in floppy driver */
/* Allow turning DMA on/off in xd driver */
/* Allow administration of md devices (mostly the above, but some
extra ioctls) */
/* Allow tuning the ide driver */
/* Allow access to the nvram device */
/* Allow administration of apm_bios, serial and bttv (TV) device */
/* Allow manufacturer commands in isdn CAPI support driver */
/* Allow reading non-standardized portions of pci configuration space */
/* Allow DDI debug ioctl on sbpcd driver */
/* Allow setting up serial ports */
/* Allow sending raw qic-117 commands */
/* Allow enabling/disabling tagged queuing on SCSI controllers and sending
arbitrary SCSI commands */
/* Allow setting encryption key on loopback filesystem */
#define CAP_SYS_ADMIN 21
/* Allow use of reboot() */
#define CAP_SYS_BOOT 22
/* Allow raising priority and setting priority on other (different
UID) processes */
/* Allow use of FIFO and round-robin (realtime) scheduling on own
processes and setting the scheduling algorithm used by another
process. */
#define CAP_SYS_NICE 23
/* Override resource limits. Set resource limits. */
/* Override quota limits. */
/* Override reserved space on ext2 filesystem */
/* NOTE: ext2 honors fsuid when checking for resource overrides, so
you can override using fsuid too */
/* Override size restrictions on IPC message queues */
/* Allow more than 64hz interrupts from the real-time clock */
/* Override max number of consoles on console allocation */
/* Override max number of keymaps */
#define CAP_SYS_RESOURCE 24
/* Allow manipulation of system clock */
/* Allow irix_stime on mips */
/* Allow setting the real-time clock */
#define CAP_SYS_TIME 25
/* Allow configuration of tty devices */
/* Allow vhangup() of tty */
#define CAP_SYS_TTY_CONFIG 26
/* Allow the privileged aspects of mknod() */
#define CAP_MKNOD 27
/* Allow taking of leases on files */
#define CAP_LEASE 28
代码的作者已经加了详尽的注释,我们这里就不做解释了。
。。。。。。
|