blob: 0875d553b89be0875b53c61d9c24af80817d2a21 [file] [log] [blame]
// Copyright 2022 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 {
assert_matches::assert_matches,
fidl::endpoints::create_proxy,
fidl_fuchsia_io as fio,
io_conformance_util::{test_harness::TestHarness, *},
};
/// Creates a directory with a remote mount inside of it, and checks that the remote can be opened.
#[fuchsia::test]
async fn open_remote_directory_test() {
let harness = TestHarness::new().await;
if !harness.config.supports_remote_dir.unwrap_or_default() {
return;
}
let remote_name = "remote_directory";
let remote_mount = root_directory(vec![]);
let remote_client = harness.get_directory(
remote_mount,
fio::OpenFlags::RIGHT_READABLE | fio::OpenFlags::RIGHT_WRITABLE,
);
// Create a directory with the remote directory inside of it.
let root = root_directory(vec![remote_directory(remote_name, remote_client)]);
let root_dir = harness
.get_directory(root, fio::OpenFlags::RIGHT_READABLE | fio::OpenFlags::RIGHT_WRITABLE);
open_node::<fio::DirectoryMarker>(
&root_dir,
fio::OpenFlags::RIGHT_READABLE | fio::OpenFlags::RIGHT_WRITABLE | fio::OpenFlags::DIRECTORY,
remote_name,
)
.await;
}
/// Creates a directory with a remote mount containing a file inside of it, and checks that the
/// file can be opened through the remote.
#[fuchsia::test]
async fn open_remote_file_test() {
let harness = TestHarness::new().await;
if !harness.config.supports_remote_dir.unwrap_or_default() {
return;
}
let remote_name = "remote_directory";
let remote_dir = root_directory(vec![file(TEST_FILE, vec![])]);
let remote_client = harness.get_directory(remote_dir, fio::OpenFlags::RIGHT_READABLE);
// Create a directory with the remote directory inside of it.
let root = root_directory(vec![remote_directory(remote_name, remote_client)]);
let root_dir = harness
.get_directory(root, fio::OpenFlags::RIGHT_READABLE | fio::OpenFlags::RIGHT_WRITABLE);
// Test opening file by opening the remote directory first and then opening the file.
let remote_dir_proxy = open_node::<fio::DirectoryMarker>(
&root_dir,
fio::OpenFlags::RIGHT_READABLE | fio::OpenFlags::DIRECTORY,
remote_name,
)
.await;
open_node::<fio::NodeMarker>(&remote_dir_proxy, fio::OpenFlags::RIGHT_READABLE, TEST_FILE)
.await;
// Test opening file directly though local directory by crossing remote automatically.
open_node::<fio::NodeMarker>(
&root_dir,
fio::OpenFlags::RIGHT_READABLE,
[remote_name, "/", TEST_FILE].join("").as_str(),
)
.await;
}
/// Ensure specifying POSIX_* flags cannot cause rights escalation (https://fxbug.dev/42116881).
/// The test sets up the following hierarchy of nodes:
///
/// --------------------- RW --------------------------
/// | root_proxy | ---> | root |
/// --------------------- (a) | - /mount_point | RWX ---------------------
/// | (remote_proxy) | ---> | remote_dir |
/// -------------------------- (b) ---------------------
///
/// To validate the right escalation issue has been resolved, we call Open() on the test_dir_proxy
/// passing in both POSIX_* flags, which if handled correctly, should result in opening
/// remote_dir_server as RW (and NOT RWX, which can occur if both flags are passed directly to the
/// remote instead of being removed).
#[fuchsia::test]
async fn open_remote_directory_right_escalation_test() {
let harness = TestHarness::new().await;
if !harness.config.supports_remote_dir.unwrap_or_default() {
return;
}
let mount_point = "mount_point";
// Use the test harness to serve a directory with RWX permissions.
let remote_dir = root_directory(vec![]);
let remote_proxy = harness.get_directory(
remote_dir,
fio::OpenFlags::RIGHT_READABLE
| fio::OpenFlags::RIGHT_WRITABLE
| fio::OpenFlags::RIGHT_EXECUTABLE,
);
// Mount the remote directory through root, and ensure that the connection only has RW
// RW permissions (which is thus a sub-set of the permissions the remote_proxy has).
let root = root_directory(vec![remote_directory(mount_point, remote_proxy)]);
let root_proxy = harness
.get_directory(root, fio::OpenFlags::RIGHT_READABLE | fio::OpenFlags::RIGHT_WRITABLE);
// Create a new proxy/server for opening the remote node through test_dir_proxy.
// Here we pass the POSIX flag, which should only expand to the maximum set of
// rights available along the open chain.
let (node_proxy, node_server) = create_proxy::<fio::NodeMarker>().expect("Cannot create proxy");
root_proxy
.open(
fio::OpenFlags::RIGHT_READABLE
| fio::OpenFlags::POSIX_WRITABLE
| fio::OpenFlags::POSIX_EXECUTABLE
| fio::OpenFlags::DIRECTORY,
fio::ModeType::empty(),
mount_point,
node_server,
)
.expect("Cannot open remote directory");
// Since the root node only has RW permissions, and even though the remote has RWX,
// we should only get RW permissions back.
let (_, node_flags) = node_proxy.get_flags().await.unwrap();
assert_eq!(node_flags, fio::OpenFlags::RIGHT_READABLE | fio::OpenFlags::RIGHT_WRITABLE);
}
/// Creates a directory with a remote mount inside of it, and checks that the remote can be opened.
#[fuchsia::test]
async fn open2_remote_directory_test() {
let harness = TestHarness::new().await;
if !(harness.config.supports_remote_dir.unwrap_or_default()
&& harness.config.supports_open2.unwrap_or_default())
{
return;
}
let remote_name = "remote_directory";
let remote_mount = root_directory(vec![]);
let remote_client = harness.get_directory(
remote_mount,
fio::OpenFlags::RIGHT_READABLE | fio::OpenFlags::RIGHT_WRITABLE,
);
// Create a directory with the remote directory inside of it.
let root = root_directory(vec![remote_directory(remote_name, remote_client)]);
let root_dir = harness
.get_directory(root, fio::OpenFlags::RIGHT_READABLE | fio::OpenFlags::RIGHT_WRITABLE);
root_dir
.open2_node::<fio::DirectoryMarker>(
remote_name,
fio::NodeOptions {
protocols: Some(fio::NodeProtocols {
directory: Some(Default::default()),
..Default::default()
}),
rights: Some(fio::Operations::READ_BYTES | fio::Operations::WRITE_BYTES),
..Default::default()
},
)
.await
.expect("failed to open remote directory");
}
/// Creates a directory with a remote mount containing a file inside of it, and checks that the
/// file can be opened through the remote.
#[fuchsia::test]
async fn open2_remote_file_test() {
let harness = TestHarness::new().await;
if !(harness.config.supports_remote_dir.unwrap_or_default()
&& harness.config.supports_open2.unwrap_or_default())
{
return;
}
let remote_name = "remote_directory";
let remote_dir = root_directory(vec![file(TEST_FILE, vec![])]);
let remote_client = harness.get_directory(remote_dir, fio::OpenFlags::RIGHT_READABLE);
// Create a directory with the remote directory inside of it.
let root = root_directory(vec![remote_directory(remote_name, remote_client)]);
let root_dir = harness
.get_directory(root, fio::OpenFlags::RIGHT_READABLE | fio::OpenFlags::RIGHT_WRITABLE);
// Test opening file by opening the remote directory first and then opening the file.
let remote_dir_proxy = root_dir
.open2_node::<fio::DirectoryMarker>(
remote_name,
fio::NodeOptions {
protocols: Some(fio::NodeProtocols {
directory: Some(Default::default()),
..Default::default()
}),
rights: Some(fio::Operations::READ_BYTES),
..Default::default()
},
)
.await
.expect("failed to open remote directory");
let file_options = fio::NodeOptions {
protocols: Some(fio::NodeProtocols {
file: Some(Default::default()),
..Default::default()
}),
rights: Some(fio::Operations::READ_BYTES),
..Default::default()
};
remote_dir_proxy
.open2_node::<fio::NodeMarker>(TEST_FILE, file_options.clone())
.await
.expect("failed to open file in remote directory");
// Test opening file directly though local directory by crossing remote automatically.
root_dir
.open2_node::<fio::NodeMarker>(
[remote_name, "/", TEST_FILE].join("").as_str(),
file_options,
)
.await
.expect("failed to open file when traversing a remote mount point");
}
/// Ensure specifying optional rights cannot cause rights escalation. The test sets up the following
/// hierarchy of nodes:
///
/// --------------------- RW --------------------------
/// | root_proxy | ---> | root |
/// --------------------- (a) | - /mount_point | RWX ---------------------
/// | (remote_proxy) | ---> | remote_dir |
/// -------------------------- (b) ---------------------
///
/// It then verifies that opening `remote_dir` through `root_proxy` will remove any specified
/// optional rights not present during any intermediate opening steps.
#[fuchsia::test]
async fn open2_remote_directory_right_escalation_test() {
let harness = TestHarness::new().await;
if !(harness.config.supports_remote_dir.unwrap_or_default()
&& harness.config.supports_open2.unwrap_or_default())
{
return;
}
let mount_point = "mount_point";
// Use the test harness to serve a directory with RWX permissions.
let remote_dir = root_directory(vec![]);
let remote_proxy = harness.get_directory(
remote_dir,
fio::OpenFlags::RIGHT_READABLE
| fio::OpenFlags::RIGHT_WRITABLE
| fio::OpenFlags::RIGHT_EXECUTABLE,
);
// Mount the remote directory through root, and ensure that the connection only has RW
// RW permissions (which is thus a sub-set of the permissions the remote_proxy has).
let root = root_directory(vec![remote_directory(mount_point, remote_proxy)]);
let root_proxy = harness
.get_directory(root, fio::OpenFlags::RIGHT_READABLE | fio::OpenFlags::RIGHT_WRITABLE);
// Open the remote with read rights as required, but write/execute as optional.
let options = fio::NodeOptions {
protocols: Some(fio::NodeProtocols {
directory: Some(fio::DirectoryProtocolOptions {
optional_rights: Some(fio::Rights::WRITE_BYTES | fio::Rights::EXECUTE),
..Default::default()
}),
..Default::default()
}),
rights: Some(fio::Rights::READ_BYTES),
..Default::default()
};
let proxy = root_proxy
.open2_node::<fio::NodeMarker>(mount_point, options)
.await
.expect("failed to open remote node");
// Ensure the resulting connection expanded write but not execute rights.
let connection_info = proxy.get_connection_info().await.unwrap();
assert_matches!(connection_info, fio::ConnectionInfo{ rights: Some(rights), .. } => {
assert!(!rights.contains(fio::Operations::EXECUTE));
assert!(rights.intersects(fio::Operations::READ_BYTES | fio::Operations::WRITE_BYTES))});
}