RFC-0028:处理权利

RFC-0028:处理权限
状态已接受
区域
  • FIDL
说明

在 FIDL 中为必需或排除的句柄权利添加注解。

作者
提交日期(年-月-日)2019-04-01
审核日期(年-月-日)2019-04-18

“消失的‘要员’”

摘要

在 FIDL 中为必需或排除的句柄权利添加注解,具体而言:

  1. 提供一种机制来描述所有句柄类型(例如 Protocolrequest<Protocol>handle)的权限

  2. 在序列化时,强制执行权限过滤,即权限说明决定了掩码,该掩码表示用户必须拥有的一组权限。

  3. 在反序列化时,强制执行权限验证和提供的一组权限。

设计初衷

句柄权限是用于在 Fuchsia 中减弱特权的工具。在此 FTP 之前,FIDL 无法描述句柄权限。目前,在一些对标识名权限有重要限制或要求的地方,此类说明会被降级为方法注释。

可取的权限限制示例包括只读 IPC 或 VMO 句柄,以及不可执行的 VMO 句柄。

可取的权限要求示例包括可写入的 IPC 句柄。

设计

权限限制

此提案向 FIDL 添加了一种新类型的约束条件:权限注解。这适用于 FIDL 数据结构和方法参数中的所有句柄类型:句柄、协议和协议请求。

可以针对类型化句柄声明指定权限约束条件:handle<*subtype*, *RC*>。您无法对普通句柄指定这些权限,因为权限仅在特定句柄子类型的上下文中才有意义。如果未在句柄声明中指定权限限制,系统会将权限以相同的权限转发(现有行为不会发生变化)。

示例:

必须具有 handle<vmo, zx.rights.READ | zx.rights.WRITE> 读取和写入权限,发送给客户端时会移除任何其他权限

handle<vmo> 未指定权利,转发现有权利

必须为每个权限约束条件指定至少一项权限(例如 handle<vmo, > 是非法权限)。

无法为服务器和客户端协议端点 (myprotocolrequest<myprotocol>) 指定权限约束条件。这些端点始终具有权限 zx.TRANSFER | zx.WAIT | zx.INSPECT | zx.WRITE | zx.READ | zx.SIGNAL | zx.SIGNAL_PEER,并且子类型设为“渠道”。如果需要自定义权限(例如,对于不可移动的协议),可以使用 handle<*channel*, *RRC, ORC*>。(这可以通过使用模板进一步优化,我们将在未来的 FTP 中进行讨论)。

权限约束条件必须遵循“默认拒绝”政策。必须明确列出所有权利。

语法

句柄权限约束条件是位类型的表达式,可借助按位运算符指定。在上面的示例中,| 是按位或运算符。该语法是通用的,也适用于 FIDL 语言中其他位置的位类型值。在 zx 库中,将有一个位定义 zx.rights,其中包含所有权限。

虽然重复 zx.rights.[*right*] 的权限规范很冗长,但有建议在未来的 FTP 中缩短该规范,因此该规范被视为不在本 FTP 的适用范围之内。

绑定

绑定必须通过销毁消息并使用 epitaph ZX_ERR_ACCESS_DENIED 关闭通道,来响应接收和反序列化具有不正确必需权限的句柄。

绑定必须通过失败序列化、销毁消息和禁止发送来响应具有不正确必需权限的句柄的序列化。绑定也必须使用 ZX_ERR_BAD_STATE 关闭通道。

默认值

不存在默认的句柄权限。原因如下:

  • 默认值的行为不明确,可能会因对象类型而异。给定对象的默认权限对用户而言并不透明。

  • 默认值不鼓励使用精细的句柄权限,因为在编写 FIDL 定义时,很容易忽略权限。

  • 如果存在默认值,对于许多对象类型,最合适的候选项是无权限或最大权限。设置最大权限默认值会限制此 FTP 中更改的有效性,但不设置权限默认值的效果与不设置默认值相同。这两种方法都没什么帮助。

重复使用句柄声明

没有默认值的一个后果是,指定权限可能会很冗长。为了改进这一点,我们将能够使用重影功能(即 using 关键字)为整个句柄声明命名。

using readable_vmo = handle<vmo, zx.READ>

替代方案可能是允许为权限约束条件使用别名(例如为 zx.READ | zx.WRITE 使用“io”别名),但这会提供一层间接层,使权限不易被发现,尤其是在权限被广泛使用的情况下。通过允许在对象级别使用别名,它会将使用限制为类型相同的位置。

可参数化

我们希望创建包含可参数化的权限约束条件的通道的通用消息。

例如,请考虑 fuchsia.mem.Buffer,其中包含 handle<vmo>。应该可以说 fuchsia.mem.Buffer:C,其中限制条件 C 会流向限制 handle<vmo>

一般化类型别名只是一个开始,引入模板将进一步满足这一需求。虽然这超出了此提案的范围,但在开展相关工作时必须考虑这一要求。

实施策略

接收消息应依赖于 zx_channel_read_etc 系统调用,以便在调用时提供权限信息。权限信息将由绑定使用,以验证是否存在所需权限,并滤除除所需权限之外收到的任何其他权限。

发送消息应依赖于 zx_channel_write_etc,它会将发送的权限减少到指定权限集,并验证是否存在所有必需的权限,如果验证失败,则返回 ACCESS_DENIED。绑定将负责在收到此响应后关闭通道。为了与现有行为保持一致,系统会对此系统调用使用 ZX_HANDLE_OP_MOVE,这相当于依次调用 zx_handle_replacezx_channel_write。如果未指定任何权利,系统会使用 ZX_RIGHT_SAME_RIGHTS 来代替权利。

