mirror of
https://github.com/uutils/coreutils.git
synced 2026-06-10 15:48:22 -07:00
ls: Add SMACK support (#9868)
* Add SMACK support for ls utility * More idiomatic match statement Co-authored-by: Sylvestre Ledru <sylvestre@debian.org> * More idiomatic match statement for getting label Co-authored-by: Sylvestre Ledru <sylvestre@debian.org> * ls: fix smack formatting and cache is_smack_enabled result * ls: use translate!() macro for SELinux warning messages * Move SMACK locale strings to ls-specific locale file to avoid performance regression --------- Co-authored-by: Sylvestre Ledru <sylvestre@debian.org>
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -60,3 +60,4 @@ harness = false
|
||||
|
||||
[features]
|
||||
feat_selinux = ["selinux", "uucore/selinux"]
|
||||
smack = ["uucore/smack"]
|
||||
|
||||
@@ -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
|
||||
|
||||
+51
-32
@@ -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,
|
||||
@@ -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,
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -162,6 +162,7 @@ ranges = []
|
||||
ringbuffer = []
|
||||
safe-traversal = ["libc"]
|
||||
selinux = ["dep:selinux"]
|
||||
smack = ["xattr"]
|
||||
signals = []
|
||||
sum = [
|
||||
"digest",
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
@@ -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)]
|
||||
|
||||
Reference in New Issue
Block a user