Files

370 lines
10 KiB
Rust
Raw Permalink Normal View History

2023-08-21 10:49:27 +02:00
// This file is part of the uutils coreutils package.
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.
2022-09-30 17:33:52 -10:00
// spell-checker:ignore (words) araba newroot userspec chdir pwd's isroot
2025-03-28 09:51:51 +01:00
use uutests::at_and_ucmd;
use uutests::new_ucmd;
2023-03-22 12:02:01 +08:00
#[cfg(not(target_os = "android"))]
2025-03-28 09:51:51 +01:00
use uutests::util::is_ci;
use uutests::util::{TestScenario, run_ucmd_as_root};
use uutests::util_name;
2021-03-21 18:18:47 +03:00
2022-09-10 18:38:14 +02:00
#[test]
fn test_invalid_arg() {
2025-03-01 15:29:11 +01:00
new_ucmd!().arg("--definitely-invalid").fails_with_code(125);
2022-09-10 18:38:14 +02:00
}
2021-03-21 18:18:47 +03:00
#[test]
fn test_missing_operand() {
2025-03-01 15:29:11 +01:00
let result = new_ucmd!().fails_with_code(125);
2021-03-21 18:18:47 +03:00
2025-03-24 21:04:32 +01:00
assert!(
result
.stderr_str()
.starts_with("error: the following required arguments were not provided")
);
2021-03-21 18:18:47 +03:00
2021-04-22 22:37:44 +02:00
assert!(result.stderr_str().contains("<newroot>"));
2021-03-21 18:18:47 +03:00
}
#[test]
#[cfg(not(target_os = "android"))]
2021-03-21 18:18:47 +03:00
fn test_enter_chroot_fails() {
2021-09-22 12:03:22 +02:00
// NOTE: since #2689 this test also ensures that we don't regress #2687
2021-03-21 18:18:47 +03:00
let (at, mut ucmd) = at_and_ucmd!();
at.mkdir("jail");
2025-03-01 15:29:11 +01:00
let result = ucmd.arg("jail").fails_with_code(125);
2025-03-24 21:04:32 +01:00
assert!(
result
.stderr_str()
.starts_with("chroot: cannot chroot to 'jail': Operation not permitted (os error 1)")
);
2021-03-21 18:18:47 +03:00
}
#[test]
fn test_no_such_directory() {
let (at, mut ucmd) = at_and_ucmd!();
2023-02-13 02:00:03 -07:00
at.touch(at.plus_as_string("a"));
2021-03-21 18:18:47 +03:00
ucmd.arg("a")
2025-03-01 15:29:11 +01:00
.fails_with_code(125)
.stderr_is("chroot: cannot change root directory to 'a': no such directory\n");
2021-03-21 18:18:47 +03:00
}
2025-01-12 01:08:38 +05:30
#[test]
fn test_multiple_group_args() {
let ts = TestScenario::new(util_name!());
let at = &ts.fixtures;
at.mkdir("id");
if let Ok(result) = run_ucmd_as_root(
&ts,
&["--groups='invalid ignored'", "--groups=''", "/", "id", "-G"],
) {
result.success().stdout_is("0");
} else {
print!("Test skipped; requires root user");
}
}
2021-03-21 18:18:47 +03:00
#[test]
fn test_invalid_user_spec() {
2024-12-31 12:05:25 -05:00
let ts = TestScenario::new(util_name!());
2021-03-21 18:18:47 +03:00
2024-12-31 12:05:25 -05:00
if let Ok(result) = run_ucmd_as_root(&ts, &["--userspec=ARABA:", "/"]) {
result
.failure()
.code_is(125)
.stderr_is("chroot: invalid user");
} else {
print!("Test skipped; requires root user");
}
2021-03-21 18:18:47 +03:00
2024-12-31 12:05:25 -05:00
if let Ok(result) = run_ucmd_as_root(&ts, &["--userspec=ARABA:ARABA", "/"]) {
result
.failure()
.code_is(125)
.stderr_is("chroot: invalid user");
} else {
print!("Test skipped; requires root user");
}
if let Ok(result) = run_ucmd_as_root(&ts, &["--userspec=:ARABA", "/"]) {
result
.failure()
.code_is(125)
.stderr_is("chroot: invalid group");
} else {
print!("Test skipped; requires root user");
}
2021-03-21 18:18:47 +03:00
}
2024-12-28 22:27:53 +01:00
#[test]
fn test_invalid_user() {
let ts = TestScenario::new(util_name!());
let at = &ts.fixtures;
let dir = "CHROOT_DIR";
at.mkdir(dir);
if let Ok(result) = run_ucmd_as_root(&ts, &[dir, "whoami"]) {
result.success().no_stderr().stdout_is("root");
} else {
print!("Test skipped; requires root user");
}
2024-12-31 12:05:25 -05:00
// `--user` is an abbreviation of `--userspec`.
2024-12-28 22:27:53 +01:00
if let Ok(result) = run_ucmd_as_root(&ts, &["--user=nobody:+65535", dir, "pwd"]) {
2024-12-31 12:05:25 -05:00
result.failure().stderr_is("chroot: invalid user");
2024-12-28 22:27:53 +01:00
} else {
print!("Test skipped; requires root user");
}
}
2021-03-21 18:18:47 +03:00
#[test]
#[cfg(not(target_os = "android"))]
2021-03-21 18:18:47 +03:00
fn test_preference_of_userspec() {
let scene = TestScenario::new(util_name!());
let result = scene.cmd("whoami").run();
2021-04-22 22:37:44 +02:00
if is_ci() && result.stderr_str().contains("No such user/group") {
2021-03-21 18:18:47 +03:00
// In the CI, some server are failing to return whoami.
// As seems to be a configuration issue, ignoring it
return;
}
2021-04-05 23:03:43 +03:00
println!("result.stdout = {}", result.stdout_str());
println!("result.stderr = {}", result.stderr_str());
let username = result.stdout_str().trim_end();
2021-03-21 18:18:47 +03:00
let ts = TestScenario::new("id");
let result = ts.cmd("id").arg("-g").arg("-n").run();
2021-04-05 23:03:43 +03:00
println!("result.stdout = {}", result.stdout_str());
println!("result.stderr = {}", result.stderr_str());
2021-03-21 18:18:47 +03:00
2021-04-22 22:37:44 +02:00
if is_ci() && result.stderr_str().contains("cannot find name for user ID") {
2021-03-21 18:18:47 +03:00
// In the CI, some server are failing to return id.
// As seems to be a configuration issue, ignoring it
return;
}
2021-04-05 23:03:43 +03:00
let group_name = result.stdout_str().trim_end();
2021-03-21 18:18:47 +03:00
let (at, mut ucmd) = at_and_ucmd!();
at.mkdir("a");
2024-12-31 12:05:25 -05:00
// `--user` is an abbreviation of `--userspec`.
2021-03-21 18:18:47 +03:00
let result = ucmd
.arg("a")
.arg("--user")
.arg("fake")
2025-01-02 13:35:19 +01:00
.arg("--groups")
2021-03-21 18:18:47 +03:00
.arg("ABC,DEF")
.arg(format!("--userspec={username}:{group_name}"))
2025-03-01 15:29:11 +01:00
.fails_with_code(125);
2021-03-21 18:18:47 +03:00
2021-04-05 23:03:43 +03:00
println!("result.stdout = {}", result.stdout_str());
println!("result.stderr = {}", result.stderr_str());
2021-03-21 18:18:47 +03:00
}
2021-09-19 22:21:29 +02:00
#[test]
fn test_default_shell() {
2021-09-22 12:03:22 +02:00
// NOTE: This test intends to trigger code which can only be reached with root permissions.
2021-09-19 22:21:29 +02:00
let ts = TestScenario::new(util_name!());
let at = &ts.fixtures;
let dir = "CHROOT_DIR";
at.mkdir(dir);
let shell = std::env::var("SHELL").unwrap_or_else(|_| "/bin/sh".to_string());
let expected = format!("chroot: failed to run command '{shell}': No such file or directory");
2021-09-19 22:21:29 +02:00
2022-09-20 22:27:19 +02:00
if let Ok(result) = run_ucmd_as_root(&ts, &[dir]) {
result.stderr_contains(expected);
} else {
print!("Test skipped; requires root user");
2022-09-20 22:27:19 +02:00
}
}
#[test]
fn test_chroot_command_not_found_error() {
let ts = TestScenario::new(util_name!());
let at = &ts.fixtures;
let dir = "CHROOT_DIR";
at.mkdir(dir);
let missing = "definitely_missing_command";
if let Ok(result) = run_ucmd_as_root(&ts, &[dir, missing]) {
result
.failure()
.code_is(127)
.stderr_contains(format!("failed to run command '{missing}'"))
.stderr_contains("No such file or directory");
} else {
print!("Test skipped; requires root user");
}
}
#[test]
fn test_chroot_command_permission_denied_error() {
let ts = TestScenario::new(util_name!());
let at = &ts.fixtures;
let dir = "CHROOT_DIR";
at.mkdir(dir);
let script_path = format!("{dir}/noexec.sh");
at.write(&script_path, "#!/bin/sh\necho unreachable\n");
#[cfg(not(windows))]
{
at.set_mode(&script_path, 0o644);
}
if let Ok(result) = run_ucmd_as_root(&ts, &[dir, "/noexec.sh"]) {
result
.failure()
.code_is(126)
.stderr_contains("failed to run command '/noexec.sh'")
.stderr_contains("Permission denied");
} else {
print!("Test skipped; requires root user");
}
}
2022-09-20 22:27:19 +02:00
#[test]
fn test_chroot() {
let ts = TestScenario::new(util_name!());
let at = &ts.fixtures;
let dir = "CHROOT_DIR";
at.mkdir(dir);
if let Ok(result) = run_ucmd_as_root(&ts, &[dir, "whoami"]) {
result.success().no_stderr().stdout_is("root");
} else {
print!("Test skipped; requires root user");
}
if let Ok(result) = run_ucmd_as_root(&ts, &[dir, "pwd"]) {
result.success().no_stderr().stdout_is("/");
} else {
print!("Test skipped; requires root user");
}
2021-09-19 22:21:29 +02:00
}
2022-09-20 22:29:30 +02:00
#[test]
fn test_chroot_retains_uid_gid() {
let ts = TestScenario::new(util_name!());
let at = &ts.fixtures;
let dir = "CHROOT_DIR";
at.mkdir(dir);
if let Ok(result) = run_ucmd_as_root(&ts, &[dir, "id", "-u"]) {
result.success().no_stderr().stdout_is("0");
} else {
print!("Test skipped; requires root user");
}
if let Ok(result) = run_ucmd_as_root(&ts, &[dir, "id", "-g"]) {
result.success().no_stderr().stdout_is("0");
} else {
print!("Test skipped; requires root user");
}
}
2022-09-30 17:33:52 -10:00
#[test]
fn test_chroot_skip_chdir_not_root() {
let (at, mut ucmd) = at_and_ucmd!();
let dir = "foobar";
at.mkdir(dir);
ucmd.arg("--skip-chdir")
.arg(dir)
2025-03-01 15:29:11 +01:00
.fails_with_code(125)
.stderr_contains("chroot: option --skip-chdir only permitted if NEWROOT is old '/'");
2022-09-30 17:33:52 -10:00
}
2022-09-20 22:29:30 +02:00
#[test]
fn test_chroot_skip_chdir() {
let ts = TestScenario::new(util_name!());
let at = &ts.fixtures;
2022-09-30 17:33:52 -10:00
let dirs = ["/", "/.", "/..", "isroot"];
at.symlink_file("/", "isroot");
for dir in dirs {
let env_cd = std::env::current_dir().unwrap();
if let Ok(result) = run_ucmd_as_root(&ts, &[dir, "--skip-chdir"]) {
// Should return the same path
assert_eq!(
result.success().no_stderr().stdout_str(),
env_cd.to_str().unwrap()
);
} else {
print!("Test skipped; requires root user");
}
2022-09-20 22:29:30 +02:00
}
}
#[test]
fn test_chroot_extra_arg() {
let ts = TestScenario::new(util_name!());
let at = &ts.fixtures;
let dir = "CHROOT_DIR";
at.mkdir(dir);
let env_cd = std::env::current_dir().unwrap();
// Verify that -P is pwd's and not chroot
if let Ok(result) = run_ucmd_as_root(&ts, &[dir, "pwd", "-P"]) {
assert_eq!(
result.success().no_stderr().stdout_str(),
env_cd.to_str().unwrap()
);
} else {
print!("Test skipped; requires root user");
}
}
#[test]
fn test_chroot_userspec_does_not_set_gid_with_uid() {
use uucore::entries::{usr2gid, usr2uid};
let ts = TestScenario::new(util_name!());
if let Ok(uid) = usr2uid("sync") {
if let Ok(gid) = usr2gid("sync") {
if gid == uid {
println!("Test skipped; requires sync user to have uid != gid");
return;
}
// Ubuntu has a sync user whose gid is 65534 per default
if let Ok(result) = run_ucmd_as_root(&ts, &["--userspec=sync", "/", "id", "-g"]) {
result.success().no_stderr().stdout_is(format!("{gid}\n"));
} else {
println!("Test skipped; requires root user");
}
} else {
println!("Test skipped; requires 'sync' user");
}
} else {
println!("Test skipped; requires 'sync' user");
}
}
#[test]
fn test_chroot_userspec_unknown_uid() {
let ts = TestScenario::new(util_name!());
if let Ok(result) =
run_ucmd_as_root(&ts, &["--userspec=99999", "--groups=root", "/", "id", "-g"])
{
result
.failure()
.code_is(125)
.stderr_is("chroot: no group specified for unknown uid: 99999\n");
} else {
println!("Test skipped; requires root user");
}
}