Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@ feat_selinux = [
"selinux",
"stat/selinux",
]
# "feat_smack" == enable support for SMACK Security Context (by using `--features feat_smack`)
# NOTE:
# * Running a uutils compiled with `feat_smack` requires a SMACK enabled Kernel at run time.
feat_smack = ["ls/smack"]
##
## feature sets
## (common/core and Tier1) feature sets
Expand Down
1 change: 1 addition & 0 deletions src/uu/ls/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,4 @@ harness = false

[features]
feat_selinux = ["selinux", "uucore/selinux"]
smack = ["uucore/smack"]
10 changes: 10 additions & 0 deletions src/uu/ls/locales/en-US.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -124,3 +124,13 @@ ls-invalid-columns-width = ignoring invalid width in environment variable COLUMN
ls-invalid-ignore-pattern = Invalid pattern for ignore: {$pattern}
ls-invalid-hide-pattern = Invalid pattern for hide: {$pattern}
ls-total = total {$size}

# Security context warnings
ls-warning-failed-to-get-security-context = failed to get security context of: {$path}
ls-warning-getting-security-context = getting security context of: {$path}: {$error}

# SMACK error messages (used by uucore::smack when called from ls)
smack-error-not-enabled = SMACK is not enabled on this system
smack-error-label-retrieval-failure = failed to get SMACK label: { $error }
smack-error-label-set-failure = failed to set SMACK label to '{ $context }': { $error }
smack-error-no-label-set = no SMACK label set
83 changes: 51 additions & 32 deletions src/uu/ls/src/ls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -365,7 +365,10 @@ pub struct Config {
time_format_recent: String, // Time format for recent dates
time_format_older: Option<String>, // Time format for older dates (optional, if not present, time_format_recent is used)
context: bool,
#[cfg(all(feature = "selinux", target_os = "linux"))]
selinux_supported: bool,
#[cfg(all(feature = "smack", target_os = "linux"))]
smack_supported: bool,
group_directories_first: bool,
line_ending: LineEnding,
dired: bool,
Expand Down Expand Up @@ -1157,16 +1160,10 @@ impl Config {
time_format_recent,
time_format_older,
context,
selinux_supported: {
#[cfg(all(feature = "selinux", target_os = "linux"))]
{
uucore::selinux::is_selinux_enabled()
}
#[cfg(not(all(feature = "selinux", target_os = "linux")))]
{
false
}
},
#[cfg(all(feature = "selinux", target_os = "linux"))]
selinux_supported: uucore::selinux::is_selinux_enabled(),
#[cfg(all(feature = "smack", target_os = "linux"))]
smack_supported: uucore::smack::is_smack_enabled(),
group_directories_first: options.get_flag(options::GROUP_DIRECTORIES_FIRST),
line_ending: LineEnding::from_zero_flag(options.get_flag(options::ZERO)),
dired,
Expand Down Expand Up @@ -3387,37 +3384,59 @@ fn get_security_context<'a>(
}
}

