Skip to content

Commit

Permalink
Working installation process
Browse files Browse the repository at this point in the history
  • Loading branch information
LeChatP committed Sep 4, 2024
1 parent 70e7c24 commit db3b1de
Show file tree
Hide file tree
Showing 8 changed files with 167 additions and 87 deletions.
68 changes: 44 additions & 24 deletions rar-common/src/util.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,23 @@
use std::{error::Error, fs::File, os::fd::AsRawFd, path::PathBuf, ffi::CString, path::Path};
use std::{
error::Error, ffi::CString, fs::File, io, os::{
fd::AsRawFd,
unix::fs::{MetadataExt, PermissionsExt},
}, path::{Path, PathBuf}
};

use capctl::{prctl, CapState};
use capctl::{Cap, CapSet, ParseCapError};
use libc::{FS_IOC_GETFLAGS, FS_IOC_SETFLAGS};
use serde::Serialize;
use strum::EnumIs;
use tracing::{debug, warn, Level};
use capctl::{prctl, CapState};
use serde::Serialize;
use tracing_subscriber::util::SubscriberInitExt;


pub const RST: &str = "\x1B[0m";
pub const BOLD: &str = "\x1B[1m";
pub const UNDERLINE: &str = "\x1B[4m";
pub const RED: &str = "\x1B[31m";