我们正在着手实现一个文档,以详细说明实现方式。

工效学设计

从长远来看,这是一种人体工学改进。权限文档和检查是通过 FIDL 以标准方式完成的,而不是通过临时注释和检查完成的。

文档和示例

这将需要对以下文档进行更改:

向后兼容性

ABI 兼容性

权限更改不得破坏 ABI 兼容性。

来源兼容性

权限更改可能会破坏源代码兼容性。不过,破坏源代码兼容性是意料之外的,如果绑定作者选择此路径,则应明确记录。

添加所需权限

在消息接收方,具有额外必需权限是向后兼容的,因为它只会提供更多功能。不过,在消息发送方,添加必需权限是一项向后不兼容的更改,因为现在需要在发送前拥有该权限。

移除必需权限

它在发件人端向后兼容,可移除所需权限,但在收件人端向后不兼容。现在,收件人将无法获得预期的权限。

添加可选权利

添加可选权限是向后兼容的。

移除可选权利

在移除可选权限时,它在发件人端向后兼容,但在收件人端向后不兼容。收件人现在将无法获得之前可能获得的权利。

氛围假设

可能会有环境假设认为此模型的部署会中断。例如,客户端可能会假定通过连接收到的所有 VMO 都是可映射的,即使服务器不打算提供此保证也是如此。

可以将这种状态视为此 FTP 的全部动机:消除这种普遍存在且隐含的合约。

性能

微基准测试表明,zx_channel_write_etczx_channel_read_etc 的性能非常相似。对于包含 1 个句柄的 64 字节消息,zx_channel_write/zx_channel_read 需要 962 纳秒,而 zx_channel_write_etc/zx_channel_read_etc 需要 1000 纳秒。

读取或写入句柄时所需的句柄数组将从 256 字节 (ZX_CHANNEL_MAX_MSG_HANDLES * sizeof(zx_handle_t)) 增加到 1024 字节 (ZX_CHANNEL_MAX_MSG_HANDLES * sizeof(zx_handle_info_t))。同样,在写入句柄时堆分配的数组将从 256 字节增加到 1280 字节 (ZX_CHANNEL_MAX_MSG_HANDLES * sizeof(zx_handle_disposition))

为了将堆栈分配限制为最多 256 字节,如果句柄表太大(读取句柄大于 16 个或写入句柄大于 12 个),我们将需要在堆上分配句柄表。在进行这项更改的过程中,我们将研究消息大小和句柄表的总栈分配要求(目前,我们只考虑消息大小)。

安全

这是一次安全改进。这样一来,我们就可以更准确地审核 API 接口。它会将权限检查移至绑定中,这样比在所有调用点进行相同的检查更容易进行审核。

例如,在权限被完全使用后,您可以轻松审核转让可执行 VMO 所有权的所有位置。这在今天很难做到。

测试

应对每个绑定实现进行单元测试。

推出此功能时还应确保,经过修改以使用此功能的 FIDL 协议实现会测试新功能。

缺点、替代方案和未知

缺点:公共 API 会冗长

从某种意义上讲,此特征是“噪声”特征。在系统的典型执行过程中,系统可能永远不会采用缺少权限的路径。但它们仍然会占用公共系统 API 中的空间。

我们认为,为了清晰和准确,付出这样的代价是值得的,而且善用别名即可满足大多数需求。

例如,考虑一下可以避免或减少的两类问题:

  1. 由于权限不兼容而导致远程断开:失败可能发生在距离源代码很远的地方(如果句柄再次传输,甚至可能发生在某些其他进程中的下游)。

  2. 未记录的假设会导致兼容性问题:某些客户端/服务器可能会传递权限过多的句柄,导致其对等方假定它们可以依赖这些权限,如果这些假设被证明是错误的,就会导致兼容性问题。

我们知道,明确说明权限会很烦人,但这相当于说明相应功能的类型信息。

替代方案:权限下限和上限

初始设计要求对权限设置下限和上限(例如“无可执行权限”或“具有可写权限”)。从安全角度来看,Fuchsia 应对所有功能(和权限)采用“默认拒绝”政策。

因此,如果某项 capability(或权利)未在任何位置明确提及,则不应授予该 capability。组件清单在沙盒化方面已经采用了这种行为方式,理想情况下,FIDL API 也应采用这种方式。

我们可能还希望允许明确表达“可选权利”,即可以提供或不提供的权利。

因此,从限制语法(即用户在编写 FIDL API 时输入的内容)的角度来看,我们将重点放在列出权限上,并将部分权限标记为可选。

请注意,从约束条件语义的角度(即需要在 JSON IR 中表达并通过绑定实现的内容)来看,这种语法更改仍然会表达下限和上限检查。

强制性源代码兼容性

在 Fuchsia 中,权限通常表示为 uint32,并且权限的值可以随时更改。因此,我们似乎可以合理地预期,FIDL 中的权限值的更改不会实质性地导致生成的源代码发生变化。不过,某些用例可能会破坏源代码兼容性,例如,如果存在给定权限(在本例中为 zx.rights.WRITE),则生成特定方法(例如 write())。因此,我们并未规定权限更改不能破坏源代码兼容性。