#[cfg(all(feature = "selinux", target_os = "linux"))]
if config.selinux_supported {
#[cfg(all(feature = "selinux", target_os = "linux"))]
{
match selinux::SecurityContext::of_path(path, must_dereference, false) {
Err(_r) => {
// TODO: show the actual reason why it failed
show_warning!("failed to get security context of: {}", path.quote());
return Cow::Borrowed(SUBSTITUTE_STRING);
}
Ok(None) => return Cow::Borrowed(SUBSTITUTE_STRING),
Ok(Some(context)) => {
let context = context.as_bytes();
match selinux::SecurityContext::of_path(path, must_dereference, false) {
Err(_r) => {
// TODO: show the actual reason why it failed
show_warning!(
"{}",
translate!(
"ls-warning-failed-to-get-security-context",
"path" => path.quote().to_string()
)
);
return Cow::Borrowed(SUBSTITUTE_STRING);
}
Ok(None) => return Cow::Borrowed(SUBSTITUTE_STRING),
Ok(Some(context)) => {
let context = context.as_bytes();

let context = context.strip_suffix(&[0]).unwrap_or(context);
let context = context.strip_suffix(&[0]).unwrap_or(context);

let res: String = String::from_utf8(context.to_vec()).unwrap_or_else(|e| {
show_warning!(
"getting security context of: {}: {}",
path.quote(),
e.to_string()
);
let res: String = String::from_utf8(context.to_vec()).unwrap_or_else(|e| {
show_warning!(
"{}",
translate!(
"ls-warning-getting-security-context",
"path" => path.quote().to_string(),
"error" => e.to_string()
)
);

String::from_utf8_lossy(context).to_string()
});
String::from_utf8_lossy(context).to_string()
});

return Cow::Owned(res);
}
return Cow::Owned(res);
}
}
}

#[cfg(all(feature = "smack", target_os = "linux"))]
if config.smack_supported {
// For SMACK, use the path to get the label
// If must_dereference is true, we follow the symlink
let target_path = if must_dereference {
std::fs::canonicalize(path).unwrap_or_else(|_| path.to_path_buf())
} else {
path.to_path_buf()
};

return uucore::smack::get_smack_label_for_path(&target_path)
.map(Cow::Owned)
.unwrap_or(Cow::Borrowed(SUBSTITUTE_STRING));
}

Cow::Borrowed(SUBSTITUTE_STRING)
}

Expand Down
1 change: 1 addition & 0 deletions src/uucore/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ ranges = []
ringbuffer = []
safe-traversal = ["libc"]
selinux = ["dep:selinux"]
smack = ["xattr"]
signals = []
sum = [
"digest",
Expand Down
1 change: 1 addition & 0 deletions src/uucore/locales/en-US.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ selinux-error-context-retrieval-failure = failed to retrieve the security contex
selinux-error-context-set-failure = failed to set default file creation context to '{ $context }': { $error }
selinux-error-context-conversion-failure = failed to set default file creation context to '{ $context }': { $error }


# Safe traversal error messages
safe-traversal-error-path-contains-null = path contains null byte
safe-traversal-error-open-failed = failed to open { $path }: { $source }
Expand Down
2 changes: 2 additions & 0 deletions src/uucore/src/lib/features.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ pub mod hardware;
pub mod selinux;
#[cfg(all(unix, not(target_os = "fuchsia"), feature = "signals"))]
pub mod signals;
#[cfg(all(target_os = "linux", feature = "smack"))]
pub mod smack;
#[cfg(feature = "feat_systemd_logind")]
pub mod systemd_logind;
#[cfg(all(
Expand Down
77 changes: 77 additions & 0 deletions src/uucore/src/lib/features/smack.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// This file is part of the uutils uucore package.
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.

// spell-checker:ignore smackfs
//! SMACK (Simplified Mandatory Access Control Kernel) support

use std::io;
use std::path::Path;
use std::sync::OnceLock;

use thiserror::Error;

use crate::error::{UError, strip_errno};
use crate::translate;

#[derive(Debug, Error)]
pub enum SmackError {
#[error("{}", translate!("smack-error-not-enabled"))]
SmackNotEnabled,

#[error("{}", translate!("smack-error-label-retrieval-failure", "error" => strip_errno(.0)))]
LabelRetrievalFailure(io::Error),

#[error("{}", translate!("smack-error-label-set-failure", "context" => .0.clone(), "error" => strip_errno(.1)))]
LabelSetFailure(String, io::Error),
}

impl UError for SmackError {
fn code(&self) -> i32 {
match self {
Self::SmackNotEnabled => 1,
Self::LabelRetrievalFailure(_) => 2,
Self::LabelSetFailure(_, _) => 3,
}
}
}

impl From<SmackError> for i32 {
fn from(error: SmackError) -> Self {
error.code()
}
}

/// Checks if SMACK is enabled by verifying smackfs is mounted.
/// The result is cached after the first call.
pub fn is_smack_enabled() -> bool {
static SMACK_ENABLED: OnceLock<bool> = OnceLock::new();
*SMACK_ENABLED.get_or_init(|| Path::new("/sys/fs/smackfs").exists())
}

/// Gets the SMACK label for a filesystem path via xattr.
pub fn get_smack_label_for_path(path: &Path) -> Result<String, SmackError> {
if !is_smack_enabled() {
return Err(SmackError::SmackNotEnabled);
}

match xattr::get(path, "security.SMACK64") {
Ok(Some(value)) => Ok(String::from_utf8_lossy(&value).trim().to_string()),
Ok(None) => Err(SmackError::LabelRetrievalFailure(io::Error::new(
io::ErrorKind::NotFound,
translate!("smack-error-no-label-set"),
))),
Err(e) => Err(SmackError::LabelRetrievalFailure(e)),
}
}

/// Sets the SMACK label for a filesystem path via xattr.
pub fn set_smack_label_for_path(path: &Path, label: &str) -> Result<(), SmackError> {
if !is_smack_enabled() {
return Err(SmackError::SmackNotEnabled);
}

xattr::set(path, "security.SMACK64", label.as_bytes())
.map_err(|e| SmackError::LabelSetFailure(label.to_string(), e))
}
3 changes: 3 additions & 0 deletions src/uucore/src/lib/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,9 @@ pub use crate::features::fsxattr;
#[cfg(all(target_os = "linux", feature = "selinux"))]
pub use crate::features::selinux;

#[cfg(all(target_os = "linux", feature = "smack"))]
pub use crate::features::smack;

//## core functions

#[cfg(unix)]
Expand Down
Loading