From c8eb1485d9049ae2033923925300e057a3046648 Mon Sep 17 00:00:00 2001 From: Hiroyuki Moriya <41197469+Gekko0114@users.noreply.github.com> Date: Fri, 16 Aug 2024 21:25:52 +0900 Subject: [PATCH 01/14] selinux: implemented remaining selinux functions (#2850) * added selinux functions Signed-off-by: Hiroyuki Moriya <41197469+Gekko0114@users.noreply.github.com> * not use arc Signed-off-by: Hiroyuki Moriya <41197469+Gekko0114@users.noreply.github.com> * follow reviewer comment Signed-off-by: Hiroyuki Moriya <41197469+Gekko0114@users.noreply.github.com> * divided selinux impl into two files Signed-off-by: Hiroyuki Moriya <41197469+Gekko0114@users.noreply.github.com> * fix Signed-off-by: Hiroyuki Moriya <41197469+Gekko0114@users.noreply.github.com> * fix Signed-off-by: Hiroyuki Moriya <41197469+Gekko0114@users.noreply.github.com> * fix Signed-off-by: Hiroyuki Moriya <41197469+Gekko0114@users.noreply.github.com> * use SELinuxLabel struct Signed-off-by: Hiroyuki Moriya <41197469+Gekko0114@users.noreply.github.com> * use pointer instead of clone Signed-off-by: Hiroyuki Moriya <41197469+Gekko0114@users.noreply.github.com> * not loop Signed-off-by: Hiroyuki Moriya <41197469+Gekko0114@users.noreply.github.com> * add main.rs Signed-off-by: Hiroyuki Moriya <41197469+Gekko0114@users.noreply.github.com> --------- Signed-off-by: Hiroyuki Moriya <41197469+Gekko0114@users.noreply.github.com> --- experiment/selinux/Cargo.lock | 23 + experiment/selinux/Cargo.toml | 3 +- experiment/selinux/README.md | 5 + experiment/selinux/src/lib.rs | 5 +- experiment/selinux/src/main.rs | 43 ++ experiment/selinux/src/selinux.rs | 647 ++++++++++-------- experiment/selinux/src/selinux_label.rs | 376 ++++++++++ experiment/selinux/src/tools/mod.rs | 5 + experiment/selinux/src/tools/sockopt.rs | 38 + .../selinux/src/{xattrs => tools}/xattr.rs | 22 +- experiment/selinux/src/xattrs/mod.rs | 3 - 11 files changed, 883 insertions(+), 287 deletions(-) create mode 100644 experiment/selinux/src/main.rs create mode 100644 experiment/selinux/src/selinux_label.rs create mode 100644 experiment/selinux/src/tools/mod.rs create mode 100644 experiment/selinux/src/tools/sockopt.rs rename experiment/selinux/src/{xattrs => tools}/xattr.rs (89%) delete mode 100644 experiment/selinux/src/xattrs/mod.rs diff --git a/experiment/selinux/Cargo.lock b/experiment/selinux/Cargo.lock index 60ecb75d8..5a515cc41 100644 --- a/experiment/selinux/Cargo.lock +++ b/experiment/selinux/Cargo.lock @@ -2,6 +2,18 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "anyhow" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" + +[[package]] +name = "autocfg" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" + [[package]] name = "bitflags" version = "2.5.0" @@ -48,6 +60,15 @@ version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +[[package]] +name = "memoffset" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +dependencies = [ + "autocfg", +] + [[package]] name = "nix" version = "0.29.0" @@ -58,6 +79,7 @@ dependencies = [ "cfg-if", "cfg_aliases", "libc", + "memoffset", ] [[package]] @@ -95,6 +117,7 @@ dependencies = [ name = "selinux" version = "0.1.0" dependencies = [ + "anyhow", "nix", "rustix", "tempfile", diff --git a/experiment/selinux/Cargo.toml b/experiment/selinux/Cargo.toml index 87f873198..785c7cd75 100644 --- a/experiment/selinux/Cargo.toml +++ b/experiment/selinux/Cargo.toml @@ -12,7 +12,8 @@ autoexamples = true keywords = ["youki", "container", "selinux"] [dependencies] -nix = { version = "0.29.0", features = ["process", "fs"] } +anyhow = "1.0.86" +nix = { version = "0.29.0", features = ["process", "fs", "socket"] } rustix = { version = "0.38.34", features = ["fs"] } tempfile = "3.10.1" thiserror = "1.0.61" diff --git a/experiment/selinux/README.md b/experiment/selinux/README.md index 8e0d90656..a47794427 100644 --- a/experiment/selinux/README.md +++ b/experiment/selinux/README.md @@ -3,5 +3,10 @@ Ref: https://github.com/containers/youki/issues/2718. Reimplementation of [opencontainers/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. +Referenced the implementation of xattr in [unix](golang.org/x/sys/unix) repo. Please import and use this project. + +```console +$ cargo run +``` diff --git a/experiment/selinux/src/lib.rs b/experiment/selinux/src/lib.rs index eebcd32c4..9a9bfd990 100644 --- a/experiment/selinux/src/lib.rs +++ b/experiment/selinux/src/lib.rs @@ -1,2 +1,5 @@ pub mod selinux; -pub mod xattrs; +pub mod selinux_label; +pub mod tools; + +pub use selinux::SELinux; diff --git a/experiment/selinux/src/main.rs b/experiment/selinux/src/main.rs new file mode 100644 index 000000000..35d5a4e16 --- /dev/null +++ b/experiment/selinux/src/main.rs @@ -0,0 +1,43 @@ +use anyhow::Result; +use selinux::selinux::*; +use selinux::selinux_label::*; +use std::fs::File; +use std::path::Path; + +fn main() -> Result<()> { + let mut selinux_instance: SELinux = SELinux::new(); + + if selinux_instance.get_enabled() { + println!("selinux is enabled"); + } else { + println!("selinux is not enabled"); + + match selinux_instance.set_enforce_mode(SELinuxMode::PERMISSIVE) { + Ok(_) => println!("set selinux mode as permissive"), + Err(e) => println!("{}", e), + } + } + println!( + "default enforce mode is: {}", + selinux_instance.default_enforce_mode() + ); + println!( + "current enforce mode is: {}", + selinux_instance.enforce_mode() + ); + + match selinux_instance.current_label() { + Ok(l) => println!("SELinux label of current process is: {}", l), + Err(e) => println!("{}", e), + } + + let file_path = Path::new("./test_file.txt"); + let _file = File::create(file_path)?; + let selinux_label = + SELinuxLabel::try_from("unconfined_u:object_r:public_content_t:s1".to_string())?; + SELinux::set_file_label(file_path, selinux_label)?; + let current_label = SELinux::file_label(file_path)?; + println!("file label is {}", current_label); + + Ok(()) +} diff --git a/experiment/selinux/src/selinux.rs b/experiment/selinux/src/selinux.rs index 9e8a34ba0..e724cf997 100644 --- a/experiment/selinux/src/selinux.rs +++ b/experiment/selinux/src/selinux.rs @@ -1,15 +1,65 @@ -use crate::xattrs::*; +use crate::selinux_label::SELinuxLabel; use nix::errno::Errno; use nix::sys::statfs; use nix::unistd::gettid; -use std::fs::File; -use std::io::Read; +use std::collections::HashMap; +use std::convert::From; +use std::fmt; +use std::fs::{self, File, OpenOptions}; +use std::io::{BufRead, BufReader, Read, Write}; use std::os::fd::{AsFd, AsRawFd}; use std::path::{Path, PathBuf}; use std::sync::atomic::{AtomicBool, Ordering}; -const XATTR_NAME_SELINUX: &str = "security.selinux"; -const ERR_EMPTY_PATH: &str = "empty path"; +#[derive(Debug, Copy, Clone)] +pub enum SELinuxMode { + // ENFORCING constant to indicate SELinux is in enforcing mode + ENFORCING = 1, + // PERMISSIVE constant to indicate SELinux is in permissive mode + PERMISSIVE = 0, + // DISABLED constant to indicate SELinux is disabled + DISABLED = -1, +} + +impl From for SELinuxMode { + fn from(mode: i32) -> Self { + match mode { + 1 => SELinuxMode::ENFORCING, + 0 => SELinuxMode::PERMISSIVE, + -1 => SELinuxMode::DISABLED, + _ => SELinuxMode::DISABLED, + } + } +} + +impl From<&str> for SELinuxMode { + fn from(mode: &str) -> Self { + match mode { + "enforcing" => SELinuxMode::ENFORCING, + "permissive" => SELinuxMode::PERMISSIVE, + _ => SELinuxMode::DISABLED, + } + } +} + +impl fmt::Display for SELinuxMode { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let s = match self { + SELinuxMode::ENFORCING => "enforcing", + SELinuxMode::PERMISSIVE => "permissive", + SELinuxMode::DISABLED => "disabled", + }; + write!(f, "{}", s) + } +} + +pub(crate) const ERR_EMPTY_PATH: &str = "empty path"; +const SELINUX_FS_MOUNT: &str = "/sys/fs/selinux"; +const CONTEXT_FILE: &str = "/usr/share/containers/selinux/contexts"; +const SELINUX_TYPE_TAG: &str = "SELINUXTYPE"; +const SELINUX_TAG: &str = "SELINUX"; +const SELINUX_DIR: &str = "/etc/selinux/"; +const SELINUX_CONFIG: &str = "config"; #[derive(Debug, thiserror::Error)] pub enum SELinuxError { @@ -27,11 +77,44 @@ pub enum SELinuxError { ReadConFd(String), #[error("Failed to call read_con for SELinux: {0}")] ReadCon(String), + #[error("Failed to call write_con for SELinux: {0}")] + WriteCon(String), + #[error("Failed to find the index for a given class: {0}")] + ClassIndex(String), + #[error("Failed to call peer_label for SELinux: {0}")] + PeerLabel(String), + #[error("Failed to call open_context_file for SELinux: {0}")] + OpenContextFile(String), + #[error("Failed to set enforce mode of SELinux: {0}")] + SetEnforceMode(String), + #[error("Failed to read config file of SELinux: {0}")] + GetConfigKey(String), + #[error("Invalid format for SELinux label: {0}")] + InvalidSELinuxLabel(String), } pub struct SELinux { + // for attr_path() have_thread_self: AtomicBool, - init_done: AtomicBool, + attr_path_init_done: AtomicBool, + + // for selinuxfs + selinuxfs_init_done: AtomicBool, + selinuxfs: Option, + + // for policy_root() + policy_root_init_done: AtomicBool, + policy_root: Option, + + // for load_labels() + pub(crate) load_labels_init_done: AtomicBool, + pub(crate) labels: HashMap, + + // for read config and get config key + read_config_init_done: AtomicBool, + configs: HashMap, + + pub(crate) read_only_file_label: Option, } impl Default for SELinux { @@ -44,268 +127,291 @@ impl SELinux { pub fn new() -> Self { SELinux { have_thread_self: AtomicBool::new(false), - init_done: AtomicBool::new(false), - } - } + attr_path_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") - } + selinuxfs_init_done: AtomicBool::new(false), + selinuxfs: None, - // 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") - } + policy_root_init_done: AtomicBool::new(false), + policy_root: None, - // 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 { - unimplemented!("not implemented yet") - } + load_labels_init_done: AtomicBool::new(false), + labels: HashMap::new(), + + read_config_init_done: AtomicBool::new(false), + configs: HashMap::new(), - // 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())); + read_only_file_label: None, } + } - loop { - match fpath.set_xattr(XATTR_NAME_SELINUX, label.as_bytes()) { - Ok(_) => break, - // When a system call is interrupted by a signal, it needs to be retried. - Err(XattrError::EINTR(_)) => continue, - Err(e) => { - return Err(SELinuxError::SetFileLabel(format!( - "set_xattr failed: {}", - e - ))); + // This function returns policy_root. + // Directories under policy root has configuration files etc. + fn policy_root(&mut self) -> Option<&PathBuf> { + // Avoiding code conflicts and ensuring thread-safe execution once only. + if !self.policy_root_init_done.load(Ordering::SeqCst) { + let policy_root_path = Self::get_config_key(self, SELINUX_TYPE_TAG).unwrap_or_default(); + self.policy_root = Some(PathBuf::from(policy_root_path)); + self.policy_root_init_done.store(true, Ordering::SeqCst); + } + self.policy_root.as_ref() + } + + // This function reads SELinux config file and returns the value with a specified key. + fn get_config_key(&mut self, target_key: &str) -> Result { + if !self.read_config_init_done.load(Ordering::SeqCst) { + let config_path = Path::new(SELINUX_DIR).join(SELINUX_CONFIG); + if let Ok(file) = File::open(config_path) { + let reader = BufReader::new(file); + for line in reader.lines().map_while(Result::ok) { + if line.is_empty() { + continue; + } + if (line.starts_with(';')) || (line.starts_with('#')) { + continue; + } + let fields: Vec<&str> = line.splitn(2, '=').collect(); + if fields.len() < 2 { + continue; + } + let key = fields[0].trim().to_string(); + let value = fields[1].trim().to_string(); + self.configs.insert(key, value); } } + self.read_config_init_done.store(true, Ordering::SeqCst); } - Ok(()) + self.configs + .get(target_key) + .cloned() + .filter(|s| !s.is_empty()) + .ok_or(SELinuxError::GetConfigKey(format!( + "can't find the target label in the config file: {}", + target_key + ))) } - // 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())); + // get_enabled returns whether SELinux is enabled or not. + pub fn get_enabled(&mut self) -> bool { + match Self::get_selinux_mountpoint(self) { + // If there is no SELinux mountpoint, SELinux is not enabled. + None => false, + Some(_) => match Self::current_label(self) { + Ok(con) => { + // Check whether label is "kernel" or not. + if con.user != "kernel" { + return true; + } + false + } + Err(_) => false, + }, } + } + // verify_selinux_fs_mount verifies if the specified mount point is + // properly mounted as a writable SELinux filesystem. + fn verify_selinux_fs_mount>(mnt: P) -> bool { + let mnt = mnt.as_ref(); loop { - match fpath.lset_xattr(XATTR_NAME_SELINUX, label.as_bytes()) { - Ok(_) => break, - // When a system call is interrupted by a signal, it needs to be retried. - Err(XattrError::EINTR(_)) => continue, - Err(e) => { - return Err(SELinuxError::LSetFileLabel(format!( - "lset_xattr failed: {}", - e - ))); + match statfs::statfs(mnt) { + Ok(stat) => { + // In go-selinux, return false if it is not read-only, + // but selinux code in SELinuxProject return true even though it is read-only. + // https://github.com/SELinuxProject/selinux/blob/1f080ffd7ab24b0ad2b46f79db63d62c2ae2747c/libselinux/src/init.c#L44 + // Therefore, this function doesn't check whether it is read-only or not. + + // verify if the file is SELinux filesystem + return stat.filesystem_type() == statfs::SELINUX_MAGIC; } + // check again if there is an issue while calling statfs + Err(Errno::EAGAIN) | Err(Errno::EINTR) => continue, + Err(_) => return false, } } - 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 { - if !fpath.exists() { - return Err(SELinuxError::FileLabel(ERR_EMPTY_PATH.to_string())); + // check_line_include_selinux_fs_mount_point returns a next selinuxfs mount point found, + // if there is one, or None in case of EOF or error. + fn check_line_include_selinux_fs_mount_point(line: &str) -> Option { + if !line.contains(" - selinuxfs ") { + return None; } - fpath - .get_xattr(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 { - if !fpath.exists() { - return Err(SELinuxError::LFileLabel(ERR_EMPTY_PATH.to_string())); + // Need to return the path like /sys/fs/selinux + // example: 28 24 0:25 / /sys/fs/selinux rw,relatime - selinuxfs selinuxfs rw + let m_pos = 5; + let fields: Vec<&str> = line.splitn(m_pos + 1, ' ').collect(); + if fields.len() < m_pos + 1 { + return None; } - fpath - .lget_xattr(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 { - 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 { - 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 { - 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 { - 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") + let mountpoint = fields[m_pos - 1].to_string(); + Some(PathBuf::from(mountpoint)) } - // 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), + // find_selinux_fs finds the SELinux filesystem mount point. + fn find_selinux_fs() -> Option { + // fast path: check the default mount first + let selinux_fs_mount_path = PathBuf::from(SELINUX_FS_MOUNT); + if Self::verify_selinux_fs_mount(&selinux_fs_mount_path) { + return Some(selinux_fs_mount_path); } - } - - // 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") - } + // check if selinuxfs is available before going the slow path + let fs = fs::read_to_string("/proc/filesystems").unwrap_or_default(); + if !fs.contains("\tselinuxfs\n") { + return None; + } - // 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") + // slow path: try to find among the mounts + match File::open("/proc/self/mountinfo") { + Ok(file) => { + let reader = BufReader::new(file); + for line in reader.lines().map_while(Result::ok) { + if let Some(mnt) = Self::check_line_include_selinux_fs_mount_point(&line) { + if Self::verify_selinux_fs_mount(&mnt) { + return Some(mnt); + } + } + } + } + Err(_) => return None, + } + None } - // 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") + // This function returns the path to the mountpoint of an selinuxfs + // filesystem or an empty string if no mountpoint is found. Selinuxfs is + // a proc-like pseudo-filesystem that exposes the SELinux policy API to + // processes. The existence of an seliuxfs mount is used to determine + // whether SELinux is currently enabled or not. + pub fn get_selinux_mountpoint(&mut self) -> Option<&PathBuf> { + // Avoiding code conflicts and ensuring thread-safe execution once only. + if !self.selinuxfs_init_done.load(Ordering::SeqCst) { + self.selinuxfs = Self::find_selinux_fs(); + self.selinuxfs_init_done.store(true, Ordering::SeqCst); + } + self.selinuxfs.as_ref() + } + + // classIndex returns the int index for an object class in the loaded policy, or an error. + // For example, if a class is "file" or "dir", return the corresponding index for selinux. + pub fn class_index(&mut self, class: &str) -> Result { + let permpath = format!("class/{}/index", class); + let mountpoint = Self::get_selinux_mountpoint(self) + .ok_or_else(|| SELinuxError::ClassIndex("SELinux mount point not found".to_string()))?; + let indexpath = mountpoint.join(permpath); + + match fs::read_to_string(indexpath) { + Ok(index_b) => match index_b.parse::() { + Ok(index) => Ok(index), + Err(e) => Err(SELinuxError::ClassIndex(e.to_string())), + }, + Err(e) => Err(SELinuxError::ClassIndex(e.to_string())), + } } - // 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") + // This function attempts to open a selinux context file, and if it fails, it tries to open another file + // under policy root's directory. + pub(crate) fn open_context_file(&mut self) -> Result { + match File::open(CONTEXT_FILE) { + Ok(file) => Ok(file), + Err(_) => { + let policy_path = Self::policy_root(self).ok_or_else(|| { + SELinuxError::OpenContextFile("can't get policy root".to_string()) + })?; + let context_on_policy_root = policy_path.join("contexts").join("lxc_contexts"); + match File::open(context_on_policy_root) { + Ok(file) => Ok(file), + Err(e) => Err(SELinuxError::OpenContextFile(e.to_string())), + } + } + } } - // 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); + // This returns selinux enforce path by using selinux mountpoint. + // The enforce path dynamically changes SELinux mode at runtime, + // while the config file need OS to reboot after changing the config file. + fn selinux_enforce_path(&mut self) -> Option { + let selinux_mountpoint = Self::get_selinux_mountpoint(self); + selinux_mountpoint.map(|m| m.join("enforce")) + } + + // enforce_mode returns the current SELinux mode Enforcing, Permissive, Disabled + pub fn enforce_mode(&mut self) -> SELinuxMode { + let mode = match Self::selinux_enforce_path(self) { + Some(enforce_path) => match fs::read_to_string(enforce_path) { + Ok(content) => content.trim().parse::().unwrap_or(-1), + Err(_) => -1, + }, + None => -1, + }; + SELinuxMode::from(mode) + } + + // is_mls_enabled checks if MLS is enabled. + pub fn is_mls_enabled(&mut self) -> bool { + if let Some(mountpoint) = Self::get_selinux_mountpoint(self) { + let mls_path = Path::new(&mountpoint).join("mls"); + match fs::read(mls_path) { + Ok(enabled_b) => return enabled_b == vec![b'1'], + Err(_) => return false, } } - formatted_src - } + false + } + + // This function updates the enforce mode of selinux. + // Disabled is not valid, since this needs to be set at boot time. + pub fn set_enforce_mode(&mut self, mode: SELinuxMode) -> Result<(), SELinuxError> { + let enforce_path = Self::selinux_enforce_path(self).ok_or_else(|| { + SELinuxError::SetEnforceMode("can't get selinux enforce path".to_string()) + })?; + fs::write(enforce_path, mode.to_string().as_bytes()) + .map_err(|e| SELinuxError::SetEnforceMode(e.to_string())) + } + + // This returns the systems default SELinux mode Enforcing, Permissive or Disabled. + // note this is just the default at boot time. + // enforce_mode function tells you the system current mode. + pub fn default_enforce_mode(&mut self) -> SELinuxMode { + SELinuxMode::from( + Self::get_config_key(self, SELINUX_TAG) + .unwrap_or_default() + .as_str(), + ) + } + + // write_con writes a specified value to a given file path, handling SELinux context. + pub fn write_con>( + &mut self, + fpath: P, + val: &str, + ) -> Result { + let path = fpath.as_ref(); + if path.as_os_str().is_empty() { + return Err(SELinuxError::WriteCon(ERR_EMPTY_PATH.to_string())); + } + if val.is_empty() && !Self::get_enabled(self) { + return Err(SELinuxError::WriteCon("SELinux is not enabled".to_string())); + } - // function similar with writeCon in go-selinux repo. - pub fn write_con(fpath: &Path, val: &str) -> Result<(), SELinuxError> { - unimplemented!("not implemented yet"); + let mut out = OpenOptions::new() + .write(true) + .create(false) + .open(fpath) + .map_err(|e| SELinuxError::WriteCon(format!("failed to open file: {}", e)))?; + + Self::is_proc_handle(&out)?; + match out.write(val.as_bytes()) { + Ok(u) => Ok(u), + Err(e) => Err(SELinuxError::WriteCon(format!( + "failed to write in file: {}", + e + ))), + } } - // function similar with isProcHandle in go-selinux repo. + // This function checks whether this file is on the procfs filesystem. pub fn is_proc_handle(file: &File) -> Result<(), SELinuxError> { loop { match statfs::fstatfs(file.as_fd()) { @@ -328,8 +434,8 @@ impl SELinux { Ok(()) } - // function similar with readConFd in go-selinux repo. - pub fn read_con_fd(file: &mut File) -> Result { + // This function reads a given file descriptor into a string. + pub fn read_con_fd(file: &mut F) -> Result { let mut data = String::new(); file.read_to_string(&mut data) .map_err(|e| SELinuxError::ReadConFd(e.to_string()))?; @@ -339,9 +445,10 @@ impl SELinux { Ok(trimmed_data.to_string()) } - // function similar with readCon in go-selinux repo. - pub fn read_con(fpath: &Path) -> Result { - if fpath.as_os_str().is_empty() { + // read_con reads a label to a given file path, handling SELinux context. + pub fn read_con>(fpath: P) -> Result { + let path = fpath.as_ref(); + if path.as_os_str().is_empty() { return Err(SELinuxError::ReadCon(ERR_EMPTY_PATH.to_string())); } let mut in_file = File::open(fpath) @@ -351,18 +458,17 @@ impl SELinux { 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) { + if !self.attr_path_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); + self.attr_path_init_done.store(true, Ordering::SeqCst); } if self.have_thread_self.load(Ordering::SeqCst) { return PathBuf::from(&format!("{}/{}", THREAD_SELF_PREFIX, attr)); @@ -375,9 +481,11 @@ impl SELinux { #[cfg(test)] mod tests { use crate::selinux::*; - use std::fs::{self, File}; + use std::fs::File; use std::io::Write; use std::path::Path; + use std::str; + use tempfile::NamedTempFile; fn create_temp_file(content: &[u8], file_name: &str) { let path = Path::new(file_name); @@ -386,49 +494,21 @@ mod tests { 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); + let mut temp_file = NamedTempFile::new().expect("Failed to create temp file"); + temp_file + .write_all(content) + .expect("Failed to write to temp file"); // Need to open again to get read permission. - let mut file = File::open(file_name).expect("Failed to open file"); + let mut file = File::open(temp_file).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"); } } @@ -443,7 +523,7 @@ mod tests { 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.attr_path_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); @@ -475,4 +555,23 @@ mod tests { } } } + + #[test] + fn test_check_line_include_selinux_fs_mount_point() { + let input_array = [ + "28 24 0:25 / /sys/fs/selinux rw,relatime - selinuxfs selinuxfs rw", + "28 24 0:25 /", + "28 24 0:25 / /sys/fs/selinux rw,relatime selinuxfs rw", + ]; + let expected_array = ["/sys/fs/selinux", "", ""]; + let succeeded_array = [true, false, false]; + + for (i, input) in input_array.iter().enumerate() { + let expected = PathBuf::from(expected_array[i]); + match SELinux::check_line_include_selinux_fs_mount_point(input) { + Some(output) => assert_eq!(expected, output), + None => assert_eq!(succeeded_array[i], false), + } + } + } } diff --git a/experiment/selinux/src/selinux_label.rs b/experiment/selinux/src/selinux_label.rs new file mode 100644 index 000000000..04180fec6 --- /dev/null +++ b/experiment/selinux/src/selinux_label.rs @@ -0,0 +1,376 @@ +use crate::selinux::*; +use crate::tools::PathXattr; +use crate::tools::*; +use nix::sys::socket::getsockopt; +use std::convert::TryFrom; +use std::io::{BufRead, BufReader}; +use std::os::fd::AsFd; +use std::path::Path; +use std::sync::atomic::Ordering; + +const XATTR_NAME_SELINUX: &str = "security.selinux"; +const KEY_LABEL_PATH: &str = "/proc/self/attr/keycreate"; + +#[derive(Default, Clone)] +pub struct SELinuxLabel { + pub(crate) user: String, + role: String, + type_: String, + level: Option, +} + +impl std::fmt::Display for SELinuxLabel { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.level { + Some(level) => write!(f, "{}:{}:{}:{}", self.user, self.role, self.type_, level), + None => write!(f, "{}:{}:{}", self.user, self.role, self.type_), + } + } +} + +impl TryFrom for SELinuxLabel { + type Error = SELinuxError; + fn try_from(label: String) -> Result { + let fields: Vec<&str> = label.split(':').collect(); + if fields.len() < 3 { + return Err(SELinuxError::InvalidSELinuxLabel(label)); + } + + // It is possible that input label is "", which means no label is set. + let user = fields + .first() + .ok_or(SELinuxError::InvalidSELinuxLabel(label.clone()))? + .to_string(); + let role = fields + .get(1) + .ok_or(SELinuxError::InvalidSELinuxLabel(label.clone()))? + .to_string(); + let type_ = fields + .get(2) + .ok_or(SELinuxError::InvalidSELinuxLabel(label.clone()))? + .to_string(); + let level = fields.get(3).map(|&s| s.to_string()); + Ok(SELinuxLabel { + user, + role, + type_, + level, + }) + } +} + +// This impl is for methods related to labels in SELinux struct. +impl SELinux { + // set_file_label sets the SELinux label for this path, following symlinks, or returns an error. + pub fn set_file_label + PathXattr>( + fpath: P, + label: SELinuxLabel, + ) -> Result<(), SELinuxError> { + let path = fpath.as_ref(); + if !path.exists() { + return Err(SELinuxError::SetFileLabel(ERR_EMPTY_PATH.to_string())); + } + + loop { + match fpath.set_xattr(XATTR_NAME_SELINUX, label.to_string().as_bytes()) { + Ok(_) => break, + // When a system call is interrupted by a signal, it needs to be retried. + Err(XattrError::EINTR(_)) => continue, + Err(e) => { + return Err(SELinuxError::SetFileLabel(e.to_string())); + } + } + } + Ok(()) + } + + // lset_file_label sets the SELinux label for this path, not following symlinks, + // or returns an error. + pub fn lset_file_label + PathXattr>( + fpath: P, + label: SELinuxLabel, + ) -> Result<(), SELinuxError> { + let path = fpath.as_ref(); + if !path.exists() { + return Err(SELinuxError::LSetFileLabel(ERR_EMPTY_PATH.to_string())); + } + + loop { + match fpath.lset_xattr(XATTR_NAME_SELINUX, label.to_string().as_bytes()) { + Ok(_) => break, + // When a system call is interrupted by a signal, it needs to be retried. + Err(XattrError::EINTR(_)) => continue, + Err(e) => { + return Err(SELinuxError::LSetFileLabel(e.to_string())); + } + } + } + Ok(()) + } + + // fileLabel returns the SELinux label for this path, following symlinks, + // or returns an error. + pub fn file_label + PathXattr>(fpath: P) -> Result { + let path = fpath.as_ref(); + if !path.exists() { + return Err(SELinuxError::FileLabel(ERR_EMPTY_PATH.to_string())); + } + let label_str = fpath + .get_xattr(XATTR_NAME_SELINUX) + .map_err(|e| SELinuxError::FileLabel(e.to_string()))?; + SELinuxLabel::try_from(label_str) + } + + // lfile_label returns the SELinux label for this path, not following symlinks, + // or returns an error. + pub fn lfile_label + PathXattr>(fpath: P) -> Result { + let path = fpath.as_ref(); + if !path.exists() { + return Err(SELinuxError::LFileLabel(ERR_EMPTY_PATH.to_string())); + } + let label_str = fpath + .lget_xattr(XATTR_NAME_SELINUX) + .map_err(|e| SELinuxError::LFileLabel(e.to_string()))?; + SELinuxLabel::try_from(label_str) + } + + // set_fscreate_label sets the default label the kernel which the kernel is using + // for file system objects. + pub fn set_fscreate_label(&mut self, label: SELinuxLabel) -> Result { + return Self::write_con( + self, + self.attr_path("fscreate").as_path(), + label.to_string().as_str(), + ); + } + + // 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 { + let label = Self::read_con(self.attr_path("fscreate").as_path())?; + SELinuxLabel::try_from(label) + } + + // pid_label returns the SELinux label of the given pid, or an error. + pub fn pid_label(pid: i64) -> Result { + let file_name = &format!("/proc/{}/attr/current", pid); + let file_path = Path::new(file_name); + let label = Self::read_con(file_path)?; + SELinuxLabel::try_from(label) + } + + // 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 { + let label = Self::read_con(self.attr_path("exec").as_path())?; + SELinuxLabel::try_from(label) + } + + // 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(&mut self, label: SELinuxLabel) -> Result { + Self::write_con( + self, + self.attr_path("exec").as_path(), + label.to_string().as_str(), + ) + } + + // set_task_label sets the SELinux label for the current thread, or an error. + // This requires the dyntransition permission because this changes the context of current thread. + pub fn set_task_label(&mut self, label: SELinuxLabel) -> Result { + Self::write_con( + self, + self.attr_path("current").as_path(), + label.to_string().as_str(), + ) + } + + // 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(&mut self, label: SELinuxLabel) -> Result { + Self::write_con( + self, + self.attr_path("sockcreate").as_path(), + label.to_string().as_str(), + ) + } + + // socket_label retrieves the current socket label setting. + pub fn socket_label(&self) -> Result { + let label = Self::read_con(self.attr_path("sockcreate").as_path())?; + SELinuxLabel::try_from(label) + } + + // current_label returns the SELinux label of the current process thread, or an error. + pub fn current_label(&self) -> Result { + let label = SELinux::read_con(self.attr_path("current").as_path())?; + SELinuxLabel::try_from(label) + } + + // peer_label retrieves the label of the client on the other side of a socket. + pub fn peer_label(fd: F) -> Result { + // getsockopt manipulate options for the socket referred to by the file descriptor. + // https://man7.org/linux/man-pages/man2/getsockopt.2.html + match getsockopt(&fd, PeerSec) { + Ok(label) => match label.into_string() { + Ok(label_str) => SELinuxLabel::try_from(label_str), + Err(e) => Err(SELinuxError::PeerLabel(e.to_string())), + }, + Err(e) => Err(SELinuxError::PeerLabel(e.to_string())), + } + } + + // 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(&mut self, label: SELinuxLabel) -> Result { + Self::write_con(self, Path::new(KEY_LABEL_PATH), label.to_string().as_str()) + } + + // key_label retrieves the current kernel keyring label setting + pub fn key_label() -> Result { + let label = Self::read_con(Path::new(KEY_LABEL_PATH))?; + SELinuxLabel::try_from(label) + } + + // kvm_container_labels returns the default processLabel and mountLabel to be used + // for kvm containers by the calling process. + pub fn kvm_container_labels(&mut self) -> (Option, Option) { + let process_label = + Self::label(self, "kvm_process").or_else(|| Self::label(self, "process")); + (process_label, Self::label(self, "file")) + // TODO: use addMcs + } + + // 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(&mut self) -> (Option, Option) { + let process_label = + Self::label(self, "init_process").or_else(|| Self::label(self, "process")); + (process_label, Self::label(self, "file")) + // TODO: use addMcs + } + + // container_labels returns an allocated processLabel and fileLabel to be used for + // container labeling by the calling process. + pub fn container_labels(&mut self) -> (Option, Option) { + if !Self::get_enabled(self) { + return (None, None); + } + let process_label = Self::label(self, "process"); + let file_label = Self::label(self, "file"); + + if process_label.is_none() || file_label.is_none() { + return (process_label, file_label); + } + + let mut read_only_file_label = Self::label(self, "ro_file"); + if read_only_file_label.is_none() { + read_only_file_label = file_label.clone(); + } + self.read_only_file_label = read_only_file_label; + + (process_label, file_label) + // TODO: use addMcs + } + + // This function returns the value of given key on selinux context + fn label(&mut self, key: &str) -> Option { + if !self.load_labels_init_done.load(Ordering::SeqCst) { + Self::load_labels(self); + self.load_labels_init_done.store(true, Ordering::SeqCst); + } + self.labels.get(key).cloned() + } + + // This function loads context file and reads labels and stores it. + fn load_labels(&mut self) { + // The context file should have pairs of key and value like below. + // ---------- + // process = "system_u:system_r:container_t:s0" + // file = "system_u:object_r:container_file_t:s0" + // ---------- + if let Ok(file) = Self::open_context_file(self) { + let reader = BufReader::new(file); + for line in reader.lines().map_while(Result::ok) { + let line = line.trim(); + if line.is_empty() || line.starts_with(';') || line.starts_with('#') { + continue; + } + let fields: Vec<&str> = line.splitn(2, '=').collect(); + if fields.len() != 2 { + continue; + } + let key = fields[0].trim().to_string(); + let value = fields[1].trim_matches('"').trim().to_string(); + if let Ok(value_label) = SELinuxLabel::try_from(value) { + self.labels.insert(key, value_label); + } + } + } + } + + // 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") + } + + // 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 + } +} + +#[cfg(test)] +mod tests { + use crate::selinux::*; + + #[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 + ); + } + } +} diff --git a/experiment/selinux/src/tools/mod.rs b/experiment/selinux/src/tools/mod.rs new file mode 100644 index 000000000..8909a7920 --- /dev/null +++ b/experiment/selinux/src/tools/mod.rs @@ -0,0 +1,5 @@ +mod sockopt; +mod xattr; + +pub use sockopt::*; +pub use xattr::*; diff --git a/experiment/selinux/src/tools/sockopt.rs b/experiment/selinux/src/tools/sockopt.rs new file mode 100644 index 000000000..25928642f --- /dev/null +++ b/experiment/selinux/src/tools/sockopt.rs @@ -0,0 +1,38 @@ +use nix::libc; +use nix::sys::socket::GetSockOpt; +use std::ffi::CString; +use std::os::fd::{AsFd, AsRawFd}; + +#[derive(Debug, Copy, Clone)] +pub struct PeerSec; + +// This function implements the GetSockOpt for PeerSec, retrieving the security context label +// of a socket file descriptor into a CString. +// This function utilizes nix's GetSockOpt implementation. +// https://github.com/nix-rust/nix/blob/50e4283b35f3f34e138d138fd889f7e3c424a5c2/src/sys/socket/mod.rs#L2219 +impl GetSockOpt for PeerSec { + type Val = CString; + + fn get(&self, fd: &F) -> nix::Result { + let mut len: libc::socklen_t = libc::c_int::MAX as libc::socklen_t; + let mut buf = vec![0u8; len as usize]; + let fd_i32 = fd.as_fd().as_raw_fd(); + + let ret = unsafe { + libc::getsockopt( + fd_i32, + libc::SOL_SOCKET, + libc::SO_PEERSEC, + buf.as_mut_ptr() as *mut libc::c_void, + &mut len, + ) + }; + + if ret == -1 { + return Err(nix::Error::last()); + } + + buf.truncate(len as usize); + Ok(CString::new(buf).unwrap()) + } +} diff --git a/experiment/selinux/src/xattrs/xattr.rs b/experiment/selinux/src/tools/xattr.rs similarity index 89% rename from experiment/selinux/src/xattrs/xattr.rs rename to experiment/selinux/src/tools/xattr.rs index a9ea43b07..2e44ce006 100644 --- a/experiment/selinux/src/xattrs/xattr.rs +++ b/experiment/selinux/src/tools/xattr.rs @@ -16,6 +16,7 @@ pub enum XattrError { EINTR(i32), } +// SELinux label is not so big, so we allocate 1024 bytes for the buffer. const INITIAL_BUF_SIZE: usize = 1024; pub trait PathXattr { @@ -25,11 +26,15 @@ pub trait PathXattr { fn lget_xattr(&self, attr: &str) -> Result; } -impl PathXattr for Path { +impl

PathXattr for P +where + P: AsRef, +{ // function similar with setxattr in golang.org/x/sys/unix repo. // set_xattr sets extended attributes on a file specified by its path. fn set_xattr(&self, attr: &str, data: &[u8]) -> Result<(), XattrError> { - match rfs::setxattr(self, attr, data, rfs::XattrFlags::CREATE) { + let path = self.as_ref(); + match rfs::setxattr(path, attr, data, rfs::XattrFlags::CREATE) { Ok(_) => Ok(()), Err(e) => { let errno = e.raw_os_error(); @@ -44,7 +49,8 @@ impl PathXattr for Path { // function similar with lsetxattr in golang.org/x/sys/unix repo. // lset_xattr sets extended attributes on a symbolic link. fn lset_xattr(&self, attr: &str, data: &[u8]) -> Result<(), XattrError> { - match rfs::lsetxattr(self, attr, data, rfs::XattrFlags::CREATE) { + let path = self.as_ref(); + match rfs::lsetxattr(path, attr, data, rfs::XattrFlags::CREATE) { Ok(_) => Ok(()), Err(e) => { let errno = e.raw_os_error(); @@ -59,12 +65,12 @@ impl PathXattr for Path { // function similar with getattr in go-selinux repo. // get_xattr returns the value of an extended attribute attr set for path. fn get_xattr(&self, attr: &str) -> Result { - // SELinux label is not so big, so we allocate 1024 bytes for the buffer. + let path = self.as_ref(); let mut buf_size = INITIAL_BUF_SIZE; let mut buf = vec![0u8; buf_size]; loop { - match rfs::getxattr(self, attr, &mut buf) { + match rfs::getxattr(path, attr, &mut buf) { Ok(size) => { if size == buf_size { buf_size *= 2; @@ -85,12 +91,12 @@ impl PathXattr for Path { // function similar with lgetxattr in go-selinux repo. // lget_xattr returns the value of an extended attribute attr set for path. fn lget_xattr(&self, attr: &str) -> Result { - // SELinux label is not so big, so we allocate 1024 bytes for the buffer. + let path = self.as_ref(); let mut buf_size = INITIAL_BUF_SIZE; let mut buf = vec![0u8; buf_size]; loop { - match rfs::lgetxattr(self, attr, &mut buf) { + match rfs::lgetxattr(path, attr, &mut buf) { Ok(size) => { if size == buf_size { buf_size *= 2; @@ -111,7 +117,7 @@ impl PathXattr for Path { #[cfg(test)] mod tests { - use crate::xattrs::*; + use crate::tools::*; use tempfile::NamedTempFile; #[test] diff --git a/experiment/selinux/src/xattrs/mod.rs b/experiment/selinux/src/xattrs/mod.rs deleted file mode 100644 index df4efc217..000000000 --- a/experiment/selinux/src/xattrs/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -mod xattr; - -pub use xattr::*; From c7d323a79f5643aff29447d8766431efd6c769b9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 16 Aug 2024 00:08:27 +0000 Subject: [PATCH 02/14] Bump the patch group with 3 updates Bumps the patch group with 3 updates: [serde](https://github.com/serde-rs/serde), [libc](https://github.com/rust-lang/libc) and [serde_json](https://github.com/serde-rs/json). Updates `serde` from 1.0.207 to 1.0.208 - [Release notes](https://github.com/serde-rs/serde/releases) - [Commits](https://github.com/serde-rs/serde/compare/v1.0.207...v1.0.208) Updates `libc` from 0.2.155 to 0.2.156 - [Release notes](https://github.com/rust-lang/libc/releases) - [Changelog](https://github.com/rust-lang/libc/blob/0.2.156/CHANGELOG.md) - [Commits](https://github.com/rust-lang/libc/compare/0.2.155...0.2.156) Updates `serde_json` from 1.0.124 to 1.0.125 - [Release notes](https://github.com/serde-rs/json/releases) - [Commits](https://github.com/serde-rs/json/compare/v1.0.124...1.0.125) --- updated-dependencies: - dependency-name: serde dependency-type: direct:production update-type: version-update:semver-patch dependency-group: patch - dependency-name: libc dependency-type: direct:production update-type: version-update:semver-patch dependency-group: patch - dependency-name: serde_json dependency-type: direct:production update-type: version-update:semver-patch dependency-group: patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 16 ++++++++-------- crates/libcgroups/Cargo.toml | 2 +- crates/libcontainer/Cargo.toml | 2 +- tests/contest/runtimetest/Cargo.toml | 2 +- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 32a14f121..14b8e1349 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1941,9 +1941,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.155" +version = "0.2.156" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" +checksum = "a5f43f184355eefb8d17fc948dbecf6c13be3c141f20d834ae842193a448c72a" [[package]] name = "libcgroups" @@ -3399,9 +3399,9 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "serde" -version = "1.0.207" +version = "1.0.208" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5665e14a49a4ea1b91029ba7d3bca9f299e1f7cfa194388ccc20f14743e784f2" +checksum = "cff085d2cb684faa248efb494c39b68e522822ac0de72ccf08109abde717cfb2" dependencies = [ "serde_derive", ] @@ -3429,9 +3429,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.207" +version = "1.0.208" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6aea2634c86b0e8ef2cfdc0c340baede54ec27b1e46febd7f80dffb2aa44a00e" +checksum = "24008e81ff7613ed8e5ba0cfaf24e2c2f1e5b8a0495711e44fcd4882fca62bcf" dependencies = [ "proc-macro2", "quote", @@ -3440,9 +3440,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.124" +version = "1.0.125" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66ad62847a56b3dba58cc891acd13884b9c61138d330c0d7b6181713d4fce38d" +checksum = "83c8e735a073ccf5be70aa8066aa984eaf2fa000db6c8d0100ae605b366d31ed" dependencies = [ "itoa", "memchr", diff --git a/crates/libcgroups/Cargo.toml b/crates/libcgroups/Cargo.toml index 981e5a2c0..7caeb0f00 100644 --- a/crates/libcgroups/Cargo.toml +++ b/crates/libcgroups/Cargo.toml @@ -28,7 +28,7 @@ serde = { version = "1.0", features = ["derive"] } rbpf = { version = "0.2.0", optional = true } libbpf-sys = { version = "1.4.3", optional = true } errno = { version = "0.3.9", optional = true } -libc = { version = "0.2.155", optional = true } +libc = { version = "0.2.156", optional = true } thiserror = "1.0.63" tracing = { version = "0.1.40", features = ["attributes"] } diff --git a/crates/libcontainer/Cargo.toml b/crates/libcontainer/Cargo.toml index 8918956a1..0097f5ca3 100644 --- a/crates/libcontainer/Cargo.toml +++ b/crates/libcontainer/Cargo.toml @@ -28,7 +28,7 @@ chrono = { version = "0.4", default-features = false, features = [ ] } fastrand = "^2.1.0" futures = { version = "0.3", features = ["thread-pool"] } -libc = "0.2.155" +libc = "0.2.156" nix = { version = "0.28.0", features = [ "socket", "sched", diff --git a/tests/contest/runtimetest/Cargo.toml b/tests/contest/runtimetest/Cargo.toml index 4a9c30651..43622b5f0 100644 --- a/tests/contest/runtimetest/Cargo.toml +++ b/tests/contest/runtimetest/Cargo.toml @@ -7,5 +7,5 @@ edition = "2021" oci-spec = { version = "0.6.8", features = ["runtime"] } nix = "0.28.0" anyhow = "1.0" -libc = "0.2.155" # TODO (YJDoc2) upgrade to latest +libc = "0.2.156" # TODO (YJDoc2) upgrade to latest nc = "0.8.23" From 6a038ca1d08ac3f45d59ee76e062ff910a9f6769 Mon Sep 17 00:00:00 2001 From: sat0ken <15720506+sat0ken@users.noreply.github.com> Date: Thu, 22 Aug 2024 18:50:26 +0900 Subject: [PATCH 03/14] add syscalls lib to get systemcall number from string Signed-off-by: sat0ken <15720506+sat0ken@users.noreply.github.com> --- experiment/seccomp/Cargo.lock | 42 +++++++++++++++++++++++++++++++++++ experiment/seccomp/Cargo.toml | 1 + 2 files changed, 43 insertions(+) diff --git a/experiment/seccomp/Cargo.lock b/experiment/seccomp/Cargo.lock index d3af855d6..9ee70dd61 100644 --- a/experiment/seccomp/Cargo.lock +++ b/experiment/seccomp/Cargo.lock @@ -266,10 +266,42 @@ dependencies = [ "nix 0.27.1", "prctl", "syscall-numbers", + "syscalls", "thiserror", "tokio", ] +[[package]] +name = "serde" +version = "1.0.208" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cff085d2cb684faa248efb494c39b68e522822ac0de72ccf08109abde717cfb2" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.208" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24008e81ff7613ed8e5ba0cfaf24e2c2f1e5b8a0495711e44fcd4882fca62bcf" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_repr" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "signal-hook-registry" version = "1.4.2" @@ -312,6 +344,16 @@ version = "3.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "084e382bf467cd3381fdec080d883505792ee0d16a004b1b090abf2db5dc2a29" +[[package]] +name = "syscalls" +version = "0.6.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43d0e35dc7d73976a53c7e6d7d177ef804a0c0ee774ec77bcc520c2216fd7cbe" +dependencies = [ + "serde", + "serde_repr", +] + [[package]] name = "thiserror" version = "1.0.58" diff --git a/experiment/seccomp/Cargo.toml b/experiment/seccomp/Cargo.toml index 56c4fae40..626004a2d 100644 --- a/experiment/seccomp/Cargo.toml +++ b/experiment/seccomp/Cargo.toml @@ -24,3 +24,4 @@ prctl = "1.0.0" anyhow = "1.0" tokio = { version = "1", features = ["full"] } syscall-numbers = "3.1.1" +syscalls = "0.6.18" \ No newline at end of file From 8dc6cf46e1f3263d5504501726931cf2b354c58a Mon Sep 17 00:00:00 2001 From: sat0ken <15720506+sat0ken@users.noreply.github.com> Date: Thu, 22 Aug 2024 18:51:48 +0900 Subject: [PATCH 04/14] add AArch64 definition to enum Signed-off-by: sat0ken <15720506+sat0ken@users.noreply.github.com> --- experiment/seccomp/src/instruction/arch.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/experiment/seccomp/src/instruction/arch.rs b/experiment/seccomp/src/instruction/arch.rs index 521a0828b..3ee2736b1 100644 --- a/experiment/seccomp/src/instruction/arch.rs +++ b/experiment/seccomp/src/instruction/arch.rs @@ -2,12 +2,13 @@ use crate::instruction::Instruction; use crate::instruction::*; pub enum Arch { - X86, + X86,AArch64 } pub fn gen_validate(arc: &Arch) -> Vec { let arch = match arc { Arch::X86 => AUDIT_ARCH_X86_64, + Arch::AArch64 => AUDIT_ARCH_AARCH64 }; vec![ From 298812d635a295692556eec94bd025655a8fbea6 Mon Sep 17 00:00:00 2001 From: sat0ken <15720506+sat0ken@users.noreply.github.com> Date: Thu, 22 Aug 2024 18:52:34 +0900 Subject: [PATCH 05/14] add function for develop Signed-off-by: sat0ken <15720506+sat0ken@users.noreply.github.com> --- experiment/seccomp/src/main.rs | 29 ++------------- experiment/seccomp/src/seccomp.rs | 62 +++++++++++++++++++++++++++++-- 2 files changed, 62 insertions(+), 29 deletions(-) diff --git a/experiment/seccomp/src/main.rs b/experiment/seccomp/src/main.rs index 9fd04bdae..77e173e24 100644 --- a/experiment/seccomp/src/main.rs +++ b/experiment/seccomp/src/main.rs @@ -10,7 +10,6 @@ use std::slice; use anyhow::Result; use nix::{ - libc, sys::{ signal::Signal, socket::{ @@ -22,6 +21,7 @@ use nix::{ unistd::{close, mkdir}, }; use syscall_numbers::x86_64; +use seccomp::seccomp::set_instruction; fn send_fd(sock: OwnedFd, fd: &F) -> nix::Result<()> { let fd = fd.as_raw_fd(); @@ -89,31 +89,8 @@ async fn main() -> Result<()> { SockFlag::empty(), )?; - let _ = prctl::set_no_new_privileges(true); - - let mut bpf_prog = instruction::gen_validate(&Arch::X86); - bpf_prog.append(&mut vec![ - // A: Check if syscall is getcwd - Instruction::stmt(BPF_LD | BPF_W | BPF_ABS, 0), - Instruction::jump(BPF_JMP | BPF_JEQ | BPF_K, 0, 1, libc::SYS_getcwd as u32), // If false, go to B - Instruction::stmt(BPF_RET | BPF_K, SECCOMP_RET_KILL_PROCESS), - // B: Check if syscall is write and it is writing to stderr(fd=2) - Instruction::stmt(BPF_LD | BPF_W | BPF_ABS, 0), - Instruction::jump(BPF_JMP | BPF_JEQ | BPF_K, 0, 3, libc::SYS_write as u32), // If false, go to C - // Load the file descriptor - Instruction::stmt(BPF_LD | BPF_W | BPF_ABS, seccomp_data_args_offset().into()), - // Check if args is stderr - Instruction::jump(BPF_JMP | BPF_JEQ | BPF_K, 0, 1, libc::STDERR_FILENO as u32), // If false, go to C - Instruction::stmt(BPF_RET | BPF_K, SECCOMP_RET_KILL_PROCESS), - // C: Check if syscall is mkdir and if so, return seccomp notify - Instruction::stmt(BPF_LD | BPF_W | BPF_ABS, 0), - Instruction::jump(BPF_JMP | BPF_JEQ | BPF_K, 0, 1, libc::SYS_mkdir as u32), // If false, go to D - Instruction::stmt(BPF_RET | BPF_K, SECCOMP_RET_USER_NOTIF), - // D: Pass - Instruction::stmt(BPF_RET | BPF_K, SECCOMP_RET_ALLOW), - ]); - - let seccomp = Seccomp { filters: bpf_prog }; + let syscalls_arr : Vec = vec!["getcwd".to_string(), "write".to_string(), "mkdir".to_string()]; + let mut seccomp = Seccomp {filters: set_instruction(&Arch::X86, SECCOMP_RET_KILL_PROCESS, syscalls_arr)}; tokio::spawn(async move { tokio::signal::ctrl_c() diff --git a/experiment/seccomp/src/seccomp.rs b/experiment/seccomp/src/seccomp.rs index bc45741df..9389db827 100644 --- a/experiment/seccomp/src/seccomp.rs +++ b/experiment/seccomp/src/seccomp.rs @@ -6,15 +6,15 @@ use std::{ unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}, }, }; - +use std::str::FromStr; use nix::{ errno::Errno, ioctl_readwrite, ioctl_write_ptr, libc, libc::{SECCOMP_FILTER_FLAG_NEW_LISTENER, SECCOMP_SET_MODE_FILTER}, unistd, }; - -use crate::instruction::{Instruction, SECCOMP_IOC_MAGIC}; +use crate::instruction::{*}; +use crate::instruction::{Arch, Instruction, SECCOMP_IOC_MAGIC}; #[derive(Debug, thiserror::Error)] pub enum SeccompError { @@ -198,3 +198,59 @@ struct Filters { pub len: c_ushort, pub filter: *const Instruction, } + +fn get_syscall_number(_arc: &Arch, name: &str) -> Option { + match syscalls::x86_64::Sysno::from_str(name) { + Ok(syscall) => Some(syscall as u64), + Err(_) => None, + } +} + +pub fn set_instruction(arc: &Arch, def_action: u32, systemcall_arr: Vec) -> Vec { + let _ = prctl::set_no_new_privileges(true); + let mut bpf_prog = gen_validate(arc); + + for syscall in &systemcall_arr { + bpf_prog.append(&mut vec![Instruction::stmt(BPF_LD | BPF_W | BPF_ABS, 0)]); + bpf_prog.append(&mut vec![Instruction::jump(BPF_JMP | BPF_JEQ | BPF_K, 0, 1, get_syscall_number(arc, syscall).unwrap() as c_uint)]); + + if (syscall == "write") { + // Check if syscall is write and it is writing to stderr(fd=2) + // Load the file descriptor + bpf_prog.append(&mut vec![Instruction::stmt(BPF_LD | BPF_W | BPF_ABS, seccomp_data_args_offset().into())]); + bpf_prog.append(&mut vec![Instruction::jump(BPF_JMP | BPF_JEQ | BPF_K, 0, 1, libc::STDERR_FILENO as u32)]); + } + + if (syscall != "mkdir") { + bpf_prog.append(&mut vec![Instruction::stmt(BPF_RET | BPF_K, def_action)]); + } else { + bpf_prog.append(&mut vec![Instruction::stmt(BPF_RET | BPF_K, SECCOMP_RET_USER_NOTIF)]); + } + + } + + bpf_prog.append(&mut vec![Instruction::stmt(BPF_RET | BPF_K, SECCOMP_RET_ALLOW)]); + + // bpf_prog.append(&mut vec![ + // // A: Check if syscall is getcwd + // Instruction::stmt(BPF_LD | BPF_W | BPF_ABS, 0), + // Instruction::jump(BPF_JMP | BPF_JEQ | BPF_K, 0, 1, get_syscall_number(arc, "getcwd").unwrap() as c_uint), // If false, go to B + // Instruction::stmt(BPF_RET | BPF_K, def_action), + // // B: Check if syscall is write and it is writing to stderr(fd=2) + // Instruction::stmt(BPF_LD | BPF_W | BPF_ABS, 0), + // Instruction::jump(BPF_JMP | BPF_JEQ | BPF_K, 0, 3, get_syscall_number(arc, "write").unwrap() as c_uint), // If false, go to C + // // Load the file descriptor + // Instruction::stmt(BPF_LD | BPF_W | BPF_ABS, seccomp_data_args_offset().into()), + // // Check if args is stderr + // Instruction::jump(BPF_JMP | BPF_JEQ | BPF_K, 0, 1, libc::STDERR_FILENO as u32), // If false, go to C + // Instruction::stmt(BPF_RET | BPF_K, def_action), + // // C: Check if syscall is mkdir and if so, return seccomp notify + // Instruction::stmt(BPF_LD | BPF_W | BPF_ABS, 0), + // Instruction::jump(BPF_JMP | BPF_JEQ | BPF_K, 0, 1, get_syscall_number(arc, "mkdir").unwrap() as c_uint), // If false, go to D + // Instruction::stmt(BPF_RET | BPF_K, SECCOMP_RET_USER_NOTIF), + // // D: Pass + // Instruction::stmt(BPF_RET | BPF_K, SECCOMP_RET_ALLOW), + // ]); + + return bpf_prog; +} \ No newline at end of file From 7fb2b4b0b5cb07c53cc33f1dee90bce92b2541f9 Mon Sep 17 00:00:00 2001 From: sat0ken <15720506+sat0ken@users.noreply.github.com> Date: Mon, 26 Aug 2024 18:19:44 +0900 Subject: [PATCH 06/14] update Cargo.toml, add feature aarch64 Signed-off-by: sat0ken <15720506+sat0ken@users.noreply.github.com> --- experiment/seccomp/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/experiment/seccomp/Cargo.toml b/experiment/seccomp/Cargo.toml index 626004a2d..0b5bbc8e2 100644 --- a/experiment/seccomp/Cargo.toml +++ b/experiment/seccomp/Cargo.toml @@ -24,4 +24,4 @@ prctl = "1.0.0" anyhow = "1.0" tokio = { version = "1", features = ["full"] } syscall-numbers = "3.1.1" -syscalls = "0.6.18" \ No newline at end of file +syscalls = { version = "0.6.18", features = ["std", "serde", "aarch64", "x86_64"]} \ No newline at end of file From 131ee6aa21399fe05a690ac9525a4c907a68ee7d Mon Sep 17 00:00:00 2001 From: sat0ken <15720506+sat0ken@users.noreply.github.com> Date: Mon, 26 Aug 2024 18:23:14 +0900 Subject: [PATCH 07/14] add derive(PartialEq) to enum Arch Signed-off-by: sat0ken <15720506+sat0ken@users.noreply.github.com> --- experiment/seccomp/src/instruction/arch.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/experiment/seccomp/src/instruction/arch.rs b/experiment/seccomp/src/instruction/arch.rs index 3ee2736b1..05a0b31b8 100644 --- a/experiment/seccomp/src/instruction/arch.rs +++ b/experiment/seccomp/src/instruction/arch.rs @@ -1,6 +1,7 @@ use crate::instruction::Instruction; use crate::instruction::*; +#[derive(PartialEq)] pub enum Arch { X86,AArch64 } From 84a1a37ba872ea60f95b4d5221a4a480f012ff98 Mon Sep 17 00:00:00 2001 From: sat0ken <15720506+sat0ken@users.noreply.github.com> Date: Mon, 26 Aug 2024 18:24:10 +0900 Subject: [PATCH 08/14] update fn get_syscall_number to get system call number of X86 and aarch64 Signed-off-by: sat0ken <15720506+sat0ken@users.noreply.github.com> --- experiment/seccomp/src/seccomp.rs | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/experiment/seccomp/src/seccomp.rs b/experiment/seccomp/src/seccomp.rs index 9389db827..7d9085e31 100644 --- a/experiment/seccomp/src/seccomp.rs +++ b/experiment/seccomp/src/seccomp.rs @@ -6,6 +6,7 @@ use std::{ unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}, }, }; + use std::str::FromStr; use nix::{ errno::Errno, @@ -199,11 +200,19 @@ struct Filters { pub filter: *const Instruction, } -fn get_syscall_number(_arc: &Arch, name: &str) -> Option { - match syscalls::x86_64::Sysno::from_str(name) { - Ok(syscall) => Some(syscall as u64), - Err(_) => None, +fn get_syscall_number(arc: &Arch, name: &str) -> Option { + if arc == &Arch::X86 { + match syscalls::x86_64::Sysno::from_str(name) { + Ok(syscall) => Some(syscall as u64), + Err(_) => None, + } + } else { + match syscalls::aarch64::Sysno::from_str(name) { + Ok(syscall) => Some(syscall as u64), + Err(_) => None, + } } + } pub fn set_instruction(arc: &Arch, def_action: u32, systemcall_arr: Vec) -> Vec { @@ -214,14 +223,14 @@ pub fn set_instruction(arc: &Arch, def_action: u32, systemcall_arr: Vec) bpf_prog.append(&mut vec![Instruction::stmt(BPF_LD | BPF_W | BPF_ABS, 0)]); bpf_prog.append(&mut vec![Instruction::jump(BPF_JMP | BPF_JEQ | BPF_K, 0, 1, get_syscall_number(arc, syscall).unwrap() as c_uint)]); - if (syscall == "write") { + if syscall == "write" { // Check if syscall is write and it is writing to stderr(fd=2) // Load the file descriptor bpf_prog.append(&mut vec![Instruction::stmt(BPF_LD | BPF_W | BPF_ABS, seccomp_data_args_offset().into())]); bpf_prog.append(&mut vec![Instruction::jump(BPF_JMP | BPF_JEQ | BPF_K, 0, 1, libc::STDERR_FILENO as u32)]); } - if (syscall != "mkdir") { + if syscall != "mkdir" { bpf_prog.append(&mut vec![Instruction::stmt(BPF_RET | BPF_K, def_action)]); } else { bpf_prog.append(&mut vec![Instruction::stmt(BPF_RET | BPF_K, SECCOMP_RET_USER_NOTIF)]); From 3516f928395285ffd13b580defad4c83e03c451e Mon Sep 17 00:00:00 2001 From: sat0ken <15720506+sat0ken@users.noreply.github.com> Date: Wed, 28 Aug 2024 07:51:48 +0900 Subject: [PATCH 09/14] replace func to from trait Signed-off-by: sat0ken <15720506+sat0ken@users.noreply.github.com> --- experiment/seccomp/src/instruction/arch.rs | 2 +- experiment/seccomp/src/main.rs | 10 ++- experiment/seccomp/src/seccomp.rs | 75 +++++++++------------- 3 files changed, 39 insertions(+), 48 deletions(-) diff --git a/experiment/seccomp/src/instruction/arch.rs b/experiment/seccomp/src/instruction/arch.rs index 05a0b31b8..2883f5daa 100644 --- a/experiment/seccomp/src/instruction/arch.rs +++ b/experiment/seccomp/src/instruction/arch.rs @@ -1,7 +1,7 @@ use crate::instruction::Instruction; use crate::instruction::*; -#[derive(PartialEq)] +#[derive(PartialEq, Debug)] pub enum Arch { X86,AArch64 } diff --git a/experiment/seccomp/src/main.rs b/experiment/seccomp/src/main.rs index 77e173e24..e84fd4dd3 100644 --- a/experiment/seccomp/src/main.rs +++ b/experiment/seccomp/src/main.rs @@ -21,7 +21,7 @@ use nix::{ unistd::{close, mkdir}, }; use syscall_numbers::x86_64; -use seccomp::seccomp::set_instruction; +use seccomp::seccomp::{InstructionData}; fn send_fd(sock: OwnedFd, fd: &F) -> nix::Result<()> { let fd = fd.as_raw_fd(); @@ -89,8 +89,12 @@ async fn main() -> Result<()> { SockFlag::empty(), )?; - let syscalls_arr : Vec = vec!["getcwd".to_string(), "write".to_string(), "mkdir".to_string()]; - let mut seccomp = Seccomp {filters: set_instruction(&Arch::X86, SECCOMP_RET_KILL_PROCESS, syscalls_arr)}; + let inst_data = InstructionData{ + arc: Arch::X86, + def_action: SECCOMP_RET_KILL_PROCESS, + syscall_arr: vec!["getcwd".to_string(), "write".to_string(), "mkdir".to_string()] + }; + let mut seccomp = Seccomp {filters: Vec::from(inst_data)}; tokio::spawn(async move { tokio::signal::ctrl_c() diff --git a/experiment/seccomp/src/seccomp.rs b/experiment/seccomp/src/seccomp.rs index 7d9085e31..8541114bc 100644 --- a/experiment/seccomp/src/seccomp.rs +++ b/experiment/seccomp/src/seccomp.rs @@ -14,6 +14,7 @@ use nix::{ libc::{SECCOMP_FILTER_FLAG_NEW_LISTENER, SECCOMP_SET_MODE_FILTER}, unistd, }; + use crate::instruction::{*}; use crate::instruction::{Arch, Instruction, SECCOMP_IOC_MAGIC}; @@ -215,51 +216,37 @@ fn get_syscall_number(arc: &Arch, name: &str) -> Option { } -pub fn set_instruction(arc: &Arch, def_action: u32, systemcall_arr: Vec) -> Vec { - let _ = prctl::set_no_new_privileges(true); - let mut bpf_prog = gen_validate(arc); - - for syscall in &systemcall_arr { - bpf_prog.append(&mut vec![Instruction::stmt(BPF_LD | BPF_W | BPF_ABS, 0)]); - bpf_prog.append(&mut vec![Instruction::jump(BPF_JMP | BPF_JEQ | BPF_K, 0, 1, get_syscall_number(arc, syscall).unwrap() as c_uint)]); - - if syscall == "write" { - // Check if syscall is write and it is writing to stderr(fd=2) - // Load the file descriptor - bpf_prog.append(&mut vec![Instruction::stmt(BPF_LD | BPF_W | BPF_ABS, seccomp_data_args_offset().into())]); - bpf_prog.append(&mut vec![Instruction::jump(BPF_JMP | BPF_JEQ | BPF_K, 0, 1, libc::STDERR_FILENO as u32)]); - } +#[derive(Debug)] +pub struct InstructionData { + pub arc: Arch, + pub def_action: u32, + pub syscall_arr: Vec +} +impl From for Vec { + fn from(inst_data: InstructionData) -> Self { + let _ = prctl::set_no_new_privileges(true); + let mut bpf_prog = gen_validate(&inst_data.arc); + for syscall in &inst_data.syscall_arr { + bpf_prog.append(&mut vec![Instruction::stmt(BPF_LD | BPF_W | BPF_ABS, 0)]); + bpf_prog.append(&mut vec![Instruction::jump(BPF_JMP | BPF_JEQ | BPF_K, 0, 1, + get_syscall_number(&inst_data.arc, syscall).unwrap() as c_uint)]); + + if syscall == "write" { + // Check if syscall is write and it is writing to stderr(fd=2) + // Load the file descriptor + bpf_prog.append(&mut vec![Instruction::stmt(BPF_LD | BPF_W | BPF_ABS, seccomp_data_args_offset().into())]); + bpf_prog.append(&mut vec![Instruction::jump(BPF_JMP | BPF_JEQ | BPF_K, 0, 1, libc::STDERR_FILENO as u32)]); + } + + if syscall != "mkdir" { + bpf_prog.append(&mut vec![Instruction::stmt(BPF_RET | BPF_K, inst_data.def_action)]); + } else { + bpf_prog.append(&mut vec![Instruction::stmt(BPF_RET | BPF_K, SECCOMP_RET_USER_NOTIF)]); + } - if syscall != "mkdir" { - bpf_prog.append(&mut vec![Instruction::stmt(BPF_RET | BPF_K, def_action)]); - } else { - bpf_prog.append(&mut vec![Instruction::stmt(BPF_RET | BPF_K, SECCOMP_RET_USER_NOTIF)]); } + bpf_prog.append(&mut vec![Instruction::stmt(BPF_RET | BPF_K, SECCOMP_RET_ALLOW)]); + return bpf_prog; } - - bpf_prog.append(&mut vec![Instruction::stmt(BPF_RET | BPF_K, SECCOMP_RET_ALLOW)]); - - // bpf_prog.append(&mut vec![ - // // A: Check if syscall is getcwd - // Instruction::stmt(BPF_LD | BPF_W | BPF_ABS, 0), - // Instruction::jump(BPF_JMP | BPF_JEQ | BPF_K, 0, 1, get_syscall_number(arc, "getcwd").unwrap() as c_uint), // If false, go to B - // Instruction::stmt(BPF_RET | BPF_K, def_action), - // // B: Check if syscall is write and it is writing to stderr(fd=2) - // Instruction::stmt(BPF_LD | BPF_W | BPF_ABS, 0), - // Instruction::jump(BPF_JMP | BPF_JEQ | BPF_K, 0, 3, get_syscall_number(arc, "write").unwrap() as c_uint), // If false, go to C - // // Load the file descriptor - // Instruction::stmt(BPF_LD | BPF_W | BPF_ABS, seccomp_data_args_offset().into()), - // // Check if args is stderr - // Instruction::jump(BPF_JMP | BPF_JEQ | BPF_K, 0, 1, libc::STDERR_FILENO as u32), // If false, go to C - // Instruction::stmt(BPF_RET | BPF_K, def_action), - // // C: Check if syscall is mkdir and if so, return seccomp notify - // Instruction::stmt(BPF_LD | BPF_W | BPF_ABS, 0), - // Instruction::jump(BPF_JMP | BPF_JEQ | BPF_K, 0, 1, get_syscall_number(arc, "mkdir").unwrap() as c_uint), // If false, go to D - // Instruction::stmt(BPF_RET | BPF_K, SECCOMP_RET_USER_NOTIF), - // // D: Pass - // Instruction::stmt(BPF_RET | BPF_K, SECCOMP_RET_ALLOW), - // ]); - - return bpf_prog; -} \ No newline at end of file +} From c54e4a3d740ab705cdf9583256c2a2b2a24d1aef Mon Sep 17 00:00:00 2001 From: sat0ken <15720506+sat0ken@users.noreply.github.com> Date: Thu, 29 Aug 2024 12:32:19 +0900 Subject: [PATCH 10/14] prctl back to main.rs Signed-off-by: sat0ken <15720506+sat0ken@users.noreply.github.com> --- experiment/seccomp/src/main.rs | 2 +- experiment/seccomp/src/seccomp.rs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/experiment/seccomp/src/main.rs b/experiment/seccomp/src/main.rs index e84fd4dd3..0a664c9ce 100644 --- a/experiment/seccomp/src/main.rs +++ b/experiment/seccomp/src/main.rs @@ -88,7 +88,7 @@ async fn main() -> Result<()> { None, SockFlag::empty(), )?; - + let _ = prctl::set_no_new_privileges(true); let inst_data = InstructionData{ arc: Arch::X86, def_action: SECCOMP_RET_KILL_PROCESS, diff --git a/experiment/seccomp/src/seccomp.rs b/experiment/seccomp/src/seccomp.rs index 8541114bc..82e9f7551 100644 --- a/experiment/seccomp/src/seccomp.rs +++ b/experiment/seccomp/src/seccomp.rs @@ -224,7 +224,6 @@ pub struct InstructionData { } impl From for Vec { fn from(inst_data: InstructionData) -> Self { - let _ = prctl::set_no_new_privileges(true); let mut bpf_prog = gen_validate(&inst_data.arc); for syscall in &inst_data.syscall_arr { bpf_prog.append(&mut vec![Instruction::stmt(BPF_LD | BPF_W | BPF_ABS, 0)]); From 114e2c0a2ce78dbfc8679c7061e7b43039813ea0 Mon Sep 17 00:00:00 2001 From: sat0ken <15720506+sat0ken@users.noreply.github.com> Date: Thu, 29 Aug 2024 18:04:26 +0900 Subject: [PATCH 11/14] add rule struct Signed-off-by: sat0ken <15720506+sat0ken@users.noreply.github.com> --- experiment/seccomp/src/main.rs | 28 ++++++++++++--------- experiment/seccomp/src/seccomp.rs | 41 ++++++++++++++++++++++--------- 2 files changed, 45 insertions(+), 24 deletions(-) diff --git a/experiment/seccomp/src/main.rs b/experiment/seccomp/src/main.rs index 0a664c9ce..15ab5572e 100644 --- a/experiment/seccomp/src/main.rs +++ b/experiment/seccomp/src/main.rs @@ -9,19 +9,18 @@ use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; use std::slice; use anyhow::Result; -use nix::{ - sys::{ - signal::Signal, - socket::{ - self, ControlMessage, ControlMessageOwned, MsgFlags, SockFlag, SockType, UnixAddr, - }, - stat::Mode, - wait::{self, WaitStatus}, +use nix::{libc, sys::{ + signal::Signal, + socket::{ + self, ControlMessage, ControlMessageOwned, MsgFlags, SockFlag, SockType, UnixAddr, }, - unistd::{close, mkdir}, -}; + stat::Mode, + wait::{self, WaitStatus}, +}, unistd::{close, mkdir}}; + use syscall_numbers::x86_64; -use seccomp::seccomp::{InstructionData}; +use syscalls::syscall_args; +use seccomp::seccomp::{InstructionData, Rule}; fn send_fd(sock: OwnedFd, fd: &F) -> nix::Result<()> { let fd = fd.as_raw_fd(); @@ -88,11 +87,16 @@ async fn main() -> Result<()> { None, SockFlag::empty(), )?; + let _ = prctl::set_no_new_privileges(true); let inst_data = InstructionData{ arc: Arch::X86, def_action: SECCOMP_RET_KILL_PROCESS, - syscall_arr: vec!["getcwd".to_string(), "write".to_string(), "mkdir".to_string()] + rule_arr: vec![ + Rule::new("getcwd".parse()?, 0, syscall_args!(),false), + Rule::new("write".parse()?,1, syscall_args!(libc::STDERR_FILENO as usize), false), + Rule::new("mkdir".parse()?,0, syscall_args!(), true) + ] }; let mut seccomp = Seccomp {filters: Vec::from(inst_data)}; diff --git a/experiment/seccomp/src/seccomp.rs b/experiment/seccomp/src/seccomp.rs index 82e9f7551..5d502e85e 100644 --- a/experiment/seccomp/src/seccomp.rs +++ b/experiment/seccomp/src/seccomp.rs @@ -14,7 +14,7 @@ use nix::{ libc::{SECCOMP_FILTER_FLAG_NEW_LISTENER, SECCOMP_SET_MODE_FILTER}, unistd, }; - +use syscalls::{SyscallArgs}; use crate::instruction::{*}; use crate::instruction::{Arch, Instruction, SECCOMP_IOC_MAGIC}; @@ -220,32 +220,49 @@ fn get_syscall_number(arc: &Arch, name: &str) -> Option { pub struct InstructionData { pub arc: Arch, pub def_action: u32, - pub syscall_arr: Vec + pub rule_arr: Vec } impl From for Vec { fn from(inst_data: InstructionData) -> Self { let mut bpf_prog = gen_validate(&inst_data.arc); - for syscall in &inst_data.syscall_arr { + for rule in &inst_data.rule_arr { bpf_prog.append(&mut vec![Instruction::stmt(BPF_LD | BPF_W | BPF_ABS, 0)]); bpf_prog.append(&mut vec![Instruction::jump(BPF_JMP | BPF_JEQ | BPF_K, 0, 1, - get_syscall_number(&inst_data.arc, syscall).unwrap() as c_uint)]); + get_syscall_number(&inst_data.arc, &rule.syscall).unwrap() as c_uint)]); - if syscall == "write" { - // Check if syscall is write and it is writing to stderr(fd=2) - // Load the file descriptor + // TODO: Checks for only one argument, but supports two or more arguments + if rule.arg_cnt != 0 { bpf_prog.append(&mut vec![Instruction::stmt(BPF_LD | BPF_W | BPF_ABS, seccomp_data_args_offset().into())]); - bpf_prog.append(&mut vec![Instruction::jump(BPF_JMP | BPF_JEQ | BPF_K, 0, 1, libc::STDERR_FILENO as u32)]); + bpf_prog.append(&mut vec![Instruction::jump(BPF_JMP | BPF_JEQ | BPF_K, 0, 1, rule.args.arg0 as c_uint)]); } - if syscall != "mkdir" { - bpf_prog.append(&mut vec![Instruction::stmt(BPF_RET | BPF_K, inst_data.def_action)]); - } else { + if rule.is_notify { bpf_prog.append(&mut vec![Instruction::stmt(BPF_RET | BPF_K, SECCOMP_RET_USER_NOTIF)]); + } else { + bpf_prog.append(&mut vec![Instruction::stmt(BPF_RET | BPF_K, inst_data.def_action)]); } - } bpf_prog.append(&mut vec![Instruction::stmt(BPF_RET | BPF_K, SECCOMP_RET_ALLOW)]); return bpf_prog; } } + +#[derive(Debug)] +pub struct Rule { + pub syscall: String, + pub arg_cnt: u8, + pub args: SyscallArgs, + pub is_notify: bool +} + +impl Rule { + pub fn new(syscall: String, arg_cnt: u8, args: SyscallArgs, is_notify: bool) -> Self { + Self { + syscall, + arg_cnt, + args, + is_notify, + } + } +} \ No newline at end of file From 86efdc8f937255dedc6cb2336a4b8eba9f058c22 Mon Sep 17 00:00:00 2001 From: sat0ken <15720506+sat0ken@users.noreply.github.com> Date: Wed, 18 Sep 2024 17:07:18 +0900 Subject: [PATCH 12/14] add func to rule struct Signed-off-by: sat0ken <15720506+sat0ken@users.noreply.github.com> --- experiment/seccomp/src/seccomp.rs | 39 ++++++++++++++++++------------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/experiment/seccomp/src/seccomp.rs b/experiment/seccomp/src/seccomp.rs index 5d502e85e..5db32f454 100644 --- a/experiment/seccomp/src/seccomp.rs +++ b/experiment/seccomp/src/seccomp.rs @@ -15,6 +15,7 @@ use nix::{ unistd, }; use syscalls::{SyscallArgs}; +use syscalls::Sysno::bpf; use crate::instruction::{*}; use crate::instruction::{Arch, Instruction, SECCOMP_IOC_MAGIC}; @@ -222,25 +223,13 @@ pub struct InstructionData { pub def_action: u32, pub rule_arr: Vec } + impl From for Vec { fn from(inst_data: InstructionData) -> Self { let mut bpf_prog = gen_validate(&inst_data.arc); + for rule in &inst_data.rule_arr { - bpf_prog.append(&mut vec![Instruction::stmt(BPF_LD | BPF_W | BPF_ABS, 0)]); - bpf_prog.append(&mut vec![Instruction::jump(BPF_JMP | BPF_JEQ | BPF_K, 0, 1, - get_syscall_number(&inst_data.arc, &rule.syscall).unwrap() as c_uint)]); - - // TODO: Checks for only one argument, but supports two or more arguments - if rule.arg_cnt != 0 { - bpf_prog.append(&mut vec![Instruction::stmt(BPF_LD | BPF_W | BPF_ABS, seccomp_data_args_offset().into())]); - bpf_prog.append(&mut vec![Instruction::jump(BPF_JMP | BPF_JEQ | BPF_K, 0, 1, rule.args.arg0 as c_uint)]); - } - - if rule.is_notify { - bpf_prog.append(&mut vec![Instruction::stmt(BPF_RET | BPF_K, SECCOMP_RET_USER_NOTIF)]); - } else { - bpf_prog.append(&mut vec![Instruction::stmt(BPF_RET | BPF_K, inst_data.def_action)]); - } + bpf_prog.append(&mut Rule::to_instruction(&inst_data.arc, inst_data.def_action, rule)); } bpf_prog.append(&mut vec![Instruction::stmt(BPF_RET | BPF_K, SECCOMP_RET_ALLOW)]); @@ -265,4 +254,22 @@ impl Rule { is_notify, } } -} \ No newline at end of file + + pub fn to_instruction(arch: &Arch, action: u32, rule: &Rule) -> Vec { + let mut bpf_prog = gen_validate(&arch); + bpf_prog.append(&mut vec![Instruction::stmt(BPF_LD | BPF_W | BPF_ABS, 0)]); + bpf_prog.append(&mut vec![Instruction::jump(BPF_JMP | BPF_JEQ | BPF_K, 0, 1, + get_syscall_number(arch, &rule.syscall).unwrap() as c_uint)]); + if rule.arg_cnt != 0 { + bpf_prog.append(&mut vec![Instruction::stmt(BPF_LD | BPF_W | BPF_ABS, seccomp_data_args_offset().into())]); + bpf_prog.append(&mut vec![Instruction::jump(BPF_JMP | BPF_JEQ | BPF_K, 0, 1, rule.args.arg0 as c_uint)]); + } + + if rule.is_notify { + bpf_prog.append(&mut vec![Instruction::stmt(BPF_RET | BPF_K, SECCOMP_RET_USER_NOTIF)]); + } else { + bpf_prog.append(&mut vec![Instruction::stmt(BPF_RET | BPF_K, action)]); + } + return bpf_prog + } +} From 9971644311b70020b5a3ea8d929d936805b6ef0d Mon Sep 17 00:00:00 2001 From: sat0ken <15720506+sat0ken@users.noreply.github.com> Date: Wed, 2 Oct 2024 23:54:13 +0900 Subject: [PATCH 13/14] change if to match Signed-off-by: sat0ken <15720506+sat0ken@users.noreply.github.com> --- experiment/seccomp/src/seccomp.rs | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/experiment/seccomp/src/seccomp.rs b/experiment/seccomp/src/seccomp.rs index 5db32f454..603a0d285 100644 --- a/experiment/seccomp/src/seccomp.rs +++ b/experiment/seccomp/src/seccomp.rs @@ -203,18 +203,20 @@ struct Filters { } fn get_syscall_number(arc: &Arch, name: &str) -> Option { - if arc == &Arch::X86 { - match syscalls::x86_64::Sysno::from_str(name) { - Ok(syscall) => Some(syscall as u64), - Err(_) => None, - } - } else { - match syscalls::aarch64::Sysno::from_str(name) { - Ok(syscall) => Some(syscall as u64), - Err(_) => None, + match arc { + &Arch::X86 => { + match syscalls::x86_64::Sysno::from_str(name) { + Ok(syscall) => Some(syscall as u64), + Err(_) => None, + } + }, + &Arch::AArch64 => { + match syscalls::aarch64::Sysno::from_str(name) { + Ok(syscall) => Some(syscall as u64), + Err(_) => None, + } } } - } #[derive(Debug)] From 43bcdfae7ddc307b2ef868838cfc44db3778f0c9 Mon Sep 17 00:00:00 2001 From: sat0ken <15720506+sat0ken@users.noreply.github.com> Date: Thu, 3 Oct 2024 00:00:15 +0900 Subject: [PATCH 14/14] fix code by cargo clippy Signed-off-by: sat0ken <15720506+sat0ken@users.noreply.github.com> --- experiment/seccomp/src/main.rs | 4 ++-- experiment/seccomp/src/seccomp.rs | 11 +++++------ 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/experiment/seccomp/src/main.rs b/experiment/seccomp/src/main.rs index 15ab5572e..e884c066d 100644 --- a/experiment/seccomp/src/main.rs +++ b/experiment/seccomp/src/main.rs @@ -1,5 +1,5 @@ use seccomp::{ - instruction::{self, *}, + instruction::{*}, seccomp::{NotifyFd, Seccomp}, }; @@ -98,7 +98,7 @@ async fn main() -> Result<()> { Rule::new("mkdir".parse()?,0, syscall_args!(), true) ] }; - let mut seccomp = Seccomp {filters: Vec::from(inst_data)}; + let seccomp = Seccomp {filters: Vec::from(inst_data)}; tokio::spawn(async move { tokio::signal::ctrl_c() diff --git a/experiment/seccomp/src/seccomp.rs b/experiment/seccomp/src/seccomp.rs index 603a0d285..f5a83cf45 100644 --- a/experiment/seccomp/src/seccomp.rs +++ b/experiment/seccomp/src/seccomp.rs @@ -15,7 +15,6 @@ use nix::{ unistd, }; use syscalls::{SyscallArgs}; -use syscalls::Sysno::bpf; use crate::instruction::{*}; use crate::instruction::{Arch, Instruction, SECCOMP_IOC_MAGIC}; @@ -204,13 +203,13 @@ struct Filters { fn get_syscall_number(arc: &Arch, name: &str) -> Option { match arc { - &Arch::X86 => { + Arch::X86 => { match syscalls::x86_64::Sysno::from_str(name) { Ok(syscall) => Some(syscall as u64), Err(_) => None, } }, - &Arch::AArch64 => { + Arch::AArch64 => { match syscalls::aarch64::Sysno::from_str(name) { Ok(syscall) => Some(syscall as u64), Err(_) => None, @@ -235,7 +234,7 @@ impl From for Vec { } bpf_prog.append(&mut vec![Instruction::stmt(BPF_RET | BPF_K, SECCOMP_RET_ALLOW)]); - return bpf_prog; + bpf_prog } } @@ -258,7 +257,7 @@ impl Rule { } pub fn to_instruction(arch: &Arch, action: u32, rule: &Rule) -> Vec { - let mut bpf_prog = gen_validate(&arch); + let mut bpf_prog = gen_validate(arch); bpf_prog.append(&mut vec![Instruction::stmt(BPF_LD | BPF_W | BPF_ABS, 0)]); bpf_prog.append(&mut vec![Instruction::jump(BPF_JMP | BPF_JEQ | BPF_K, 0, 1, get_syscall_number(arch, &rule.syscall).unwrap() as c_uint)]); @@ -272,6 +271,6 @@ impl Rule { } else { bpf_prog.append(&mut vec![Instruction::stmt(BPF_RET | BPF_K, action)]); } - return bpf_prog + bpf_prog } }