这篇文章是在学习了 Linux 0.11 部分源码后再写的。个人将 Linux 下的权限分为硬件和操作系统两个层面。这样的分类是比较宽泛的。
另外,本文的讨论环境是 32 位保护模式。
硬件层面
硬件层面的权限准确地讲应该是特权级(Priviledge Level)
。特权级分成 4 级,用 0、1、2、3 来表示不同级别,值越小级别越高。其中,特权级 1、2 都不用,0 代表内核级别,3 代表用户级别。
特权级共有 3 种:
CPL(Current Privilege Level)
,代码段选择子当前特权级别,跟 CPU 当前特权级一样,不能通过代码来设定级别大小
RPL(Rquest Privilege Level)
,数据段(如 ds、ss)选择子请求特权级别,表示请求存储段(代码段、数据段、栈段)描述符、系统描述符(TSS、LDT)的特权级别,可以通过代码来设定级别大小
DPL(Descriptor Privilege Level)
,描述符特权级别,描述符可以是门描述符、存储段描述符、系统描述符,可以通过代码设定级别大小
这 3 者关系如下:
图片来源
操作系统层面
用户和组
这部分摘录自博文浅谈Linux用户权限管理之一(用户与组的概念)。
用户的角色分类
在linux下用户是根据角色定义的,具体分为三种角色:
超级用户:拥有对系统的最高管理权限,默认是root用户。
普通用户:只能对自己目录下的文件进行访问和修改,具有登录系统的权限。
虚拟用户:也叫“伪”用户,这类用户最大的特点是不能登录系统,它们的存在主要是方便系统管理,满足相应的系统进程对文件属主的要求。例如系统默认的 bin、adm、nobody 用户等。一般运行的 web 服务,默认就是使用的 nobody 用户,但是 nobody 用户是不能登录系统的。
用户组
用户组是具有相同特征用户的逻辑集合,有时我们需要让多个用户具有相同的权限,比如查看、修改某一个文件的权限,一种方法是分别对多个用户进行文件访问授权,如果有 10 个用户的话,就需要授权 10 次,显然这种方法不太合理;另一种方法是建立一个组,让这个组具有查看、修改此文件的权限,然后将所有需要访问此文件的用户放入这个组中,那么所有用户就具有了和组一样的权限。这就是用户组,将用户分组是 Linux 系统中对用户进行管理及控制访问权限的一种手段,通过定义用户组,在很大程度上简化了管理工作。
用户和组的关系
用户和用户组的对应关系有:一对一、一对多、多对一和多对多;下图展示了这种关系:
图片来源
一对一:即一个用户可以存在一个组中,也可以是组中的唯一成员。
一对多:即一个用户可以存在多个用户组中。那么此用户具有多个组的共同权限。
多对一:多个用户可以存在一个组中,这些用户具有和组相同的权限。
多对多:多个用户可以存在多个组中。其实就是上面三个对应关系的扩展。
uid、euid 和 suid
uid、euid 和 suid 这 3 个词分别是 (real) user identifier、effective user identifier 和 saved user identifier 的缩写。关于这三者,文献 Setuid Demystified (百度云备份)有一个简要的描述:
Each process has three user IDs: the real user ID (real uid, or ruid), the effective user ID (effective uid, or euid), and the saved user ID (saved uid, or suid).
The real uid identifies the owner of the process
[i.e. the user launching the application],the effective uid is used in most access control decisions
, andthe saved uid stores a previous user ID so that it can be restored later
.
3 者区别
对于这 3 者的区别,博文 5 Things You Don’t Know About User IDs That Will Destroy You有一个很简洁但很容易理解的解释(很赞!!!):
Real ID
– The real ID is the ID of the process that created the current process. So, let’s say you log in to your box as joe, your shell is then launched with its real ID set to joe. All processes you start from your shell will inherit the real ID joe as their real ID.Effective ID
– The effective ID is the ID that the system uses to determine whether a process can take a particular action. There are two popular ways to change your effective ID:
- su – the su program changes your effective, real, and saved IDs to the ID of the user you are switching to.
- set ID upon execute (abbreviated setuid) – You can mark a program’s set uid upon execute bit so that the program runs with its effective and saved ID set to the owner of the program (which may not necessarily be you). The real ID will remain untouched. For example, if you have a program:
- 图片来源
Saved ID
– The saved ID is set to the effective ID when the program starts. This exists so that a program can regain its original effective ID after it drops its effective ID to an unprivileged ID. This use-case can cause problems (as we’ll see soon) if it is not correctly managed.- If you start a program as yourself, and it does not have its set ID upon execute bit set, then the program will start running with its real, effective, and saved IDs set to your user ID.
- If you run a setuid program, your real ID remains unchanged, but your effective and saved IDs are set to the owner of the file.
- su does the same as running a setuid program, but it also changes your real ID.
实验
实验代码如下:
1 |
|
将以上代码编译链接为可执行目标文件 test。
实验1:用户 xiehongfeng100,执行 test:
实验2:切换到 root 用户并执行 test:
实验 1 和实验 2 说明:当只是简单切换用户时,uid、euid 和 suid 只跟当前用户相关。
实验3:用户 xiehongfeng100,为 test 添加 setuid 位,执行 test:
实验4:这时切换到 root 用户并执行 test:
实验 3 和实验 4 说明:通过设置 setuid 位,可以让用户在执行程序时,身份变成该程序的所有者,这样就能拥有程序所有者的所有权利。
sudo 命令就是赋予普通用户 root 用户权限的命令。
文件权限
UGO 权限
Linux 采用了 UGO (User, Group and Other) 权限对文件的权限进行管理。利用 ls -l
命令可以显示文件权限:
UGO 在代码中实际是用 16 个权限位来表示的:
1 |
注意权限位还有这些情况:
According to POSIX, the effect when other bits are set in mode is unspecified. On Linux, the following bits are also honored in mode:
1 | S_ISUID 0004000 set-user-ID bit |
chmod
通过 chmod
命令来修改文件的 UGO 权限(chown
和 chgrp
是修改文件的所属用户/组):
图片来源
另外,R、W 和 X 3 个权限可以用数字来表示:
- R - 4
- W - 2
- X - 1
如:
umask
umask 是权限掩码,它用来设置新建文件的默认权限。umask 值与权限的对照表如下所示:
1 | umask 文件 目录 |
示例如下:
打开文件权限
在 Linux 中,open 函数定义如下:
1 | int open(const char *path, int oflags,mode_t mode); // 第 3 个参数 mode 仅当创建文件时使用,用于指定文件权限位(UGO) |
其中的 oflag
用于指定文件的打开/创建模式,这个参数可由以下常量(定义于 fcntl.h)通过逻辑或构成:
1 | O_RDONLY 只读模式 |
打开/创建文件时,至少得使用上述三个常量中的一个。以下常量是选用的:
1 | O_APPEND 每次写操作都写入文件的末尾 |
以下三个常量同样是选用的,它们用于同步输入输出:
1 | O_DSYNC 等待物理 I/O 结束后再 write。在不影响读取新写入的数据的前提下,不等待文件属性更新。 |
更详细的解释可参考 Linux 手册。