Skip to content

Commit

Permalink
fix(chflags/eperm): add chflags and lchflags tests
Browse files Browse the repository at this point in the history
  • Loading branch information
saidsay-so committed Apr 27, 2024
1 parent 9b7bec8 commit 405e2a9
Show file tree
Hide file tree
Showing 3 changed files with 248 additions and 256 deletions.
47 changes: 29 additions & 18 deletions rust/src/tests/chflags.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,28 +25,33 @@ use super::{
};

mod eperm;
mod lchflags;

//TODO: Split tests with unprivileged tests for user flags

fn get_flags(ctx: &TestContext) -> (FileFlag, FileFlag, FileFlag) {
static USER_FLAGS: OnceLock<HashSet<FileFlags>> = OnceLock::new();
USER_FLAGS.get_or_init(|| HashSet::from([
FileFlags::UF_NODUMP,
FileFlags::UF_IMMUTABLE,
FileFlags::UF_APPEND,
#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
FileFlags::UF_NOUNLINK,
#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
FileFlags::UF_OPAQUE,
]));
USER_FLAGS.get_or_init(|| {
HashSet::from([
FileFlags::UF_NODUMP,
FileFlags::UF_IMMUTABLE,
FileFlags::UF_APPEND,
#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
FileFlags::UF_NOUNLINK,
#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
FileFlags::UF_OPAQUE,
])
});
static SYSTEM_FLAGS: OnceLock<HashSet<FileFlags>> = OnceLock::new();
SYSTEM_FLAGS.get_or_init(|| HashSet::from([
FileFlags::SF_ARCHIVED,
FileFlags::SF_IMMUTABLE,
FileFlags::SF_APPEND,
#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
FileFlags::SF_NOUNLINK,
]));
SYSTEM_FLAGS.get_or_init(|| {
HashSet::from([
FileFlags::SF_ARCHIVED,
FileFlags::SF_IMMUTABLE,
FileFlags::SF_APPEND,
#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
FileFlags::SF_NOUNLINK,
])
});

let allflags: FileFlag = ctx
.features_config()
Expand Down Expand Up @@ -256,7 +261,11 @@ fn securelevel(ctx: &mut TestContext, ft: FileType) {
let jail = jail.start().unwrap();
ctx.set_jail(jail);

for flag in [FileFlags::SF_IMMUTABLE, FileFlags::SF_APPEND, FileFlags::SF_NOUNLINK] {
for flag in [
FileFlags::SF_IMMUTABLE,
FileFlags::SF_APPEND,
FileFlags::SF_NOUNLINK,
] {
let file = ctx.create(ft.clone()).unwrap();
lchflags(&file, flag.into()).unwrap();

Expand All @@ -269,7 +278,9 @@ fn securelevel(ctx: &mut TestContext, ft: FileType) {
.output()
.unwrap();
assert!(!r.status.success());
assert!(OsStr::from_bytes(&r.stderr).to_string_lossy().contains("Operation not permitted"));
assert!(OsStr::from_bytes(&r.stderr)
.to_string_lossy()
.contains("Operation not permitted"));
}
}

Expand Down
240 changes: 2 additions & 238 deletions rust/src/tests/chflags/eperm.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,13 @@
use std::path::Path;

use nix::{
errno::Errno,
sys::stat::{lstat, stat, FileFlag},
unistd::{chflags, chown, Uid, User},
sys::stat::{stat, FileFlag},
unistd::{chflags, chown},
};

use crate::{
config::Config,
context::{FileType, SerializedTestContext},
features::FileSystemFeature,
tests::supports_file_flags,
utils::{lchflags, lchown},
};

crate::test_case! {
Expand Down Expand Up @@ -56,61 +52,6 @@ fn immutable_append_nounlink_not_root(ctx: &mut SerializedTestContext, ft: FileT
let actual_flags = stat(&file).unwrap().st_flags;
assert_eq!(set_flags, actual_flags);
}

for flag in flags {
assert!(lchflags(&file, flag).is_ok());
let set_flags = stat(&file).unwrap().st_flags;

ctx.as_user(&owner, None, || {
assert_eq!(lchflags(&file, FileFlag::UF_NODUMP), Err(Errno::EPERM));
});

let actual_flags = stat(&file).unwrap().st_flags;
assert_eq!(set_flags, actual_flags);
}
}

crate::test_case! {
/// chflags returns EPERM when one of SF_IMMUTABLE, SF_APPEND, or SF_NOUNLINK
/// is set and the user is not the super-user
// chflags/08.t
immutable_append_nounlink_not_root_symlink, serialized, root;
supports_file_flags!(
SF_IMMUTABLE,
SF_APPEND,
SF_NOUNLINK
)
}
fn immutable_append_nounlink_not_root_symlink(ctx: &mut SerializedTestContext) {
let file = ctx.create(FileType::Symlink(None)).unwrap();
let owner = ctx.get_new_user();
let not_owner = ctx.get_new_user();
lchown(&file, Some(owner.uid), Some(owner.gid)).unwrap();

let flags = [
FileFlag::SF_IMMUTABLE,
FileFlag::SF_APPEND,
FileFlag::SF_NOUNLINK,
];

for flag in flags {
assert!(lchflags(&file, flag).is_ok());
let set_flags = lstat(&file).unwrap().st_flags;

ctx.as_user(&not_owner, None, || {
assert_eq!(lchflags(&file, FileFlag::UF_NODUMP), Err(Errno::EPERM));
});

let actual_flags = lstat(&file).unwrap().st_flags;
assert_eq!(set_flags, actual_flags);

ctx.as_user(&owner, None, || {
assert_eq!(lchflags(&file, FileFlag::UF_NODUMP), Err(Errno::EPERM));
});

let actual_flags = lstat(&file).unwrap().st_flags;
assert_eq!(set_flags, actual_flags);
}
}

crate::test_case! {
Expand Down Expand Up @@ -153,60 +94,6 @@ fn set_immutable_append_nounlink_not_root(ctx: &mut SerializedTestContext, ft: F
let actual_flags = stat(&file).unwrap().st_flags;
assert_eq!(set_flags, actual_flags);
}

for flag in flags {
assert!(lchflags(&file, flag).is_ok());
let set_flags = stat(&file).unwrap().st_flags;

ctx.as_user(&owner, None, || {
assert_eq!(lchflags(&file, FileFlag::UF_NODUMP), Err(Errno::EPERM));
});

let actual_flags = stat(&file).unwrap().st_flags;
assert_eq!(set_flags, actual_flags);
}
}

crate::test_case! {
/// chflags returns EPERM if non-super-user tries to set one of SF_IMMUTABLE, SF_APPEND, or SF_NOUNLINK
// chflags/10.t
set_immutable_append_nounlink_not_root_symlink, serialized, root;
supports_file_flags!(
SF_IMMUTABLE,
SF_APPEND,
SF_NOUNLINK
)
}
fn set_immutable_append_nounlink_not_root_symlink(ctx: &mut SerializedTestContext) {
let file = ctx.create(FileType::Symlink(None)).unwrap();
let owner = ctx.get_new_user();
let not_owner = ctx.get_new_user();
lchown(&file, Some(owner.uid), Some(owner.gid)).unwrap();

let flags = [
FileFlag::SF_IMMUTABLE,
FileFlag::SF_APPEND,
FileFlag::SF_NOUNLINK,
];

for flag in flags {
assert!(lchflags(&file, flag).is_ok());
let set_flags = lstat(&file).unwrap().st_flags;

ctx.as_user(&not_owner, None, || {
assert_eq!(lchflags(&file, flag), Err(Errno::EPERM));
});

let actual_flags = lstat(&file).unwrap().st_flags;
assert_eq!(set_flags, actual_flags);

ctx.as_user(&owner, None, || {
assert_eq!(lchflags(&file, flag), Err(Errno::EPERM));
});

let actual_flags = lstat(&file).unwrap().st_flags;
assert_eq!(set_flags, actual_flags);
}
}

crate::test_case! {
Expand All @@ -217,7 +104,6 @@ crate::test_case! {
}
fn not_owner_not_root(ctx: &mut SerializedTestContext, ft: FileType) {
let file = ctx.create(ft).unwrap();
let current_owner = User::from_uid(Uid::effective()).unwrap().unwrap();
let other_owner = ctx.get_new_user();
let not_owner = ctx.get_new_user();

Expand All @@ -240,63 +126,6 @@ fn not_owner_not_root(ctx: &mut SerializedTestContext, ft: FileType) {

let flags = stat(&file).unwrap().st_flags;
assert_eq!(default_flags, flags);

// lchflags

lchown(&file, Some(current_owner.uid), Some(current_owner.gid)).unwrap();

let default_flags = lstat(&file).unwrap().st_flags;

ctx.as_user(&not_owner, None, || {
assert_eq!(lchflags(&file, FileFlag::UF_NODUMP), Err(Errno::EPERM));
});

let flags = lstat(&file).unwrap().st_flags;
assert_eq!(default_flags, flags);

lchown(&file, Some(other_owner.uid), Some(other_owner.gid)).unwrap();

ctx.as_user(&not_owner, None, || {
assert_eq!(lchflags(&file, FileFlag::UF_NODUMP), Err(Errno::EPERM));
});

let flags = lstat(&file).unwrap().st_flags;
assert_eq!(default_flags, flags);
}

crate::test_case! {
/// chflags returns EPERM when the effective user ID does not match
/// the owner of the file and the effective user ID is not the super-user
// chflags/07.t
not_owner_not_root_symlink, serialized, root
}
fn not_owner_not_root_symlink(ctx: &mut SerializedTestContext) {
let file = ctx.create(FileType::Symlink(None)).unwrap();
let current_owner = User::from_uid(Uid::effective()).unwrap().unwrap();
let other_owner = ctx.get_new_user();
let not_owner = ctx.get_new_user();

lchown(&file, Some(current_owner.uid), Some(current_owner.gid)).unwrap();

lchflags(&file, FileFlag::empty()).unwrap();
let default_flags = lstat(&file).unwrap().st_flags;

ctx.as_user(&not_owner, None, || {
assert_eq!(lchflags(&file, FileFlag::UF_NODUMP), Err(Errno::EPERM));
});

let flags = lstat(&file).unwrap().st_flags;
assert_eq!(default_flags, flags);

lchown(&file, Some(other_owner.uid), Some(other_owner.gid)).unwrap();
lchflags(&file, FileFlag::empty()).unwrap();

ctx.as_user(&not_owner, None, || {
assert_eq!(lchflags(&file, FileFlag::UF_NODUMP), Err(Errno::EPERM));
});

let flags = lstat(&file).unwrap().st_flags;
assert_eq!(default_flags, flags);
}

crate::test_case! {
Expand All @@ -307,7 +136,6 @@ crate::test_case! {
}
fn set_sf_snapshot_user(ctx: &mut SerializedTestContext, ft: FileType) {
let file = ctx.create(ft).unwrap();
let current_owner = User::from_uid(Uid::effective()).unwrap().unwrap();
let other_owner = ctx.get_new_user();
let not_owner = ctx.get_new_user();

Expand All @@ -334,68 +162,4 @@ fn set_sf_snapshot_user(ctx: &mut SerializedTestContext, ft: FileType) {

let flags = stat(&file).unwrap().st_flags;
assert_eq!(default_flags, flags);

// lchflags

lchown(&file, Some(current_owner.uid), Some(current_owner.gid)).unwrap();

let default_flags = lstat(&file).unwrap().st_flags;

ctx.as_user(&not_owner, None, || {
assert_eq!(lchflags(&file, FileFlag::SF_SNAPSHOT), Err(Errno::EPERM));
});

assert_eq!(lchflags(&file, FileFlag::SF_SNAPSHOT), Err(Errno::EPERM));

let flags = lstat(&file).unwrap().st_flags;
assert_eq!(default_flags, flags);

lchown(&file, Some(other_owner.uid), Some(other_owner.gid)).unwrap();

ctx.as_user(&not_owner, None, || {
assert_eq!(lchflags(&file, FileFlag::SF_SNAPSHOT), Err(Errno::EPERM));
});

assert_eq!(chflags(&file, FileFlag::SF_SNAPSHOT), Err(Errno::EPERM));

let flags = lstat(&file).unwrap().st_flags;
assert_eq!(default_flags, flags);
}

crate::test_case! {
/// chflags returns EPERM if a user tries to set or remove the SF_SNAPSHOT flag
// chflags/11.t
set_sf_snapshot_user_symlink, serialized, root, FileSystemFeature::ChflagsSfSnapshot
}
fn set_sf_snapshot_user_symlink(ctx: &mut SerializedTestContext) {
let file = ctx.create(FileType::Symlink(None)).unwrap();
let current_owner = User::from_uid(Uid::effective()).unwrap().unwrap();
let other_owner = ctx.get_new_user();
let not_owner = ctx.get_new_user();

lchown(&file, Some(current_owner.uid), Some(current_owner.gid)).unwrap();

lchflags(&file, FileFlag::empty()).unwrap();
let default_flags = lstat(&file).unwrap().st_flags;

ctx.as_user(&not_owner, None, || {
assert_eq!(lchflags(&file, FileFlag::SF_SNAPSHOT), Err(Errno::EPERM));
});

assert_eq!(lchflags(&file, FileFlag::SF_SNAPSHOT), Err(Errno::EPERM));

let flags = lstat(&file).unwrap().st_flags;
assert_eq!(default_flags, flags);

lchown(&file, Some(other_owner.uid), Some(other_owner.gid)).unwrap();
lchflags(&file, FileFlag::empty()).unwrap();

ctx.as_user(&not_owner, None, || {
assert_eq!(lchflags(&file, FileFlag::SF_SNAPSHOT), Err(Errno::EPERM));
});

assert_eq!(lchflags(&file, FileFlag::SF_SNAPSHOT), Err(Errno::EPERM));

let flags = lstat(&file).unwrap().st_flags;
assert_eq!(default_flags, flags);
}
Loading

0 comments on commit 405e2a9

Please sign in to comment.