blob: 3098deeb66cfe667e9c8da81101356b2a2238d8f [file] [log] [blame]
// Copyright 2020 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::{
capability_source::CapabilitySourceInterface,
component_instance::ComponentInstanceInterface,
config::{
AllowlistEntry, CapabilityAllowlistKey, CapabilityAllowlistSource, RuntimeConfig,
},
},
fuchsia_zircon_status as zx,
log::{error, warn},
moniker::{
AbsoluteMoniker, ChildMonikerBase, ExtendedMoniker, RelativeMoniker, RelativeMonikerBase,
},
std::sync::{Arc, Weak},
thiserror::Error,
};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
/// Errors returned by the PolicyChecker and the ScopedPolicyChecker.
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize), serde(rename_all = "snake_case"))]
#[derive(Debug, Clone, Error, PartialEq)]
pub enum PolicyError {
#[error("security policy was unavailable to check")]
PolicyUnavailable,
#[error("feature \"{policy}\" used by \"{moniker}\" is not supported by this instance of component manager")]
Unsupported { policy: String, moniker: AbsoluteMoniker },
#[error("security policy disallows \"{policy}\" job policy for \"{moniker}\"")]
JobPolicyDisallowed { policy: String, moniker: AbsoluteMoniker },
#[error("security policy disallows \"{policy}\" child policy for \"{moniker}\"")]
ChildPolicyDisallowed { policy: String, moniker: AbsoluteMoniker },
#[error("security policy was unable to extract the source from the routed capability")]
InvalidCapabilitySource,
#[error("security policy disallows \"{cap}\" from \"{source_moniker}\" being used at \"{target_moniker}\"")]
CapabilityUseDisallowed {
cap: String,
source_moniker: ExtendedMoniker,
target_moniker: AbsoluteMoniker,
},
#[error("debug security policy disallows \"{cap}\" from \"{source_moniker}\" being routed from environment \"{env_moniker}:{env_name}\" to \"{target_moniker}\"")]
DebugCapabilityUseDisallowed {
cap: String,
source_moniker: ExtendedMoniker,
env_moniker: AbsoluteMoniker,
env_name: String,
target_moniker: AbsoluteMoniker,
},
}
impl PolicyError {
fn unsupported(policy: impl Into<String>, moniker: &AbsoluteMoniker) -> Self {
PolicyError::Unsupported { policy: policy.into(), moniker: moniker.clone() }
}
fn job_policy_disallowed(policy: impl Into<String>, moniker: &AbsoluteMoniker) -> Self {
PolicyError::JobPolicyDisallowed { policy: policy.into(), moniker: moniker.clone() }
}
fn child_policy_disallowed(policy: impl Into<String>, moniker: &AbsoluteMoniker) -> Self {
PolicyError::ChildPolicyDisallowed { policy: policy.into(), moniker: moniker.clone() }
}
fn capability_use_disallowed(
cap: impl Into<String>,
source_moniker: &ExtendedMoniker,
target_moniker: &AbsoluteMoniker,
) -> Self {
PolicyError::CapabilityUseDisallowed {
cap: cap.into(),
source_moniker: source_moniker.clone(),
target_moniker: target_moniker.clone(),
}
}
fn debug_capability_use_disallowed(
cap: impl Into<String>,
source_moniker: &ExtendedMoniker,
env_moniker: &AbsoluteMoniker,
env_name: impl Into<String>,
target_moniker: &AbsoluteMoniker,
) -> Self {
PolicyError::DebugCapabilityUseDisallowed {
cap: cap.into(),
source_moniker: source_moniker.clone(),
env_moniker: env_moniker.clone(),
env_name: env_name.into(),
target_moniker: target_moniker.clone(),
}
}
/// Convert this error into its approximate `zx::Status` equivalent.
pub fn as_zx_status(&self) -> zx::Status {
zx::Status::ACCESS_DENIED
}
}
/// Evaluates security policy globally across the entire Model and all components.
/// This is used to enforce runtime capability routing restrictions across all
/// components to prevent high privilleged capabilities from being routed to
/// components outside of the list defined in the runtime configs security
/// policy.
#[derive(Clone, Debug, Default)]
pub struct GlobalPolicyChecker {
/// The runtime configuration containing the security policy to apply.
config: Arc<RuntimeConfig>,
}
impl GlobalPolicyChecker {
/// Constructs a new PolicyChecker object configured by the
/// RuntimeConfig::SecurityPolicy.
pub fn new(config: Arc<RuntimeConfig>) -> Self {
Self { config: config }
}
fn get_policy_key<'a, C>(
capability_source: &'a CapabilitySourceInterface<C>,
) -> Result<CapabilityAllowlistKey, PolicyError>
where
C: ComponentInstanceInterface,
{
Ok(match &capability_source {
CapabilitySourceInterface::Namespace { capability, .. } => CapabilityAllowlistKey {
source_moniker: ExtendedMoniker::ComponentManager,
source_name: capability
.source_name()
.ok_or(PolicyError::InvalidCapabilitySource)?
.clone(),
source: CapabilityAllowlistSource::Self_,
capability: capability.type_name(),
},
CapabilitySourceInterface::Component { capability, component }
| CapabilitySourceInterface::FilteredService { capability, component, .. } => {
CapabilityAllowlistKey {
source_moniker: ExtendedMoniker::ComponentInstance(
component.abs_moniker.clone(),
),
source_name: capability
.source_name()
.ok_or(PolicyError::InvalidCapabilitySource)?
.clone(),
source: CapabilityAllowlistSource::Self_,
capability: capability.type_name(),
}
}
CapabilitySourceInterface::Builtin { capability, .. } => CapabilityAllowlistKey {
source_moniker: ExtendedMoniker::ComponentManager,
source_name: capability.source_name().clone(),
source: CapabilityAllowlistSource::Self_,
capability: capability.type_name(),
},
CapabilitySourceInterface::Framework { capability, component } => {
CapabilityAllowlistKey {
source_moniker: ExtendedMoniker::ComponentInstance(
component.abs_moniker.clone(),
),
source_name: capability.source_name().clone(),
source: CapabilityAllowlistSource::Framework,
capability: capability.type_name(),
}
}
CapabilitySourceInterface::Capability { source_capability, component } => {
CapabilityAllowlistKey {
source_moniker: ExtendedMoniker::ComponentInstance(
component.abs_moniker.clone(),
),
source_name: source_capability
.source_name()
.ok_or(PolicyError::InvalidCapabilitySource)?
.clone(),
source: CapabilityAllowlistSource::Capability,
capability: source_capability.type_name(),
}
}
CapabilitySourceInterface::Collection { capability, component, .. } => {
CapabilityAllowlistKey {
source_moniker: ExtendedMoniker::ComponentInstance(
component.abs_moniker.clone(),
),
source_name: capability.source_name().clone(),
source: CapabilityAllowlistSource::Self_,
capability: capability.type_name(),
}
}
})
}
/// Returns Ok(()) if the provided capability source can be routed to the
/// given target_moniker, else a descriptive PolicyError.
pub fn can_route_capability<'a, C>(
&self,
capability_source: &'a CapabilitySourceInterface<C>,
target_moniker: &'a AbsoluteMoniker,
) -> Result<(), PolicyError>
where
C: ComponentInstanceInterface,
{
let policy_key = Self::get_policy_key(capability_source).map_err(|e| {
error!("Security policy could not generate a policy key for `{}`", capability_source);
e
})?;
match self.config.security_policy.capability_policy.get(&policy_key) {
Some(entries) => {
// Use the HashSet to find any exact matches quickly.
if entries.contains(&AllowlistEntry::Exact(target_moniker.clone())) {
return Ok(());
}
// Otherwise linear search for any non-exact matches.
if entries.iter().any(|entry| allowlist_entry_matches(entry, &target_moniker)) {
Ok(())
} else {
warn!(
"Security policy prevented `{}` from `{}` being routed to `{}`.",
policy_key.source_name, policy_key.source_moniker, target_moniker
);
Err(PolicyError::capability_use_disallowed(
policy_key.source_name.str(),
&policy_key.source_moniker,
&target_moniker,
))
}
}
None => Ok(()),
}
}
/// Returns Ok(()) if the provided debug capability source is allowed to be routed from given
/// environment.
pub fn can_route_debug_capability<'a, C>(
&self,
capability_source: &'a CapabilitySourceInterface<C>,
env_moniker: &'a AbsoluteMoniker,
env_name: &'a str,
target_moniker: &'a AbsoluteMoniker,
) -> Result<(), PolicyError>
where
C: ComponentInstanceInterface,
{
let policy_key = Self::get_policy_key(capability_source).map_err(|e| {
error!("Security policy could not generate a policy key for `{}`", capability_source);
e
})?;
if let Some(allowed_envs) =
self.config.security_policy.debug_capability_policy.get(&policy_key)
{
if let Some(_) = allowed_envs.get(&(env_moniker.clone(), env_name.to_string())) {
return Ok(());
}
}
warn!(
"Debug security policy prevented `{}` from `{}` being routed to `{}`.",
policy_key.source_name, policy_key.source_moniker, target_moniker
);
Err(PolicyError::debug_capability_use_disallowed(
policy_key.source_name.str(),
&policy_key.source_moniker,
&env_moniker,
env_name,
&target_moniker,
))
}
/// Returns Ok(()) if `target_moniker` is allowed to have `on_terminate=REBOOT` set.
pub fn reboot_on_terminate_allowed(
&self,
target_moniker: &AbsoluteMoniker,
) -> Result<(), PolicyError> {
if !self.config.reboot_on_terminate_enabled {
return Err(PolicyError::unsupported("reboot_on_terminate", &target_moniker));
}
if self
.config
.security_policy
.child_policy
.reboot_on_terminate
.iter()
.any(|entry| allowlist_entry_matches(entry, &target_moniker))
{
Ok(())
} else {
Err(PolicyError::child_policy_disallowed("reboot_on_terminate", &target_moniker))
}
}
}
fn allowlist_entry_matches(
allowlist_entry: &AllowlistEntry,
target_moniker: &AbsoluteMoniker,
) -> bool {
match allowlist_entry {
AllowlistEntry::Exact(moniker) => {
// An exact absolute moniker must match everything but the instance ID,
// which won't be deterministic in a dynamic collection of components.
moniker == target_moniker
}
AllowlistEntry::Realm(realm) => {
// For a Realm entry we are looking for the target_moniker to be
// contained in the realm (i.e. empty up path) and the down_path to be
// non-empty (i.e. children are allowed but not the realm itself).
let relative = RelativeMoniker::from_absolute(realm, target_moniker);
relative.up_path().is_empty() && !relative.down_path().is_empty()
}
AllowlistEntry::Collection(realm, collection) => {
// For a Collection entry we are looking for the target_moniker to be
// contained in the realm (i.e. empty up path) and that the first element of
// the down path is in a collection with a matching name.
let relative = RelativeMoniker::from_absolute(realm, target_moniker);
relative.up_path().is_empty()
&& relative
.down_path()
.first()
.map_or(false, |first| first.collection() == Some(collection))
}
}
}
/// Evaluates security policy relative to a specific Component (based on that Component's
/// AbsoluteMoniker).
pub struct ScopedPolicyChecker {
/// The runtime configuration containing the security policy to apply.
config: Weak<RuntimeConfig>,
/// The absolute moniker of the component that policy will be evaluated for.
moniker: AbsoluteMoniker,
}
impl ScopedPolicyChecker {
pub fn new(config: Weak<RuntimeConfig>, moniker: AbsoluteMoniker) -> Self {
ScopedPolicyChecker { config, moniker }
}
pub fn get_scope(&self) -> &AbsoluteMoniker {
&self.moniker
}
// This interface is super simple for now since there's only three allowlists. In the future
// we'll probably want a different interface than an individual function per policy item.
pub fn ambient_mark_vmo_exec_allowed(&self) -> Result<(), PolicyError> {
let config = self.config.upgrade().ok_or(PolicyError::PolicyUnavailable)?;
if config
.security_policy
.job_policy
.ambient_mark_vmo_exec
.iter()
.any(|entry| allowlist_entry_matches(entry, &self.moniker))
{
Ok(())
} else {
Err(PolicyError::job_policy_disallowed("ambient_mark_vmo_exec", &self.moniker))
}
}
pub fn main_process_critical_allowed(&self) -> Result<(), PolicyError> {
let config = self.config.upgrade().ok_or(PolicyError::PolicyUnavailable)?;
if config
.security_policy
.job_policy
.main_process_critical
.iter()
.any(|entry| allowlist_entry_matches(entry, &self.moniker))
{
Ok(())
} else {
Err(PolicyError::job_policy_disallowed("main_process_critical", &self.moniker))
}
}
pub fn create_raw_processes_allowed(&self) -> Result<(), PolicyError> {
let config = self.config.upgrade().ok_or(PolicyError::PolicyUnavailable)?;
if config
.security_policy
.job_policy
.create_raw_processes
.iter()
.any(|entry| allowlist_entry_matches(entry, &self.moniker))
{
Ok(())
} else {
Err(PolicyError::job_policy_disallowed("create_raw_processes", &self.moniker))
}
}
}
#[cfg(test)]
mod tests {
use {
super::*,
crate::config::{
ChildPolicyAllowlists, JobPolicyAllowlists, RuntimeConfig, SecurityPolicy,
},
assert_matches::assert_matches,
moniker::{AbsoluteMoniker, AbsoluteMonikerBase, ChildMoniker},
std::collections::HashMap,
};
#[test]
fn allowlist_entry_checker() {
let root = AbsoluteMoniker::root();
let allowed = AbsoluteMoniker::from(vec!["foo", "bar"]);
let disallowed_child_of_allowed = AbsoluteMoniker::from(vec!["foo", "bar", "baz"]);
let disallowed = AbsoluteMoniker::from(vec!["baz", "fiz"]);
let allowlist_exact = AllowlistEntry::Exact(allowed.clone());
assert!(allowlist_entry_matches(&allowlist_exact, &allowed));
assert!(!allowlist_entry_matches(&allowlist_exact, &root));
assert!(!allowlist_entry_matches(&allowlist_exact, &disallowed));
assert!(!allowlist_entry_matches(&allowlist_exact, &disallowed_child_of_allowed));
let allowed_realm_root = AbsoluteMoniker::from(vec!["qux"]);
let allowed_child_of_realm = AbsoluteMoniker::from(vec!["qux", "quux"]);
let allowed_nested_child_of_realm = AbsoluteMoniker::from(vec!["qux", "quux", "foo"]);
let allowlist_realm = AllowlistEntry::Realm(allowed_realm_root.clone());
assert!(!allowlist_entry_matches(&allowlist_realm, &allowed_realm_root));
assert!(allowlist_entry_matches(&allowlist_realm, &allowed_child_of_realm));
assert!(allowlist_entry_matches(&allowlist_realm, &allowed_nested_child_of_realm));
assert!(!allowlist_entry_matches(&allowlist_realm, &disallowed));
assert!(!allowlist_entry_matches(&allowlist_realm, &root));
let collection_holder = AbsoluteMoniker::from(vec!["corge"]);
let collection_child = AbsoluteMoniker::from(vec!["corge", "collection:child"]);
let collection_nested_child =
AbsoluteMoniker::from(vec!["corge", "collection:child", "inner-child"]);
let non_collection_child = AbsoluteMoniker::from(vec!["corge", "grault"]);
let allowlist_collection =
AllowlistEntry::Collection(collection_holder.clone(), "collection".into());
assert!(!allowlist_entry_matches(&allowlist_collection, &collection_holder));
assert!(allowlist_entry_matches(&allowlist_collection, &collection_child));
assert!(allowlist_entry_matches(&allowlist_collection, &collection_nested_child));
assert!(!allowlist_entry_matches(&allowlist_collection, &non_collection_child));
assert!(!allowlist_entry_matches(&allowlist_collection, &disallowed));
assert!(!allowlist_entry_matches(&allowlist_collection, &root));
}
#[test]
fn scoped_policy_checker_vmex() {
macro_rules! assert_vmex_allowed_matches {
($config:expr, $moniker:expr, $expected:pat) => {
let result = ScopedPolicyChecker::new($config.clone(), $moniker.clone())
.ambient_mark_vmo_exec_allowed();
assert_matches!(result, $expected);
};
}
macro_rules! assert_vmex_disallowed {
($config:expr, $moniker:expr) => {
assert_vmex_allowed_matches!(
$config,
$moniker,
Err(PolicyError::JobPolicyDisallowed { .. })
);
};
}
let strong_config = Arc::new(RuntimeConfig::default());
let config = Arc::downgrade(&strong_config);
assert_vmex_disallowed!(config, AbsoluteMoniker::root());
assert_vmex_disallowed!(config, AbsoluteMoniker::from(vec!["foo"]));
let allowed1 = AbsoluteMoniker::from(vec!["foo", "bar"]);
let allowed2 = AbsoluteMoniker::from(vec!["baz", "fiz"]);
let strong_config = Arc::new(RuntimeConfig {
security_policy: SecurityPolicy {
job_policy: JobPolicyAllowlists {
ambient_mark_vmo_exec: vec![
AllowlistEntry::Exact(allowed1.clone()),
AllowlistEntry::Exact(allowed2.clone()),
],
main_process_critical: vec![
AllowlistEntry::Exact(allowed1.clone()),
AllowlistEntry::Exact(allowed2.clone()),
],
create_raw_processes: vec![
AllowlistEntry::Exact(allowed1.clone()),
AllowlistEntry::Exact(allowed2.clone()),
],
},
capability_policy: HashMap::new(),
debug_capability_policy: HashMap::new(),
child_policy: ChildPolicyAllowlists {
reboot_on_terminate: vec![
AllowlistEntry::Exact(allowed1.clone()),
AllowlistEntry::Exact(allowed2.clone()),
],
},
},
..Default::default()
});
let config = Arc::downgrade(&strong_config);
assert_vmex_allowed_matches!(config, allowed1, Ok(()));
assert_vmex_allowed_matches!(config, allowed2, Ok(()));
assert_vmex_disallowed!(config, AbsoluteMoniker::root());
assert_vmex_disallowed!(config, allowed1.parent().unwrap());
assert_vmex_disallowed!(config, allowed1.child(ChildMoniker::from("baz")));
drop(strong_config);
assert_vmex_allowed_matches!(config, allowed1, Err(PolicyError::PolicyUnavailable));
assert_vmex_allowed_matches!(config, allowed2, Err(PolicyError::PolicyUnavailable));
}
#[test]
fn scoped_policy_checker_create_raw_processes() {
macro_rules! assert_create_raw_processes_allowed_matches {
($config:expr, $moniker:expr, $expected:pat) => {
let result = ScopedPolicyChecker::new($config.clone(), $moniker.clone())
.create_raw_processes_allowed();
assert_matches!(result, $expected);
};
}
macro_rules! assert_create_raw_processes_disallowed {
($config:expr, $moniker:expr) => {
assert_create_raw_processes_allowed_matches!(
$config,
$moniker,
Err(PolicyError::JobPolicyDisallowed { .. })
);
};
}
let strong_config = Arc::new(RuntimeConfig::default());
let config = Arc::downgrade(&strong_config);
assert_create_raw_processes_disallowed!(config, AbsoluteMoniker::root());
assert_create_raw_processes_disallowed!(config, AbsoluteMoniker::from(vec!["foo"]));
let allowed1 = AbsoluteMoniker::from(vec!["foo", "bar"]);
let allowed2 = AbsoluteMoniker::from(vec!["baz", "fiz"]);
let strong_config = Arc::new(RuntimeConfig {
security_policy: SecurityPolicy {
job_policy: JobPolicyAllowlists {
ambient_mark_vmo_exec: vec![],
main_process_critical: vec![],
create_raw_processes: vec![
AllowlistEntry::Exact(allowed1.clone()),
AllowlistEntry::Exact(allowed2.clone()),
],
},
capability_policy: HashMap::new(),
debug_capability_policy: HashMap::new(),
child_policy: ChildPolicyAllowlists { reboot_on_terminate: vec![] },
},
..Default::default()
});
let config = Arc::downgrade(&strong_config);
assert_create_raw_processes_allowed_matches!(config, allowed1, Ok(()));
assert_create_raw_processes_allowed_matches!(config, allowed2, Ok(()));
assert_create_raw_processes_disallowed!(config, AbsoluteMoniker::root());
assert_create_raw_processes_disallowed!(config, allowed1.parent().unwrap());
assert_create_raw_processes_disallowed!(config, allowed1.child(ChildMoniker::from("baz")));
drop(strong_config);
assert_create_raw_processes_allowed_matches!(
config,
allowed1,
Err(PolicyError::PolicyUnavailable)
);
assert_create_raw_processes_allowed_matches!(
config,
allowed2,
Err(PolicyError::PolicyUnavailable)
);
}
#[test]
fn scoped_policy_checker_main_process_critical_allowed() {
macro_rules! assert_critical_allowed_matches {
($config:expr, $moniker:expr, $expected:pat) => {
let result = ScopedPolicyChecker::new($config.clone(), $moniker.clone())
.main_process_critical_allowed();
assert_matches!(result, $expected);
};
}
macro_rules! assert_critical_disallowed {
($config:expr, $moniker:expr) => {
assert_critical_allowed_matches!(
$config,
$moniker,
Err(PolicyError::JobPolicyDisallowed { .. })
);
};
}
let strong_config = Arc::new(RuntimeConfig::default());
let config = Arc::downgrade(&strong_config);
assert_critical_disallowed!(config, AbsoluteMoniker::root());
assert_critical_disallowed!(config, AbsoluteMoniker::from(vec!["foo"]));
let allowed1 = AbsoluteMoniker::from(vec!["foo", "bar"]);
let allowed2 = AbsoluteMoniker::from(vec!["baz", "fiz"]);
let strong_config = Arc::new(RuntimeConfig {
security_policy: SecurityPolicy {
job_policy: JobPolicyAllowlists {
ambient_mark_vmo_exec: vec![
AllowlistEntry::Exact(allowed1.clone()),
AllowlistEntry::Exact(allowed2.clone()),
],
main_process_critical: vec![
AllowlistEntry::Exact(allowed1.clone()),
AllowlistEntry::Exact(allowed2.clone()),
],
create_raw_processes: vec![
AllowlistEntry::Exact(allowed1.clone()),
AllowlistEntry::Exact(allowed2.clone()),
],
},
capability_policy: HashMap::new(),
debug_capability_policy: HashMap::new(),
child_policy: ChildPolicyAllowlists { reboot_on_terminate: vec![] },
},
..Default::default()
});
let config = Arc::downgrade(&strong_config);
assert_critical_allowed_matches!(config, allowed1, Ok(()));
assert_critical_allowed_matches!(config, allowed2, Ok(()));
assert_critical_disallowed!(config, AbsoluteMoniker::root());
assert_critical_disallowed!(config, allowed1.parent().unwrap());
assert_critical_disallowed!(config, allowed1.child(ChildMoniker::from("baz")));
drop(strong_config);
assert_critical_allowed_matches!(config, allowed1, Err(PolicyError::PolicyUnavailable));
assert_critical_allowed_matches!(config, allowed2, Err(PolicyError::PolicyUnavailable));
}
#[test]
fn scoped_policy_checker_reboot_policy_allowed() {
macro_rules! assert_reboot_allowed_matches {
($config:expr, $moniker:expr, $expected:pat) => {
let result = GlobalPolicyChecker::new($config.clone())
.reboot_on_terminate_allowed(&$moniker);
assert_matches!(result, $expected);
};
}
macro_rules! assert_reboot_disallowed {
($config:expr, $moniker:expr) => {
assert_reboot_allowed_matches!(
$config,
$moniker,
Err(PolicyError::ChildPolicyDisallowed { .. })
);
};
}
// Empty config and enabled.
let config =
Arc::new(RuntimeConfig { reboot_on_terminate_enabled: true, ..Default::default() });
assert_reboot_disallowed!(config, AbsoluteMoniker::root());
assert_reboot_disallowed!(config, AbsoluteMoniker::from(vec!["foo"]));
// Nonempty config and enabled.
let allowed1 = AbsoluteMoniker::from(vec!["foo", "bar"]);
let allowed2 = AbsoluteMoniker::from(vec!["baz", "fiz"]);
let config = Arc::new(RuntimeConfig {
security_policy: SecurityPolicy {
job_policy: JobPolicyAllowlists {
ambient_mark_vmo_exec: vec![],
main_process_critical: vec![],
create_raw_processes: vec![],
},
capability_policy: HashMap::new(),
debug_capability_policy: HashMap::new(),
child_policy: ChildPolicyAllowlists {
reboot_on_terminate: vec![
AllowlistEntry::Exact(allowed1.clone()),
AllowlistEntry::Exact(allowed2.clone()),
],
},
},
reboot_on_terminate_enabled: true,
..Default::default()
});
assert_reboot_allowed_matches!(config, allowed1, Ok(()));
assert_reboot_allowed_matches!(config, allowed2, Ok(()));
assert_reboot_disallowed!(config, AbsoluteMoniker::root());
assert_reboot_disallowed!(config, allowed1.parent().unwrap());
assert_reboot_disallowed!(config, allowed1.child(ChildMoniker::from("baz")));
// Nonempty config and disabled.
let config = Arc::new(RuntimeConfig {
security_policy: SecurityPolicy {
job_policy: JobPolicyAllowlists {
ambient_mark_vmo_exec: vec![],
main_process_critical: vec![],
create_raw_processes: vec![],
},
capability_policy: HashMap::new(),
debug_capability_policy: HashMap::new(),
child_policy: ChildPolicyAllowlists {
reboot_on_terminate: vec![AllowlistEntry::Exact(allowed1.clone())],
},
},
..Default::default()
});
assert_reboot_allowed_matches!(config, allowed1, Err(PolicyError::Unsupported { .. }));
assert_reboot_allowed_matches!(
config,
AbsoluteMoniker::root(),
Err(PolicyError::Unsupported { .. })
);
}
}