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