Unix 权限涉及三个部分:** 用户 、 进程 、 文件 **。
权限分为常用权限、SELinux 权限。
manpage: man 2 stat
中搜索 “mode” 可以看到几种常用权限的详情。
用户和分组
用户用 user ID 区分。多个用户可以划入同一个用户分组,一个用户可以同时属于不同的分组。这就是 UID(User ID)和 GID(Group ID)。
现在我将使用我的凭据登录到 shell 并运行:
1 | grep $LOGNAME /etc/passwd |
rotem: x:1000:1000:rotem,,,:/home/rotem:/bin/bash
您可以看到我的日志名 (rotem)、均为 1000 的 UID 和 GID,以及其他详细信息,例如我登录的 shell。
进程的用户 ID (UID)
每个进程都有一个所有者,并且每个进程都属于一个组。
进程有 3 种 UID:real user ID
、effective user ID
、saved user ID
。其中还有一个 set-user-ID
的概念,这个概念和 effective user ID
紧密关联。
实际用户 ID (RUID, Real User ID) [^5]
在我们的 shell 中,我们现在将运行的每个进程都将继承我的用户帐户的权限,并将使用相同的 UID 和 GID 运行。
当您 fork
一个新进程时,该进程会继承父进程的 RUID。通常父级的 RUID 是你的 shell 并且它有当前登录用户的 UID。所以新进程有当前登录用户的 UID 的 RUID。通常这不会改变,只有 root 可以改变它。
举个例子,想想 init 进程派生了你的登录 shell。在 fork 期间,shell 将具有 root 的 RUID(因为 shell 的父级是 init)。但是,init 进程使用 /etc/passwd
将 shell 的 RUID 改成你的 UID. 因此,此后登录 shell 的 RUID 将是您的 UID,而不是 root。所以,我们可以说 RUID 是进程所有者的。
让我们运行一个简单的命令来检查它:
1 | $ sleep 100 & ps aux | grep 'sleep' |
然后根据 ps
命令打印出的 PID (3741) ,查进程 UID 和 GID:
1 | $ stat -c "%u %g" /proc/3741 |
然而,系统判断一个进程对一个文件是否有权限时,要验证的 ID 是 effective user ID,而不是 real user ID。
有效用户 ID(EUID, Effective User ID)
Linux 通常都不建议用户使用 root 权限去进行一般的处理,但是普通用户在做很多很多 services 相关的操作的时候,可能需要一个特殊的权限。为 了满足这样的要求,许多 services 相关的 executable 有一个标志,这就是 set-user-ID bit
。当这个 set-user-ID bit=ON
的时候,这个 executable 被用 exec 启动之后的进程的 effective user ID 就是这个 executable 的 owner id,而并非 parent process real user id。如果 set-user-ID bit=OFF
的时候,这个被 exec 起来的进程的 effective user ID 应该是等于进程的 user ID 的。
我们以 ping 命令为例。
使用 which
命令搜索二进制位置,然后运行 ls -la
:
-rwsr-xr-x 1 root root 64424 Mar 10 2017 ping
可以看到文件的所有者和组是 root. 这是因为该 ping
命令需要打开一个套接字,而 Linux 内核 root 为此需要特权。
但是,如果没有 root 特权,我如何使用 ping
?
注意文件权限的所有者部分中的 “s
” 字母而不是 “x
”。
这是特定二进制可执行文件(如 ping
和 sudo
)的特殊权限位,称为 set-user-ID
。
这是 EUID
和 EGID
发挥作用。
将会发生的情况是,当执行设置了 setuid 的二进制文件 ping
时,该进程将其有效用户 ID (EUID
) 从默认值 RUID
更改为此特殊二进制可执行文件的所有者,在本例中为 root。
这一切都是通过这个文件有这个简单的事实来完成的 set-user-ID
。
内核通过查看进程的 EUID
来决定该进程是否具有特权。因为现在 EUID
指向 root,操作不会被内核拒绝。
注意:在最新的 Linux 版本中,ping 命令的输出看起来会有所不同,因为它们采用了 Linux Capabilities 方法而不是这种 setuid 方法(对于不熟悉的人)请阅读 此处。
set-user-ID
参考 链接 1 。
Unix 包含另一个权限位,即该权限 set-user-ID
位。如果为可执行文件设置了该位,那么只要所有者以外的用户执行该文件,该用户就可以访问所有者的其他任何文件,从而获得所有者的所有文件读 / 写 / 执行特权!
这会导致运行该文件的任何人或进程都可以访问系统资源,就好像他们是该文件的所有者一样。
1 | $ ls -l testfile |
为文件添加 set-user-ID
权限:
1 | $ sudo chmod u+s testfile |
说明:大写 S
表示,有 set-user-ID
权限,但是没有执行权限。
set-group-ID
阅读 Manual:man 2 stat
。
The set-group-ID bit (S_ISGID) has several special uses.
For a directory, it indicates that BSD semantics is to be used for that directory: files created there inherit their group ID from the directory, not from the effective group ID of the creating process, and directories created there will also get the S_ISGID bit set.
如果目录具有设置组 ID(S_ISGID):
- 其下创建的文件将从其父目录的继承组 ID,而不能从创建其的进程中继承有效组 ID;
- 其下创建的子目录将继承设置组 ID 位(S_ISGID)。
For a file that does not have the group execution bit (S_IXGRP) set, the set-group-ID bit indicates mandatory file/record locking.
如果一个文件有 “设置组 ID(set-group-ID)” 但是没有 “组执行权限(S_IXGRP)”,也就说具有如下权限:
1 | % ls -l a.txt |
那么,此时 set-group-ID 位喻示强制文件 / 记录锁。
TODO:强制文件 / 记录锁是什么?
Mandatory File Locking
Why remove group execute for mandatory file lock?
粘滞位:
The sticky bit (S_ISVTX) on a directory means that a file in that directory can be renamed or deleted only by the owner of the file, by the owner of the directory, and by a privileged process.
大写 T
表示,有 restricted deletion flag or sticky bit(粘滞位),但是没有执行权限,t
权限只对目录有效,作用是保护目录项不能被其他用户删除。目录要同时具有 x
和 s
才能保证粘滞位有效。
保存的用户 ID (SUID, Saved User ID)
为什么要设置一个 saved set-user-ID 呢?它的意义是,它相当于是一个 buffer, 在 exec 启动进程之后,它会从 effective user ID 位拷贝信息到自己。
- 对于非 root 用户,可以在未来使用
setuid()
来将 effective user ID 设置成为 real user ID 和 saved set-user-ID 中的任何一个。但是非 root 用户是不允许用setuid()
把 effective user ID 设置成为任何第三个 user id。 - 对于 root 来说,就没有那么大的意义了。因为 root 调用 setuid() 的时候,将会设置所有的这三个 user ID 位。所以可以综合说,这三个位的设置为为了让 unprivilege user 可以获得两种不同的 permission 而设置的。
《Unix 环境高级编程》的例子是,普通用户去执行一个 tip 进程,set-user-ID bit=ON,执行起来的时候,进程可以有 uucp (executable owner) 的权限来写 lock 文件,也有当前普通用户的权限来写数据文件。在两种文件操作之间,使用 setuid() 来切换 effective user id。但是正是因为 setuid() 的限制,该进程无法获得更多的第三种用户的权限。
saved set-user-ID 是无法取出来的,是 kernel 来控制的。注意 saved set-user-ID 是进程中的 id,而 set-user-ID bit 则是文件上的权限。
总结
用户 ID:
属性 | 决定特权 | 说明 |
---|---|---|
EUID | ✅ 是 | 内核权限检查时使用 |
RUID | ❌ 否 | 表示谁启动了进程 |
FSUID | ⚠️ 有时 | 文件访问权限相关 |
SUID | ⚠️ 有时 | 用于权限恢复 |
组 ID:
属性 | 说明 | 用途 |
---|---|---|
EGID | 有效组 ID | 用于权限检查 |
RGID | 实际组 ID | 表示谁启动了进程 |
SGID | 保存组 ID | 用于权限恢复 |
FSGID | 文件系统组 ID | 文件访问专用 |
以上都是讨论用户 ID,如果没有特殊说明,以上的组 ID 和用户 ID 基本是类似的,只是作用对象为组。
文件权限
符号形式的权限(Symbolic permissions)
符号形式表示文件权限有 5 种:rwxst
。
r
:可读。对文件来说,意味着能执行vim
查看、cat
等。对目录来说,意味着能执行ls
查看其下的文件列表。w
:可写。对文件来说,意味着能执行vim
等工具编辑并保存。对目录来说,意味着能够创建、删除文件或新的目录。x
:可执行(文件) / 可搜索(目录)。对文件来说,意味着可以执行。对目录来说,意味着能够cd
进入该目录。
《UNIX 环境高级编程》P80 中如此描述:
读权限允许我们读目录,获得该目录中所有文件名的列表。当一个目录是我们要访问文件的路径名的一个组成部分时,对该目录的执行权限使我们可以通过该目录(也就是搜索该目录,寻找一个特定的文件名)。
ls 没有可搜索权限的目录
可以看到文件列表、文件类型,但是不能看到其他信息,比如文件权限、所有者、大小、修改时间等,因为这些信息保存在 inode 中,必须先cd
进入该目录,才能读取这些信息。同样,ls -R
不能显示没有执行权限的目录下的子目录下的文件,因为这也必须先cd
该目录,然后执行ls
显示子目录的文件。
1 | $ ls -lR mydir/ |
给目录递归恢复权限:
1 | $ chmod -R u+X mydir/ |
s
:即set-user-ID
权限,如果可执行文件有s
权限属性,那么任意进程执行该文件时,将自动获得该文件所有者相同的所有权限。如果文件没有x
权限,却有s
权限,那么ls -l
命令将该文件显示为大写的S
。文件只有同时具备s
权限和x
权限,才有意义,因为一个文件要应用set-user-ID
属性,首先要保证其可执行。
例如 ping
文件:
1 | $ ls -l /bin/ping |
由于设置了 s
权限,所以任何文件都能以 root 用户的身份运行,也就被内核允许打开套接字。
t
:即restricted deletion flag or sticky bit
,称为 “粘滞位” 或 “限制删除标记”。仅仅对目录有效,对文件无效。在一个目录上设了t
权限位后,(如/home
,权限为1777
) 任何的用户都能够在这个目录下创建文档,但只能删除自己创建的文档 (root 除外),这就对任何用户能写的目录下的用户文档 启到了保护的作用。如果目录 / 文件没有x
权限,却有s
权限,则ls -l
命令将目录 / 文件显示为大写的T
。目录只有同时具备t
权限和x
权限,才有意义,因为一个目录如果本来就不允许增删目录项(x
权限),删除其他用户的文件更无须提了。- 当一个目录设置了
t
权限时:- 所有用户都可以在该目录中创建文件(如果有写权限)
- 但只能删除自己创建的文件(当然要有写权限,否则也不能创建文件)
root
用户除外,始终可以删除任何文件
- 当一个目录设置了
例如:/tmp
和 /var/tmp
目录供所有用户暂时存取文件,亦即每位用户皆拥有完整的权限进入该目录,去浏览、删除和移动文件。
数字形式的权限(Numeric permissions)
可以用 4 位八进制数(0-7)表示这些文件权限,由 4、2、1 相加得到,0 表示所有权限都没有。
这 4 位的含义如下:
- 第 1 bit:4 表示 set-user-ID,2 表示 set-group-ID,1 表示 restricted deletion or sticky 属性(粘滞位);
- 第 2 bit:表示文件所有者的权限:可读(4)、可写(2)、可执行(1);
- 第 3 bit:表示文件所属组的权限:可读(4)、可写(2)、可执行(1);
- 第 4 bit:表示其他用户的权限:可读(4)、可写(2)、可执行(1)。
以下是 chmod
的 man page 的说明 [^2]:
A numeric mode is from one to four octal digits (0-7), derived by adding up the bits with values 4, 2, and 1. Omitted digits are assumed to be leading zeros. The first digit selects the set user ID (4) and set group ID (2) and restricted deletion or sticky (1) attributes. The second digit selects permissions for the user who owns the file: read (4), write (2), and execute (1); the third selects permissions for other users in the file’s group, with the same values; and the fourth for other users not in the file’s group, with the same values.
相关命令
ls
命令
ls -l
命令用于查看文件权限。
chmod
命令
chmod
命令用于改变文件权限。
基本用法:
1 | chmod [OPTION]... MODE[,MODE]... FILE... |
** 大写 X
参数 ** ^1:
例如 chmod u+X filename
或 chmod u-X filename
。
在 chmod
的 man page 中介绍如下:
The letters
rwxXst
select file mode bits for the affected users: read (r
), write (w
), execute (or search for directories) (x
), execute/search only if the file is a directory or
already has execute permission for some user (X
), set user or group ID on execution (s
), restricted deletion flag or sticky bit (t
) [^2].
加黑体的话很费解,但是又十分准确,解释如下:
对所有目录赋予执行权限,这意味着可以执行
cd
。对所有文件,如果原来文件的
ugo
(user / group / others)任意一个原先有执行权限,那么动作与小写-x
参数相同;如果原先没有,那么忽略。例如:1
ls -l
结果:
-rw-rw-r– 1 rotem rotem 0 Nov 9 10:55 file1
-rw-rw-r-x 1 rotem rotem 0 Nov 9 10:56 file2可以看到,原来 file1 的
ugo
都不具备执行权限,file2 的 others 具备执行权限。- 对 file1 执行
X
动作,将无效:
1
2sudo chmod u+X file1
ls -l file1-rw-rw-r– 1 rotem rotem 0 Nov 9 10:55 file1
- 但是对 file2 执行
X
,能够生效:
1
2sudo chmod u+X file2
ls -l file2-rwxrw-r-x 1 rotem rotem 0 Nov 9 10:56 file2
题外话:
chmod -R u-X mydir
该命令无法递归执行,因为当执行完顶层目录的权限更改之后,已经没有权限cd
顶层目录了,其他执行全部被停止。- 对 file1 执行
- getfacl/setfacl
- umask
SELinux 权限
SELinux 提供更为严格的访问控制。
可以用 ls -Z
查看文件的 SELinux 权限(安全上下文)。这部分将来另用一篇博客说明。暂时可以参考文献 [^3]。
参考文献
[^2]: chmod 的 man page
[^3]:https://www.jianshu.com/p/73621cc7c222
[^4]:https://en.wikipedia.org/wiki/User_identifier#Saved_user_ID
[^5]:https://stackoverflow.com/questions/32455684/difference-between-real-user-ID-effective-user-ID-and-saved-user-ID
[^6]:https://cloud.tencent.com/developer/article/1722142