RFC-0028:处理权限 | |
---|---|
状态 | 已接受 |
区域 |
|
说明 | 在 FIDL 中为必需或排除的句柄权利添加注解。 |
作者 | |
提交日期(年-月-日) | 2019-04-01 |
审核日期(年-月-日) | 2019-04-18 |
“消失的‘要员’”
摘要
在 FIDL 中为必需或排除的句柄权利添加注解,具体而言:
提供一种机制来描述所有句柄类型(例如
Protocol
、request<Protocol>
、handle
)的权限在序列化时,强制执行权限过滤,即权限说明决定了掩码,该掩码表示用户必须拥有的一组权限。
在反序列化时,强制执行权限验证和提供的一组权限。
设计初衷
句柄权限是用于在 Fuchsia 中减弱特权的工具。在此 FTP 之前,FIDL 无法描述句柄权限。目前,在一些对标识名权限有重要限制或要求的地方,此类说明会被降级为方法注释。
可取的权限限制示例包括只读 IPC 或 VMO 句柄,以及不可执行的 VMO 句柄。
可取的权限要求示例包括可写入的 IPC 句柄。
设计
权限限制
此提案向 FIDL 添加了一种新类型的约束条件:权限注解。这适用于 FIDL 数据结构和方法参数中的所有句柄类型:句柄、协议和协议请求。
可以针对类型化句柄声明指定权限约束条件:handle<*subtype*, *RC*>
。您无法对普通句柄指定这些权限,因为权限仅在特定句柄子类型的上下文中才有意义。如果未在句柄声明中指定权限限制,系统会将权限以相同的权限转发(现有行为不会发生变化)。
示例:
必须具有 handle<vmo, zx.rights.READ | zx.rights.WRITE>
读取和写入权限,发送给客户端时会移除任何其他权限
handle<vmo>
未指定权利,转发现有权利
必须为每个权限约束条件指定至少一项权限(例如 handle<vmo, >
是非法权限)。
无法为服务器和客户端协议端点 (myprotocol
和 request<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_replace
和 zx_channel_write
。如果未指定任何权利,系统会使用 ZX_RIGHT_SAME_RIGHTS
来代替权利。
我们正在着手实现一个文档,以详细说明实现方式。
工效学设计
从长远来看,这是一种人体工学改进。权限文档和检查是通过 FIDL 以标准方式完成的,而不是通过临时注释和检查完成的。
文档和示例
这将需要对以下文档进行更改:
向后兼容性
ABI 兼容性
权限更改不得破坏 ABI 兼容性。
来源兼容性
权限更改可能会破坏源代码兼容性。不过,破坏源代码兼容性是意料之外的,如果绑定作者选择此路径,则应明确记录。
添加所需权限
在消息接收方,具有额外必需权限是向后兼容的,因为它只会提供更多功能。不过,在消息发送方,添加必需权限是一项向后不兼容的更改,因为现在需要在发送前拥有该权限。
移除必需权限
它在发件人端向后兼容,可移除所需权限,但在收件人端向后不兼容。现在,收件人将无法获得预期的权限。
添加可选权利
添加可选权限是向后兼容的。
移除可选权利
在移除可选权限时,它在发件人端向后兼容,但在收件人端向后不兼容。收件人现在将无法获得之前可能获得的权利。
氛围假设
可能会有环境假设认为此模型的部署会中断。例如,客户端可能会假定通过连接收到的所有 VMO 都是可映射的,即使服务器不打算提供此保证也是如此。
可以将这种状态视为此 FTP 的全部动机:消除这种普遍存在且隐含的合约。
性能
微基准测试表明,zx_channel_write_etc
和 zx_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 中的空间。
我们认为,为了清晰和准确,付出这样的代价是值得的,而且善用别名即可满足大多数需求。
例如,考虑一下可以避免或减少的两类问题:
由于权限不兼容而导致远程断开:失败可能发生在距离源代码很远的地方(如果句柄再次传输,甚至可能发生在某些其他进程中的下游)。
未记录的假设会导致兼容性问题:某些客户端/服务器可能会传递权限过多的句柄,导致其对等方假定它们可以依赖这些权限,如果这些假设被证明是错误的,就会导致兼容性问题。
我们知道,明确说明权限会很烦人,但这相当于说明相应功能的类型信息。
替代方案:权限下限和上限
初始设计要求对权限设置下限和上限(例如“无可执行权限”或“具有可写权限”)。从安全角度来看,Fuchsia 应对所有功能(和权限)采用“默认拒绝”政策。
因此,如果某项 capability(或权利)未在任何位置明确提及,则不应授予该 capability。组件清单在沙盒化方面已经采用了这种行为方式,理想情况下,FIDL API 也应采用这种方式。
我们可能还希望允许明确表达“可选权利”,即可以提供或不提供的权利。
因此,从限制语法(即用户在编写 FIDL API 时输入的内容)的角度来看,我们将重点放在列出权限上,并将部分权限标记为可选。
请注意,从约束条件语义的角度(即需要在 JSON IR 中表达并通过绑定实现的内容)来看,这种语法更改仍然会表达下限和上限检查。
强制性源代码兼容性
在 Fuchsia 中,权限通常表示为 uint32
,并且权限的值可以随时更改。因此,我们似乎可以合理地预期,FIDL 中的权限值的更改不会实质性地导致生成的源代码发生变化。不过,某些用例可能会破坏源代码兼容性,例如,如果存在给定权限(在本例中为 zx.rights.WRITE
),则生成特定方法(例如 write())。因此,我们并未规定权限更改不能破坏源代码兼容性。