mirror of
https://github.com/uutils/coreutils
synced 2024-07-08 19:56:10 +00:00
chcon: added implementation and integration tests
The ToDo list was updated to mark `chcon` as done. Building and testing `chcon` requires enabling the `feat_selinux` feature. If `make` is used for building, then please specify `SELINUX_ENABLED=1` if building and testing on a system where SELinux is not enabled.
This commit is contained in:
parent
63fd139b04
commit
090be5bb94
|
@ -63,6 +63,7 @@ abspath
|
||||||
addprefix
|
addprefix
|
||||||
addsuffix
|
addsuffix
|
||||||
endef
|
endef
|
||||||
|
findstring
|
||||||
firstword
|
firstword
|
||||||
ifeq
|
ifeq
|
||||||
ifneq
|
ifneq
|
||||||
|
@ -89,5 +90,9 @@ markdownlint
|
||||||
rerast
|
rerast
|
||||||
rollup
|
rollup
|
||||||
sed
|
sed
|
||||||
|
selinuxenabled
|
||||||
wslpath
|
wslpath
|
||||||
xargs
|
xargs
|
||||||
|
|
||||||
|
# * directories
|
||||||
|
sbin
|
||||||
|
|
|
@ -106,6 +106,7 @@ whoami
|
||||||
# * vars/errno
|
# * vars/errno
|
||||||
errno
|
errno
|
||||||
EEXIST
|
EEXIST
|
||||||
|
ENODATA
|
||||||
ENOENT
|
ENOENT
|
||||||
ENOSYS
|
ENOSYS
|
||||||
EPERM
|
EPERM
|
||||||
|
@ -118,7 +119,9 @@ fcntl
|
||||||
vmsplice
|
vmsplice
|
||||||
|
|
||||||
# * vars/libc
|
# * vars/libc
|
||||||
|
COMFOLLOW
|
||||||
FILENO
|
FILENO
|
||||||
|
FTSENT
|
||||||
HOSTSIZE
|
HOSTSIZE
|
||||||
IDSIZE
|
IDSIZE
|
||||||
IFBLK
|
IFBLK
|
||||||
|
@ -151,6 +154,7 @@ SIGTERM
|
||||||
SYS_fdatasync
|
SYS_fdatasync
|
||||||
SYS_syncfs
|
SYS_syncfs
|
||||||
USERSIZE
|
USERSIZE
|
||||||
|
accpath
|
||||||
addrinfo
|
addrinfo
|
||||||
addrlen
|
addrlen
|
||||||
blocksize
|
blocksize
|
||||||
|
@ -174,15 +178,18 @@ inode
|
||||||
inodes
|
inodes
|
||||||
isatty
|
isatty
|
||||||
lchown
|
lchown
|
||||||
|
pathlen
|
||||||
setgid
|
setgid
|
||||||
setgroups
|
setgroups
|
||||||
settime
|
settime
|
||||||
setuid
|
setuid
|
||||||
socktype
|
socktype
|
||||||
statfs
|
statfs
|
||||||
|
statp
|
||||||
statvfs
|
statvfs
|
||||||
strcmp
|
strcmp
|
||||||
strerror
|
strerror
|
||||||
|
strlen
|
||||||
syncfs
|
syncfs
|
||||||
umask
|
umask
|
||||||
waitpid
|
waitpid
|
||||||
|
@ -274,6 +281,13 @@ winerror
|
||||||
winnt
|
winnt
|
||||||
winsock
|
winsock
|
||||||
|
|
||||||
|
# * vars/selinux
|
||||||
|
freecon
|
||||||
|
getfilecon
|
||||||
|
lgetfilecon
|
||||||
|
lsetfilecon
|
||||||
|
setfilecon
|
||||||
|
|
||||||
# * vars/uucore
|
# * vars/uucore
|
||||||
optflag
|
optflag
|
||||||
optflagmulti
|
optflagmulti
|
||||||
|
|
24
Cargo.lock
generated
24
Cargo.lock
generated
|
@ -309,6 +309,7 @@ dependencies = [
|
||||||
"uu_base64",
|
"uu_base64",
|
||||||
"uu_basename",
|
"uu_basename",
|
||||||
"uu_cat",
|
"uu_cat",
|
||||||
|
"uu_chcon",
|
||||||
"uu_chgrp",
|
"uu_chgrp",
|
||||||
"uu_chmod",
|
"uu_chmod",
|
||||||
"uu_chown",
|
"uu_chown",
|
||||||
|
@ -697,6 +698,16 @@ version = "1.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2022715d62ab30faffd124d40b76f4134a550a87792276512b18d63272333394"
|
checksum = "2022715d62ab30faffd124d40b76f4134a550a87792276512b18d63272333394"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fts-sys"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d31ec9f1580e270ee49a1fae7b102f54514142d9be2d4aa363c361363d65cac9"
|
||||||
|
dependencies = [
|
||||||
|
"bindgen",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fuchsia-cprng"
|
name = "fuchsia-cprng"
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
|
@ -1972,6 +1983,19 @@ dependencies = [
|
||||||
"uucore_procs",
|
"uucore_procs",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "uu_chcon"
|
||||||
|
version = "0.0.7"
|
||||||
|
dependencies = [
|
||||||
|
"clap",
|
||||||
|
"fts-sys",
|
||||||
|
"libc",
|
||||||
|
"selinux-sys",
|
||||||
|
"thiserror",
|
||||||
|
"uucore",
|
||||||
|
"uucore_procs",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uu_chgrp"
|
name = "uu_chgrp"
|
||||||
version = "0.0.7"
|
version = "0.0.7"
|
||||||
|
|
|
@ -145,7 +145,7 @@ feat_os_unix_musl = [
|
||||||
# NOTE:
|
# NOTE:
|
||||||
# The selinux(-sys) crate requires `libselinux` headers and shared library to be accessible in the C toolchain at compile time.
|
# The selinux(-sys) crate requires `libselinux` headers and shared library to be accessible in the C toolchain at compile time.
|
||||||
# Running a uutils compiled with `feat_selinux` requires an SELinux enabled Kernel at run time.
|
# Running a uutils compiled with `feat_selinux` requires an SELinux enabled Kernel at run time.
|
||||||
feat_selinux = ["id/selinux", "selinux"]
|
feat_selinux = ["id/selinux", "selinux", "feat_require_selinux"]
|
||||||
## feature sets with requirements (restricting cross-platform availability)
|
## feature sets with requirements (restricting cross-platform availability)
|
||||||
#
|
#
|
||||||
# ** NOTE: these `feat_require_...` sets should be minimized as much as possible to encourage cross-platform availability of utilities
|
# ** NOTE: these `feat_require_...` sets should be minimized as much as possible to encourage cross-platform availability of utilities
|
||||||
|
@ -185,6 +185,10 @@ feat_require_unix_utmpx = [
|
||||||
"users",
|
"users",
|
||||||
"who",
|
"who",
|
||||||
]
|
]
|
||||||
|
# "feat_require_selinux" == set of utilities depending on SELinux.
|
||||||
|
feat_require_selinux = [
|
||||||
|
"chcon",
|
||||||
|
]
|
||||||
## (alternate/newer/smaller platforms) feature sets
|
## (alternate/newer/smaller platforms) feature sets
|
||||||
# "feat_os_unix_fuchsia" == set of utilities which can be built/run on the "Fuchsia" OS (refs: <https://fuchsia.dev>; <https://en.wikipedia.org/wiki/Google_Fuchsia>)
|
# "feat_os_unix_fuchsia" == set of utilities which can be built/run on the "Fuchsia" OS (refs: <https://fuchsia.dev>; <https://en.wikipedia.org/wiki/Google_Fuchsia>)
|
||||||
feat_os_unix_fuchsia = [
|
feat_os_unix_fuchsia = [
|
||||||
|
@ -246,6 +250,7 @@ base32 = { optional=true, version="0.0.7", package="uu_base32", path="src/uu/b
|
||||||
base64 = { optional=true, version="0.0.7", package="uu_base64", path="src/uu/base64" }
|
base64 = { optional=true, version="0.0.7", package="uu_base64", path="src/uu/base64" }
|
||||||
basename = { optional=true, version="0.0.7", package="uu_basename", path="src/uu/basename" }
|
basename = { optional=true, version="0.0.7", package="uu_basename", path="src/uu/basename" }
|
||||||
cat = { optional=true, version="0.0.7", package="uu_cat", path="src/uu/cat" }
|
cat = { optional=true, version="0.0.7", package="uu_cat", path="src/uu/cat" }
|
||||||
|
chcon = { optional=true, version="0.0.7", package="uu_chcon", path="src/uu/chcon" }
|
||||||
chgrp = { optional=true, version="0.0.7", package="uu_chgrp", path="src/uu/chgrp" }
|
chgrp = { optional=true, version="0.0.7", package="uu_chgrp", path="src/uu/chgrp" }
|
||||||
chmod = { optional=true, version="0.0.7", package="uu_chmod", path="src/uu/chmod" }
|
chmod = { optional=true, version="0.0.7", package="uu_chmod", path="src/uu/chmod" }
|
||||||
chown = { optional=true, version="0.0.7", package="uu_chown", path="src/uu/chown" }
|
chown = { optional=true, version="0.0.7", package="uu_chown", path="src/uu/chown" }
|
||||||
|
|
20
GNUmakefile
20
GNUmakefile
|
@ -46,6 +46,15 @@ BUSYBOX_ROOT := $(BASEDIR)/tmp
|
||||||
BUSYBOX_VER := 1.32.1
|
BUSYBOX_VER := 1.32.1
|
||||||
BUSYBOX_SRC := $(BUSYBOX_ROOT)/busybox-$(BUSYBOX_VER)
|
BUSYBOX_SRC := $(BUSYBOX_ROOT)/busybox-$(BUSYBOX_VER)
|
||||||
|
|
||||||
|
ifeq ($(SELINUX_ENABLED),)
|
||||||
|
SELINUX_ENABLED := 0
|
||||||
|
ifneq ($(OS),Windows_NT)
|
||||||
|
ifeq ($(shell /sbin/selinuxenabled 2>/dev/null ; echo $$?),0)
|
||||||
|
SELINUX_ENABLED := 1
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
# Possible programs
|
# Possible programs
|
||||||
PROGS := \
|
PROGS := \
|
||||||
base32 \
|
base32 \
|
||||||
|
@ -147,10 +156,17 @@ UNIX_PROGS := \
|
||||||
users \
|
users \
|
||||||
who
|
who
|
||||||
|
|
||||||
|
SELINUX_PROGS := \
|
||||||
|
chcon
|
||||||
|
|
||||||
ifneq ($(OS),Windows_NT)
|
ifneq ($(OS),Windows_NT)
|
||||||
PROGS := $(PROGS) $(UNIX_PROGS)
|
PROGS := $(PROGS) $(UNIX_PROGS)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
ifeq ($(SELINUX_ENABLED),1)
|
||||||
|
PROGS := $(PROGS) $(SELINUX_PROGS)
|
||||||
|
endif
|
||||||
|
|
||||||
UTILS ?= $(PROGS)
|
UTILS ?= $(PROGS)
|
||||||
|
|
||||||
# Programs with usable tests
|
# Programs with usable tests
|
||||||
|
@ -159,6 +175,7 @@ TEST_PROGS := \
|
||||||
base64 \
|
base64 \
|
||||||
basename \
|
basename \
|
||||||
cat \
|
cat \
|
||||||
|
chcon \
|
||||||
chgrp \
|
chgrp \
|
||||||
chmod \
|
chmod \
|
||||||
chown \
|
chown \
|
||||||
|
@ -228,6 +245,9 @@ TEST_SPEC_FEATURE :=
|
||||||
ifneq ($(SPEC),)
|
ifneq ($(SPEC),)
|
||||||
TEST_NO_FAIL_FAST :=--no-fail-fast
|
TEST_NO_FAIL_FAST :=--no-fail-fast
|
||||||
TEST_SPEC_FEATURE := test_unimplemented
|
TEST_SPEC_FEATURE := test_unimplemented
|
||||||
|
else ifeq ($(SELINUX_ENABLED),1)
|
||||||
|
TEST_NO_FAIL_FAST :=
|
||||||
|
TEST_SPEC_FEATURE := feat_selinux
|
||||||
endif
|
endif
|
||||||
|
|
||||||
define TEST_BUSYBOX
|
define TEST_BUSYBOX
|
||||||
|
|
27
README.md
27
README.md
|
@ -370,19 +370,20 @@ To contribute to uutils, please see [CONTRIBUTING](CONTRIBUTING.md).
|
||||||
| base64 | dd | stty |
|
| base64 | dd | stty |
|
||||||
| basename | df | |
|
| basename | df | |
|
||||||
| cat | expr | |
|
| cat | expr | |
|
||||||
| chgrp | install | |
|
| chcon | install | |
|
||||||
| chmod | join | |
|
| chgrp | join | |
|
||||||
| chown | ls | |
|
| chmod | ls | |
|
||||||
| chroot | more | |
|
| chown | more | |
|
||||||
| cksum | numfmt | |
|
| chroot | numfmt | |
|
||||||
| comm | od (`--strings` and 128-bit data types missing) |
|
| cksum | od (`--strings` and 128-bit data types missing) | |
|
||||||
| csplit | pr | |
|
| comm | pr | |
|
||||||
| cut | printf | |
|
| csplit | printf | |
|
||||||
| dircolors | sort | |
|
| cut | sort | |
|
||||||
| dirname | split | |
|
| dircolors | split | |
|
||||||
| du | tac | |
|
| dirname | tac | |
|
||||||
| echo | tail | |
|
| du | tail | |
|
||||||
| env | test | |
|
| echo | test | |
|
||||||
|
| env | | |
|
||||||
| expand | | |
|
| expand | | |
|
||||||
| factor | | |
|
| factor | | |
|
||||||
| false | | |
|
| false | | |
|
||||||
|
|
27
src/uu/chcon/Cargo.toml
Normal file
27
src/uu/chcon/Cargo.toml
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
[package]
|
||||||
|
name = "uu_chcon"
|
||||||
|
version = "0.0.7"
|
||||||
|
authors = ["uutils developers"]
|
||||||
|
license = "MIT"
|
||||||
|
description = "chcon ~ (uutils) change file security context"
|
||||||
|
homepage = "https://github.com/uutils/coreutils"
|
||||||
|
repository = "https://github.com/uutils/coreutils/tree/master/src/uu/chcon"
|
||||||
|
keywords = ["coreutils", "uutils", "cli", "utility"]
|
||||||
|
categories = ["command-line-utilities"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
path = "src/chcon.rs"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
clap = { version = "2.33", features = ["wrap_help"] }
|
||||||
|
uucore = { version = ">=0.0.9", package="uucore", path="../../uucore", features=["entries", "fs", "perms"] }
|
||||||
|
uucore_procs = { version = ">=0.0.6", package="uucore_procs", path="../../uucore_procs" }
|
||||||
|
selinux-sys = { version = "0.5" }
|
||||||
|
fts-sys = { version = "0.2" }
|
||||||
|
thiserror = { version = "1.0" }
|
||||||
|
libc = { version = "0.2" }
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "chcon"
|
||||||
|
path = "src/main.rs"
|
1179
src/uu/chcon/src/chcon.rs
Normal file
1179
src/uu/chcon/src/chcon.rs
Normal file
File diff suppressed because it is too large
Load Diff
1
src/uu/chcon/src/main.rs
Normal file
1
src/uu/chcon/src/main.rs
Normal file
|
@ -0,0 +1 @@
|
||||||
|
uucore_procs::main!(uu_chcon);
|
469
tests/by-util/test_chcon.rs
Normal file
469
tests/by-util/test_chcon.rs
Normal file
|
@ -0,0 +1,469 @@
|
||||||
|
// spell-checker:ignore (jargon) xattributes
|
||||||
|
|
||||||
|
#![cfg(feature = "feat_selinux")]
|
||||||
|
|
||||||
|
use std::ffi::CString;
|
||||||
|
use std::path::Path;
|
||||||
|
use std::{io, iter, str};
|
||||||
|
|
||||||
|
use crate::common::util::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn version() {
|
||||||
|
new_ucmd!().arg("--version").succeeds();
|
||||||
|
new_ucmd!().arg("-V").succeeds();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn help() {
|
||||||
|
new_ucmd!().fails();
|
||||||
|
new_ucmd!().arg("--help").succeeds();
|
||||||
|
new_ucmd!().arg("-h").fails(); // -h is NOT --help, it is actually --no-dereference.
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn reference_errors() {
|
||||||
|
for args in &[
|
||||||
|
&["--verbose", "--reference"] as &[&str],
|
||||||
|
&["--verbose", "--reference=/dev/null"],
|
||||||
|
&["--verbose", "--reference=/inexistent", "/dev/null"],
|
||||||
|
] {
|
||||||
|
new_ucmd!().args(args).fails();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn recursive_errors() {
|
||||||
|
for args in &[
|
||||||
|
&["--verbose", "-P"] as &[&str],
|
||||||
|
&["--verbose", "-H"],
|
||||||
|
&["--verbose", "-L"],
|
||||||
|
&["--verbose", "--recursive", "-P", "--dereference"],
|
||||||
|
&["--verbose", "--recursive", "-H", "--no-dereference"],
|
||||||
|
&["--verbose", "--recursive", "-L", "--no-dereference"],
|
||||||
|
] {
|
||||||
|
new_ucmd!().args(args).fails();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn valid_context() {
|
||||||
|
let (dir, mut cmd) = at_and_ucmd!();
|
||||||
|
dir.touch("a.tmp");
|
||||||
|
dir.symlink_file("a.tmp", "la.tmp");
|
||||||
|
|
||||||
|
let la_context = get_file_context(dir.plus("a.tmp")).unwrap();
|
||||||
|
let new_la_context = "guest_u:object_r:etc_t:s0:c42";
|
||||||
|
|
||||||
|
cmd.args(&["--verbose", new_la_context])
|
||||||
|
.arg(dir.plus("la.tmp"))
|
||||||
|
.succeeds();
|
||||||
|
assert_eq!(get_file_context(dir.plus("la.tmp")).unwrap(), la_context);
|
||||||
|
assert_eq!(
|
||||||
|
get_file_context(dir.plus("a.tmp")).unwrap().as_deref(),
|
||||||
|
Some(new_la_context)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn valid_context_on_valid_symlink() {
|
||||||
|
let (dir, mut cmd) = at_and_ucmd!();
|
||||||
|
dir.touch("a.tmp");
|
||||||
|
dir.symlink_file("a.tmp", "la.tmp");
|
||||||
|
|
||||||
|
let a_context = get_file_context(dir.plus("a.tmp")).unwrap();
|
||||||
|
let new_la_context = "guest_u:object_r:etc_t:s0:c42";
|
||||||
|
|
||||||
|
cmd.args(&["--verbose", "--no-dereference", new_la_context])
|
||||||
|
.arg(dir.plus("la.tmp"))
|
||||||
|
.succeeds();
|
||||||
|
assert_eq!(
|
||||||
|
get_file_context(dir.plus("la.tmp")).unwrap().as_deref(),
|
||||||
|
Some(new_la_context)
|
||||||
|
);
|
||||||
|
assert_eq!(get_file_context(dir.plus("a.tmp")).unwrap(), a_context);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn valid_context_on_broken_symlink() {
|
||||||
|
let (dir, mut cmd) = at_and_ucmd!();
|
||||||
|
dir.symlink_file("a.tmp", "la.tmp");
|
||||||
|
|
||||||
|
let new_la_context = "guest_u:object_r:etc_t:s0:c42";
|
||||||
|
|
||||||
|
cmd.args(&["--verbose", "--no-dereference", new_la_context])
|
||||||
|
.arg(dir.plus("la.tmp"))
|
||||||
|
.succeeds();
|
||||||
|
assert_eq!(
|
||||||
|
get_file_context(dir.plus("la.tmp")).unwrap().as_deref(),
|
||||||
|
Some(new_la_context)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn valid_context_with_prior_xattributes() {
|
||||||
|
let (dir, mut cmd) = at_and_ucmd!();
|
||||||
|
dir.touch("a.tmp");
|
||||||
|
|
||||||
|
let a_context = get_file_context(dir.plus("a.tmp")).unwrap();
|
||||||
|
if a_context.is_none() {
|
||||||
|
set_file_context(dir.plus("a.tmp"), "unconfined_u:object_r:user_tmp_t:s0").unwrap();
|
||||||
|
}
|
||||||
|
let new_la_context = "guest_u:object_r:etc_t:s0:c42";
|
||||||
|
|
||||||
|
cmd.args(&["--verbose", new_la_context])
|
||||||
|
.arg(dir.plus("a.tmp"))
|
||||||
|
.succeeds();
|
||||||
|
assert_eq!(
|
||||||
|
get_file_context(dir.plus("a.tmp")).unwrap().as_deref(),
|
||||||
|
Some(new_la_context)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn valid_context_directory() {
|
||||||
|
let (dir, mut cmd) = at_and_ucmd!();
|
||||||
|
dir.mkdir("a");
|
||||||
|
dir.symlink_dir("a", "la");
|
||||||
|
|
||||||
|
let b_path = Path::new("a").join("b.txt");
|
||||||
|
dir.touch(b_path.to_str().unwrap());
|
||||||
|
|
||||||
|
let la_context = get_file_context(dir.plus("la")).unwrap();
|
||||||
|
let b_context = get_file_context(dir.plus(b_path.to_str().unwrap())).unwrap();
|
||||||
|
|
||||||
|
let new_la_context = "guest_u:object_r:etc_t:s0:c42";
|
||||||
|
|
||||||
|
cmd.args(&["--verbose", new_la_context])
|
||||||
|
.arg(dir.plus("la"))
|
||||||
|
.succeeds();
|
||||||
|
assert_eq!(get_file_context(dir.plus("la")).unwrap(), la_context);
|
||||||
|
assert_eq!(
|
||||||
|
get_file_context(dir.plus("a")).unwrap().as_deref(),
|
||||||
|
Some(new_la_context)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
get_file_context(dir.plus(b_path.to_str().unwrap())).unwrap(),
|
||||||
|
b_context
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn valid_context_directory_recursive() {
|
||||||
|
let (dir, mut cmd) = at_and_ucmd!();
|
||||||
|
dir.mkdir("a");
|
||||||
|
dir.symlink_dir("a", "la");
|
||||||
|
|
||||||
|
let b_path = Path::new("a").join("b.txt");
|
||||||
|
dir.touch(b_path.to_str().unwrap());
|
||||||
|
|
||||||
|
let a_context = get_file_context(dir.plus("a")).unwrap();
|
||||||
|
let b_context = get_file_context(dir.plus(b_path.to_str().unwrap())).unwrap();
|
||||||
|
|
||||||
|
let new_la_context = "guest_u:object_r:etc_t:s0:c42";
|
||||||
|
|
||||||
|
// -P (default): do not traverse any symbolic links.
|
||||||
|
cmd.args(&["--verbose", "--recursive", new_la_context])
|
||||||
|
.arg(dir.plus("la"))
|
||||||
|
.succeeds();
|
||||||
|
assert_eq!(
|
||||||
|
get_file_context(dir.plus("la")).unwrap().as_deref(),
|
||||||
|
Some(new_la_context)
|
||||||
|
);
|
||||||
|
assert_eq!(get_file_context(dir.plus("a")).unwrap(), a_context);
|
||||||
|
assert_eq!(
|
||||||
|
get_file_context(dir.plus(b_path.to_str().unwrap())).unwrap(),
|
||||||
|
b_context
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn valid_context_directory_recursive_follow_args_dir_symlinks() {
|
||||||
|
let (dir, mut cmd) = at_and_ucmd!();
|
||||||
|
dir.mkdir("a");
|
||||||
|
dir.symlink_dir("a", "la");
|
||||||
|
|
||||||
|
let b_path = Path::new("a").join("b.txt");
|
||||||
|
dir.touch(b_path.to_str().unwrap());
|
||||||
|
|
||||||
|
let la_context = get_file_context(dir.plus("la")).unwrap();
|
||||||
|
let new_la_context = "guest_u:object_r:etc_t:s0:c42";
|
||||||
|
|
||||||
|
/*
|
||||||
|
let lc_path = Path::new("a").join("lc");
|
||||||
|
dir.symlink_dir("c", lc_path.to_str().unwrap());
|
||||||
|
assert_eq!(
|
||||||
|
get_file_context(dir.plus(lc_path.to_str().unwrap())).unwrap(),
|
||||||
|
None
|
||||||
|
);
|
||||||
|
*/
|
||||||
|
|
||||||
|
// -H: if a command line argument is a symbolic link to a directory, traverse it.
|
||||||
|
cmd.args(&["--verbose", "--recursive", "-H", new_la_context])
|
||||||
|
.arg(dir.plus("la"))
|
||||||
|
.succeeds();
|
||||||
|
assert_eq!(
|
||||||
|
get_file_context(dir.plus("a")).unwrap().as_deref(),
|
||||||
|
Some(new_la_context)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
get_file_context(dir.plus(b_path.to_str().unwrap()))
|
||||||
|
.unwrap()
|
||||||
|
.as_deref(),
|
||||||
|
Some(new_la_context)
|
||||||
|
);
|
||||||
|
assert_eq!(get_file_context(dir.plus("la")).unwrap(), la_context);
|
||||||
|
/*
|
||||||
|
assert_eq!(
|
||||||
|
get_file_context(dir.plus(lc_path.to_str().unwrap()))
|
||||||
|
.unwrap()
|
||||||
|
.as_deref(),
|
||||||
|
Some(new_la_context)
|
||||||
|
);
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn valid_context_directory_recursive_follow_all_symlinks() {
|
||||||
|
let (dir, mut cmd) = at_and_ucmd!();
|
||||||
|
dir.mkdir("a");
|
||||||
|
dir.symlink_dir("a", "la");
|
||||||
|
|
||||||
|
let b_path = Path::new("a").join("b.txt");
|
||||||
|
dir.touch(b_path.to_str().unwrap());
|
||||||
|
|
||||||
|
let c_path = Path::new("a").join("c");
|
||||||
|
dir.touch(c_path.to_str().unwrap());
|
||||||
|
|
||||||
|
let lc_path = Path::new("a").join("lc");
|
||||||
|
dir.symlink_dir(c_path.to_str().unwrap(), lc_path.to_str().unwrap());
|
||||||
|
|
||||||
|
let la_context = get_file_context(dir.plus("la")).unwrap();
|
||||||
|
let lc_context = get_file_context(dir.plus(lc_path.to_str().unwrap())).unwrap();
|
||||||
|
|
||||||
|
let new_la_context = "guest_u:object_r:etc_t:s0:c42";
|
||||||
|
|
||||||
|
// -L: traverse every symbolic link to a directory encountered.
|
||||||
|
cmd.args(&["--verbose", "--recursive", "-L", new_la_context])
|
||||||
|
.arg(dir.plus("la"))
|
||||||
|
.succeeds();
|
||||||
|
assert_eq!(get_file_context(dir.plus("la")).unwrap(), la_context);
|
||||||
|
assert_eq!(
|
||||||
|
get_file_context(dir.plus("a")).unwrap().as_deref(),
|
||||||
|
Some(new_la_context)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
get_file_context(dir.plus(b_path.to_str().unwrap()))
|
||||||
|
.unwrap()
|
||||||
|
.as_deref(),
|
||||||
|
Some(new_la_context)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
get_file_context(dir.plus(lc_path.to_str().unwrap())).unwrap(),
|
||||||
|
lc_context
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
get_file_context(dir.plus(c_path.to_str().unwrap()))
|
||||||
|
.unwrap()
|
||||||
|
.as_deref(),
|
||||||
|
Some(new_la_context)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn user_role_range_type() {
|
||||||
|
let (dir, mut cmd) = at_and_ucmd!();
|
||||||
|
|
||||||
|
dir.touch("a.tmp");
|
||||||
|
let a_context = get_file_context(dir.plus("a.tmp")).unwrap();
|
||||||
|
if a_context.is_none() {
|
||||||
|
set_file_context(dir.plus("a.tmp"), "unconfined_u:object_r:user_tmp_t:s0").unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.args(&[
|
||||||
|
"--verbose",
|
||||||
|
"--user=guest_u",
|
||||||
|
"--role=object_r",
|
||||||
|
"--type=etc_t",
|
||||||
|
"--range=s0:c42",
|
||||||
|
])
|
||||||
|
.arg(dir.plus("a.tmp"))
|
||||||
|
.succeeds();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
get_file_context(dir.plus("a.tmp")).unwrap().as_deref(),
|
||||||
|
Some("guest_u:object_r:etc_t:s0:c42")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn user_change() {
|
||||||
|
let (dir, mut cmd) = at_and_ucmd!();
|
||||||
|
|
||||||
|
dir.touch("a.tmp");
|
||||||
|
let a_context = get_file_context(dir.plus("a.tmp")).unwrap();
|
||||||
|
let new_a_context = if let Some(a_context) = a_context {
|
||||||
|
let mut components: Vec<_> = a_context.split(':').collect();
|
||||||
|
components[0] = "guest_u";
|
||||||
|
components.join(":")
|
||||||
|
} else {
|
||||||
|
set_file_context(dir.plus("a.tmp"), "unconfined_u:object_r:user_tmp_t:s0").unwrap();
|
||||||
|
String::from("guest_u:object_r:user_tmp_t:s0")
|
||||||
|
};
|
||||||
|
|
||||||
|
cmd.args(&["--verbose", "--user=guest_u"])
|
||||||
|
.arg(dir.plus("a.tmp"))
|
||||||
|
.succeeds();
|
||||||
|
assert_eq!(
|
||||||
|
get_file_context(dir.plus("a.tmp")).unwrap(),
|
||||||
|
Some(new_a_context)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn role_change() {
|
||||||
|
let (dir, mut cmd) = at_and_ucmd!();
|
||||||
|
|
||||||
|
dir.touch("a.tmp");
|
||||||
|
let a_context = get_file_context(dir.plus("a.tmp")).unwrap();
|
||||||
|
let new_a_context = if let Some(a_context) = a_context {
|
||||||
|
let mut components: Vec<_> = a_context.split(':').collect();
|
||||||
|
components[1] = "system_r";
|
||||||
|
components.join(":")
|
||||||
|
} else {
|
||||||
|
set_file_context(dir.plus("a.tmp"), "unconfined_u:object_r:user_tmp_t:s0").unwrap();
|
||||||
|
String::from("unconfined_u:system_r:user_tmp_t:s0")
|
||||||
|
};
|
||||||
|
|
||||||
|
cmd.args(&["--verbose", "--role=system_r"])
|
||||||
|
.arg(dir.plus("a.tmp"))
|
||||||
|
.succeeds();
|
||||||
|
assert_eq!(
|
||||||
|
get_file_context(dir.plus("a.tmp")).unwrap(),
|
||||||
|
Some(new_a_context)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn type_change() {
|
||||||
|
let (dir, mut cmd) = at_and_ucmd!();
|
||||||
|
|
||||||
|
dir.touch("a.tmp");
|
||||||
|
let a_context = get_file_context(dir.plus("a.tmp")).unwrap();
|
||||||
|
let new_a_context = if let Some(a_context) = a_context {
|
||||||
|
let mut components: Vec<_> = a_context.split(':').collect();
|
||||||
|
components[2] = "etc_t";
|
||||||
|
components.join(":")
|
||||||
|
} else {
|
||||||
|
set_file_context(dir.plus("a.tmp"), "unconfined_u:object_r:user_tmp_t:s0").unwrap();
|
||||||
|
String::from("unconfined_u:object_r:etc_t:s0")
|
||||||
|
};
|
||||||
|
|
||||||
|
cmd.args(&["--verbose", "--type=etc_t"])
|
||||||
|
.arg(dir.plus("a.tmp"))
|
||||||
|
.succeeds();
|
||||||
|
assert_eq!(
|
||||||
|
get_file_context(dir.plus("a.tmp")).unwrap(),
|
||||||
|
Some(new_a_context)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn range_change() {
|
||||||
|
let (dir, mut cmd) = at_and_ucmd!();
|
||||||
|
|
||||||
|
dir.touch("a.tmp");
|
||||||
|
let a_context = get_file_context(dir.plus("a.tmp")).unwrap();
|
||||||
|
let new_a_context = if let Some(a_context) = a_context {
|
||||||
|
a_context
|
||||||
|
.split(':')
|
||||||
|
.take(3)
|
||||||
|
.chain(iter::once("s0:c42"))
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(":")
|
||||||
|
} else {
|
||||||
|
set_file_context(dir.plus("a.tmp"), "unconfined_u:object_r:user_tmp_t:s0").unwrap();
|
||||||
|
String::from("unconfined_u:object_r:user_tmp_t:s0:c42")
|
||||||
|
};
|
||||||
|
|
||||||
|
cmd.args(&["--verbose", "--range=s0:c42"])
|
||||||
|
.arg(dir.plus("a.tmp"))
|
||||||
|
.succeeds();
|
||||||
|
assert_eq!(
|
||||||
|
get_file_context(dir.plus("a.tmp")).unwrap(),
|
||||||
|
Some(new_a_context)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn valid_reference() {
|
||||||
|
let (dir, mut cmd) = at_and_ucmd!();
|
||||||
|
|
||||||
|
dir.touch("a.tmp");
|
||||||
|
let new_a_context = "guest_u:object_r:etc_t:s0:c42";
|
||||||
|
set_file_context(dir.plus("a.tmp"), new_a_context).unwrap();
|
||||||
|
|
||||||
|
dir.touch("b.tmp");
|
||||||
|
let b_context = get_file_context(dir.plus("b.tmp")).unwrap();
|
||||||
|
assert_ne!(b_context.as_deref(), Some(new_a_context));
|
||||||
|
|
||||||
|
cmd.arg("--verbose")
|
||||||
|
.arg(format!("--reference={}", dir.plus_as_string("a.tmp")))
|
||||||
|
.arg(dir.plus("b.tmp"))
|
||||||
|
.succeeds();
|
||||||
|
assert_eq!(
|
||||||
|
get_file_context(dir.plus("b.tmp")).unwrap().as_deref(),
|
||||||
|
Some(new_a_context)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_file_context(path: impl AsRef<Path>) -> Result<Option<String>, selinux::errors::Error> {
|
||||||
|
let path = path.as_ref();
|
||||||
|
match selinux::SecurityContext::of_path(path, false, false) {
|
||||||
|
Err(r) => {
|
||||||
|
println!("get_file_context failed: '{}': {}.", path.display(), &r);
|
||||||
|
Err(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(None) => {
|
||||||
|
println!(
|
||||||
|
"get_file_context: '{}': No SELinux context defined.",
|
||||||
|
path.display()
|
||||||
|
);
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Some(context)) => {
|
||||||
|
let bytes = context
|
||||||
|
.as_bytes()
|
||||||
|
.splitn(2, |&b| b == 0_u8)
|
||||||
|
.next()
|
||||||
|
.unwrap_or_default();
|
||||||
|
let context = String::from_utf8(bytes.into()).unwrap_or_default();
|
||||||
|
println!("get_file_context: '{}' => '{}'.", context, path.display());
|
||||||
|
Ok(Some(context))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_file_context(path: impl AsRef<Path>, context: &str) -> Result<(), selinux::errors::Error> {
|
||||||
|
let c_context = CString::new(context.as_bytes()).map_err(|_r| selinux::errors::Error::IO {
|
||||||
|
source: io::Error::from(io::ErrorKind::InvalidInput),
|
||||||
|
operation: "CString::new",
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let path = path.as_ref();
|
||||||
|
let r =
|
||||||
|
selinux::SecurityContext::from_c_str(&c_context, false).set_for_path(path, false, false);
|
||||||
|
if let Err(r) = &r {
|
||||||
|
println!(
|
||||||
|
"set_file_context failed: '{}' => '{}': {}.",
|
||||||
|
context,
|
||||||
|
path.display(),
|
||||||
|
r
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
println!("set_file_context: '{}' => '{}'.", context, path.display())
|
||||||
|
}
|
||||||
|
r
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user