Skip to content

Commit b7809ea

Browse files
committed
Add SMACK support for id, ls, mkdir, mkfifo, mknod
1 parent efa1aa7 commit b7809ea

26 files changed

Lines changed: 833 additions & 13 deletions

.github/workflows/GnuTests.yml

Lines changed: 70 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ name: GnuTests
22

33
# spell-checker:ignore (abbrev/names) CodeCov gnulib GnuTests Swatinem
44
# spell-checker:ignore (jargon) submodules devel
5-
# spell-checker:ignore (libs/utils) autopoint chksum dpkg getenforce getlimits gperf lcov libexpect limactl pyinotify setenforce shopt texinfo valgrind libattr libcap taiki-e
5+
# spell-checker:ignore (libs/utils) autopoint chksum dpkg getenforce getlimits gperf lcov libexpect limactl pyinotify setenforce shopt texinfo valgrind libattr libcap taiki-e zstd cpio
66
# spell-checker:ignore (options) Ccodegen Coverflow Cpanic Zpanic
77
# spell-checker:ignore (people) Dawid Dziurla * dawidd dtolnay
88
# spell-checker:ignore (vars) FILESET SUBDIRS XPASS
@@ -31,6 +31,8 @@ env:
3131
TEST_STTY_FULL_SUMMARY_FILE: 'gnu-stty-full-result.json'
3232
TEST_SELINUX_FULL_SUMMARY_FILE: 'selinux-gnu-full-result.json'
3333
TEST_SELINUX_ROOT_FULL_SUMMARY_FILE: 'selinux-root-gnu-full-result.json'
34+
TEST_SMACK_FULL_SUMMARY_FILE: 'smack-gnu-full-result.json'
35+
TEST_SMACK_ROOT_FULL_SUMMARY_FILE: 'smack-root-gnu-full-result.json'
3436

