> For the complete documentation index, see [llms.txt](https://book.bsdcn.org/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://book.bsdcn.org/man/man4/mac_do.4.md).

# mac\_do.4

`mac_do` — 允许非特权用户更改进程凭证的策略

## 名称

`mac_do`

## 概要

要将 **mac\_do** 策略编译进内核，请在内核配置文件中加入以下行：

> options MAC options MAC\_DO

或者，要在引导时加载此策略模块，请在内核配置文件中加入以下行：

> options MAC

并在 loader.conf(5) 中加入：

```sh
mac_do_load="YES"
```

## 描述

`mac_do` 策略模块允许非特权用户根据管理员配置的规则更改进程凭证。它支持按 jail 配置。

`mac_do` 策略模块仅对从可配置白名单中特定可执行文件生成的进程生效。默认情况下，此白名单仅包含 `/usr/bin/mdo` 可执行文件，有关此程序的更多详情，请参见 mdo(1)。

Sx 凭证规则 小节指定了凭证转换规则的格式，Sx 配置 小节解释了如何配置 `mac_do`，包括规则和授权的可执行文件。

## 凭证规则

规则基于当前进程凭证和期望的最终凭证，指定 `mac_do` 将允许哪些进程凭证转换。它们由管理员以字符串形式按以下描述的特定语法自上而下传入。其设计目标是以安全、紧凑的方式精细描述期望的目标凭证。

### 规则的顶层列表

在顶层，规则是一个可能为空的单个规则列表，规则之间以分号（;）分隔：

```sh
<rules >[<rule >[‘; ’<rule >]* ]>]>]>
```

它们构成一个析取式，即只要列表中至少有一条规则匹配，`mac_do` 就会授权凭证转换。

一条规则由 部分（也称为“匹配”）和 部分（也称为“目标”）组成，按此顺序，以大于号（`>`）分隔：

```sh
<rule ><from >‘> ’<to >>>>
```

### 规则的 `<from>` 部分

规则的第一部分 与请求某些凭证转换的进程的凭证进行匹配。其形式为：

```sh
<from ><type >‘= ’<id >>>>
```

必须为：

```sh
<type >[‘uid ’‘gid ’]>
```

即字面字符串 `uid` 或 `gid` 之一。 必须是用户或组的数字 ID，并与相应类型的当前进程实际 ID 进行匹配，对于 `gid` 类型还会与补充组进行匹配。

### 规则的 `<to>` 部分

规则的第二部分 是一个以逗号分隔的、非空的目标子句列表：

```sh
<to ><target_clause >[‘, ’<target_clause >]*>]>>
```

给定规则的目标子句也构成一个析取式，即它们指定的 ID 是目标凭证的备选项，下文描述的某些情况除外。

接下来的小节描述目标子句的语法、适用的默认值，以及每条规则的 部分中非冗余和非矛盾的原则。

### 目标子句

规则 部分中的目标子句必须为以下形式之一：

```sh
<target_clause >‘any ’>
```

```sh
<target_clause ><flags ><type >‘= ’<id >>>>>
```

第一种形式是一种紧凑的方式，指定允许任何目标凭证。第二种形式类似于 子句的形式，但有以下扩展：

* 也可以是字面值 `*`、`any` 或 `.`。`*` 和 `any` 都表示指定 的任意 ID，处理方式相同。. 表示进程在指定 下的当前 ID，如下所述。
* 最多可包含 `+`、`-` 和 ! 字符中的一个，仅当 为 `gid` 时才可非空。此外，如果 为 `*` 或 `any`，则只能出现 `+` 标志。

对于 `gid` 类型的目标子句，缺少标志表示指定的组 ID 可作为实际、有效和/或保存的组 ID（“主”组）。相反，存在任何允许的标志表示规范涉及补充组。每个标志有特定含义：

* `+` 表示该组 ID 可作为补充组。
* ! 表示该组 ID 是强制的，即必须列在补充组中。
* `-` 表示该组 ID 不得列在补充组中。

带 `-` 的规范只有与带 `+ Ns` 标记的规范结合使用才有用，其中只有一个规范的 为 . 。带有 ! 或 `-` 标志的目标子句是“强制”子句，因此不参与其他目标子句的析取，而是在其规则中无条件适用。

. 是调用进程在权限检查时已拥有的 ID 的占位符。对于 `uid` 类型，它表示进程的实际、有效或保存的用户 ID 中的任何一个。对于 `gid` 类型，其效果取决于是否存在标志。如果没有，它表示进程的实际、有效或保存的组 ID 中的任何一个。如果存在，它表示进程的补充组中的任何一个。

### 部分的默认值

如果 部分未列出类型为 `uid` 的目标子句，则调用进程的任何当前用户 ID 都被接受。换言之，在这种情况下，`mac_do` 的行为相当于列出了如下目标子句：

```sh
uid=.
```

类似地，如果 部分未列出类型为 `gid` 的目标子句，则假定调用进程的所有组都是必需的。更准确地说，每个期望的实际、有效和保存的组 ID 必须是当前实际、有效或保存的组 ID 之一，且所有补充组必须与当前的补充组相同。这相当于 部分包含以下两个子句：

```sh
gid=.,!gid=.
```

### 部分中的非冗余和非矛盾

单条规则的任意两个目标子句不得表达完全相同的逻辑意图，也不得相互矛盾。

实际上，任意两个子句不得显示相同的 ID，但对于组 ID，仅当每次相同的 ID 出现时都以不同的标志出现，或仅一次不带标志时才允许。此外，多次出现时指定的标志不得矛盾。例如，相同的组 ID 同时带有 `+` 和 `-` 会导致规则被拒绝。

### 解析细节

上述语法的标记周围允许任意数量的空白，但目标子句中的 和 之间不得有空格。

为方便起见，数字 ID 可指定为负整数，然后按 C 标准中 `uid_t` 和 `gid_t` 类型（均为 32 位无符号整数）的规定转换为无符号值。

## 配置

`mac_do` 的每个参数都有对应的 [sysctl(8)](/man/man8/sysctl.8.md) 旋钮。`mac_do` 的大多数参数支持按 jail 设置。

### Sysctl 旋钮

`mac_do` 提供的 [sysctl(8)](/man/man8/sysctl.8.md) 旋钮有两种类型：全局旋钮，影响该模块的所有使用；按 jail 旋钮，允许从 jail 内部（或主机内）检索或更改适用于该 jail（或主机）的设置。它们在下方的列表中相应标记。

按 jail 参数指示的默认值适用于主机。适用于 jail 的默认值在下面的 Sx Jail 参数 小节中描述。

以下旋钮可用：

**`security.mac.do.enabled`** 启用 `mac_do` 策略。（全局。默认：1。）

**`security.mac.do.print_parse_error`** 在尝试通过 `security.mac.do.rules` [sysctl(8)](/man/man8/sysctl.8.md) 旋钮设置不正确的规则时记录一条消息。（全局。默认：1。）

**`security.mac.do.rules`** 凭证转换规则列表，其语法在上面的 Sx 凭证规则 小节中描述。空字符串实际上会禁用该策略。（按 jail。默认：空。）

**`security.mac.do.exec_paths`** 授权可执行文件的绝对路径列表（相对于当前 jail 的根目录），以冒号（:）分隔。仅从这些可执行文件启动的进程会被 `mac_do` 策略考虑。空字符串实际上会禁用该策略。（按 jail。默认：`/usr/bin/mdo`。）

### Jail 参数

`mac_do` 的大多数参数是按 jail 的（参见上面的 Sx Sysctl 旋钮 小节）。按 jail 的参数有对应的 jail 参数，可用于在创建 jail 时设置其初始值，或从 jail 外部修改正在运行的 jail 中的值。

默认情况下，与主机一样，新 jail 对其进程禁用 `mac_do`，如同下面的 `mac.do` 参数被显式设为 `disable`。

除 `mac.do` 外，每个未指定的参数默认为复制正在运行的 jail 当前适用配置中的值，或在创建 jail 时从父 jail 复制。

以下 jail 参数可用：

**`new`** `mac_do` 将为 jail 使用特定配置。如果其他 jail 参数中的任何一个在应用默认值规则（参见前言）后最终为空，此情况降级为 `disable`。 **`disable`** 在 jail 中禁用 `mac_do`。通过确保 `mac.do.rules` 和 `mac.do.exec_paths` jail 参数中至少有一个为空来实现。如果在应用默认值规则（参见前言）后没有一个最终为空，`mac.do.rules` 将被强制设为空值。显式的 `disable` 与所有其他 jail 参数的显式非空值不兼容，将触发错误。 **`inherit`** 其他按 jail 参数从适用于 jail 父级的参数继承（这些参数本身可能也是继承的）。配置修改会立即传播到所有配置为继承的后代 jail（只要中间没有具有自己特定配置的 jail）。

**`mac.do`** 可能的值为：默认值取决于其他 jail 参数的缺失或存在及值。一旦后者之一存在且非空，默认值为 `new`，否则为 `disable`。继承永远不会隐式建立，必须显式请求。

**`mac.do.rules`** jail 的凭证规则。参见上面 Sx Sysctl 旋钮 小节中对应 [sysctl(8)](/man/man8/sysctl.8.md) 旋钮 `security.mac.do.rules` 的描述。另请参见前言中的默认值规则。

**`mac.do.exec_paths`** 授权的可执行文件。参见上面 Sx Sysctl 旋钮 小节中对应 [sysctl(8)](/man/man8/sysctl.8.md) 旋钮 `security.mac.do.exec_paths` 的描述。另请参见前言中的默认值规则。

### 一致性

从 jail 参数读取或设置的值始终与对应的 [sysctl(8)](/man/man8/sysctl.8.md) 旋钮一致，实际上操作的是同一个内部“变量”。

对某个参数或某个 jail 配置整体的访问，无论是读取还是修改，都是顺序一致的。换言之，它们看起来是原子的，所有线程都以相同的顺序看到它们发生。

## 实例

以下是匹配实际用户 ID 为 10001 的进程的若干单条规则示例：

**`uid=10001>uid=10002`** 允许进程将其所有实际、有效或保存的用户 ID 切换为 10002，但保留其所在的组，并保持相同的主/补充组划分。

**`uid=10001>uid=10002,uid=10003`** 与第一个示例相同，但还允许切换到 UID 10003 而非 10002，或在不同用户 ID 中同时拥有两者。

**`uid=10001>uid=10002,gid=10002`** 与第一个示例相同，但新主组必须设为 10002，且不应设置补充组。

**`uid=10001>uid=10002,gid=10002,+gid=.`** 与前一个示例相同，但额外允许保留任何当前补充组。

**`uid=10001>uid=10002,gid=10002,!gid=.`** 与前一个示例相同，但附加约束是必须保留所有当前补充组。

**`uid=10001>uid=10002,gid=10002,+gid=.,-gid=10001`** 与上面的 `uid=10001>uid=10002,gid=10002,+gid=.` 相同，但 10001 不能作为补充组保留。

**`uid=10001>uid=10002,gid=10002,+gid=.,!gid=10003`** 与上面的 `uid=10001>uid=10002,gid=10002,+gid=.` 相同，附加约束是 10003 必须出现在补充组中。

**`uid=10001>uid=10002,gid=*,+gid=*`** 与第一个示例相同，但解除对组的任何约束，允许进程加入其认为合适的任何组。

以下是匹配实际组 ID 为 10001 或在其补充组中包含 10001 的进程的若干单条规则示例：

**`gid=10001>uid=0`** 使 10001 成为更强大的 `wheel` 组，允许其成员无需密码切换到 root。

**`gid=10001>gid=10002`** 允许进程将 GID 10002 作为主组加入，但前提是放弃其所有补充组。

**`gid=10001>gid=10002,+gid=.`** 与前一个示例相同，但允许保留任何当前补充组。

**`gid=10001>gid=10002,!gid=.`** 与前一个示例相同，但附加约束是必须保留所有当前补充组。

## 参见

mdo(1), setcred(2), [mac(4)](/man/man4/mac.4.md), [jail(8)](/man/man8/jail.8.md), [sysctl(8)](/man/man8/sysctl.8.md)

## 作者

Olivier Certner <olce@FreeBSD.org> Baptiste Daroussin <bapt@FreeBSD.org> Kushagra Srivastava <kushagra1403@gmail.com>

## 缺陷

目前，`mac_do` 仅考虑通过 setcred(2) 系统调用请求的凭证转换。创建此系统调用很大程度上是为了让 `mac_do` 能查看整个凭证转换并决定是否授权，而传统 UNIX 通过连续更改不同部分的方式无法做到。

然而，对传统或标准凭证更改函数的调用本身可视为完整的转换（无论多么有限），因此同样应受到 `mac_do` 的监控。未来的工作将解除此限制。

## 安全考虑

`mac_do` 的威胁模型是将用户态程序视为通常不可信，无法决定哪些凭证更改是可接受的。这与传统的 UNIX 更改凭证方式形成对比，后者通过设置 setuid 位安装专用程序，赋予其完全的管理员权限，使其能够实际建立新的凭证。此类凭证更改程序中的漏洞可能对系统完整性造成灾难性后果。

因此，`mac_do` 不依赖伴随的用户态程序来决定某个凭证转换是否可接受。相反，它独立于用户态密码和组数据库维护自己的配置。建立此配置本身当前依赖于用户态程序发出 sysctl(3) 或 jail(2) 调用。因此，应在系统启动或 jail 启动附近、系统可能遭受任何攻击之前建立配置，并应采取进一步措施确保潜在的损坏不会影响后续重启中的配置，例如恢复原始状态或确保直至 `mac_do` 配置的引导过程可信。


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## 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, and the optional `goal` query parameter:

```
GET https://book.bsdcn.org/man/man4/mac_do.4.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

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.
