# 3.4.权限

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

本节讨论的是 FreeBSD 中使用的传统 UNIX® 权限。若需更细粒度的文件系统访问控制，另见 [访问控制列表（Access Control Lists）](https://docs.freebsd.org/en/books/handbook/security/#fs-acl)。

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

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

**表 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. 符号权限（Symbolic Permissions）

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

| 选项          | 字母 | 代表含义                      |
| ----------- | -- | ------------------------- |
| (对象 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` 的限制可能被绕过。

可以通过以下方法实时观察此现象：打开两个终端。在一个终端中以普通用户身份输入 `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
```

或直接设置完整权限为 1777：

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

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

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

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


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://handbook.bsdcn.org/di-3-zhang-freebsd-ji-chu/3.4.-quan-xian.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
