id: add support for showing SELinux context (--context/-Z)

This commit is contained in:
Jan Scheer 2021-06-19 20:26:28 +02:00
parent e2a00b67ed
commit f1d317147b
7 changed files with 315 additions and 23 deletions

View file

@ -11,7 +11,7 @@ env:
PROJECT_NAME: coreutils
PROJECT_DESC: "Core universal (cross-platform) utilities"
PROJECT_AUTH: "uutils"
RUST_MIN_SRV: "1.43.1" ## v1.43.0
RUST_MIN_SRV: "1.51.0" ## v1.43.0
RUST_COV_SRV: "2020-08-01" ## (~v1.47.0) supported rust version for code coverage; (date required/used by 'coverage') ## !maint: refactor when code coverage support is included in the stable channel
on: [push, pull_request]
@ -222,7 +222,7 @@ jobs:
- name: "Run make build"
shell: bash
run: |
sudo apt-get -y update ; sudo apt-get -y install python3-sphinx;
sudo apt-get -y update ; sudo apt-get -y install python3-sphinx libselinux1 libselinux1-dev ;
make build
build:
@ -260,6 +260,7 @@ jobs:
esac
case '${{ matrix.job.os }}' in
macos-latest) brew install coreutils ;; # needed for testing
ubuntu-latest) sudo apt-get -y update ; sudo apt-get -y install libselinux1 libselinux1-dev ;; # TODO: probably redundant here
esac
- name: Initialize workflow variables
id: vars

View file

@ -45,7 +45,14 @@ jobs:
- name: Run GNU tests
shell: bash
run: |
bash uutils/util/run-gnu-test.sh
# bash uutils/util/run-gnu-test.sh # TODO: revert after testing
bash uutils/util/run-gnu-test.sh tests/id/context.sh
bash uutils/util/run-gnu-test.sh tests/id/no-context.sh
bash uutils/util/run-gnu-test.sh tests/id/smack.sh
bash uutils/util/run-gnu-test.sh tests/id/uid.sh
bash uutils/util/run-gnu-test.sh tests/id/setgid.sh
bash uutils/util/run-gnu-test.sh tests/id/zero.sh
bash uutils/util/run-gnu-test.sh tests/id/gnu-zero-uids.sh
- name: Extract tests info
shell: bash
run: |

164
Cargo.lock generated
View file

