> For the complete documentation index, see [llms.txt](https://handbook.bsdcn.org/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://handbook.bsdcn.org/di-3-zhang-freebsd-ji-chu/3.4.-quan-xian.md).

# 3.4.权限

在 FreeBSD 中，每个文件和目录都有一组相关的权限，并提供了多种工具用于查看和修改这些权限。了解权限的工作机制非常必要，它可以确保用户能访问所需的文件，同时防止不当访问操作系统或其他用户拥有的文件。

本节讨论的是 FreeBSD 中使用的传统 UNIX® 权限。若需更细粒度的文件系统访问控制，另见 [访问控制列表（Access Control Lists）](/di-16-zhang-an-quan/16.11.-fang-wen-kong-zhi-acl.md)。

在 UNIX® 中，通过三种访问类型来分配基本权限：读（read）、写（write）和执行（execute）。这些访问类型用于决定文件的所有者、所属组和其他人（所有其他人）对该文件的访问权限。读、写和执行权限可以分别用字母 `r`、`w` 和 `x` 表示。它们也可以用二进制数字表示，因为每个权限只能是开或关（`0` 表示关闭）。在数字表示法中，顺序始终是 `rwx`，其中 `r` 的启用值为 `4`，`w` 为 `2`，`x` 为 `1`。

表 2 总结了可能的数字与字母组合方式。在阅读“目录列表”一栏时，`-` 表示该权限被关闭。

**表 2. UNIX® 权限**

| 值 | 权限说明        | 目录列表表示 |
| - | ----------- | ------ |
| 0 | 无读、无写、无执行权限 | `---`  |
| 1 | 无读、无写、有执行权限 | `--x`  |
| 2 | 无读、有写、无执行权限 | `-w-`  |
| 3 | 无读、有写、有执行权限 | `-wx`  |
| 4 | 有读、无写、无执行权限 | `r--`  |
| 5 | 有读、无写、有执行权限 | `r-x`  |
| 6 | 有读、有写、无执行权限 | `rw-`  |
| 7 | 有读、有写、有执行权限 | `rwx`  |

使用 [ls(1)](https://man.freebsd.org/cgi/man.cgi?query=ls\&sektion=1\&format=html) 的 `-l` 参数可以查看包含权限信息的详细目录列表。例如，在某个目录中运行 `ls -l` 可能显示如下内容：

```sh
% ls -l
```

输出可能类似如下：

```sh
total 530
-rw-r--r--  1 root  wheel     512 Sep  5 12:31 myfile
-rw-r--r--  1 root  wheel     512 Sep  5 12:31 otherfile
-rw-r--r--  1 root  wheel    7680 Sep  5 12:31 email.txt
```

我们来看 `myfile` 这一行，第一个字符（最左侧）表示这个文件的类型，比如常规文件、目录、特殊字符设备、套接字或其他特殊伪文件设备。本例中的 `-` 表示一个常规文件。接下来的三个字符 `rw-` 表示该文件所有者的权限。再接下来的三个字符 `r--` 表示该文件所属用户组的权限。最后的三个字符 `r--` 表示其他所有用户的权限。`-` 表示该权限被关闭。本例中，该文件的权限设置为：所有者可读写，组用户可读，其他用户也可读。根据上表，这个文件的权限可以表示为 `644`，每一位数字分别代表该文件权限的三个部分。

系统是如何控制设备权限的呢？FreeBSD 将大多数硬件设备视为文件，程序可以打开它、读写它。这些特殊的设备文件存储在 **/dev/** 目录中。

目录也被当作文件处理。它们也有读、写和执行权限。目录的可执行位与文件略有不同。当目录被标记为可执行时，意味着可以使用 [cd(1)](https://man.freebsd.org/cgi/man.cgi?query=cd\&sektion=1\&format=html) 进入该目录。同时，在文件本身权限足够的前提下，也能访问目录内的文件。

如果要列出目录内容，该目录必须具有读权限。如果要删除一个已知名称的文件，则必须对该文件所在的目录具有写权限 *和* 执行权限。

还有一些额外的权限位，主要用于特殊场景，例如 setuid 可执行文件和 sticky 目录。关于文件权限的更多信息及设置方法，请参见 [chmod(1)](https://man.freebsd.org/cgi/man.cgi?query=chmod\&sektion=1\&format=html)。

## 3.4.1. 符号权限

符号权限使用字符而不是八进制值为文件或目录分配权限。符号权限采用 `(对象)(操作)(权限)` 的语法格式，其中可用的值如下：

| 选项          | 字母 | 代表含义                      |
| ----------- | -- | ------------------------- |
| (对象 who)    | u  | 用户（User）                  |
| (对象 who)    | g  | 所属组（Group owner）          |
| (对象 who)    | o  | 其他用户（Other）               |
| (对象 who)    | a  | 所有人（All，“world”）          |
| (操作 action) | +  | 添加权限                      |
| (操作 action) | -  | 移除权限                      |
| (操作 action) | =  | 明确设定权限                    |
| (权限 perms)  | r  | 读取权限（Read）                |
| (权限 perms)  | w  | 写入权限（Write）               |
| (权限 perms)  | x  | 执行权限（Execute）             |
| (权限 perms)  | t  | 粘滞位（Sticky bit）           |
| (权限 perms)  | s  | 设置 UID 或 GID（Set UID/GID） |

这些值可与 [chmod(1)](https://man.freebsd.org/cgi/man.cgi?query=chmod\&sektion=1\&format=html) 命令结合使用，但用字母而非数字。例如，以下命令将阻止文件 *FILE* 的所属组成员及其他所有用户访问该文件：

```sh
% chmod go= FILE
```

如果要对文件做多项权限变更，可以使用逗号分隔的列表。例如，以下命令移除 *FILE* 的组用户和“其他用户”的写权限，并为所有用户添加执行权限：

```sh
% chmod go-w,a+x FILE
```

## 3.4.2. FreeBSD 文件标志（File Flag）

除了文件权限之外，FreeBSD 还支持使用“文件标志”（file flag）。这些标志为文件（但不是目录）提供了更高层次的安全性和控制。通过文件标志，即使是 `root` 用户也可能被阻止删除或修改文件。

可以使用 [chflags(1)](https://man.freebsd.org/cgi/man.cgi?query=chflags\&sektion=1\&format=html) 命令修改文件标志。例如，要在 `file1` 上启用系统不可删除标志，可以执行以下命令：

```sh
# chflags sunlink file1
```

要关闭该系统不可删除标志，在 `sunlink` 前加上 `no`：

```sh
# chflags nosunlink file1
```

要查看文件的标志，可使用带 `-lo` 选项的 [ls(1)](https://man.freebsd.org/cgi/man.cgi?query=ls\&sektion=1\&format=html) 命令：

```sh
# ls -lo file1
```

```sh
-rw-r--r--  1 trhodes  trhodes  sunlnk 0 Mar  1 05:54 file1
```

某些文件标志只能由 `root` 用户添加或移除。在其他情况下，文件所有者也可以设置其文件标志。更多信息请参阅 [chflags(1)](https://man.freebsd.org/cgi/man.cgi?query=chflags\&sektion=1\&format=html) 和 [chflags(2)](https://man.freebsd.org/cgi/man.cgi?query=chflags\&sektion=2\&format=html)。

## 3.4.3. setuid、setgid 和 sticky 权限

除了前面讨论过的权限，还有三种特定的设置是所有管理员都应了解的。它们分别是 `setuid`、`setgid` 和 `sticky` 权限。

这些设置对某些 UNIX® 操作而言非常重要，因为它们提供了普通用户通常无法获得的功能。要理解这些设置，必须理解“真实用户 ID（real UID）”与“有效用户 ID（effective UID）”之间的区别。

真实用户 ID 是指拥有或启动某进程的 UID；而有效用户 ID 则是指进程实际以哪个用户 ID 身份运行。例如，当用户更改自己的密码时，[passwd(1)](https://man.freebsd.org/cgi/man.cgi?query=passwd\&sektion=1\&format=html) 以该用户的真实 UID 运行。然而，为了更新密码数据库，该命令实际以 `root` 的有效 UID 运行。这样用户就可以修改自己的密码，而不会遇到 `Permission Denied` 错误。

可以给用户添加 `s` 权限来符号性地设置 setuid 权限，如下所示：

```sh
# chmod u+s suidexample.sh
```

也可以通过在权限值前加上数字 4 来设置 setuid 权限：

```sh
# chmod 4755 suidexample.sh
```

此时 `suidexample.sh` 的权限会变为：

```sh
-rwsr-xr-x   1 trhodes  trhodes    63 Aug 29 06:36 suidexample.sh
```

注意，文件所有者的执行位已被 `s` 替代，表示启用了 setuid。这对于需要权限提升的工具（如 [passwd(1)](https://man.freebsd.org/cgi/man.cgi?query=passwd\&sektion=1\&format=html)）来说非常关键。

> **注意**
>
> `nosuid` [mount(8)](https://man.freebsd.org/cgi/man.cgi?query=mount\&sektion=8\&format=html) 挂载选项会导致这类二进制文件静默失败，且不通知用户。该选项并非完全可靠，因为某些 nosuid 封装程序可能绕过 `nosuid` 挂载限制。

可以通过以下方法实时观察此现象：打开两个终端。在一个终端中以普通用户身份输入 `passwd`，在等待新密码的同时，在另一个终端中查看进程表，观察 [passwd(1)](https://man.freebsd.org/cgi/man.cgi?query=passwd\&sektion=1\&format=html) 的用户信息。

在终端 A 中：

```sh
Changing local password for trhodes
Old Password:
```

在终端 B 中：

```sh
# ps aux | grep passwd
```

输出示例：

```sh
trhodes  5232  0.0  0.2  3420  1608   0  R+    2:10AM   0:00.00 grep passwd
root     5211  0.0  0.2  3620  1724   2  I+    2:09AM   0:00.01 passwd
```

尽管 [passwd(1)](https://man.freebsd.org/cgi/man.cgi?query=passwd\&sektion=1\&format=html) 是由普通用户运行的，但它使用的是 `root` 的有效 UID。

`setgid` 权限与 `setuid` 的作用类似，只不过它影响的是组设置。当某个程序或工具以该权限执行时，它将获得基于文件所属组的权限，而不是发起进程的用户的组。

要用符号方式设置某文件的 `setgid` 权限，可为组添加 `s` 权限，例如：

```sh
# chmod g+s sgidexample.sh
```

或使用 `chmod` 时在权限数字前加上 2：

```sh
# chmod 2755 sgidexample.sh
```

以下示例中，可以看到 `s` 出现在组权限的位置：

```sh
-rwxr-sr-x   1 trhodes  trhodes    44 Aug 31 01:49 sgidexample.sh
```

> **注意**
>
> 尽管这些示例中的 shell 脚本是可执行文件，但它们不会以不同的有效 UID 运行。这是因为 shell 脚本无法使用 [setuid(2)](https://man.freebsd.org/cgi/man.cgi?query=setuid\&sektion=2\&format=html) 系统调用。

`setuid` 和 `setgid` 权限可能降低系统安全性，因为它们能让权限提升。第三种特殊权限 `sticky bit` 则可以增强系统安全。

当目录设置了 `sticky bit` 后，只有文件的所有者才能删除该文件。这在公共目录（如 **/tmp**）中非常有用，可防止非文件所有者的用户删除文件。要设置该权限，可以添加 `t` 模式：

```sh
# chmod +t /tmp
```

或在权限值前加上数字 1：

```sh
# chmod 1777 /tmp
```

此权限将以 `t` 的形式显示在权限字符串的最后：

```sh
# ls -al / | grep tmp
```

```sh
drwxrwxrwt  10 root  wheel         512 Aug 31 01:49 tmp
```
