| // Copyright 2021 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 anyhow::Error; |
| use assert_matches::assert_matches; |
| use async_trait::async_trait; |
| use cm_config::{ |
| AllowlistEntry, AllowlistEntryBuilder, CapabilityAllowlistKey, CapabilityAllowlistSource, |
| ChildPolicyAllowlists, DebugCapabilityAllowlistEntry, DebugCapabilityKey, JobPolicyAllowlists, |
| SecurityPolicy, |
| }; |
| use cm_rust::{CapabilityTypeName, ProtocolDecl, StorageDecl, StorageDirectorySource}; |
| use cm_types::Name; |
| use fidl_fuchsia_component_decl as fdecl; |
| use moniker::{ExtendedMoniker, Moniker}; |
| use routing::capability_source::{CapabilitySource, ComponentCapability, InternalCapability}; |
| use routing::component_instance::ComponentInstanceInterface; |
| use routing::policy::GlobalPolicyChecker; |
| use std::collections::{HashMap, HashSet}; |
| use std::sync::{Arc, Weak}; |
| |
| /// These GlobalPolicyChecker tests are run under multiple contexts, e.g. both on Fuchsia under |
| /// component_manager and on the build host under cm_fidl_analyzer. This macro helps ensure that all |
| /// tests are run in each context. |
| #[macro_export] |
| macro_rules! instantiate_global_policy_checker_tests { |
| ($fixture_impl:path) => { |
| // New GlobalPolicyCheckerTest tests must be added to this list to run. |
| instantiate_global_policy_checker_tests! { |
| $fixture_impl, |
| global_policy_checker_can_route_capability_framework_cap, |
| global_policy_checker_can_route_capability_namespace_cap, |
| global_policy_checker_can_route_capability_component_cap, |
| global_policy_checker_can_route_capability_capability_cap, |
| global_policy_checker_can_route_debug_capability_capability_cap, |
| global_policy_checker_can_route_debug_capability_with_realm_allowlist_entry, |
| global_policy_checker_can_route_debug_capability_with_collection_allowlist_entry, |
| global_policy_checker_can_route_capability_builtin_cap, |
| global_policy_checker_can_route_capability_with_realm_allowlist_entry, |
| global_policy_checker_can_route_capability_with_collection_allowlist_entry, |
| } |
| }; |
| ($fixture_impl:path, $test:ident, $($remaining:ident),+ $(,)?) => { |
| instantiate_global_policy_checker_tests! { $fixture_impl, $test } |
| instantiate_global_policy_checker_tests! { $fixture_impl, $($remaining),+ } |
| }; |
| ($fixture_impl:path, $test:ident) => { |
| fn $test() -> Result<(), Error> { |
| let mut executor = fuchsia_async::LocalExecutor::new(); |
| executor.run_singlethreaded(<$fixture_impl as Default>::default().$test()) |
| } |
| }; |
| } |
| |
| // Tests `GlobalPolicyChecker` for implementations of `ComponentInstanceInterface`. |
| #[async_trait] |
| pub trait GlobalPolicyCheckerTest<C> |
| where |
| C: ComponentInstanceInterface, |
| { |
| // Creates a `ComponentInstanceInterface` with the given `Moniker`. |
| async fn make_component(&self, moniker: Moniker) -> Arc<C>; |
| |
| // Tests `GlobalPolicyChecker::can_route_capability()` for framework capability sources. |
| async fn global_policy_checker_can_route_capability_framework_cap(&self) -> Result<(), Error> { |
| let mut policy_builder = CapabilityAllowlistPolicyBuilder::new(); |
| policy_builder.add_capability_policy( |
| CapabilityAllowlistKey { |
| source_moniker: ExtendedMoniker::ComponentInstance( |
| Moniker::try_from(vec!["foo", "bar"]).unwrap(), |
| ), |
| source_name: "fuchsia.component.Realm".parse().unwrap(), |
| source: CapabilityAllowlistSource::Framework, |
| capability: CapabilityTypeName::Protocol, |
| }, |
| vec![ |
| AllowlistEntryBuilder::new().exact("foo").exact("bar").build(), |
| AllowlistEntryBuilder::new().exact("foo").exact("bar").exact("baz").build(), |
| ], |
| ); |
| let global_policy_checker = GlobalPolicyChecker::new(Arc::new(policy_builder.build())); |
| let component = self.make_component(vec!["foo:0", "bar:0"].try_into().unwrap()).await; |
| |
| let protocol_capability = CapabilitySource::<C>::Framework { |
| capability: InternalCapability::Protocol("fuchsia.component.Realm".parse().unwrap()), |
| component: component.as_weak(), |
| }; |
| let valid_path_0 = Moniker::try_from(vec!["foo", "bar"]).unwrap(); |
| let valid_path_1 = Moniker::try_from(vec!["foo", "bar", "baz"]).unwrap(); |
| let invalid_path_0 = Moniker::try_from(vec!["foobar"]).unwrap(); |
| let invalid_path_1 = Moniker::try_from(vec!["foo", "bar", "foobar"]).unwrap(); |
| |
| assert_matches!( |
| global_policy_checker.can_route_capability(&protocol_capability, &valid_path_0), |
| Ok(()) |
| ); |
| assert_matches!( |
| global_policy_checker.can_route_capability(&protocol_capability, &valid_path_1), |
| Ok(()) |
| ); |
| assert_matches!( |
| global_policy_checker.can_route_capability(&protocol_capability, &invalid_path_0), |
| Err(_) |
| ); |
| assert_matches!( |
| global_policy_checker.can_route_capability(&protocol_capability, &invalid_path_1), |
| Err(_) |
| ); |
| Ok(()) |
| } |
| |
| // Tests `GlobalPolicyChecker::can_route_capability()` for namespace capability sources. |
| async fn global_policy_checker_can_route_capability_namespace_cap(&self) -> Result<(), Error> { |
| let mut policy_builder = CapabilityAllowlistPolicyBuilder::new(); |
| policy_builder.add_capability_policy( |
| CapabilityAllowlistKey { |
| source_moniker: ExtendedMoniker::ComponentManager, |
| source_name: "fuchsia.kernel.MmioResource".parse().unwrap(), |
| source: CapabilityAllowlistSource::Self_, |
| capability: CapabilityTypeName::Protocol, |
| }, |
| vec![ |
| AllowlistEntryBuilder::new().exact("root").build(), |
| AllowlistEntryBuilder::new().exact("root").exact("bootstrap").build(), |
| AllowlistEntryBuilder::new().exact("root").exact("core").build(), |
| ], |
| ); |
| let global_policy_checker = GlobalPolicyChecker::new(Arc::new(policy_builder.build())); |
| |
| let protocol_capability = CapabilitySource::<C>::Namespace { |
| capability: ComponentCapability::Protocol(ProtocolDecl { |
| name: "fuchsia.kernel.MmioResource".parse().unwrap(), |
| source_path: Some("/svc/fuchsia.kernel.MmioResource".parse().unwrap()), |
| delivery: Default::default(), |
| }), |
| top_instance: Weak::new(), |
| }; |
| let valid_path_0 = Moniker::try_from(vec!["root"]).unwrap(); |
| let valid_path_2 = Moniker::try_from(vec!["root", "core"]).unwrap(); |
| let valid_path_1 = Moniker::try_from(vec!["root", "bootstrap"]).unwrap(); |
| let invalid_path_0 = Moniker::try_from(vec!["foobar"]).unwrap(); |
| let invalid_path_1 = Moniker::try_from(vec!["foo", "bar", "foobar"]).unwrap(); |
| |
| assert_matches!( |
| global_policy_checker.can_route_capability(&protocol_capability, &valid_path_0), |
| Ok(()) |
| ); |
| assert_matches!( |
| global_policy_checker.can_route_capability(&protocol_capability, &valid_path_1), |
| Ok(()) |
| ); |
| assert_matches!( |
| global_policy_checker.can_route_capability(&protocol_capability, &valid_path_2), |
| Ok(()) |
| ); |
| assert_matches!( |
| global_policy_checker.can_route_capability(&protocol_capability, &invalid_path_0), |
| Err(_) |
| ); |
| assert_matches!( |
| global_policy_checker.can_route_capability(&protocol_capability, &invalid_path_1), |
| Err(_) |
| ); |
| Ok(()) |
| } |
| |
| // Tests `GlobalPolicyChecker::can_route_capability()` for component capability sources. |
| async fn global_policy_checker_can_route_capability_component_cap(&self) -> Result<(), Error> { |
| let mut policy_builder = CapabilityAllowlistPolicyBuilder::new(); |
| policy_builder.add_capability_policy( |
| CapabilityAllowlistKey { |
| source_moniker: ExtendedMoniker::ComponentInstance( |
| Moniker::try_from(vec!["foo"]).unwrap(), |
| ), |
| source_name: "fuchsia.foo.FooBar".parse().unwrap(), |
| source: CapabilityAllowlistSource::Self_, |
| capability: CapabilityTypeName::Protocol, |
| }, |
| vec![ |
| AllowlistEntryBuilder::new().exact("foo").build(), |
| AllowlistEntryBuilder::new().exact("root").exact("bootstrap").build(), |
| AllowlistEntryBuilder::new().exact("root").exact("core").build(), |
| ], |
| ); |
| let global_policy_checker = GlobalPolicyChecker::new(Arc::new(policy_builder.build())); |
| let component = self.make_component(vec!["foo:0"].try_into().unwrap()).await; |
| |
| let protocol_capability = CapabilitySource::<C>::Component { |
| capability: ComponentCapability::Protocol(ProtocolDecl { |
| name: "fuchsia.foo.FooBar".parse().unwrap(), |
| source_path: Some("/svc/fuchsia.foo.FooBar".parse().unwrap()), |
| delivery: Default::default(), |
| }), |
| component: component.as_weak(), |
| }; |
| let valid_path_0 = Moniker::try_from(vec!["root", "bootstrap"]).unwrap(); |
| let valid_path_1 = Moniker::try_from(vec!["root", "core"]).unwrap(); |
| let invalid_path_0 = Moniker::try_from(vec!["foobar"]).unwrap(); |
| let invalid_path_1 = Moniker::try_from(vec!["foo", "bar", "foobar"]).unwrap(); |
| |
| assert_matches!( |
| global_policy_checker.can_route_capability(&protocol_capability, &valid_path_0), |
| Ok(()) |
| ); |
| assert_matches!( |
| global_policy_checker.can_route_capability(&protocol_capability, &valid_path_1), |
| Ok(()) |
| ); |
| assert_matches!( |
| global_policy_checker.can_route_capability(&protocol_capability, &invalid_path_0), |
| Err(_) |
| ); |
| assert_matches!( |
| global_policy_checker.can_route_capability(&protocol_capability, &invalid_path_1), |
| Err(_) |
| ); |
| Ok(()) |
| } |
| |
| // Tests `GlobalPolicyChecker::can_route_capability()` for capability sources of type `Capability`. |
| async fn global_policy_checker_can_route_capability_capability_cap(&self) -> Result<(), Error> { |
| let mut policy_builder = CapabilityAllowlistPolicyBuilder::new(); |
| policy_builder.add_capability_policy( |
| CapabilityAllowlistKey { |
| source_moniker: ExtendedMoniker::ComponentInstance( |
| Moniker::try_from(vec!["foo"]).unwrap(), |
| ), |
| source_name: "cache".parse().unwrap(), |
| source: CapabilityAllowlistSource::Capability, |
| capability: CapabilityTypeName::Storage, |
| }, |
| vec![ |
| AllowlistEntryBuilder::new().exact("foo").build(), |
| AllowlistEntryBuilder::new().exact("root").exact("bootstrap").build(), |
| AllowlistEntryBuilder::new().exact("root").exact("core").build(), |
| ], |
| ); |
| let global_policy_checker = GlobalPolicyChecker::new(Arc::new(policy_builder.build())); |
| let component = self.make_component(vec!["foo:0"].try_into().unwrap()).await; |
| |
| let protocol_capability = CapabilitySource::<C>::Capability { |
| source_capability: ComponentCapability::Storage(StorageDecl { |
| backing_dir: "cache".parse().unwrap(), |
| name: "cache".parse().unwrap(), |
| source: StorageDirectorySource::Parent, |
| subdir: Default::default(), |
| storage_id: fdecl::StorageId::StaticInstanceIdOrMoniker, |
| }), |
| component: component.as_weak(), |
| }; |
| let valid_path_0 = Moniker::try_from(vec!["root", "bootstrap"]).unwrap(); |
| let valid_path_1 = Moniker::try_from(vec!["root", "core"]).unwrap(); |
| let invalid_path_0 = Moniker::try_from(vec!["foobar"]).unwrap(); |
| let invalid_path_1 = Moniker::try_from(vec!["foo", "bar", "foobar"]).unwrap(); |
| |
| assert_matches!( |
| global_policy_checker.can_route_capability(&protocol_capability, &valid_path_0), |
| Ok(()) |
| ); |
| assert_matches!( |
| global_policy_checker.can_route_capability(&protocol_capability, &valid_path_1), |
| Ok(()) |
| ); |
| assert_matches!( |
| global_policy_checker.can_route_capability(&protocol_capability, &invalid_path_0), |
| Err(_) |
| ); |
| assert_matches!( |
| global_policy_checker.can_route_capability(&protocol_capability, &invalid_path_1), |
| Err(_) |
| ); |
| Ok(()) |
| } |
| |
| // Tests `GlobalPolicyChecker::can_route_debug_capability()` for capability sources of type `Capability`. |
| async fn global_policy_checker_can_route_debug_capability_capability_cap( |
| &self, |
| ) -> Result<(), Error> { |
| let mut policy_builder = CapabilityAllowlistPolicyBuilder::new(); |
| policy_builder.add_debug_capability_policy( |
| DebugCapabilityKey { |
| name: "debug_service1".parse().unwrap(), |
| source: CapabilityAllowlistSource::Self_, |
| capability: CapabilityTypeName::Protocol, |
| env_name: "foo_env".parse().unwrap(), |
| }, |
| AllowlistEntryBuilder::new().exact("foo").build(), |
| ); |
| policy_builder.add_debug_capability_policy( |
| DebugCapabilityKey { |
| name: "debug_service1".parse().unwrap(), |
| source: CapabilityAllowlistSource::Self_, |
| capability: CapabilityTypeName::Protocol, |
| env_name: "bootstrap_env".parse().unwrap(), |
| }, |
| AllowlistEntryBuilder::new().exact("root").exact("bootstrap").build(), |
| ); |
| let global_policy_checker = GlobalPolicyChecker::new(Arc::new(policy_builder.build())); |
| let protocol_name: Name = "debug_service1".parse().unwrap(); |
| |
| let valid_cases = vec![ |
| (Moniker::try_from(vec!["root", "bootstrap"]).unwrap(), "bootstrap_env"), |
| (Moniker::try_from(vec!["foo"]).unwrap(), "foo_env"), |
| ]; |
| |
| let invalid_cases = vec![ |
| (Moniker::try_from(vec!["foobar"]).unwrap(), "foobar_env"), |
| (Moniker::try_from(vec!["foo", "bar", "foobar"]).unwrap(), "foobar_env"), |
| (Moniker::try_from(vec!["root", "bootstrap"]).unwrap(), "foo_env"), |
| (Moniker::try_from(vec!["root", "baz"]).unwrap(), "foo_env"), |
| ]; |
| |
| for valid_case in valid_cases { |
| assert_matches!( |
| global_policy_checker.can_register_debug_capability( |
| CapabilityTypeName::Protocol, |
| &protocol_name, |
| &valid_case.0, |
| &valid_case.1.parse().unwrap(), |
| ), |
| Ok(()), |
| "{:?}", |
| valid_case |
| ); |
| } |
| |
| for invalid_case in invalid_cases { |
| assert_matches!( |
| global_policy_checker.can_register_debug_capability( |
| CapabilityTypeName::Protocol, |
| &protocol_name, |
| &invalid_case.0, |
| &invalid_case.1.parse().unwrap(), |
| ), |
| Err(_), |
| "{:?}", |
| invalid_case |
| ); |
| } |
| |
| Ok(()) |
| } |
| |
| // Tests `GlobalPolicyChecker::can_route_debug_capability()` for capability sources of type |
| // `Capability` with realm allowlist entries. |
| async fn global_policy_checker_can_route_debug_capability_with_realm_allowlist_entry( |
| &self, |
| ) -> Result<(), Error> { |
| let mut policy_builder = CapabilityAllowlistPolicyBuilder::new(); |
| policy_builder.add_debug_capability_policy( |
| DebugCapabilityKey { |
| name: "debug_service1".parse().unwrap(), |
| source: CapabilityAllowlistSource::Self_, |
| capability: CapabilityTypeName::Protocol, |
| env_name: "bar_env".parse().unwrap(), |
| }, |
| AllowlistEntryBuilder::new().exact("root").exact("bootstrap1").any_descendant(), |
| ); |
| policy_builder.add_debug_capability_policy( |
| DebugCapabilityKey { |
| name: "debug_service1".parse().unwrap(), |
| source: CapabilityAllowlistSource::Self_, |
| capability: CapabilityTypeName::Protocol, |
| env_name: "foo_env".parse().unwrap(), |
| }, |
| AllowlistEntryBuilder::new().exact("root").exact("bootstrap2").build(), |
| ); |
| policy_builder.add_debug_capability_policy( |
| DebugCapabilityKey { |
| name: "debug_service1".parse().unwrap(), |
| source: CapabilityAllowlistSource::Self_, |
| capability: CapabilityTypeName::Protocol, |
| env_name: "baz_env".parse().unwrap(), |
| }, |
| AllowlistEntryBuilder::new().exact("root").exact("bootstrap3").any_descendant(), |
| ); |
| let global_policy_checker = GlobalPolicyChecker::new(Arc::new(policy_builder.build())); |
| |
| // dest, env |
| let valid_cases = vec![ |
| (vec!["root", "bootstrap1", "child"], "bar_env".to_string()), |
| (vec!["root", "bootstrap1", "child", "grandchild"], "bar_env".to_string()), |
| (vec!["root", "bootstrap2"], "foo_env".to_string()), |
| (vec!["root", "bootstrap3", "child"], "baz_env".to_string()), |
| (vec!["root", "bootstrap3", "child", "grandchild"], "baz_env".to_string()), |
| ]; |
| |
| let invalid_cases = vec![ |
| (vec!["root", "not_bootstrap"], "bar_env".to_string()), |
| (vec!["root", "not_bootstrap"], "foo_env".to_string()), |
| (vec!["root", "bootstrap1"], "baz_env".to_string()), |
| ]; |
| |
| for (dest, env) in valid_cases { |
| let protocol_name: Name = "debug_service1".parse().unwrap(); |
| let env: Name = env.parse().unwrap(); |
| assert_matches!( |
| global_policy_checker.can_register_debug_capability( |
| CapabilityTypeName::Protocol, |
| &protocol_name, |
| &Moniker::try_from(dest.clone()).unwrap(), |
| &env, |
| ), |
| Ok(()), |
| "{:?}", |
| (dest, env) |
| ); |
| } |
| |
| for (dest, env) in invalid_cases { |
| let protocol_name: Name = "debug_service1".parse().unwrap(); |
| let env: Name = env.parse().unwrap(); |
| assert_matches!( |
| global_policy_checker.can_register_debug_capability( |
| CapabilityTypeName::Protocol, |
| &protocol_name, |
| &Moniker::try_from(dest.clone()).unwrap(), |
| &env, |
| ), |
| Err(_), |
| "{:?}", |
| (dest, env) |
| ); |
| } |
| |
| Ok(()) |
| } |
| |
| // Tests `GlobalPolicyChecker::can_route_debug_capability()` for capability sources of type |
| // `Capability` with collection allowlist entries. |
| async fn global_policy_checker_can_route_debug_capability_with_collection_allowlist_entry( |
| &self, |
| ) -> Result<(), Error> { |
| let mut policy_builder = CapabilityAllowlistPolicyBuilder::new(); |
| policy_builder.add_debug_capability_policy( |
| DebugCapabilityKey { |
| name: "debug_service1".parse().unwrap(), |
| source: CapabilityAllowlistSource::Self_, |
| capability: CapabilityTypeName::Protocol, |
| env_name: "bar_env".parse().unwrap(), |
| }, |
| AllowlistEntryBuilder::new() |
| .exact("root") |
| .exact("bootstrap") |
| .any_descendant_in_collection("coll1"), |
| ); |
| policy_builder.add_debug_capability_policy( |
| DebugCapabilityKey { |
| name: "debug_service1".parse().unwrap(), |
| source: CapabilityAllowlistSource::Self_, |
| capability: CapabilityTypeName::Protocol, |
| env_name: "foo_env".parse().unwrap(), |
| }, |
| AllowlistEntryBuilder::new().exact("root").exact("bootstrap2").build(), |
| ); |
| policy_builder.add_debug_capability_policy( |
| DebugCapabilityKey { |
| name: "debug_service1".parse().unwrap(), |
| source: CapabilityAllowlistSource::Self_, |
| capability: CapabilityTypeName::Protocol, |
| env_name: "baz_env".parse().unwrap(), |
| }, |
| AllowlistEntryBuilder::new() |
| .exact("root") |
| .exact("bootstrap3") |
| .any_descendant_in_collection("coll4"), |
| ); |
| let global_policy_checker = GlobalPolicyChecker::new(Arc::new(policy_builder.build())); |
| |
| // dest, env |
| let valid_cases = vec![ |
| (vec!["root", "bootstrap", "coll1:instance1"], "bar_env".to_string()), |
| (vec!["root", "bootstrap", "coll1:instance1", "child"], "bar_env".to_string()), |
| (vec!["root", "bootstrap2"], "foo_env".to_string()), |
| (vec!["root", "bootstrap3", "coll4:instance4"], "baz_env".to_string()), |
| (vec!["root", "bootstrap3", "coll4:instance4", "child"], "baz_env".to_string()), |
| ]; |
| |
| let invalid_cases = vec![ |
| (vec!["root", "bootstrap"], "bar_env".to_string()), |
| (vec!["root", "not_bootstrap"], "bar_env".to_string()), |
| (vec!["root", "not_bootstrap"], "foo_env".to_string()), |
| (vec!["root", "bootstrap3", "child"], "baz_env".to_string()), |
| (vec!["root", "bootstrap"], "baz_env".to_string()), |
| ]; |
| |
| for (dest, env) in valid_cases { |
| let protocol_name: Name = "debug_service1".parse().unwrap(); |
| let env: Name = env.parse().unwrap(); |
| assert_matches!( |
| global_policy_checker.can_register_debug_capability( |
| CapabilityTypeName::Protocol, |
| &protocol_name, |
| &Moniker::try_from(dest.clone()).unwrap(), |
| &env, |
| ), |
| Ok(()), |
| "{:?}", |
| (dest, env) |
| ); |
| } |
| |
| for (dest, env) in invalid_cases { |
| let protocol_name: Name = "debug_service1".parse().unwrap(); |
| let env: Name = env.parse().unwrap(); |
| assert_matches!( |
| global_policy_checker.can_register_debug_capability( |
| CapabilityTypeName::Protocol, |
| &protocol_name, |
| &Moniker::try_from(dest.clone()).unwrap(), |
| &env, |
| ), |
| Err(_), |
| "{:?}", |
| (dest, env) |
| ); |
| } |
| |
| Ok(()) |
| } |
| |
| // Tests `GlobalPolicyChecker::can_route_capability()` for builtin capabilities. |
| async fn global_policy_checker_can_route_capability_builtin_cap(&self) -> Result<(), Error> { |
| let mut policy_builder = CapabilityAllowlistPolicyBuilder::new(); |
| policy_builder.add_capability_policy( |
| CapabilityAllowlistKey { |
| source_moniker: ExtendedMoniker::ComponentManager, |
| source_name: "test".parse().unwrap(), |
| source: CapabilityAllowlistSource::Self_, |
| capability: CapabilityTypeName::Directory, |
| }, |
| vec![ |
| AllowlistEntryBuilder::new().exact("root").build(), |
| AllowlistEntryBuilder::new().exact("root").exact("core").build(), |
| ], |
| ); |
| let global_policy_checker = GlobalPolicyChecker::new(Arc::new(policy_builder.build())); |
| |
| let dir_capability = CapabilitySource::<C>::Builtin { |
| capability: InternalCapability::Directory("test".parse().unwrap()), |
| top_instance: Weak::new(), |
| }; |
| let valid_path_0 = Moniker::try_from(vec!["root"]).unwrap(); |
| let valid_path_1 = Moniker::try_from(vec!["root", "core"]).unwrap(); |
| let invalid_path_0 = Moniker::try_from(vec!["foobar"]).unwrap(); |
| let invalid_path_1 = Moniker::try_from(vec!["foo", "bar", "foobar"]).unwrap(); |
| |
| assert_matches!( |
| global_policy_checker.can_route_capability(&dir_capability, &valid_path_0), |
| Ok(()) |
| ); |
| assert_matches!( |
| global_policy_checker.can_route_capability(&dir_capability, &valid_path_1), |
| Ok(()) |
| ); |
| assert_matches!( |
| global_policy_checker.can_route_capability(&dir_capability, &invalid_path_0), |
| Err(_) |
| ); |
| assert_matches!( |
| global_policy_checker.can_route_capability(&dir_capability, &invalid_path_1), |
| Err(_) |
| ); |
| Ok(()) |
| } |
| |
| // Tests `GlobalPolicyChecker::can_route_capability()` for policy that includes non-exact |
| // `AllowlistEntry::Realm` entries. |
| async fn global_policy_checker_can_route_capability_with_realm_allowlist_entry( |
| &self, |
| ) -> Result<(), Error> { |
| let mut policy_builder = CapabilityAllowlistPolicyBuilder::new(); |
| policy_builder.add_capability_policy( |
| CapabilityAllowlistKey { |
| source_moniker: ExtendedMoniker::ComponentManager, |
| source_name: "fuchsia.kernel.MmioResource".parse().unwrap(), |
| source: CapabilityAllowlistSource::Self_, |
| capability: CapabilityTypeName::Protocol, |
| }, |
| vec![ |
| AllowlistEntryBuilder::new().exact("tests").any_descendant(), |
| AllowlistEntryBuilder::new().exact("core").exact("tests").any_descendant(), |
| ], |
| ); |
| let global_policy_checker = GlobalPolicyChecker::new(Arc::new(policy_builder.build())); |
| let protocol_capability = CapabilitySource::<C>::Namespace { |
| capability: ComponentCapability::Protocol(ProtocolDecl { |
| name: "fuchsia.kernel.MmioResource".parse().unwrap(), |
| source_path: Some("/svc/fuchsia.kernel.MmioResource".parse().unwrap()), |
| delivery: Default::default(), |
| }), |
| top_instance: Weak::new(), |
| }; |
| |
| macro_rules! can_route { |
| ($moniker:expr) => { |
| global_policy_checker.can_route_capability(&protocol_capability, $moniker) |
| }; |
| } |
| |
| assert!(can_route!(&Moniker::try_from(vec!["tests", "test1"]).unwrap()).is_ok()); |
| assert!(can_route!(&Moniker::try_from(vec!["tests", "coll:test1"]).unwrap()).is_ok()); |
| assert!(can_route!(&Moniker::try_from(vec!["tests", "test1", "util"]).unwrap()).is_ok()); |
| assert!(can_route!(&Moniker::try_from(vec!["tests", "test2"]).unwrap()).is_ok()); |
| assert!(can_route!(&Moniker::try_from(vec!["core", "tests", "test"]).unwrap()).is_ok()); |
| assert!(can_route!(&Moniker::try_from(vec!["core", "tests", "coll:t"]).unwrap()).is_ok()); |
| |
| assert!(can_route!(&Moniker::try_from(vec!["foo"]).unwrap()).is_err()); |
| assert!(can_route!(&Moniker::try_from(vec!["tests"]).unwrap()).is_err()); |
| assert!(can_route!(&Moniker::try_from(vec!["core", "foo"]).unwrap()).is_err()); |
| assert!(can_route!(&Moniker::try_from(vec!["core", "tests"]).unwrap()).is_err()); |
| assert!(can_route!(&Moniker::try_from(vec!["core", "tests:test"]).unwrap()).is_err()); |
| Ok(()) |
| } |
| |
| // Tests `GlobalPolicyChecker::can_route_capability()` for policy that includes non-exact |
| // `AllowlistEntry::Collection` entries. |
| async fn global_policy_checker_can_route_capability_with_collection_allowlist_entry( |
| &self, |
| ) -> Result<(), Error> { |
| let mut policy_builder = CapabilityAllowlistPolicyBuilder::new(); |
| policy_builder.add_capability_policy( |
| CapabilityAllowlistKey { |
| source_moniker: ExtendedMoniker::ComponentManager, |
| source_name: "fuchsia.kernel.MmioResource".parse().unwrap(), |
| source: CapabilityAllowlistSource::Self_, |
| capability: CapabilityTypeName::Protocol, |
| }, |
| vec![ |
| AllowlistEntryBuilder::new().any_descendant_in_collection("tests"), |
| AllowlistEntryBuilder::new().exact("core").any_descendant_in_collection("tests"), |
| ], |
| ); |
| let global_policy_checker = GlobalPolicyChecker::new(Arc::new(policy_builder.build())); |
| let protocol_capability = CapabilitySource::<C>::Namespace { |
| capability: ComponentCapability::Protocol(ProtocolDecl { |
| name: "fuchsia.kernel.MmioResource".parse().unwrap(), |
| source_path: Some("/svc/fuchsia.kernel.MmioResource".parse().unwrap()), |
| delivery: Default::default(), |
| }), |
| top_instance: Weak::new(), |
| }; |
| |
| macro_rules! can_route { |
| ($moniker:expr) => { |
| global_policy_checker.can_route_capability(&protocol_capability, $moniker) |
| }; |
| } |
| |
| assert!(can_route!(&Moniker::try_from(vec!["tests:t1"]).unwrap()).is_ok()); |
| assert!(can_route!(&Moniker::try_from(vec!["tests:t2"]).unwrap()).is_ok()); |
| assert!(can_route!(&Moniker::try_from(vec!["tests:t1", "util"]).unwrap()).is_ok()); |
| assert!(can_route!(&Moniker::try_from(vec!["core", "tests:t1"]).unwrap()).is_ok()); |
| assert!(can_route!(&Moniker::try_from(vec!["core", "tests:t2"]).unwrap()).is_ok()); |
| |
| assert!(can_route!(&Moniker::try_from(vec!["foo"]).unwrap()).is_err()); |
| assert!(can_route!(&Moniker::try_from(vec!["tests"]).unwrap()).is_err()); |
| assert!(can_route!(&Moniker::try_from(vec!["coll:foo"]).unwrap()).is_err()); |
| assert!(can_route!(&Moniker::try_from(vec!["core", "foo"]).unwrap()).is_err()); |
| assert!(can_route!(&Moniker::try_from(vec!["core", "coll:tests"]).unwrap()).is_err()); |
| Ok(()) |
| } |
| } |
| |
| // Creates a SecurityPolicy based on the capability allowlist entries provided during |
| // construction. |
| struct CapabilityAllowlistPolicyBuilder { |
| capability_policy: HashMap<CapabilityAllowlistKey, HashSet<AllowlistEntry>>, |
| debug_capability_policy: HashMap<DebugCapabilityKey, HashSet<DebugCapabilityAllowlistEntry>>, |
| } |
| |
| impl CapabilityAllowlistPolicyBuilder { |
| pub fn new() -> Self { |
| Self { capability_policy: HashMap::new(), debug_capability_policy: HashMap::new() } |
| } |
| |
| /// Add a new entry to the configuration. |
| pub fn add_capability_policy<'a>( |
| &'a mut self, |
| key: CapabilityAllowlistKey, |
| value: Vec<AllowlistEntry>, |
| ) -> &'a mut Self { |
| let value_set = HashSet::from_iter(value.iter().cloned()); |
| self.capability_policy.insert(key, value_set); |
| self |
| } |
| |
| /// Add a new entry to the configuration. |
| pub fn add_debug_capability_policy<'a>( |
| &'a mut self, |
| key: DebugCapabilityKey, |
| dest: AllowlistEntry, |
| ) -> &'a mut Self { |
| self.debug_capability_policy |
| .entry(key) |
| .or_default() |
| .insert(DebugCapabilityAllowlistEntry::new(dest)); |
| self |
| } |
| |
| /// Creates a configuration from the provided policies. |
| pub fn build(&self) -> SecurityPolicy { |
| SecurityPolicy { |
| job_policy: JobPolicyAllowlists { |
| ambient_mark_vmo_exec: vec![], |
| main_process_critical: vec![], |
| create_raw_processes: vec![], |
| }, |
| capability_policy: self.capability_policy.clone(), |
| debug_capability_policy: self.debug_capability_policy.clone(), |
| child_policy: ChildPolicyAllowlists { reboot_on_terminate: vec![] }, |
| ..Default::default() |
| } |
| } |
| } |