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 的范围内。

绑定关系

绑定必须通过销毁消息并用标题 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_replace,然后再调用 zx_channel_write。如果未指定任何权利,系统将使用 ZX_RIGHT_SAME_RIGHTS 代替权利。

正在有一个用于实现详情的文档,以便其实现此目标。

工效学设计

从长远来看,这是人体工程学的改进。权利记录和检查是以标准方式在 FIDL 中完成,而不是进行临时评论和检查。

文档和示例

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

向后兼容性

ABI 兼容性

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

源代码兼容性

权限变更可能会破坏源代码兼容性。但是,破坏源代码兼容性的行为出乎意料,但如果绑定作者选择此路径,则应明确记录下来。

添加所需权限

在消息接收方上,它具有一个额外的必需权限,因为这仅提供更多功能,所以它向后兼容。但是,对于消息发送方,添加必需的权限是一种向后不兼容的更改,因为在发送之前必须存在现在的权限。

移除所需权利

它在发送端向后兼容,可移除所需权限,但在接收方端向后兼容。接收方现在将不会获得预期的权利。

添加可选权利

此功能可向后兼容,以添加可选权利。

移除可选权利

它在发送端向后兼容,可移除可选权限,但在接收方端向后兼容。接收方现在将不会获享之前可能获得的权利。

环境假设

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

我们可以将这种状态视为此 FTP 的完整动机:移除这种普遍的隐含协定。

性能

Microbenchmark 显示,zx_channel_write_etczx_channel_read_etc 具有非常相似的性能。对于带有 1 个句柄的 64 字节消息,zx_channel_write/zx_channel_read 需要 962ns,而 zx_channel_write_etc/zx_channel_read_etc 则需要 1000ns。

读取或写入句柄时所需的句柄数组将从 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 应针对所有功能(和权限)采用“默认的拒绝”政策。

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

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

因此,从约束语法的角度(即用户在编写 FIDL API 时输入的内容)而言,我们正转向采用列出权限,并将部分权限标记为可选。

请注意,从约束语义的角度(即需要在 JSON IR 中表达并通过绑定实现的内容)而言,此语法更改将继续表示下限和上限检查。

必需的源代码兼容性

在 Fuchsia 中,权利通常用 uint32 表示,并且权利的值可能会随时变化。因此,看上去合理地预期,FIDL 中权利值的变化不会对生成的源代码产生实质性影响。不过,某些用例可能会破坏源代码兼容性,例如,如果存在指定权限(在本例中为 zx.rights.WRITE),则生成特定方法(例如 write())。因此,我们不能规定权利变更不能破坏源代码兼容性。