Skip to main content

routing/bedrock/
with_policy_check.rs

1// Copyright 2024 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::component_instance::{ComponentInstanceInterface, WeakExtendedInstanceInterface};
6use crate::error::{ComponentInstanceError, RoutingError};
7use crate::policy::GlobalPolicyChecker;
8use async_trait::async_trait;
9use capability_source::CapabilitySource;
10use fidl_fuchsia_component_runtime::RouteRequest;
11use moniker::ExtendedMoniker;
12use router_error::RouterError;
13use runtime_capabilities::{CapabilityBound, Routable, Router, WeakInstanceToken};
14
15/// If the metadata for a route contains a Data::Uint64 value under this key with a value greater
16/// than 0, then no policy checks will be performed. This behavior is limited to non-fuchsia
17/// builds, and is exclusively used when performing routes from an offer declaration. This is
18/// necessary because we don't know the ultimate target of the route, and thus routes that are
19/// otherwise valid could fail due to policy checks.
20///
21/// Consider a policy that allows a component `/core/session_manager/session:session/my_cool_app`
22/// to access `fuchsia.kernel.VmexResource`. If we attempt to validate that route from the offer
23/// placed on `session_manager`, we'd have to fill in `session_manager` for the target of the route
24/// in the route request and follow the route to its source from there. If this policy check were
25/// applied on this route it would fail the route, as `session` manager is not allowed to access
26/// `fuchsia.kernel.VmexResource`. The route is valid though, because the offer on
27/// `session_manager` doesn't grant the session manager program access to the restricted
28/// capability.
29///
30/// To be able to properly support this scenario, we need to selectively disable policy checks when
31/// routing from offer declarations.
32pub const SKIP_POLICY_CHECKS: &'static str = "skip_policy_checks";
33
34pub trait WithPolicyCheck {
35    /// Returns a router that ensures the capability request is allowed by the
36    /// policy in [`GlobalPolicyChecker`].
37    fn with_policy_check<C: ComponentInstanceInterface + 'static>(
38        self,
39        capability_source: CapabilitySource,
40        policy_checker: GlobalPolicyChecker,
41    ) -> Self;
42}
43
44impl<T: CapabilityBound> WithPolicyCheck for Router<T> {
45    fn with_policy_check<C: ComponentInstanceInterface + 'static>(
46        self,
47        capability_source: CapabilitySource,
48        policy_checker: GlobalPolicyChecker,
49    ) -> Self {
50        Self::new(PolicyCheckRouter::<C, T>::new(capability_source, policy_checker, self))
51    }
52}
53
54pub struct PolicyCheckRouter<C: ComponentInstanceInterface + 'static, T: CapabilityBound> {
55    capability_source: CapabilitySource,
56    policy_checker: GlobalPolicyChecker,
57    router: Router<T>,
58    _phantom_data: std::marker::PhantomData<C>,
59}
60
61impl<C: ComponentInstanceInterface + 'static, T: CapabilityBound> PolicyCheckRouter<C, T> {
62    pub fn new(
63        capability_source: CapabilitySource,
64        policy_checker: GlobalPolicyChecker,
65        router: Router<T>,
66    ) -> Self {
67        Self {
68            capability_source,
69            policy_checker,
70            router,
71            _phantom_data: std::marker::PhantomData::<C>,
72        }
73    }
74
75    fn check_policy(
76        &self,
77        _request: &RouteRequest,
78        target_token: WeakInstanceToken,
79    ) -> Result<(), RouterError> {
80        #[cfg(not(target_os = "fuchsia"))]
81        if _request.skip_policy_checks.unwrap_or(false) {
82            return Ok(());
83        }
84        let target = target_token
85            .inner
86            .as_any()
87            .downcast_ref::<WeakExtendedInstanceInterface<C>>()
88            .ok_or(RouterError::Unknown)?;
89        let ExtendedMoniker::ComponentInstance(moniker) = target.extended_moniker() else {
90            return Err(RoutingError::from(
91                ComponentInstanceError::ComponentManagerInstanceUnexpected {},
92            )
93            .into());
94        };
95        match self.policy_checker.can_route_capability(&self.capability_source, &moniker) {
96            Ok(()) => Ok(()),
97            Err(policy_error) => Err(RoutingError::PolicyError(policy_error).into()),
98        }
99    }
100}
101
102#[async_trait]
103impl<C: ComponentInstanceInterface + 'static, T: CapabilityBound> Routable<T>
104    for PolicyCheckRouter<C, T>
105{
106    async fn route(
107        &self,
108        request: RouteRequest,
109        target_token: WeakInstanceToken,
110    ) -> Result<Option<T>, RouterError> {
111        self.check_policy(&request, target_token.clone())?;
112        self.router.route(request, target_token).await
113    }
114
115    async fn route_debug(
116        &self,
117        request: RouteRequest,
118        target_token: WeakInstanceToken,
119    ) -> Result<CapabilitySource, RouterError> {
120        self.check_policy(&request, target_token.clone())?;
121        self.router.route_debug(request, target_token).await
122    }
123}