routing/bedrock/
with_rights.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
// Copyright 2024 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.

use crate::bedrock::request_metadata::Metadata;
use crate::error::RoutingError;
use crate::rights::{Rights, RightsWalker};
use crate::walk_state::WalkStateUnit;
use async_trait::async_trait;
use fidl_fuchsia_component_sandbox as fsandbox;
use moniker::ExtendedMoniker;
use router_error::RouterError;
use sandbox::{CapabilityBound, Request, Routable, Router, RouterResponse};

struct RightsRouter<T: CapabilityBound> {
    router: Router<T>,
    rights: Rights,
    moniker: ExtendedMoniker,
}

#[async_trait]
impl<T: CapabilityBound> Routable<T> for RightsRouter<T> {
    async fn route(
        &self,
        request: Option<Request>,
        debug: bool,
    ) -> Result<RouterResponse<T>, router_error::RouterError> {
        let request = request.ok_or_else(|| RouterError::InvalidArgs)?;
        let RightsRouter { router, rights, moniker } = self;
        let request_rights: Rights =
            request.metadata.get_metadata().ok_or(fsandbox::RouterError::InvalidArgs)?;
        let request_rights = RightsWalker::new(request_rights, moniker.clone());
        let router_rights = RightsWalker::new(*rights, moniker.clone());
        // The rights of the request must be compatible with the
        // rights of this step of the route.
        match request_rights.validate_next(&router_rights) {
            Ok(()) => router.route(Some(request), debug).await,
            Err(e) => Err(RoutingError::from(e).into()),
        }
    }
}

pub trait WithRights {
    /// Returns a router that ensures the capability request does not request
    /// greater rights than provided at this stage of the route.
    fn with_rights(self, moniker: impl Into<ExtendedMoniker>, rights: Rights) -> Self;
}

impl<T: CapabilityBound> WithRights for Router<T> {
    fn with_rights(self, moniker: impl Into<ExtendedMoniker>, rights: Rights) -> Self {
        Router::<T>::new(RightsRouter { rights, router: self, moniker: moniker.into() })
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use assert_matches::assert_matches;
    use fidl_fuchsia_io as fio;
    use router_error::{DowncastErrorForTest, RouterError};
    use sandbox::{Data, Dict, WeakInstanceToken};
    use std::sync::Arc;

    #[derive(Debug)]
    struct FakeComponentToken {}

    impl FakeComponentToken {
        fn new() -> WeakInstanceToken {
            WeakInstanceToken { inner: Arc::new(FakeComponentToken {}) }
        }
    }

    impl sandbox::WeakInstanceTokenAny for FakeComponentToken {
        fn as_any(&self) -> &dyn std::any::Any {
            self
        }
    }

    #[fuchsia::test]
    async fn rights_good() {
        let source = Data::String("hello".to_string());
        let base = Router::<Data>::new_ok(source);
        let proxy = base.with_rights(ExtendedMoniker::ComponentManager, fio::RW_STAR_DIR.into());
        let metadata = Dict::new();
        metadata.set_metadata(Into::<Rights>::into(fio::R_STAR_DIR));
        let capability = proxy
            .route(Some(Request { target: FakeComponentToken::new(), metadata }), false)
            .await
            .unwrap();
        let capability = match capability {
            RouterResponse::<Data>::Capability(d) => d,
            c => panic!("Bad enum {:#?}", c),
        };
        assert_eq!(capability, Data::String("hello".to_string()));
    }

    #[fuchsia::test]
    async fn rights_bad() {
        let source = Data::String("hello".to_string());
        let base = Router::<Data>::new_ok(source);
        let proxy = base.with_rights(ExtendedMoniker::ComponentManager, fio::R_STAR_DIR.into());
        let metadata = Dict::new();
        metadata.set_metadata(Into::<Rights>::into(fio::RW_STAR_DIR));
        let error = proxy
            .route(Some(Request { target: FakeComponentToken::new(), metadata }), false)
            .await
            .unwrap_err();
        assert_matches!(
            error,
            RouterError::NotFound(err)
            if matches!(
                err.downcast_for_test::<RoutingError>(),
                RoutingError::RightsRoutingError(
                    crate::error::RightsRoutingError::Invalid { moniker: ExtendedMoniker::ComponentManager, requested, provided }
                ) if *requested == <fio::Operations as Into<Rights>>::into(fio::RW_STAR_DIR) && *provided == <fio::Operations as Into<Rights>>::into(fio::R_STAR_DIR)
            )
        );
    }
}