5.10 权限
文件系统权限机制是类 UNIX 系统安全架构的基石。
在 FreeBSD 中,每个文件和目录都关联一组权限,有多种工具可用于查看和修改这些权限。
权限决定了用户能够访问哪些文件,以及能否修改或执行不属于自身的文件。
基本的 UNIX 权限使用三种访问类型分配:读、写和执行。这些访问类型用于确定文件属主、组和其他用户的文件访问权限。
本节讨论的是 FreeBSD 中使用的传统 UNIX® 权限,不涉及细粒度的权限。
自主访问控制(DAC)模型
FreeBSD 采用的传统 UNIX 权限模型属于自主访问控制(Discretionary Access Control,DAC)范畴。DAC 的核心特征是:客体的属主(owner)有权自主决定将访问权限授予其他主体,或撤销其他主体的访问权限。在 UNIX 系统中,文件的属主可以自行修改文件的权限位,决定其他用户对该文件的访问能力。由于访问控制决策由客体属主而非系统管理员集中做出,这种模型称作“自主的”(discretionary)。
DAC 模型的理论基础可追溯至 20 世纪 60—70 年代的多用户操作系统安全研究。美国国防部于 1985 年发布的《可信计算机系统评估准则》(Trusted Computer System Evaluation Criteria,TCSEC,又称“橘皮书”)将 DAC 定义为:一种基于主体身份和/或所属组来限制对客体访问的访问控制机制,客体的属主可以自行指定或修改访问权限(来源:Department of Defense. DoD 5200.28-STD: Trusted Computer System Evaluation Criteria[S]. Washington, D.C.: Department of Defense, 1985.)。
传统 UNIX DAC 模型具有以下特征与局限:
粗粒度控制:仅区分属主、所属组和其他用户三类主体,无法为单个用户或多个组授予细粒度权限。
权限传播:文件属主可以将权限授予任意用户,包括将写权限授予非信任用户,这可能导致权限泄露。
超级用户绕过:root 用户不受 DAC 约束,可以访问任何文件。这种设计在提供管理便利性的同时,也构成了潜在的安全风险,一旦 root 权限遭非法获取,整个系统的安全防线即告瓦解。
为弥补传统 DAC 模型的不足,FreeBSD 提供了以下增强机制:
访问控制列表(ACL):基于 POSIX.1e 草案标准的 ACL 机制,允许对单个用户或组设置细粒度的访问权限,突破了传统 DAC 仅支持三类主体的限制。
强制访问控制(MAC):TrustedBSD MAC 框架提供了基于安全策略的强制访问控制能力,包括 Biba 完整性模型、MLS 多级安全模型等。与 DAC 不同,MAC 的访问控制决策由系统安全策略强制执行,客体属主无法自行修改。
文件标志(File Flags):FreeBSD 支持通过 chflags(1) 设置文件标志,如
schg(系统不可变标志)和sappnd(系统仅追加标志),即使 root 用户也无法修改带有这些标志的文件(除非先移除标志)。文件标志增加了额外的安全和控制层级,即使 root 也无法删除或修改这些文件。Capsicum 沙盒框架:通过能力(capability)机制限制进程可访问的系统资源范围,实现最小权限原则。
权限表达式
观察以下 ls 命令的输出示例,以理解权限表达式的构成:
再观察:
FreeBSD 文件访问权限可使用 10 个标志位来表示(请读者核实第一列是否为 10 位),而这 10 个标志位由 4 个部分组成:
用斜杠分隔:-/---/---/---
第一部分是第 1 位,表示文件类型 / 设备类型:
d
目录
-
普通文件
l
符号链接文件
b
块设备文件
p
管道文件
c
字符设备文件
s
套接字文件
第二部分是第 2、3、4 位,用于表示文件所属用户对文件的访问权限,用 rwx 表示读、写、执行权限(对于目录来说即访问权限,如 ls、cd),无权限则写成 -。
第三部分是第 5、6、7 位,用于标识文件所属组成员对文件的访问权限。
第四部分是第 8、9、10 位,用于标识其他用户对文件的访问权限。
读、写、执行权限除了用 rwx 表示外,也可分别对应数字 4、2、1,无权限即 0。每三位权限对应的数字相加后组合,即形成 3 位数字的表示方式(记忆口诀:“读 4 写 2 执行 1”)。
可能的数字与字母组合方式如下。
UNIX® 权限
0
无读、无写、无执行权限
---
1
无读、无写、有执行权限
--x
2
无读、有写、无执行权限
-w-
3
无读、有写、有执行权限
-wx
4
有读、无写、无执行权限
r--
5
有读、无写、有执行权限
r-x
6
有读、有写、无执行权限
rw-
7
有读、有写、有执行权限
rwx
关于上面数字代表权限的代码可以在 main/sys/sys/stat.h 找到,由 IEEE Std 1003.1 规范定义:
系统控制设备权限的机制基于传统的 UNIX 哲学“一切皆文件”,即将大多数硬件设备视为文件,程序可以打开、读写这些设备。这些特殊的设备文件存储在 /dev/ 目录中。
典型的 /dev/ 目录:
思考题
drw-------,即600,这是一个目录,只有所属用户可以读、写。这种说法正确吗,有意义吗,为什么?读者如何理解这种设计上的逻辑悖论?
目录也视为文件,同样处理,同样具有读、写和执行权限。目录的可执行位与文件略有不同,目录设置为可执行时,表示可以使用 cd 命令切换进入该目录。
这也意味着要访问该目录中的文件,前提是文件本身的权限允许访问:
如果要列出目录内容,该目录必须具有读权限。如果要删除已知名称的文件,则必须对该文件所在的目录同时具有写权限和执行权限。
还存在额外的权限位,主要用于特殊场景,例如 setuid 可执行文件和 sticky 目录。
符号权限
符号权限使用字符而非八进制值来为文件或目录分配权限,其语法为 (对象)(操作)(权限),其中可用的值如下:
对象
u
用户(User)
对象
g
组属主(Group owner)
对象
o
其他(Other)
对象
a
所有(ALL)
操作
+
添加权限
操作
-
移除权限
操作
=
显式设置权限
权限
r
读(Read)
权限
w
写(Write)
权限
x
执行(Execute)
权限
t
粘滞位(Sticky bit)
权限
s
设置 UID 或 GID
注意
chmod(1) 符号模式中的
t(sticky bit, 粘滞位)在 POSIX 标准中标记为 XSI 扩展选项,非 POSIX 基本要求。对“其他”权限(o)单独操作s(设置 UID 或 GID)或t标志(粘滞位)时会忽略。设置粘滞位应使用+t或a+t,而非o+t。
这些值与 chmod(1) 一起使用,但使用字母而非数字。例如,以下命令将阻止与 test 关联的组成员和所有其他用户访问 test:
这里的 go 并不是代表“前进”,而是“组属主”和“其他”。并且等于号后面的空格是必要的。
当需要多次更改文件权限时,可以提供逗号分隔的列表。例如,以下命令移除 test 的组和“world”写权限,并为所有用户添加执行权限:
技巧
chmod(1) 符号模式中
X权限仅在文件已是可执行或为目录时才设置执行位,常用于chmod =rw,+X保留已有执行权限。
操作符方式
赋予所有用户对 test.sh 脚本的执行权限:
在 a+x 中:
a表示操作对象为所有用户。可选项还有:u表示所属用户,g表示所属组,o表示其他用户,如果省略则依照系统默认对象操作;+是操作符,意为增加权限;可选项-则为移除权限;rwx是权限模式。r表示读、w表示写、x表示执行。可选项s表示在文件执行时将进程的属主或属组设置为文件的属主或属组。
数字方式
设置 test.sh 的权限为属主可读写执行、组用户可读执行、其他用户无权限:
其中:
7:所属用户拥有读、写、执行的权限5:同组用户有读和执行的权限0:其他用户没有任何权限
此处,选项 -R 可递归修改权限。
示例:
umask:文件创建掩码
在 UNIX 系统中,当进程调用 open(2)、mkdir(2) 等系统调用创建文件或目录时,内核使用文件创建掩码(umask)来确定最终权限。umask 是一个进程级属性,指定在文件创建时应屏蔽(移除)的权限位。umask 机制确保新创建的文件不会意外地拥有过于宽松的权限。
事实上,权限数值是八进制,而非十进制。将八进制 755 转为二进制则为:111 101 101。具体计算过程:
技巧
一般来说,任何非零数的 0 次方都等于 1。
具体而言,新文件的初始权限由创建请求中的模式参数与 umask 取反后进行按位与运算得出:
技巧
按位与
&:按位取反:
1
1
1
1
0
0
0
1
0
0
0
0
例如,进程请求以模式 0666(所有用户读写)创建文件,而当前 umask 为 0022 时:
则最终权限为 0666 & ~0022 = 0644(属主读写、组和其他用户只读)。
对于目录,通常的请求模式为 0777,在 umask 为 0022 时结果为 0755。
FreeBSD 的默认 umask 为 0022,可通过 umask(2) 系统调用或 shell 内建的 umask 命令查看和设置:
0022
0644(rw-r--r--)
0755(rwxr-xr-x)
默认值
0027
0640(rw-r-----)
0750(rwxr-x---)
更严格,禁止其他用户访问
0077
0600(rw-------)
0700(rwx------)
最严格,仅属主可访问
umask 在 shell 启动文件(如 ~/.profile 或 ~/.cshrc)中设置。在 csh/tcsh 中使用 umask 022,在 sh/bash 中同样使用 umask 022。
注意
umask 仅影响文件创建时的初始权限,不会修改已存在文件的权限。
在 Jail 容器中,umask 继承自主机系统的进程环境,不受 Jail 配置的直接影响。
chown 命令
chown 命令chown(8) 命令用于修改文件的属主,包括所属用户和所属组。只有 root 才能修改文件的属主。
注意
符号表示法:
用户:组同时修改属主和属组;:组仅修改属组;用户:修改属主并将属组设为指定用户的登录组。
示例:修改 t.sh 的属主为 test1。
示例:修改 t.sh 的属主为用户 test1、组 test。
示例:修改 /tmp 目录下所有文件的属主为用户 test1、组 test。
此处,选项 -R 可递归修改属主。
特殊权限位:setuid、setgid 与 sticky bit
除基本的读、写、执行权限外,UNIX 系统还定义了三种特殊权限位,这些设置提供了通常不授予普通用户的功能。要理解这些权限位,必须注意实际用户 ID(real user ID)与有效用户 ID(effective user ID)之间的区别:实际用户 ID 是拥有或启动进程的 UID,而有效 UID 是进程运行时所采用的 UID。
setuid(设置用户 ID)
可执行文件设置了 setuid 位后,无论哪个用户执行该文件,进程的有效用户 ID(EUID)都会设置为文件属主的 UID,而非执行者的 UID。这一机制令普通用户可以临时获得文件属主的权限来执行特定操作。
setuid 位在权限表达式中以属主执行位上的 s 表示(如 -r-sr-xr-x)。
setuid 最典型的应用场景是 passwd(1) 命令。
普通用户修改密码时需要更新 /etc/master.passwd 文件,而该文件只有 root 才有写权限。通过 setuid 机制,passwd 命令以 root 的 EUID 运行,从而获得修改密码文件的权限。passwd(1) 以普通用户的实际用户 ID 运行,但为了更新密码数据库,该命令以 root 用户的有效 ID 运行,使用户能够更改密码而不会遇到“Permission Denied”错误。
setuid 位数字表示法中在三位权限码前加 4(如 4555),使用命令显示完整权限(含类型和特殊位,八进制):
警告
setuid 机制是一把双刃剑:如果 setuid 程序存在安全漏洞,攻击者便可能利用该程序以文件属主(通常是 root)的权限执行任意代码,因此,系统管理员应尽量减少 setuid 程序的数量,并定期审计系统中的 setuid 文件。
nosuid挂载选项会使内核忽略文件系统上可执行文件的 setuid/setgid 位,程序仍可运行但不会获得提权。
setgid(设置组 ID)
setgid 的作用与 setuid 类似,但作用于组而非用户。可执行文件设置了 setgid 位后,进程的有效组 ID(EGID)会设置为文件所属组的 GID。目录设置了 setgid 位后,在该目录中创建的新文件将继承目录的所属组,而非创建者的主组。
setgid 位在权限表达式中以所属组执行位上的 s 表示(如 -r-xr-sr-x):
数字表示法中在三位权限码前加 2(如 2555):
sticky bit(粘滞位)
sticky bit 最初用于将可执行文件的代码段“粘滞”在交换空间中以提高重复启动速度,但这一用途在现代系统中已不再适用。当前,sticky bit 主要用于目录:目录设置了 sticky bit 后,只有文件的属主(或 root)才能删除或重命名该目录下的文件,即使其他用户对该目录拥有写权限。
sticky bit 最常见的应用场景是 /tmp 目录。该目录对所有用户开放写权限,但通过 sticky bit 防止用户删除他人的临时文件。目录设置了 sticky bit 时,仅文件属主可以删除该目录下的文件,这对于防止公共目录中非文件属主删除文件非常有用。
sticky bit 在权限表达式中以其他用户执行位上的 t 表示(如 drwxrwxrwt):
数字表示法中在三位权限码前加 1(如 1777):
FreeBSD 文件标志
除了文件权限外,FreeBSD 还支持使用“文件标志”。这些标志增加了额外的安全和控制层级,适用于文件和目录,通过文件标志甚至可以阻止 root 删除或修改文件。
常用标志如下:
schg / schange / simmutable
系统不可变标志
仅超级用户可设置;在安全级别(securelevel)大于 0 时无法清除
sappnd / sappend
系统仅追加标志
仅超级用户可设置;同受安全级别限制
uchg / uchange / uimmutable
用户不可变标志
文件属主或超级用户可设置;不受安全级别限制
uappnd / uappend
用户仅追加标志
文件属主或超级用户可设置
nodump
不转储标志
文件属主或超级用户可设置;dump(8) 将跳过此类文件
sunlnk / sunlink
系统不可删除标志
仅超级用户可设置
uunlnk / uunlink
用户不可删除标志
文件属主或超级用户可设置
注意
标志前加
no可清除该标志,如nouchg清除用户不可变标志。某些标志的修改能力取决于当前内核安全级别(securelevel),参见 security(7)。
仅有限数量的工具支持 chflags 标志,包括 ls(1)、cp(1)、find(1)、install(1)、dump(8)、restore(8);目前 pax(1) 不支持 chflags。
使用 chflags(1) 可修改文件标志。chflags(1) 首次出现于 4.4BSD,是 BSD 系统特有的功能,Linux 不原生支持(需通过 chattr 命令和 lsattr 命令实现类似功能)。查看文件的标志可使用 ls(1) 的 -lo 选项:
注意
如果使用 ZFS 文件系统,所有文件都可能拥有归档标志
uarch,这是符合预期的。
例如,要在文件 test 上启用系统不可删除标志,执行以下命令:
执行后,查看文件:
要禁用系统不可删除标志,在 sunlink 前面加上“no”:
文件标志通常只能由 root 用户添加或移除:
否则系统将返回操作不允许错误。
参考文献
FreeBSD Forums. File flag defaults on ZFS[EB/OL]. (2020-09-22)[2026-04-27]. https://forums.freebsd.org/threads/file-flag-defaults-on-zfs.77088/.
课后习题
查看 FreeBSD 内核中权限检查的核心源代码,尝试使其更加细粒度。
修改一个系统目录(如 /var/tmp)的默认权限配置,记录修改后对该目录下文件创建和进程访问行为的影响。
Unix 的 rwx 三级权限模型自 1970 年代沿用至今。比较此模型与 SELinux 的类型强制(Type Enforcement)和 AppArmor 的路径名限制在表达力上的差异,分析在单一系统上混合使用三种机制是否会引入策略冲突。
最后更新于