| // Copyright 2019 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| library fuchsia.io2; |
| |
| /// Options for [`Directory.Open`] and [`Node.Reopen`]. |
| table ConnectionOptions { |
| /// Flags which can affect the behavior when opening and reopening. |
| /// If absent, assumes a default of zero. |
| 1: ConnectionFlags flags; |
| |
| /// Specifies the set of representations accepted by the client, to support |
| /// a form of protocol negotiation on the node being opened. |
| /// Refer to the definition of [`NodeProtocols`] for more details. |
| /// It cannot be zero. |
| /// |
| /// In addition, clients may assert the type of the object by setting |
| /// the protocol corresponding to the expected type: |
| /// |
| /// * If the caller expected a directory but the node cannot be accessed |
| /// as a directory, the error is `ZX_ERR_NOT_DIR`. |
| /// * If the caller expected a file but the node cannot be accessed as a |
| /// file, the error is `ZX_ERR_NOT_FILE`. |
| /// * In other mismatched cases, the error is `ZX_ERR_WRONG_TYPE`. |
| /// |
| /// During [`Directory.Open`], if a new object is to be created, `protocols` |
| /// determines the type of object to create; it must be present. |
| /// If a valid object type cannot be unambiguously inferred e.g. |
| /// both `DIRECTORY` and `FILE` were set, the request must fail. |
| /// |
| /// During [`Node.Reopen`], clients may specify a different but compatible |
| /// `protocols` to do a "protocol upgrade". |
| /// |
| /// If more than one protocol is present in `protocols`, the resultant |
| /// protocol may become any one of them. Clients should specify |
| /// [`ConnectionFlags.GET_CONNECTION_INFO`] to receive a |
| /// [`Node.OnConnectionInfo`] event, in order to ascertain the protocol. |
| /// |
| /// If absent, indicates that the caller accepts any type of node, and |
| /// the resulting protocol is unspecified. |
| 2: NodeProtocols protocols; |
| |
| /// Requests rights on the new connection according to the specified rules. |
| /// See [`fuchsia.io2/RightsRequest`]. |
| /// |
| /// ## Rights Hierarchy |
| /// |
| /// Respecting principles of least privileges, rights in general must meet |
| /// the following restrictions: |
| /// |
| /// * A connection must have nonzero rights. |
| /// * From the perspective of a client, rights must never increase in a |
| /// derived connection. |
| /// * From the perspective of a directory proxy, it must ensure that |
| /// new connections opened through it cannot have more rights than |
| /// the connection where the proxy received the `Open`/`Reopen` call. |
| /// |
| /// The proper enforcement of the rights hierarchy is a powerful refinement |
| /// over the existing access control facilities offered by directory |
| /// sandboxing. |
| /// |
| /// ## Rights Inheritance |
| /// |
| /// If `rights_request` is absent, inherits at most the rights on the source |
| /// connection: |
| /// |
| /// * During [`Node.Reopen`], the new connection would have the same rights |
| /// as the connection where the `Reopen` call is made. |
| /// * During [`Directory.Open`], the rights on the connection would inherit |
| /// from the connection where the `Open` call is made. If the path crosses |
| /// intermediate proxies, a proxy may strip elements from the resulting |
| /// rights if the intermediate connection does not have the corresponding |
| /// rights. |
| /// |
| /// ## Rights vs Abilities |
| /// |
| /// The rights on a connection limits the set of operations allowed on that |
| /// connection, but does not guarantee their availability, because the |
| /// object may not support it. For convenience, clients may query the |
| /// [`ConnectionInfo.available_operations`] field on a new connection, |
| /// which is the intersection of the rights and abilities and indicates the |
| /// guaranteed set of available operations. |
| /// |
| /// See [`fuchsia.io2/Rights`] and [`fuchsia.io2/Abilities`]. |
| /// |
| /// ## Implementation Notes |
| /// |
| /// When a directory proxy encounters an absent `rights` field, let `r` be |
| /// the rights on the connection where it received this request, the proxy |
| /// should fill in this field with the following: |
| /// |
| /// ``` |
| /// RightsRequest { |
| /// at_most: r, |
| /// at_least: 0, |
| /// resolution: RightsResolution.MAXIMIZE, |
| /// } |
| /// ``` |
| /// |
| /// before forwarding the request to the remote party. |
| 3: RightsRequest rights_request; |
| }; |
| |
| /// Options for requesting rights on the new connection. Because opening a |
| /// new connection may involve multiple hops through directory proxies, |
| /// we require the client to set an upper bound and lower bound on the |
| /// rights request, and intermediate proxies to refine these bounds. |
| /// |
| /// The rights manipulation should be implemented mechanically |
| /// without knowledge of any specific rights, and servers should propagate |
| /// unknown bits members, to gracefully handle future rights extensions. |
| /// |
| /// ## Implementation Notes |
| /// |
| /// It could be common for a client to request an exact set of rights. |
| /// We recommend client libraries to define a helper function like follows: |
| /// |
| /// ``` |
| /// fn Exact(exact_rights: Rights) -> RightsRequest { |
| /// RightsRequest { |
| /// at_most: exact_rights, |
| /// at_least: exact_rights, |
| /// resolution: RightsResolution.MAXIMIZE, |
| /// } |
| /// } |
| /// ``` |
| struct RightsRequest { |
| /// Sets an upper bound on the resulting rights. The exact rights will |
| /// depend on `resolution`. |
| /// |
| /// ## Implementation Notes |
| /// |
| /// When a directory proxy encounters this variant, it should compute the |
| /// intersection between this and the rights on the connection where it |
| /// received the request, to shrink the rights. |
| /// |
| /// * If the intersection is empty, or not a superset of `at_least`, |
| /// the proxy should close `object_request` with the |
| /// `ZX_ERR_ACCESS_DENIED` epitaph. |
| /// * Otherwise, the proxy should forward the `Open` call as usual, |
| /// but update `at_most` with the shrunk rights. |
| Rights at_most; |
| |
| /// Sets a lower bound on the resulting rights. The exact rights will |
| /// depend on `resolution`. |
| /// |
| /// + During [`Directory.Open`], you may only specify the same rights as |
| /// what the directory connection already has, or a subset of those. |
| /// + During [`Node.Reopen`], similarly, you may only specify the same or |
| /// a subset of rights possessed by the original connection. |
| /// + Exceeding those rights causes `object_request` to be closed with a |
| /// `ZX_ERR_ACCESS_DENIED` epitaph. |
| /// |
| /// Therefore there are these invariants which should be maintained: |
| /// |
| /// ``` |
| /// at_most ⊋ {} |
| /// at_most ⊃ at_least |
| /// rights_on_connection_where_open_is_received ⊋ {} |
| /// rights_on_connection_where_open_is_received ⊃ at_least |
| /// ``` |
| /// |
| /// using the superset (`⊃`), proper superset (`⊋`), |
| /// and empty set (`{}`) notations. |
| Rights at_least; |
| |
| /// Controls how the rights on the new connection are computed from |
| /// `at_most` and `at_least`. See [`fuchsia.io2/RightsResolution`]. |
| RightsResolution resolution; |
| }; |
| |
| /// When an `Open`/`Reopen` request reaches its final remote server, it should |
| /// assign rights on the new connection based on one of these modes. |
| enum RightsResolution : uint32 { |
| /// The rights will be the intersection between [`RightsRequest.at_most`] |
| /// and the connection where the `Open`/`Reopen` request was received, |
| /// closing `object_request` with `ZX_ERR_ACCESS_DENIED` if it is empty. |
| MAXIMIZE = 1; |
| |
| /// The rights will be determined by the following rules: |
| /// |
| /// * If the negotiated protocol on the new connection is |
| /// [`fuchsia.io2/Directory`], the rules from the `MAXIMIZE` case applies. |
| /// * Otherwise, the rights will be [`RightsRequest.at_least`] if it |
| /// does not exceed rights on the current connection. |
| /// * Otherwise, `object_request` should be closed with |
| /// `ZX_ERR_ACCESS_DENIED`. |
| /// |
| /// The motivation for this enum is to facilitate implementing POSIX |
| /// compatibility layers. The POSIX file permission model relies on ambient |
| /// authority: access control on files are resolved based on the `mode` of |
| /// the file, and the current user. There is no concept of hierarchical |
| /// permissions. Fuchsia, on the other hand, restricts rights on file |
| /// connections to never exceed that of its containing directory connection. |
| POSIX = 2; |
| }; |
| |
| /// Flags applicable to both [`Directory.Open`] and [`Node.Reopen`]. |
| bits ConnectionFlags : uint64 { |
| /// Requests that an [`Node.OnConnectionInfo`] event be sent as the first |
| /// message on the protocol request. Requests all fields in the |
| /// [`ConnectionInfo`] table. Doing so is more efficient than calling |
| /// [`Node.Describe`] later on the connection. |
| GET_CONNECTION_INFO = 0x01; |
| |
| /// Connects to the exposed protocol if the node is a Connector. |
| /// It is an error to use this flag with other types of nodes. |
| /// |
| /// If both `GET_CONNECTION_INFO` and `CONNECT` are specified, the channel |
| /// will receive exactly one [`Node.OnConnectionInfo`] event, after which |
| /// the protocol switches from [`fuchsia.io2/Node`] to the intended protocol. |
| /// Message sent by the client prior to receiving [`Node.OnConnectionInfo`] |
| /// are queued and processed after the protocol switch. |
| /// |
| /// `CONNECT` cannot be supplied together with `APPEND`. |
| /// `CONNECT` cannot be supplied together with `TRUNCATE`. |
| /// |
| /// Requires the [`Rights.CONNECT`] right on the connection. |
| CONNECT = 0x02; |
| |
| /// Opens the node in append mode, i.e. the connection should seek to the |
| /// end of the node before every write. |
| /// |
| /// If the node does not support appending, it should result in a |
| /// `ZX_ERR_NOT_SUPPORTED` epitaph. |
| /// Currently, only [`fuchsia.io2/NodeProtocols.FILE`] connections |
| /// may be configured for appending. |
| APPEND = 0x04; |
| |
| /// Truncates the object before usage, by setting its length to 0. |
| /// Requires the [`Rights.WRITE_BYTES`] right on the connection. |
| /// |
| /// If the node does not support truncating, it should result in a |
| /// `ZX_ERR_NOT_SUPPORTED` epitaph. |
| TRUNCATE = 0x08; |
| }; |