3537
jobs:
3638
native:
@@ -318,8 +320,59 @@ jobs:
318320
gnu/tests-selinux/*.log
319321
gnu/tests-selinux/*/*.log.gz
320322
323+
smack:
324+
name: Run GNU tests (SMACK)
325+
runs-on: ubuntu-24.04
326+
steps:
327+
- name: Checkout code (uutils)
328+
uses: actions/checkout@v6
329+
with:
330+
path: 'uutils'
331+
persist-credentials: false
332+
- uses: dtolnay/rust-toolchain@master
333+
with:
334+
toolchain: stable
335+
components: rustfmt
336+
- uses: Swatinem/rust-cache@v2
337+
with:
338+
workspaces: "./uutils -> target"
339+
- name: Checkout code (GNU coreutils)
340+
run: (mkdir -p gnu && cd gnu && bash ../uutils/util/fetch-gnu.sh)
341+
- name: Install dependencies
342+
run: |
343+
sudo apt-get update
344+
sudo apt-get install -y qemu-system-x86 zstd cpio
345+
- name: Run GNU SMACK tests
346+
run: |
347+
cd uutils
348+
bash util/run-gnu-tests-smack-ci.sh "$GITHUB_WORKSPACE/gnu" "$GITHUB_WORKSPACE/gnu/tests-smack"
349+
- name: Extract testing info into JSON
350+
run: |
351+
python3 uutils/util/gnu-json-result.py gnu/tests-smack > ${{ env.TEST_SMACK_FULL_SUMMARY_FILE }}
352+
# SMACK tests run as root in QEMU, so use same results for root
353+
cp ${{ env.TEST_SMACK_FULL_SUMMARY_FILE }} ${{ env.TEST_SMACK_ROOT_FULL_SUMMARY_FILE }}
354+
- name: Upload SMACK json results
355+
uses: actions/upload-artifact@v5
356+
with:
357+
name: smack-gnu-full-result
358+
path: ${{ env.TEST_SMACK_FULL_SUMMARY_FILE }}
359+
- name: Upload SMACK root json results
360+
uses: actions/upload-artifact@v5
361+
with:
362+
name: smack-root-gnu-full-result
363+
path: ${{ env.TEST_SMACK_ROOT_FULL_SUMMARY_FILE }}
364+
- name: Compress SMACK test logs
365+
run: gzip gnu/tests-smack/*/*.log 2>/dev/null || true
366+
- name: Upload SMACK test logs
367+
uses: actions/upload-artifact@v5
368+
with:
369+
name: smack-test-logs
370+
path: |
371+
gnu/tests-smack/*.log
372+
gnu/tests-smack/*/*.log.gz
373+
321374
aggregate:
322-
needs: [native, selinux]
375+
needs: [native, selinux, smack]
323376
permissions:
324377
actions: read # for dawidd6/action-download-artifact to query and download artifacts
325378
contents: read # for actions/checkout to fetch code
@@ -384,6 +437,19 @@ jobs:
384437
name: selinux-root-gnu-full-result
385438
path: results
386439
merge-multiple: true
440+
441+
- name: Download smack json results
442+
uses: actions/download-artifact@v6
443+
with:
444+
name: smack-gnu-full-result
445+
path: results
446+
merge-multiple: true
447+
- name: Download smack root json results
448+
uses: actions/download-artifact@v6
449+
with:
450+
name: smack-root-gnu-full-result
451+
path: results
452+
merge-multiple: true
387453
- name: Extract/summarize testing info
388454
id: summary
389455
shell: bash
@@ -394,8 +460,8 @@ jobs:
394460
path_UUTILS='uutils'
395461
396462
json_count=$(ls -l results/*.json | wc -l)
397-
if [[ "$json_count" -ne 5 ]]; then
398-
echo "::error ::Failed to download all results json files (expected 4 files, found $json_count); failing early"
463+
if [[ "$json_count" -ne 7 ]]; then
464+
echo "::error ::Failed to download all results json files (expected 7 files, found $json_count); failing early"
399465
ls -lR results || true
400466
exit 1
401467
fi

Cargo.toml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,16 @@ feat_selinux = [
6565
"selinux",
6666
"stat/selinux",
6767
]
68+
# "feat_smack" == enable support for SMACK Security Context (by using `--features feat_smack`)
69+
# NOTE:
70+
# * Running a uutils compiled with `feat_smack` requires a SMACK enabled Kernel at run time.
71+
feat_smack = [
72+
"id/smack",
73+
"ls/smack",
74+
"mkdir/smack",
75+
"mkfifo/smack",
76+
"mknod/smack",
77+
]
6878
##
6979
## feature sets
7080
## (common/core and Tier1) feature sets

src/uu/id/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,4 @@ path = "src/main.rs"
2929

3030
[features]
3131
feat_selinux = ["selinux"]
32+
smack = ["uucore/smack"]

src/uu/id/src/id.rs

Lines changed: 45 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,10 @@ macro_rules! cstr2cow {
6262
}
6363

6464
fn get_context_help_text() -> String {
65-
#[cfg(not(feature = "selinux"))]
66-
return translate!("id-context-help-disabled");
67-
#[cfg(feature = "selinux")]
65+
#[cfg(any(feature = "selinux", feature = "smack"))]
6866
return translate!("id-context-help-enabled");
67+
#[cfg(not(any(feature = "selinux", feature = "smack")))]
68+
return translate!("id-context-help-disabled");
6969
}
7070

7171
mod options {
@@ -99,6 +99,7 @@ struct State {
9999
zflag: bool, // --zero
100100
cflag: bool, // --context
101101
selinux_supported: bool,
102+
smack_supported: bool,
102103
ids: Option<Ids>,
103104
// The behavior for calling GNU's `id` and calling GNU's `id $USER` is similar but different.
104105
// * The SELinux context is only displayed without a specified user.
@@ -146,6 +147,16 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
146147
false
147148
}
148149
},
150+
smack_supported: {
151+
#[cfg(feature = "smack")]
152+
{
153+
uucore::smack::is_smack_enabled()
154+
}
155+
#[cfg(not(feature = "smack"))]
156+
{
157+
false
158+
}
159+
},
149160
user_specified: !users.is_empty(),
150161
ids: None,
151162
};
@@ -179,7 +190,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
179190
let line_ending = LineEnding::from_zero_flag(state.zflag);
180191

181192
if state.cflag {
182-
return if state.selinux_supported {
193+
if state.selinux_supported {
183194
// print SElinux context and exit
184195
#[cfg(all(any(target_os = "linux", target_os = "android"), feature = "selinux"))]
185196
if let Ok(context) = selinux::SecurityContext::current(false) {
@@ -192,13 +203,28 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
192203
translate!("id-error-cannot-get-context"),
193204
));
194205
}
195-
Ok(())
206+
return Ok(());
207+
} else if state.smack_supported {
208+
// print SMACK context and exit
209+
#[cfg(all(target_os = "linux", feature = "smack"))]
210+
match uucore::smack::get_smack_label_for_self() {
211+
Ok(label) => {
212+
print!("{}{line_ending}", label);
213+
}
214+
Err(_) => {
215+
return Err(USimpleError::new(
216+
1,
217+
translate!("id-error-cannot-get-context"),
218+
));
219+
}
220+
}
221+
return Ok(());
196222
} else {
197-
Err(USimpleError::new(
223+
return Err(USimpleError::new(
198224
1,
199225
translate!("id-error-context-selinux-only"),
200-
))
201-
};
226+
));
227+
}
202228
}
203229

204230
for i in 0..=users.len() {
@@ -676,6 +702,17 @@ fn id_print(state: &State, groups: &[u32]) {
676702
print!(" context={}", String::from_utf8_lossy(bytes));
677703
}
678704
}
705+
706+
#[cfg(all(target_os = "linux", feature = "smack"))]
707+
if state.smack_supported
708+
&& !state.user_specified
709+
&& std::env::var_os("POSIXLY_CORRECT").is_none()
710+
{
711+
// print SMACK context (does not depend on "-Z")
712+
if let Ok(label) = uucore::smack::get_smack_label_for_self() {
713+
print!(" context={}", label);
714+
}
715+
}
679716
}
680717

681718
#[cfg(not(any(target_os = "linux", target_os = "android", target_os = "openbsd")))]

src/uu/ls/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,3 +60,4 @@ harness = false
6060

6161
[features]
6262
feat_selinux = ["selinux", "uucore/selinux"]
63+
smack = ["uucore/smack"]

src/uu/ls/src/ls.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -366,6 +366,7 @@ pub struct Config {
366366
time_format_older: Option<String>, // Time format for older dates (optional, if not present, time_format_recent is used)
367367
context: bool,
368368
selinux_supported: bool,
369+
smack_supported: bool,
369370
group_directories_first: bool,
370371
line_ending: LineEnding,
371372
dired: bool,
@@ -1167,6 +1168,16 @@ impl Config {
11671168
false
11681169
}
11691170
},
1171+
smack_supported: {
1172+
#[cfg(all(feature = "smack", target_os = "linux"))]
1173+
{
1174+
uucore::smack::is_smack_enabled()
1175+
}
1176+
#[cfg(not(all(feature = "smack", target_os = "linux")))]
1177+
{
1178+
false
1179+
}
1180+
},
11701181
group_directories_first: options.get_flag(options::GROUP_DIRECTORIES_FIRST),
11711182
line_ending: LineEnding::from_zero_flag(options.get_flag(options::ZERO)),
11721183
dired,
@@ -3418,6 +3429,30 @@ fn get_security_context<'a>(
34183429
}
34193430
}
34203431

3432+
if config.smack_supported {
3433+
#[cfg(all(feature = "smack", target_os = "linux"))]
3434+
{
3435+
// For SMACK, use the path to get the label
3436+
// If must_dereference is true, we follow the symlink
3437+
let target_path = if must_dereference {
3438+
match std::fs::canonicalize(path) {
3439+
Ok(p) => p,
3440+
Err(_) => path.to_path_buf(),
3441+
}
3442+
} else {
3443+
path.to_path_buf()
3444+
};
3445+
3446+
match uucore::smack::get_smack_label_for_path(&target_path) {
3447+
Ok(label) => return Cow::Owned(label),
3448+
Err(_) => {
3449+
// No label or error getting label
3450+
return Cow::Borrowed(SUBSTITUTE_STRING);
3451+
}
3452+
}
3453+
}
3454+
}
3455+
34213456
Cow::Borrowed(SUBSTITUTE_STRING)
34223457
}
34233458

src/uu/mkdir/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ fluent = { workspace = true }
2424

2525
[features]
2626
selinux = ["uucore/selinux"]
27+
smack = ["uucore/smack"]
2728

2829
[[bin]]
2930
name = "mkdir"

src/uu/mkdir/locales/en-US.ftl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ mkdir-error-empty-directory-name = cannot create directory '': No such file or d
1414
mkdir-error-file-exists = { $path }: File exists
1515
mkdir-error-failed-to-create-tree = failed to create whole tree
1616
mkdir-error-cannot-set-permissions = cannot set permissions { $path }
17+
mkdir-error-smack-context = failed to set default file creation context to '{ $context }':
1718
1819
# Verbose output
1920
mkdir-verbose-created-directory = { $util_name }: created directory { $path }

src/uu/mkdir/src/mkdir.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,17 @@ fn create_single_dir(path: &Path, is_parent: bool, config: &Config) -> UResult<(
300300
}
301301
}
302302

303+
// Apply SMACK context if requested
304+
#[cfg(feature = "smack")]
305+
if config.set_selinux_context && uucore::smack::is_smack_enabled() {
306+
if let Some(ctx) = config.context {
307+
if let Err(e) = uucore::smack::set_smack_label_for_path(path, ctx) {
308+
let _ = std::fs::remove_dir(path);
309+
return Err(USimpleError::new(1, e.to_string()));
310+
}
311+
}
312+
}
313+
303314
Ok(())
304315
}
305316

src/uu/mkfifo/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ fluent = { workspace = true }
2525

2626
[features]
2727
selinux = ["uucore/selinux"]
28+
smack = ["uucore/smack"]
2829

2930
[[bin]]
3031
name = "mkfifo"

0 commit comments

Comments
 (0)