#[macro_export]
macro_rules! upweak {
($e:expr) => {
Expand Down Expand Up @@ -51,42 +54,59 @@ pub enum ImmutableLock {
Unset,
}

fn immutable_required_privileges(file: &File, effective: bool) -> Result<(), capctl::Error> {
//get file owner
let metadata = file.metadata().unwrap();
let uid = metadata.uid();
let gid = metadata.gid();
immutable_effective(effective)?;
// check if the current user is the owner
if nix::unistd::Uid::effective() != nix::unistd::Uid::from_raw(uid)
&& nix::unistd::Gid::effective() != nix::unistd::Gid::from_raw(gid)
{
read_or_dac_override(effective)?;
fowner_effective(effective)?;
}
Ok(())
}

fn read_or_dac_override(effective: bool) -> Result<(), capctl::Error> {
Ok(match effective {
false => {
read_effective(false)
.and(dac_override_effective(false))?;
}
true => {
read_effective(true)
.or(dac_override_effective(true))?;
}
})
}

/// Set or unset the immutable flag on a file
/// # Arguments
/// * `file` - The file to set the immutable flag on
/// * `lock` - Whether to set or unset the immutable flag
pub fn toggle_lock_config(file: &PathBuf, lock: ImmutableLock) -> Result<(), String> {
let file = match open_with_privileges(file) {
Err(e) => return Err(e.to_string()),
Ok(f) => f,
};
pub fn toggle_lock_config<P:AsRef<Path>>(file: &P, lock: ImmutableLock) -> io::Result<()> {
let file = open_with_privileges(file)?;
let mut val = 0;
let fd = file.as_raw_fd();
if unsafe { nix::libc::ioctl(fd, FS_IOC_GETFLAGS, &mut val) } < 0 {
return Err(std::io::Error::last_os_error().to_string());
return Err(std::io::Error::last_os_error());
}
if lock.is_unset() {
val &= !(FS_IMMUTABLE_FL);
} else {
val |= FS_IMMUTABLE_FL;
}
debug!("Setting immutable privilege");
immutable_effective(true).map_err(|e| e.to_string())?;
debug!("Setting dac override privilege");
read_effective(true)
.or(dac_override_effective(true))
.map_err(|e| e.to_string())?;
fowner_effective(true).map_err(|e| e.to_string())?;
debug!("Setting immutable flag");

immutable_required_privileges(&file, true)?;
if unsafe { nix::libc::ioctl(fd, FS_IOC_SETFLAGS, &mut val) } < 0 {
return Err(std::io::Error::last_os_error().to_string());
return Err(std::io::Error::last_os_error());
}
debug!("Resetting immutable privilege");
immutable_effective(false).map_err(|e| e.to_string())?;
read_effective(false)
.and(dac_override_effective(false))
.map_err(|e| e.to_string())?;
fowner_effective(false).map_err(|e| e.to_string())?;
immutable_required_privileges(&file, false)?;
Ok(())
}

Expand Down
2 changes: 1 addition & 1 deletion xtask/src/ebpf/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ pub mod run;


pub fn build_all(opts: &BuildOptions) -> Result<(), anyhow::Error> {
build_ebpf(&opts.ebpf, &opts.profile).context("Error while building eBPF program")?;
build_ebpf(&opts.ebpf_toolchain, &opts.profile).context("Error while building eBPF program")?;
build(opts).context("Error while building userspace application")
}

Expand Down
1 change: 1 addition & 0 deletions xtask/src/install/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ pub fn build(options: &BuildOptions) -> Result<(), anyhow::Error> {
if options.profile.is_release() {
args.push("--release");
}
println!("Building sr and chsr with {:?}", &args);
Command::new("cargo")
.args(args)
.status()
Expand Down
95 changes: 57 additions & 38 deletions xtask/src/install/configure.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use std::path::Path;

use anyhow::Context;
use nix::unistd::{getresuid, getuid};
use rar_common::util::toggle_lock_config;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use strum::EnumIs;
Expand Down Expand Up @@ -42,7 +43,6 @@ pub const CONFIG_FILE: &str = "/etc/security/rootasrole.json";
const DEFAULT_PATH: &str = "resources/rootasrole.json";
pub const PAM_CONFIG_PATH: &str = "/etc/pam.d/sr";


fn is_running_in_container() -> bool {
// Check for environment files that might indicate a container
let container_env_files = ["/run/.containerenv", "/.dockerenv", "/run/container_type"];
Expand All @@ -64,7 +64,11 @@ fn is_running_in_container() -> bool {
let reader = io::BufReader::new(file);
for line in reader.lines() {
if let Ok(line) = line {
if line.contains("docker") || line.contains("kubepods") || line.contains("lxc") || line.contains("containerd") {
if line.contains("docker")
|| line.contains("kubepods")
|| line.contains("lxc")
|| line.contains("containerd")
{
return true;
}
}
Expand All @@ -81,15 +85,26 @@ fn check_filesystem() -> io::Result<()> {
if let Some(fs_type) = get_filesystem_type(CONFIG_FILE)? {
match fs_type.as_str() {
"ext2" | "ext3" | "ext4" | "xfs" | "btrfs" | "ocfs2" | "jfs" | "reiserfs" => {
println!(
"{} is compatble for immutability, setting immutable flag",
fs_type
);
set_immutable(&mut config, true);
toggle_lock_config(
&CONFIG_FILE.to_string(),
rar_common::util::ImmutableLock::Set,
)?;
return Ok(());
}
_ => {
set_immutable(&mut config, false);
}
_ => println!(
"{} is not compatible for immutability, removing immutable flag",
fs_type
),
}
} else {
set_immutable(&mut config, false);
println!("Failed to get filesystem type, removing immutable flag");
}
set_immutable(&mut config, false);
Ok(())
}

Expand Down Expand Up @@ -137,11 +152,29 @@ fn deploy_config_file() -> Result<ConfigState, anyhow::Error> {
let mut status = ConfigState::Unchanged;
// Check if the target file exists
if !Path::new(CONFIG_FILE).exists() {
println!("Config file does not exist, deploying default file");
// If the target file does not exist, copy the default file
deploy_config(CONFIG_FILE)?;
} else {
status = config_state()?;
}

match status {
ConfigState::Unchanged => {
println!("Config file newly created or has not been modified checking if filesystem allows immutability");
let res = check_filesystem().context("Failed to configure the filesystem parameter");
if res.is_err() {
// If the filesystem check fails, ignore the error if running in a container as it may not have immutable access
if is_running_in_container() {
return Ok(status);
}
res?;
}
}
ConfigState::Modified => {
println!("Config file has been modified by the user, skipping immutable configuration");
}
}
Ok(status)
}

Expand All @@ -157,22 +190,19 @@ pub fn config_state() -> Result<ConfigState, anyhow::Error> {
Ok(status)
}

fn deploy_config<P:AsRef<Path>>(config_path: P) -> Result<(), anyhow::Error> {
fn deploy_config<P: AsRef<Path>>(config_path: P) -> Result<(), anyhow::Error> {
let config = File::open(DEFAULT_PATH)?;
let mut buf = BufReader::new(config);
let mut content = String::new();
// Read the default config file
buf.read_to_string(&mut content)?;
// Get the real user

let user = retrieve_real_user()?;
// Replace the placeholder with the current user, which will act as the main administrator
match user {
Some(user) => {
content = content.replace(
"\"ROOTADMINISTRATOR\"",
&format!("\"{}\"", user.name),
);
content = content.replace("\"ROOTADMINISTRATOR\"", &format!("\"{}\"", user.name));
}
None => {
eprintln!("Failed to get the current user from passwd file, using UID instead");
Expand All @@ -189,19 +219,17 @@ fn deploy_config<P:AsRef<Path>>(config_path: P) -> Result<(), anyhow::Error> {
fn retrieve_real_user() -> Result<Option<nix::unistd::User>, anyhow::Error> {
// if sudo_user is not set, get the real user
if let Ok(sudo_user) = env::var("SUDO_USER") {
let user = nix::unistd::User::from_name(&sudo_user)
.context("Failed to get the sudo user")?;
let user =
nix::unistd::User::from_name(&sudo_user).context("Failed to get the sudo user")?;
return Ok(user);
} else {
let ruid = getresuid()?.real;
let user = nix::unistd::User::from_uid(ruid)
.context("Failed to get the real user")?;
let user = nix::unistd::User::from_uid(ruid).context("Failed to get the real user")?;
Ok(user)
}

}

pub fn default_pam_path(os : &OsTarget) -> &'static str {
pub fn default_pam_path(os: &OsTarget) -> &'static str {
match os {
OsTarget::Debian | OsTarget::Ubuntu => "resources/debian/deb_sr_pam.conf",
OsTarget::RedHat | OsTarget::CentOS | OsTarget::Fedora => "resources/redhat/rh_sr_pam.conf",
Expand All @@ -211,34 +239,25 @@ pub fn default_pam_path(os : &OsTarget) -> &'static str {

fn deploy_pam_config(os: &OsTarget) -> io::Result<u64> {
if fs::metadata(PAM_CONFIG_PATH).is_err() {
println!("Deploying PAM configuration file");
return fs::copy(default_pam_path(os), PAM_CONFIG_PATH);
}
Ok(0)
}

pub fn configure(os: &Option<OsTarget>) -> Result<(), anyhow::Error> {
pub fn configure(os: Option<OsTarget>) -> Result<(), anyhow::Error> {
let os = if let Some(os) = os {
os
} else {
&OsTarget::detect().context("Failed to detect the OS")?
OsTarget::detect()
.and_then(|t| {
println!("Detected OS is : {}", t);
Ok(t)
})
.context("Failed to detect the OS")?
};
deploy_pam_config(os).context("Failed to deploy the PAM configuration file")?;

deploy_config_file()
.context("Failed to configure the config file")
.and_then(|state| match state {
ConfigState::Unchanged => {
let res = check_filesystem().context("Failed to configure the filesystem parameter");
if res.is_err() {
// If the filesystem check fails, ignore the error if running in a container as it may not have immutable access
if is_running_in_container() {
return Ok(());
}
}
res
}
ConfigState::Modified => Ok(()),
})
deploy_pam_config(&os).context("Failed to deploy the PAM configuration file")?;


deploy_config_file().context("Failed to configure the config file")?;
Ok(())
}
13 changes: 9 additions & 4 deletions xtask/src/install/install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,15 @@ use super::util::{cap_clear, cap_effective};
use super::{InstallOptions, CAPABLE_DEST, CHSR_DEST, SR_DEST};

fn copy_files(profile: &Profile, ebpf: Option<EbpfArchitecture>) -> Result<(), anyhow::Error> {
fs::copy(format!("target/{}/sr", profile), SR_DEST)?;
fs::copy(format!("target/{}/chsr", profile), CHSR_DEST)?;
let binding = std::env::current_dir()?;
let cwd = binding.to_str().context("unable to get current dir as string")?;
println!("Current working directory: {}", cwd);
println!("Copying files {}/target/{}/sr to {} and {}", cwd, profile, SR_DEST, CHSR_DEST);
fs::rename(format!("{}/target/{}/sr", cwd, profile), SR_DEST)?;
fs::rename(format!("{}/target/{}/chsr", cwd, profile), CHSR_DEST)?;
if let Some(ebpf) = ebpf {
fs::copy(format!("target/{}/capable", ebpf), CAPABLE_DEST)?;
println!("Copying file {}/target/{}/capable to {}", cwd, ebpf, CAPABLE_DEST);
fs::rename(format!("{}/target/{}/capable", cwd, ebpf), CAPABLE_DEST)?;
}

chmod()?;
Expand Down Expand Up @@ -64,7 +69,7 @@ pub fn install(options: &InstallOptions) -> Result<(), anyhow::Error> {
// cp target/{release}/sr,chsr,capable /usr/bin
copy_files(
&options.build.profile,
options.ebpf_build,
if options.build_ebpf { Some(options.ebpf_build) } else { None },
)
.context("Failed to copy sr and chsr files")?;

Expand Down
Loading

0 comments on commit db3b1de

Please sign in to comment.