@ -71,6 +71,29 @@ dependencies = [
"compare",
]
[[package]]
name = "bindgen"
version = "0.58.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f8523b410d7187a43085e7e064416ea32ded16bd0a4e6fc025e21616d01258f"
dependencies = [
"bitflags",
"cexpr",
"clang-sys",
"clap",
"env_logger 0.8.4",
"lazy_static",
"lazycell",
"log",
"peeking_take_while",
"proc-macro2",
"quote 1.0.9",
"regex",
"rustc-hash",
"shlex",
"which",
]
[[package]]
name = "bit-set"
version = "0.5.2"
@ -142,6 +165,15 @@ version = "1.0.68"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a72c244c1ff497a746a7e1fb3d14bd08420ecda70c8f25c7112f2781652d787"
[[package]]
name = "cexpr"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4aedb84272dbe89af497cf81375129abda4fc0a9e7c5d317498c15cc30c0d27"
dependencies = [
"nom",
]
[[package]]
name = "cfg-if"
version = "0.1.10"
@ -167,6 +199,17 @@ dependencies = [
"winapi 0.3.9",
]
[[package]]
name = "clang-sys"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "853eda514c284c2287f4bf20ae614f8781f40a81d32ecda6e91449304dfe077c"
dependencies = [
"glob 0.3.0",
"libc",
"libloading",
]
[[package]]
name = "clap"
version = "2.33.3"
@ -228,6 +271,7 @@ dependencies = [
"rand 0.7.3",
"regex",
"rlimit",
"selinux",
"sha1",
"tempfile",
"textwrap",
@ -578,6 +622,19 @@ dependencies = [
"regex",
]
[[package]]
name = "env_logger"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a19187fea3ac7e84da7dacf48de0c45d63c6a76f9490dae389aead16c243fce3"
dependencies = [
"atty",
"humantime",
"log",
"regex",
"termcolor",
]
[[package]]
name = "fake-simd"
version = "0.1.2"
@ -727,6 +784,12 @@ dependencies = [
"winapi 0.3.9",
]
[[package]]
name = "humantime"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
[[package]]
name = "if_rust_version"
version = "1.0.0"
@ -782,12 +845,28 @@ version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "lazycell"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
[[package]]
name = "libc"
version = "0.2.85"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ccac4b00700875e6a07c6cde370d44d32fa01c5a65cdd2fca6858c479d28bb3"
[[package]]
name = "libloading"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f84d96438c15fcd6c3f244c8fce01d1e2b9c6b5623e9c711dc9286d8fc92d6a"
dependencies = [
"cfg-if 1.0.0",
"winapi 0.3.9",
]
[[package]]
name = "locale"
version = "0.2.2"
@ -919,6 +998,16 @@ version = "0.1.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb"
[[package]]
name = "nom"
version = "5.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ffb4262d26ed83a1c0a33a38fe2bb15797329c85770da05e6b828ddb782627af"
dependencies = [
"memchr 2.4.0",
"version_check",
]
[[package]]
name = "ntapi"
version = "0.3.6"
@ -982,9 +1071,9 @@ checksum = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef"
[[package]]
name = "once_cell"
version = "1.7.2"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af8b08b04175473088b46763e51ee54da5f9a164bc162f615b91bc179dbf15a3"
checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56"
[[package]]
name = "onig"
@ -1084,6 +1173,12 @@ dependencies = [
"proc-macro-hack",
]
[[package]]
name = "peeking_take_while"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
[[package]]
name = "pkg-config"
version = "0.3.19"
@ -1175,7 +1270,7 @@ version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a44883e74aa97ad63db83c4bf8ca490f02b2fc02f92575e720c8551e843c945f"
dependencies = [
"env_logger",
"env_logger 0.7.1",
"log",
"rand 0.7.3",
"rand_core 0.5.1",
@ -1364,6 +1459,12 @@ dependencies = [
"redox_syscall 0.2.8",
]
[[package]]
name = "reference-counted-singleton"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e03aec30be874d0786379a6ee483c653c58dd6863ad4ed873e697ed968f9358"
[[package]]
name = "regex"
version = "1.5.4"
@ -1418,6 +1519,12 @@ version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3e52c148ef37f8c375d49d5a73aa70713125b7f19095948a923f80afdeb22ec2"
[[package]]
name = "rustc-hash"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
[[package]]
name = "same-file"
version = "1.0.6"
@ -1433,6 +1540,32 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "selinux"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a5e36fd4d2d189b51696bcff8d45a8defe6d9c3a353a6b1449ccc46d22a81d8e"
dependencies = [
"bitflags",
"libc",
"once_cell",
"reference-counted-singleton",
"selinux-sys",
"thiserror",
]
[[package]]
name = "selinux-sys"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9dc2e3e97d611bf255de85a72a385565d1f6340bc6fc6fd6402555b54382a9f"
dependencies = [
"bindgen",
"cc",
"dunce",
"walkdir",
]
[[package]]
name = "semver"
version = "0.9.0"
@ -1479,6 +1612,12 @@ dependencies = [
"generic-array",
]
[[package]]
name = "shlex"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42a568c8f2cd051a4d283bd6eb0343ac214c1b0f1ac19f93e1175b2dee38c73d"
[[package]]
name = "signal-hook"
version = "0.1.17"
@ -1599,6 +1738,15 @@ dependencies = [
"winapi 0.3.9",
]
[[package]]
name = "termcolor"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4"
dependencies = [
"winapi-util",
]
[[package]]
name = "termion"
version = "1.5.6"
@ -2073,6 +2221,7 @@ name = "uu_id"
version = "0.0.6"
dependencies = [
"clap",
"selinux",
"uucore",
"uucore_procs",
]
@ -2830,6 +2979,15 @@ version = "0.10.2+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
[[package]]
name = "which"
version = "3.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d011071ae14a2f6671d0b74080ae0cd8ebf3a6f8c9589a2cd45f23126fe29724"
dependencies = [
"libc",
]
[[package]]
name = "wild"
version = "2.0.4"

View file

@ -352,6 +352,7 @@ unindent = "0.1"
uucore = { version=">=0.0.8", package="uucore", path="src/uucore", features=["entries", "process"] }
walkdir = "2.2"
atty = "0.2"
selinux = "0.1.1"
[target.'cfg(unix)'.dev-dependencies]
rlimit = "0.4.0"

View file

@ -18,6 +18,7 @@ path = "src/id.rs"
clap = "2.33"
uucore = { version=">=0.0.8", package="uucore", path="../../uucore", features=["entries", "process"] }
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
selinux = "0.1.1"
[[bin]]
name = "id"

View file

@ -26,7 +26,7 @@
// * Help text based on BSD's `id` manpage and GNU's `id` manpage.
//
// spell-checker:ignore (ToDO) asid auditid auditinfo auid cstr egid emod euid getaudit getlogin gflag nflag pline rflag termid uflag gsflag zflag testsuite
// spell-checker:ignore (ToDO) asid auditid auditinfo auid cstr egid emod euid getaudit getlogin gflag nflag pline rflag termid uflag gsflag zflag cflag testsuite
#![allow(non_camel_case_types)]
#![allow(dead_code)]
@ -35,6 +35,8 @@
extern crate uucore;
use clap::{crate_version, App, Arg};
#[cfg(target_os = "linux")]
use selinux::{self, KernelSupport, SecurityContext};
use std::ffi::CStr;
use uucore::entries::{self, Group, Locate, Passwd};
pub use uucore::libc;
@ -93,6 +95,8 @@ struct State {
gsflag: bool, // --groups
rflag: bool, // --real
zflag: bool, // --zero
cflag: bool, // --context
selinux_supported: bool,
ids: Option<Ids>,
// The behavior for calling GNU's `id` and calling GNU's `id $USER` is similar but different.
// * The SELinux context is only displayed without a specified user.
@ -147,6 +151,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
Arg::with_name(options::OPT_GROUP)
.short("g")
.long(options::OPT_GROUP)
.conflicts_with(options::OPT_EFFECTIVE_USER)
.help("Display only the effective group ID as a number"),
)
.arg(
@ -156,6 +161,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
.conflicts_with_all(&[
options::OPT_GROUP,
options::OPT_EFFECTIVE_USER,
options::OPT_CONTEXT,
options::OPT_HUMAN_READABLE,
options::OPT_PASSWORD,
options::OPT_AUDIT,
@ -207,7 +213,8 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
Arg::with_name(options::OPT_CONTEXT)
.short("Z")
.long(options::OPT_CONTEXT)
.help("NotImplemented: print only the security context of the process"),
.conflicts_with_all(&[options::OPT_GROUP, options::OPT_EFFECTIVE_USER])
.help("print only the security context of the process"),
)
.arg(
Arg::with_name(options::ARG_USERS)
@ -229,6 +236,12 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
gsflag: matches.is_present(options::OPT_GROUPS),
rflag: matches.is_present(options::OPT_REAL_ID),
zflag: matches.is_present(options::OPT_ZERO),
cflag: matches.is_present(options::OPT_CONTEXT),
#[cfg(not(target_os = "linux"))]
selinux_supported: false,
#[cfg(target_os = "linux")]
selinux_supported: selinux::kernel_support() != KernelSupport::Unsupported,
user_specified: !users.is_empty(),
ids: None,
};
@ -238,13 +251,16 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
!(state.uflag || state.gflag || state.gsflag)
};
if (state.nflag || state.rflag) && default_format {
if (state.nflag || state.rflag) && default_format && !state.cflag {
crash!(1, "cannot print only names or real IDs in default format");
}
if (state.zflag) && default_format {
if state.zflag && default_format && !state.cflag {
// NOTE: GNU testsuite "id/zero.sh" needs this stderr output:
crash!(1, "option --zero not permitted in default format");
}
if state.user_specified && state.cflag {
crash!(1, "cannot print security context when user specified");
}
let delimiter = {
if state.zflag {
@ -262,6 +278,23 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
};
let mut exit_code = 0;
if state.cflag {
if state.selinux_supported {
// print SElinux context and exit
#[cfg(target_os = "linux")]
if let Ok(context) = SecurityContext::current(false) {
let bytes = context.as_bytes();
print!("{}{}", String::from_utf8_lossy(bytes), line_ending);
} else {
// print error because `cflag` was explicitly requested
crash!(1, "can't get process context");
}
return exit_code;
} else {
crash!(1, "--context (-Z) works only on an SELinux-enabled kernel");
}
}
for i in 0..=users.len() {
let possible_pw = if !state.user_specified {
None
@ -518,11 +551,17 @@ fn id_print(state: &State, groups: Vec<u32>) {
.join(",")
);
// NOTE: (SELinux NotImplemented) placeholder:
// if !state.user_specified {
// // print SElinux context (does not depend on "-Z")
// print!(" context={}", get_selinux_contexts().join(":"));
// }
#[cfg(target_os = "linux")]
if state.selinux_supported
&& !state.user_specified
&& std::env::var_os("POSIXLY_CORRECT").is_none()
{
// print SElinux context (does not depend on "-Z")
if let Ok(context) = SecurityContext::current(false) {
let bytes = context.as_bytes();
print!(" context={}", String::from_utf8_lossy(bytes));
}
}
}
#[cfg(not(target_os = "linux"))]

View file

@ -52,14 +52,6 @@ fn test_id_no_specified_user() {
let exp_result = unwrap_or_return!(expected_result(&[]));
let mut _exp_stdout = exp_result.stdout_str().to_string();
#[cfg(target_os = "linux")]
{
// NOTE: (SELinux NotImplemented) strip 'context' part from exp_stdout:
if let Some(context_offset) = exp_result.stdout_str().find(" context=") {
_exp_stdout.replace_range(context_offset.._exp_stdout.len() - 1, "");
}
}
result
.stdout_is(_exp_stdout)
.stderr_is(exp_result.stderr_str())
@ -425,6 +417,99 @@ fn test_id_zero() {
}
}
#[test]
#[cfg(target_os = "linux")]
fn test_id_context() {
use selinux::{self, KernelSupport};
if selinux::kernel_support() == KernelSupport::Unsupported {
println!(
"{}: test skipped: Kernel has no support for SElinux context",
UUTILS_INFO
);
return;
}
let scene = TestScenario::new(util_name!());
for c_flag in &["-Z", "--context"] {
scene
.ucmd()
.args(&[c_flag])
.succeeds()
.stdout_only(unwrap_or_return!(expected_result(&[c_flag])).stdout_str());
for &z_flag in &["-z", "--zero"] {
let args = [c_flag, z_flag];
scene
.ucmd()
.args(&args)
.succeeds()
.stdout_only(unwrap_or_return!(expected_result(&args)).stdout_str());
for &opt1 in &["--name", "--real"] {
// id: cannot print only names or real IDs in default format
let args = [opt1, c_flag];
scene
.ucmd()
.args(&args)
.succeeds()
.stdout_only(unwrap_or_return!(expected_result(&args)).stdout_str());
let args = [opt1, c_flag, z_flag];
scene
.ucmd()
.args(&args)
.succeeds()
.stdout_only(unwrap_or_return!(expected_result(&args)).stdout_str());
for &opt2 in &["--user", "--group", "--groups"] {
// u/g/G n/r z Z
// for now, we print clap's standard response for "conflicts_with" instead of:
// id: cannot print "only" of more than one choice
let args = [opt2, c_flag, opt1];
let _result = scene.ucmd().args(&args).fails();
// let exp_result = unwrap_or_return!(expected_result(&args));
// result
// .stdout_is(exp_result.stdout_str())
// .stderr_is(exp_result.stderr_str())
// .code_is(exp_result.code());
}
}
for &opt2 in &["--user", "--group", "--groups"] {
// u/g/G z Z
// for now, we print clap's standard response for "conflicts_with" instead of:
// id: cannot print "only" of more than one choice
let args = [opt2, c_flag];
let _result = scene.ucmd().args(&args).fails();
// let exp_result = unwrap_or_return!(expected_result(&args));
// result
// .stdout_is(exp_result.stdout_str())
// .stderr_is(exp_result.stderr_str())
// .code_is(exp_result.code());
}
}
}
}
#[test]
#[cfg(unix)]
fn test_id_no_specified_user_posixly() {
// gnu/tests/id/no-context.sh
let scene = TestScenario::new(util_name!());
let result = scene.ucmd().env("POSIXLY_CORRECT", "1").succeeds();
assert!(!result.stdout_str().contains("context="));
#[cfg(target_os = "linux")]
{
use selinux::{self, KernelSupport};
if selinux::kernel_support() == KernelSupport::Unsupported {
println!(
"{}: test skipped: Kernel has no support for SElinux context",
UUTILS_INFO
);
return;
} else {
let result = scene.ucmd().succeeds();
assert!(result.stdout_str().contains("context="));
}
}
}
fn check_coreutil_version(util_name: &str, version_expected: &str) -> String {
// example:
// $ id --version | head -n 1