Skip to main content

routing/
rights.rs

1// Copyright 2021 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use crate::error::{RightsRoutingError, RoutingError};
6use fidl_fuchsia_component_runtime::RouteRequest;
7use fidl_fuchsia_io as fio;
8use moniker::ExtendedMoniker;
9use router_error::RouterError;
10#[cfg(feature = "serde")]
11use serde::{Deserialize, Serialize, de::Deserializer, ser::Serializer};
12use std::fmt;
13
14/// Opaque rights type to define new traits like PartialOrd on.
15#[derive(Debug, PartialEq, Eq, Clone, Copy)]
16pub struct Rights(fio::Operations);
17
18impl Rights {
19    /// Ensures the next walk state of rights satisfies a monotonic increasing sequence. Used to
20    /// verify the expectation that no right requested from a use, offer, or expose is missing as
21    /// capability routing walks from the capability's consumer to its provider.
22    pub fn validate_next(
23        &self,
24        next_rights: &Self,
25        moniker: ExtendedMoniker,
26    ) -> Result<(), RightsRoutingError> {
27        if next_rights.0.contains(self.0) {
28            Ok(())
29        } else {
30            Err(RightsRoutingError::Invalid { moniker, requested: *self, provided: *next_rights })
31        }
32    }
33}
34
35/// Allows creating rights from fio::Operations.
36impl From<fio::Operations> for Rights {
37    fn from(rights: fio::Operations) -> Self {
38        Rights(rights)
39    }
40}
41
42impl From<Rights> for fio::Flags {
43    fn from(rights: Rights) -> Self {
44        fio::Flags::from_bits_retain(rights.0.bits())
45    }
46}
47
48impl Into<u64> for Rights {
49    fn into(self) -> u64 {
50        self.0.bits()
51    }
52}
53
54impl Into<fio::Operations> for Rights {
55    fn into(self) -> fio::Operations {
56        let Self(ops) = self;
57        ops
58    }
59}
60
61impl From<fio::Flags> for Rights {
62    fn from(flags: fio::Flags) -> Self {
63        Self(
64            fio::Operations::from_bits(flags.bits())
65                .expect("operations is bit-compatible with flags"),
66        )
67    }
68}
69
70impl fmt::Display for Rights {
71    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
72        let Self(rights) = &self;
73        match *rights {
74            fio::R_STAR_DIR => write!(f, "r*"),
75            fio::W_STAR_DIR => write!(f, "w*"),
76            fio::X_STAR_DIR => write!(f, "x*"),
77            fio::RW_STAR_DIR => write!(f, "rw*"),
78            fio::RX_STAR_DIR => write!(f, "rx*"),
79            ops => write!(f, "{:?}", ops),
80        }
81    }
82}
83
84#[cfg(feature = "serde")]
85impl Serialize for Rights {
86    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
87    where
88        S: Serializer,
89    {
90        let Self(rights) = self;
91        rights.bits().serialize(serializer)
92    }
93}
94
95#[cfg(feature = "serde")]
96impl<'de> Deserialize<'de> for Rights {
97    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
98    where
99        D: Deserializer<'de>,
100    {
101        let bits: u64 = Deserialize::deserialize(deserializer)?;
102        let rights = fio::Operations::from_bits(bits)
103            .ok_or_else(|| serde::de::Error::custom("invalid value for fuchsia.io/Operations"))?;
104        Ok(Self(rights))
105    }
106}
107
108pub fn validate_rights(
109    moniker: ExtendedMoniker,
110    allowed_rights: fio::Operations,
111    request: &mut RouteRequest,
112) -> Result<(), RouterError> {
113    let allowed_rights = Rights::from(allowed_rights);
114    let inherit = request.inherit_rights.ok_or_else(|| RoutingError::RouteRequestMissingField {
115        moniker: moniker.clone(),
116        missing_field: "inherit_rights".to_string(),
117    })?;
118    let request_rights: Rights = match request.directory_rights {
119        Some(request_rights) => request_rights.into(),
120        None => {
121            if inherit {
122                request.directory_rights = Some(fio::Flags::from(allowed_rights));
123                allowed_rights
124            } else {
125                Err(RoutingError::RouteRequestMissingField {
126                    moniker: moniker.clone(),
127                    missing_field: "directory_rights".to_string(),
128                })?
129            }
130        }
131    };
132    request_rights.validate_next(&allowed_rights, moniker.clone()).map_err(RoutingError::from)?;
133    if let Some(intermediate_rights) = request.directory_intermediate_rights {
134        Rights::from(intermediate_rights)
135            .validate_next(&allowed_rights, moniker)
136            .map_err(|e| router_error::RouterError::from(RoutingError::from(e)))?;
137    };
138    Ok(())
139}
140
141#[cfg(test)]
142mod tests {
143    use super::*;
144    use assert_matches::assert_matches;
145
146    #[test]
147    fn validate_next() {
148        assert_matches!(
149            Rights(fio::Operations::empty())
150                .validate_next(&Rights(fio::R_STAR_DIR,), ExtendedMoniker::ComponentManager),
151            Ok(())
152        );
153        assert_matches!(
154            Rights(fio::Operations::READ_BYTES | fio::Operations::GET_ATTRIBUTES,)
155                .validate_next(&Rights(fio::R_STAR_DIR), ExtendedMoniker::ComponentManager),
156            Ok(())
157        );
158        let provided = fio::Operations::READ_BYTES | fio::Operations::GET_ATTRIBUTES;
159        assert_eq!(
160            Rights(fio::R_STAR_DIR)
161                .validate_next(&Rights(provided), ExtendedMoniker::ComponentManager),
162            Err(RightsRoutingError::Invalid {
163                moniker: ExtendedMoniker::ComponentManager,
164                requested: Rights::from(fio::R_STAR_DIR),
165                provided: Rights::from(provided),
166            })
167        );
168        let provided = fio::Operations::READ_BYTES | fio::Operations::GET_ATTRIBUTES;
169        assert_eq!(
170            Rights(fio::Operations::WRITE_BYTES)
171                .validate_next(&Rights(provided), ExtendedMoniker::ComponentManager),
172            Err(RightsRoutingError::Invalid {
173                moniker: ExtendedMoniker::ComponentManager,
174                requested: Rights::from(fio::Operations::WRITE_BYTES),
175                provided: Rights::from(provided),
176            })
177        );
178    }
179}