mirror of
https://github.com/containers/youki
synced 2024-11-22 08:51:59 +01:00
Init a selinux project (#2800)
* selinux_init Signed-off-by: Hiroyuki Moriya <41197469+Gekko0114@users.noreply.github.com> * use_unimplemented Signed-off-by: Hiroyuki Moriya <41197469+Gekko0114@users.noreply.github.com> * add_explanation Signed-off-by: Hiroyuki Moriya <41197469+Gekko0114@users.noreply.github.com> * update Signed-off-by: Hiroyuki Moriya <41197469+Gekko0114@users.noreply.github.com> * use_path_instead_of_str Signed-off-by: Hiroyuki Moriya <41197469+Gekko0114@users.noreply.github.com> * use_thiserror Signed-off-by: Hiroyuki Moriya <41197469+Gekko0114@users.noreply.github.com> * use_struct Signed-off-by: Hiroyuki Moriya <41197469+Gekko0114@users.noreply.github.com> --------- Signed-off-by: Hiroyuki Moriya <41197469+Gekko0114@users.noreply.github.com>
This commit is contained in:
parent
af804e4c99
commit
6dd0d7f031
@ -1,7 +1,7 @@
|
||||
[workspace]
|
||||
resolver = "2"
|
||||
members = ["crates/*", "tests/contest/*", "tools/*"]
|
||||
exclude = ["experiment/seccomp"]
|
||||
exclude = ["experiment/seccomp", "experiment/selinux"]
|
||||
|
||||
[profile.release]
|
||||
lto = true
|
||||
|
102
experiment/selinux/Cargo.lock
generated
Normal file
102
experiment/selinux/Cargo.lock
generated
Normal file
@ -0,0 +1,102 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "cfg_aliases"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.155"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.29.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cfg-if",
|
||||
"cfg_aliases",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.85"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.36"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "selinux"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"nix",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.66"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.61"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.61"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
16
experiment/selinux/Cargo.toml
Normal file
16
experiment/selinux/Cargo.toml
Normal file
@ -0,0 +1,16 @@
|
||||
[package]
|
||||
name = "selinux"
|
||||
version = "0.1.0"
|
||||
description = "Library for selinux"
|
||||
license-file = "../../LICENSE"
|
||||
repository = "https://github.com/containers/youki"
|
||||
homepage = "https://containers.github.io/youki"
|
||||
readme = "README.md"
|
||||
authors = ["youki team"]
|
||||
edition = "2021"
|
||||
autoexamples = true
|
||||
keywords = ["youki", "container", "selinux"]
|
||||
|
||||
[dependencies]
|
||||
nix = { version = "0.29.0", features = ["process", "fs"] }
|
||||
thiserror = "1.0.61"
|
7
experiment/selinux/README.md
Normal file
7
experiment/selinux/README.md
Normal file
@ -0,0 +1,7 @@
|
||||
This is an experimental project in order to create selinux library in Rust.
|
||||
Ref: https://github.com/containers/youki/issues/2718.
|
||||
Reimplementation of (selinux)[https://github.com/opencontainers/selinux] in Rust.
|
||||
Also selinux depends on xattr, but nix doesn't cover xattr function.
|
||||
Therefore, this PR will implement xattr in Rust.
|
||||
|
||||
Please import and use this project.
|
2
experiment/selinux/src/lib.rs
Normal file
2
experiment/selinux/src/lib.rs
Normal file
@ -0,0 +1,2 @@
|
||||
pub mod selinux;
|
||||
pub mod xattr;
|
469
experiment/selinux/src/selinux.rs
Normal file
469
experiment/selinux/src/selinux.rs
Normal file
@ -0,0 +1,469 @@
|
||||
use crate::xattr::*;
|
||||
use nix::unistd::gettid;
|
||||
use nix::sys::statfs;
|
||||
use nix::errno::Errno;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::fs::File;
|
||||
use std::io::Read;
|
||||
use std::os::fd::{AsFd, AsRawFd};
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
const XATTR_NAME_SELINUX: &str = "security.selinux";
|
||||
const ERR_EMPTY_PATH: &str = "empty path";
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum SELinuxError {
|
||||
#[error("Failed to set file label for SELinux: {0}")]
|
||||
SetFileLabel(String),
|
||||
#[error("Failed to lset file label for SELinux: {0}")]
|
||||
LSetFileLabel(String),
|
||||
#[error("Failed to get file label for SELinux: {0}")]
|
||||
FileLabel(String),
|
||||
#[error("Failed to get lfile label for SELinux: {0}")]
|
||||
LFileLabel(String),
|
||||
#[error("Failed to call is_proc_handle for SELinux: {0}")]
|
||||
IsProcHandle(String),
|
||||
#[error("Failed to call read_con_fd for SELinux: {0}")]
|
||||
ReadConFd(String),
|
||||
#[error("Failed to call read_con for SELinux: {0}")]
|
||||
ReadCon(String),
|
||||
}
|
||||
|
||||
pub struct SELinux {
|
||||
have_thread_self: AtomicBool,
|
||||
init_done: AtomicBool,
|
||||
}
|
||||
|
||||
impl Default for SELinux {
|
||||
fn default() -> Self {
|
||||
SELinux::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl SELinux {
|
||||
pub fn new() -> Self {
|
||||
SELinux {
|
||||
have_thread_self: AtomicBool::new(false),
|
||||
init_done: AtomicBool::new(false),
|
||||
}
|
||||
}
|
||||
|
||||
// function similar with setDisabled in go-selinux repo.
|
||||
// set_disabled disables SELinux support for the package.
|
||||
pub fn set_disabled() {
|
||||
unimplemented!("not implemented yet")
|
||||
}
|
||||
|
||||
// function similar with getEnabled in go-selinux repo.
|
||||
// get_enabled returns whether SELinux is enabled or not.
|
||||
pub fn get_enabled() -> bool {
|
||||
unimplemented!("not implemented yet")
|
||||
}
|
||||
|
||||
// function similar with classIndex in go-selinux repo.
|
||||
// classIndex returns the int index for an object class in the loaded policy,
|
||||
// or -1 and an error.
|
||||
pub fn class_index(class: &str) -> Result<i64, String> {
|
||||
unimplemented!("not implemented yet")
|
||||
}
|
||||
|
||||
// function similar with setFileLabel in go-selinux repo.
|
||||
// set_file_label sets the SELinux label for this path, following symlinks, or returns an error.
|
||||
pub fn set_file_label(fpath: &Path, label: &str) -> Result<(), SELinuxError> {
|
||||
if !fpath.exists() {
|
||||
return Err(SELinuxError::SetFileLabel(ERR_EMPTY_PATH.to_string()));
|
||||
}
|
||||
|
||||
loop {
|
||||
match set_xattr(fpath, XATTR_NAME_SELINUX, label.as_bytes(), 0) {
|
||||
Ok(_) => break,
|
||||
// TODO: This line will be fixed after implementing set_xattr.
|
||||
Err(EINTR) => continue,
|
||||
Err(e) => {
|
||||
return Err(SELinuxError::SetFileLabel(
|
||||
format!("set_xattr failed: {}", e),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// function similar with lSetFileLabel in go-selinux repo.
|
||||
// lset_file_label sets the SELinux label for this path, not following symlinks,
|
||||
// or returns an error.
|
||||
pub fn lset_file_label(fpath: &Path, label: &str) -> Result<(), SELinuxError> {
|
||||
if !fpath.exists() {
|
||||
return Err(SELinuxError::LSetFileLabel(ERR_EMPTY_PATH.to_string()));
|
||||
}
|
||||
|
||||
loop {
|
||||
match lset_xattr(fpath, XATTR_NAME_SELINUX, label.as_bytes(), 0) {
|
||||
Ok(_) => break,
|
||||
// TODO: This line will be fixed after implementing lset_xattr.
|
||||
Err(EINTR) => continue,
|
||||
Err(e) => {
|
||||
return Err(SELinuxError::LSetFileLabel(format!("lset_xattr failed: {}", e)));
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// function similar with fileLabel in go-selinux repo.
|
||||
// fileLabel returns the SELinux label for this path, following symlinks,
|
||||
// or returns an error.
|
||||
pub fn file_label(fpath: &Path) -> Result<String, SELinuxError> {
|
||||
if !fpath.exists() {
|
||||
return Err(SELinuxError::FileLabel(ERR_EMPTY_PATH.to_string()));
|
||||
}
|
||||
get_xattr(fpath, XATTR_NAME_SELINUX)
|
||||
.map_err(|e| SELinuxError::FileLabel(e.to_string()))
|
||||
}
|
||||
|
||||
// function similar with lFileLabel in go-selinux repo.
|
||||
// lfile_label returns the SELinux label for this path, not following symlinks,
|
||||
// or returns an error.
|
||||
pub fn lfile_label(fpath: &Path) -> Result<String, SELinuxError> {
|
||||
if !fpath.exists() {
|
||||
return Err(SELinuxError::LFileLabel(ERR_EMPTY_PATH.to_string()));
|
||||
}
|
||||
lget_xattr(fpath, XATTR_NAME_SELINUX)
|
||||
.map_err(|e| SELinuxError::LFileLabel(e.to_string()))
|
||||
}
|
||||
|
||||
// function similar with setFSCreateLabel in go-selinux repo.
|
||||
// set_fscreate_label sets the default label the kernel which the kernel is using
|
||||
// for file system objects.
|
||||
pub fn set_fscreate_label(&self, label: &str) -> Result<(), SELinuxError> {
|
||||
return Self::write_con(self.attr_path("fscreate").as_path(), label);
|
||||
}
|
||||
|
||||
// function similar with fsCreateLabel in go-selinux repo.
|
||||
// fscreate_label returns the default label the kernel which the kernel is using
|
||||
// for file system objects created by this task. "" indicates default.
|
||||
pub fn fscreate_label(&self) -> Result<String, SELinuxError> {
|
||||
return Self::read_con(self.attr_path("fscreate").as_path());
|
||||
}
|
||||
|
||||
// function similar with currentLabel in go-selinux repo.
|
||||
// current_label returns the SELinux label of the current process thread, or an error.
|
||||
pub fn current_label(&self) -> Result<String, SELinuxError> {
|
||||
return Self::read_con(self.attr_path("current").as_path());
|
||||
}
|
||||
|
||||
// function similar with pidLabel in go-selinux repo.
|
||||
// pid_label returns the SELinux label of the given pid, or an error.
|
||||
pub fn pid_label(pid: i64) -> Result<String, SELinuxError> {
|
||||
let file_name = &format!("/proc/{}/attr/current", pid);
|
||||
let label = Path::new(file_name);
|
||||
Self::read_con(label)
|
||||
}
|
||||
|
||||
// function similar with execLabel in go-selinux repo.
|
||||
// exec_label returns the SELinux label that the kernel will use for any programs
|
||||
// that are executed by the current process thread, or an error.
|
||||
pub fn exec_label(&self) -> Result<String, SELinuxError> {
|
||||
return Self::read_con(self.attr_path("exec").as_path());
|
||||
}
|
||||
|
||||
// function similar with SetExecLabel in go-selinux repo.
|
||||
// set_exec_label sets the SELinux label that the kernel will use for any programs
|
||||
// that are executed by the current process thread, or an error.
|
||||
pub fn set_exec_label(label: &str) {
|
||||
unimplemented!("not implemented yet")
|
||||
}
|
||||
|
||||
// function similar with SetTaskLabel in go-selinux repo.
|
||||
// set_task_label sets the SELinux label for the current thread, or an error.
|
||||
// This requires the dyntransition permission.
|
||||
pub fn set_task_label(label: &str) {
|
||||
unimplemented!("not implemented yet")
|
||||
}
|
||||
|
||||
// function similar with SetSocketLabel in go-selinux repo.
|
||||
// set_socket_label takes a process label and tells the kernel to assign the
|
||||
// label to the next socket that gets created.
|
||||
pub fn set_socket_label(label: &str) {
|
||||
unimplemented!("not implemented yet")
|
||||
}
|
||||
|
||||
// function similar with SocketLabel in go-selinux repo.
|
||||
// socket_label retrieves the current socket label setting.
|
||||
pub fn socket_label() {
|
||||
unimplemented!("not implemented yet")
|
||||
}
|
||||
|
||||
// function similar with peerLabel in go-selinux repo.
|
||||
// peer_label retrieves the label of the client on the other side of a socket.
|
||||
pub fn peer_label() {
|
||||
unimplemented!("not implemented yet")
|
||||
}
|
||||
|
||||
// function similar with setKeyLabel in go-selinux repo.
|
||||
// set_key_label takes a process label and tells the kernel to assign the
|
||||
// label to the next kernel keyring that gets created.
|
||||
pub fn set_key_label(label: &str) -> Result<(), SELinuxError> {
|
||||
match Self::write_con(Path::new("/proc/self/attr/keycreate"), label) {
|
||||
Ok(v) => Ok(v),
|
||||
// TODO: This line will be fixed after implementing write_con.
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
|
||||
// function similar with KeyLabel in go-selinux repo.
|
||||
// key_label retrieves the current kernel keyring label setting
|
||||
pub fn key_label() {
|
||||
unimplemented!("not implemented yet")
|
||||
}
|
||||
|
||||
// function similar with clearLabels in go-selinux repo.
|
||||
// clear_labels clears all reserved labels.
|
||||
pub fn clear_labels() {
|
||||
unimplemented!("not implemented yet")
|
||||
}
|
||||
|
||||
// function similar with reserveLabel in go-selinux repo.
|
||||
// reserve_label reserves the MLS/MCS level component of the specified label
|
||||
pub fn reserve_label(label: &str) {
|
||||
unimplemented!("not implemented yet")
|
||||
}
|
||||
|
||||
// function similar with roFileLabel in go-selinux repo.
|
||||
// ro_file_label returns the specified SELinux readonly file label
|
||||
pub fn ro_file_label() {
|
||||
unimplemented!("not implemented yet")
|
||||
}
|
||||
|
||||
// function similar with kvmContainerLabels in go-selinux repo.
|
||||
// kvm_container_labels returns the default processLabel and mountLabel to be used
|
||||
// for kvm containers by the calling process.
|
||||
pub fn kvm_container_labels() {
|
||||
unimplemented!("not implemented yet")
|
||||
}
|
||||
|
||||
// function similar with initContainerLabels in go-selinux repo.
|
||||
// init_container_labels returns the default processLabel and file labels to be
|
||||
// used for containers running an init system like systemd by the calling process.
|
||||
pub fn init_container_labels() {
|
||||
unimplemented!("not implemented yet")
|
||||
}
|
||||
|
||||
// function similar with containerLabels in go-selinux repo.
|
||||
// container_labels returns an allocated processLabel and fileLabel to be used for
|
||||
// container labeling by the calling process.
|
||||
pub fn container_labels() {
|
||||
unimplemented!("not implemented yet")
|
||||
}
|
||||
|
||||
// function similar with PrivContainerMountLabel in go-selinux repo.
|
||||
// priv_container_mount_label returns mount label for privileged containers.
|
||||
pub fn priv_container_mount_label() {
|
||||
unimplemented!("not implemented yet")
|
||||
}
|
||||
|
||||
// function similar with FormatMountLabel in go-selinux repo.
|
||||
// format_mount_label returns a string to be used by the mount command.
|
||||
// Using the SELinux `context` mount option.
|
||||
// Changing labels of files on mount points with this option can never be changed.
|
||||
// format_mount_label returns a string to be used by the mount command.
|
||||
// The format of this string will be used to alter the labeling of the mountpoint.
|
||||
// The string returned is suitable to be used as the options field of the mount command.
|
||||
// If you need to have additional mount point options, you can pass them in as
|
||||
// the first parameter. The second parameter is the label that you wish to apply
|
||||
// to all content in the mount point.
|
||||
pub fn format_mount_label(src: &str, mount_label: &str) -> String {
|
||||
Self::format_mount_label_by_type(src, mount_label, "context")
|
||||
}
|
||||
|
||||
// function similar with FormatMountLabelByType in go-selinux repo.
|
||||
// format_mount_label_by_type returns a string to be used by the mount command.
|
||||
// Allow caller to specify the mount options. For example using the SELinux
|
||||
// `fscontext` mount option would allow certain container processes to change
|
||||
// labels of files created on the mount points, where as `context` option does not.
|
||||
pub fn format_mount_label_by_type(src: &str, mount_label: &str, context_type: &str) -> String {
|
||||
let mut formatted_src = src.to_owned();
|
||||
|
||||
if !mount_label.is_empty() {
|
||||
if formatted_src.is_empty() {
|
||||
formatted_src = format!("{}=\"{}\"", context_type, mount_label);
|
||||
} else {
|
||||
formatted_src = format!("{},{}=\"{}\"", formatted_src, context_type, mount_label);
|
||||
}
|
||||
}
|
||||
formatted_src
|
||||
}
|
||||
|
||||
// function similar with writeCon in go-selinux repo.
|
||||
pub fn write_con(fpath: &Path, val: &str) -> Result<(), SELinuxError> {
|
||||
unimplemented!("not implemented yet");
|
||||
}
|
||||
|
||||
// function similar with isProcHandle in go-selinux repo.
|
||||
pub fn is_proc_handle(file: &File) -> Result<(), SELinuxError> {
|
||||
loop {
|
||||
match statfs::fstatfs(file.as_fd()) {
|
||||
Ok(stat) if stat.filesystem_type() == statfs::PROC_SUPER_MAGIC => break,
|
||||
Ok(_) => {
|
||||
return Err(SELinuxError::IsProcHandle(format!("file {} is not on procfs", file.as_raw_fd())
|
||||
));
|
||||
},
|
||||
Err(Errno::EINTR) => continue,
|
||||
Err(err) => {
|
||||
return Err(SELinuxError::IsProcHandle(format!("fstatfs failed: {}", err)))
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// function similar with readConFd in go-selinux repo.
|
||||
pub fn read_con_fd(file: &mut File) -> Result<String, SELinuxError> {
|
||||
let mut data = String::new();
|
||||
file.read_to_string(&mut data)
|
||||
.map_err(|e| SELinuxError::ReadConFd(e.to_string()))?;
|
||||
|
||||
// Remove null bytes on the end of a file.
|
||||
let trimmed_data = data.trim_end_matches(char::from(0));
|
||||
Ok(trimmed_data.to_string())
|
||||
}
|
||||
|
||||
// function similar with readCon in go-selinux repo.
|
||||
pub fn read_con(fpath: &Path) -> Result<String, SELinuxError> {
|
||||
if fpath.as_os_str().is_empty() {
|
||||
return Err(SELinuxError::ReadCon(ERR_EMPTY_PATH.to_string()));
|
||||
}
|
||||
let mut in_file = File::open(fpath)
|
||||
.map_err(|e| SELinuxError::ReadCon(format!("failed to open file: {}", e)))?;
|
||||
|
||||
Self::is_proc_handle(&in_file)?;
|
||||
Self::read_con_fd(&mut in_file)
|
||||
}
|
||||
|
||||
// function similar with attrPath in go-selinux repo.
|
||||
// attr_path determines the correct file path for accessing SELinux
|
||||
// attributes of a process or thread in a Linux environment.
|
||||
pub fn attr_path(&self, attr: &str) -> PathBuf {
|
||||
// Linux >= 3.17 provides this
|
||||
const THREAD_SELF_PREFIX: &str = "/proc/thread-self/attr";
|
||||
// Avoiding code conflicts and ensuring thread-safe execution once only.
|
||||
if !self.init_done.load(Ordering::SeqCst) {
|
||||
let path = PathBuf::from(THREAD_SELF_PREFIX);
|
||||
let is_dir = path.is_dir();
|
||||
self.have_thread_self.store(is_dir, Ordering::SeqCst);
|
||||
self.init_done.store(true, Ordering::SeqCst);
|
||||
}
|
||||
if self.have_thread_self.load(Ordering::SeqCst) {
|
||||
return PathBuf::from(&format!("{}/{}", THREAD_SELF_PREFIX, attr));
|
||||
}
|
||||
|
||||
PathBuf::from(&format!("/proc/self/task/{}/attr/{}", gettid(), attr))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::selinux::*;
|
||||
use std::fs::{self, File};
|
||||
use std::path::Path;
|
||||
use std::io::Write;
|
||||
|
||||
fn create_temp_file(content: &[u8], file_name: &str) {
|
||||
let path = Path::new(file_name);
|
||||
let mut file = File::create(&path).expect("Failed to create file");
|
||||
file.write_all(content).expect("Failed to write to file");
|
||||
file.sync_all().expect("Failed to sync file");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_format_mount_label() {
|
||||
let src_array = ["", "src", "src"];
|
||||
let mount_label_array = ["foobar", "foobar", ""];
|
||||
let expected_array = ["context=\"foobar\"", "src,context=\"foobar\"", "src"];
|
||||
for (i, src) in src_array.iter().enumerate() {
|
||||
let mount_label = mount_label_array[i];
|
||||
let expected = expected_array[i];
|
||||
assert_eq!(
|
||||
SELinux::format_mount_label(src, mount_label),
|
||||
expected
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_format_mount_label_by_type() {
|
||||
let src_array = ["", "src", "src"];
|
||||
let mount_label_array = ["foobar", "foobar", ""];
|
||||
let context_array = ["fscontext", "fscontext", "rootcontext"];
|
||||
let expected_array = ["fscontext=\"foobar\"", "src,fscontext=\"foobar\"", "src"];
|
||||
for (i, src) in src_array.iter().enumerate() {
|
||||
let mount_label = mount_label_array[i];
|
||||
let context = context_array[i];
|
||||
let expected = expected_array[i];
|
||||
assert_eq!(
|
||||
SELinux::format_mount_label_by_type(src, mount_label, context),
|
||||
expected
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_read_con_fd() {
|
||||
let content_array: Vec<&[u8]> = vec![b"Hello, world\0", b"Hello, world\0\0\0", b"Hello,\0world"];
|
||||
let expected_array = ["Hello, world", "Hello, world", "Hello,\0world"];
|
||||
let file_name = "test.txt";
|
||||
for (i, content) in content_array.iter().enumerate() {
|
||||
let expected = expected_array[i];
|
||||
create_temp_file(content, file_name);
|
||||
// Need to open again to get read permission.
|
||||
let mut file = File::open(file_name).expect("Failed to open file");
|
||||
let result = SELinux::read_con_fd(&mut file).expect("Failed to read file");
|
||||
assert_eq!(result, expected);
|
||||
fs::remove_file(file_name).expect("Failed to remove test file");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_attr_path() {
|
||||
let selinux = SELinux::new();
|
||||
// Test with "/proc/thread-self/attr" path (Linux >= 3.17)
|
||||
let attr = "bar";
|
||||
let expected_name = &format!("/proc/thread-self/attr/{}", attr);
|
||||
let expected_path = Path::new(expected_name);
|
||||
let actual_path = selinux.attr_path(attr);
|
||||
assert_eq!(expected_path, actual_path);
|
||||
|
||||
// Test with not having "/proc/thread-self/attr" path by setting HAVE_THREAD_SELF as false
|
||||
selinux.init_done.store(true, Ordering::SeqCst);
|
||||
selinux.have_thread_self.store(false, Ordering::SeqCst);
|
||||
let thread_id = gettid();
|
||||
let expected_name = &format!("/proc/self/task/{}/attr/{}", thread_id, attr);
|
||||
let expected_path = Path::new(expected_name);
|
||||
let actual_path = selinux.attr_path(attr);
|
||||
assert_eq!(expected_path, actual_path);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_is_proc_handle() {
|
||||
let filename_array = ["/proc/self/status", "/tmp/testfile"];
|
||||
let expected_array = [true, false];
|
||||
|
||||
for (i, filename) in filename_array.iter().enumerate() {
|
||||
let expected_ok = expected_array[i];
|
||||
let path = Path::new(filename);
|
||||
let file = match File::open(path) {
|
||||
Ok(file) => file,
|
||||
Err(_) => {
|
||||
create_temp_file(b"", filename);
|
||||
File::open(path).expect("failed to open file")
|
||||
}
|
||||
};
|
||||
let result = SELinux::is_proc_handle(&file);
|
||||
if expected_ok {
|
||||
assert!(result.is_ok(), "Expected Ok, but got Err: {:?}", result);
|
||||
} else {
|
||||
assert!(result.is_err(), "Expected Err, but got Ok");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
3
experiment/selinux/src/xattr/mod.rs
Normal file
3
experiment/selinux/src/xattr/mod.rs
Normal file
@ -0,0 +1,3 @@
|
||||
mod xattr;
|
||||
|
||||
pub use xattr::*;
|
65
experiment/selinux/src/xattr/xattr.rs
Normal file
65
experiment/selinux/src/xattr/xattr.rs
Normal file
@ -0,0 +1,65 @@
|
||||
use std::path::Path;
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum XattrError {
|
||||
#[error("Failed to set_xattr: {0}")]
|
||||
SetXattr(String),
|
||||
#[error("Failed to lset_xattr: {0}")]
|
||||
LSetXattr(String),
|
||||
#[error("Failed to get_xattr: {0}")]
|
||||
GetXattr(String),
|
||||
#[error("Failed to call lget_xattr: {0}")]
|
||||
LGetXattr(String),
|
||||
}
|
||||
|
||||
// function similar with setxattr in golang.org/x/sys/unix repo.
|
||||
// set_xattr sets extended attributes on a file specified by its path.
|
||||
pub fn set_xattr(fpath: &Path, attr: &str, data: &[u8], flags: i64) -> Result<(), XattrError> {
|
||||
unimplemented!("not implemented yet")
|
||||
}
|
||||
|
||||
// function similar with lsetxattr in golang.org/x/sys/unix repo.
|
||||
// lset_xattr sets extended attributes on a symbolic link.
|
||||
pub fn lset_xattr(fpath: &Path, attr: &str, data: &[u8], flags: i64) -> Result<(), XattrError> {
|
||||
unimplemented!("not implemented yet")
|
||||
}
|
||||
|
||||
// function similar with getattr in go-selinux repo.
|
||||
// get_xattr returns the value of an extended attribute attr set for path.
|
||||
pub fn get_xattr(fpath: &Path, attr: &str) -> Result<String, XattrError> {
|
||||
unimplemented!("not implemented yet")
|
||||
/*
|
||||
match label {
|
||||
Ok(mut v) => {
|
||||
if (!v.is_empty()) && (v.chars().last() == Some('\x00')) {
|
||||
v = (&v[0..v.len() - 1]).to_string();
|
||||
}
|
||||
return Ok(v);
|
||||
},
|
||||
Err(e) => return Err(std::io::Error::new(
|
||||
std::io::ErrorKind::Other,
|
||||
format!("get_xattr failed: {}", e),
|
||||
))
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
// function similar with lgetxattr in go-selinux repo.
|
||||
// lget_xattr returns the value of an extended attribute attr set for path.
|
||||
pub fn lget_xattr(fpath: &Path, attr: &str) -> Result<String, XattrError> {
|
||||
unimplemented!("not implemented yet")
|
||||
/*
|
||||
match label {
|
||||
Ok(mut v) => {
|
||||
if (!v.is_empty()) && (v.chars().last() == Some('\x00')) {
|
||||
v = (&v[0..v.len() - 1]).to_string();
|
||||
}
|
||||
return Ok(v);
|
||||
},
|
||||
Err(e) => return Err(std::io::Error::new(
|
||||
std::io::ErrorKind::Other,
|
||||
format!("lget_xattr failed: {}", e),
|
||||
))
|
||||
}
|
||||
*/
|
||||
}
|
Loading…
Reference in New Issue
Block a user