blob: a9547882982b9df3f37bdae3115afb9cfd88fc79 [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 {
anyhow::{Context, Error},
fidl::endpoints::Proxy,
fidl_test_policy as ftest, fuchsia_async as fasync,
fuchsia_component::client,
fuchsia_zircon as zx,
futures::future::{select, Either},
security_policy_test_util::{bind_child, start_policy_test},
};
const COMPONENT_MANAGER_URL: &str = "fuchsia-pkg://fuchsia.com/security-policy-critical-integration-test#meta/component_manager.cmx";
const ROOT_URL: &str =
"fuchsia-pkg://fuchsia.com/security-policy-critical-integration-test#meta/test_root.cm";
const TEST_CONFIG_PATH: &str = "/pkg/data/cm_config";
const COMPONENT_MANAGER_DEATH_TIMEOUT: i64 = 5;
#[fasync::run_singlethreaded(test)]
async fn verify_main_process_critical_default_denied() -> Result<(), Error> {
let (mut test, realm) =
start_policy_test(COMPONENT_MANAGER_URL, ROOT_URL, TEST_CONFIG_PATH).await?;
let child_name = "policy_not_requested";
let exposed_dir = bind_child(&realm, child_name).await.expect("bind should succeed");
let exit_controller =
client::connect_to_protocol_at_dir_root::<ftest::ExitControllerMarker>(&exposed_dir)
.context("failed to connect to test service after bind")?;
exit_controller.exit(1)?;
// The child will now exit. Observe this by seeing the exit_controller handle be closed.
exit_controller
.on_closed()
.await
.context("failed to wait for exposed dir handle to become readable")?;
// component_manager should still be running. Observe this by not seeing component_manager exit
// within COMPONENT_MANAGER_DEATH_TIMEOUT seconds.
let timer = fasync::Timer::new(fasync::Time::after(zx::Duration::from_seconds(
COMPONENT_MANAGER_DEATH_TIMEOUT,
)));
match select(timer, test.component_manager_app.wait()).await {
Either::Left(((), _)) => return Ok(()),
Either::Right((Ok(exit_status), _)) => {
if exit_status.exited() {
panic!("unexpected termination of component_manager");
} else {
panic!("unexpected message on realm channel");
}
}
Either::Right((Err(e), _)) => return Err(e.into()),
}
}
#[fasync::run_singlethreaded(test)]
async fn verify_main_process_critical_nonzero_flag_used() -> Result<(), Error> {
let (mut test, realm) =
start_policy_test(COMPONENT_MANAGER_URL, ROOT_URL, TEST_CONFIG_PATH).await?;
let child_name = "policy_allowed";
let exposed_dir = bind_child(&realm, child_name).await.expect("bind should succeed");
let exit_controller =
client::connect_to_protocol_at_dir_root::<ftest::ExitControllerMarker>(&exposed_dir)
.context("failed to connect to test service after bind")?;
exit_controller.exit(0)?;
// The child will now exit. Observe this by seeing the exit_controller handle be closed.
exit_controller
.on_closed()
.await
.context("failed to wait for exposed dir handle to become readable")?;
// component_manager should still be running. The critical marking will not kill
// component_manager's job in this case because the critical component exited with a 0 return
// code. Observe this by not seeing component_manager exit within
// COMPONENT_MANAGER_DEATH_TIMEOUT seconds.
let timer = fasync::Timer::new(fasync::Time::after(zx::Duration::from_seconds(
COMPONENT_MANAGER_DEATH_TIMEOUT,
)));
match select(timer, test.component_manager_app.wait()).await {
Either::Left(((), _)) => return Ok(()),
Either::Right((Ok(exit_status), _)) => {
if exit_status.exited() {
panic!("unexpected termination of component_manager");
} else {
panic!("unexpected message on realm channel");
}
}
Either::Right((Err(e), _)) => return Err(e.into()),
}
}
#[fasync::run_singlethreaded(test)]
async fn verify_main_process_critical_allowed() -> Result<(), Error> {
let (mut test, realm) =
start_policy_test(COMPONENT_MANAGER_URL, ROOT_URL, TEST_CONFIG_PATH).await?;
let child_name = "policy_allowed";
let exposed_dir = bind_child(&realm, child_name).await.expect("bind should succeed");
let exit_controller =
client::connect_to_protocol_at_dir_root::<ftest::ExitControllerMarker>(&exposed_dir)
.context("failed to connect to test service after bind")?;
exit_controller.exit(1)?;
// The child will now exit. Observe this by seeing the exit_controller handle be closed.
exit_controller
.on_closed()
.await
.context("failed to wait for exposed dir handle to become readable")?;
// component_manager should be killed too as a result of the critical marking.
let exit_status = test.component_manager_app.wait().await?;
assert!(exit_status.exited(), "component_manager failed to exit: {:?}", exit_status.reason());
Ok(())
}
#[fasync::run_singlethreaded(test)]
async fn verify_main_process_critical_denied() -> Result<(), Error> {
let (_test, realm) =
start_policy_test(COMPONENT_MANAGER_URL, ROOT_URL, TEST_CONFIG_PATH).await?;
// This security policy is enforced inside the ELF runner. The component will fail to launch
// because of the denial, but BindChild will return success because the runtime successfully
// asks the runner to start the component. We watch for the exposed_dir to get dropped to
// detect the launch failure.
// N.B. We could alternatively look for a Started and then a Stopped event to verify that the
// component failed to launch, but fxbug.dev/53414 prevented that at the time this was written.
let child_name = "policy_denied";
let exposed_dir = bind_child(&realm, child_name).await.expect("bind should succeed");
exposed_dir.on_closed().await.expect("failed to wait for exposed_dir PEER_CLOSED");
Ok(())
}