mirror of
https://github.com/containers/youki
synced 2024-11-26 06:08:07 +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]
|
[workspace]
|
||||||
resolver = "2"
|
resolver = "2"
|
||||||
members = ["crates/*", "tests/contest/*", "tools/*"]
|
members = ["crates/*", "tests/contest/*", "tools/*"]
|
||||||
exclude = ["experiment/seccomp"]
|
exclude = ["experiment/seccomp", "experiment/selinux"]
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
lto = true
|
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