From fb8ec078c92f916b555d74640538660fb884436d Mon Sep 17 00:00:00 2001 From: LeChatP Date: Mon, 9 Sep 2024 16:21:16 +0200 Subject: [PATCH] WIP --- .github/workflows/build.yml | 2 +- .github/workflows/pkg.yml | 12 +-- README.md | 2 +- build.rs | 16 +-- rar-common/src/api.rs | 2 +- rar-common/src/database/finder.rs | 20 ++-- rar-common/src/database/mod.rs | 6 +- rar-common/src/database/options.rs | 23 ++--- rar-common/src/database/versionning.rs | 2 +- rar-common/src/lib.rs | 14 ++- rar-common/src/plugin/hashchecker.rs | 11 ++- rar-common/src/plugin/ssd.rs | 2 +- rar-common/src/util.rs | 18 ++-- resources/rootasrole.json | 4 + src/chsr/cli/data.rs | 2 +- src/chsr/cli/mod.rs | 17 +++- src/chsr/cli/pair.rs | 3 +- src/chsr/cli/process.rs | 2 +- src/chsr/cli/process/json.rs | 13 ++- src/chsr/main.rs | 8 +- src/sr/main.rs | 17 ++-- src/sr/pam/mod.rs | 4 +- src/sr/timeout.rs | 5 +- xtask/Cargo.toml | 2 + xtask/src/configure.rs | 73 ++++++++------ xtask/src/deploy/arch.rs | 6 +- xtask/src/deploy/debian.rs | 29 ++++-- xtask/src/deploy/mod.rs | 18 ++-- xtask/src/deploy/redhat.rs | 25 +++-- xtask/src/install/build.rs | 4 +- xtask/src/install/dependencies.rs | 126 ++++++++++++----------- xtask/src/install/install.rs | 132 +++++++++++++++---------- xtask/src/install/mod.rs | 60 ++++++----- xtask/src/install/uninstall.rs | 10 +- xtask/src/install/util.rs | 4 +- xtask/src/main.rs | 26 +++-- xtask/src/postinst.rs | 19 ++-- xtask/src/prerm.rs | 9 +- xtask/src/util.rs | 55 ++++++++--- 39 files changed, 464 insertions(+), 339 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4be5f3f..e4ea797 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -25,7 +25,7 @@ jobs: session requisite pam_permit.so session required pam_permit.so" | tee /etc/pam.d/sr' - name: Install RootAsRole - run: sudo cargo xtask install --debug + run: cargo xtask install -d -i -p sudo - name: Add read access on config on rootasrole... Because Github Actions... run: sudo chmod a+r /etc/security/rootasrole.json - name: print config diff --git a/.github/workflows/pkg.yml b/.github/workflows/pkg.yml index 29c0f64..dce81e6 100644 --- a/.github/workflows/pkg.yml +++ b/.github/workflows/pkg.yml @@ -24,15 +24,9 @@ jobs: override: true - name: Install Dependencies - run: ./dependencies.sh -yd - - - name: Configure - run: sudo ./configure.sh -yd - - - name: Install cargo deb - run: cargo install cargo-deb + run: sudo cargo xtask dependencies -i -d - name: Build - run: cargo deb + run: cargo xtask deploy debian redhat - - name: Upload to GitHub \ No newline at end of file + \ No newline at end of file diff --git a/README.md b/README.md index e833cbf..2bb49f3 100644 --- a/README.md +++ b/README.md @@ -50,7 +50,7 @@ 1. `git clone ` 1. `cd RootAsRole` - 1. `cargo xtask install -i` + 1. `sudo cargo xtask install -i -b` ### Additional Installation Options diff --git a/build.rs b/build.rs index a078950..093c291 100644 --- a/build.rs +++ b/build.rs @@ -166,10 +166,14 @@ fn main() { if let Err(err) = set_cargo_version(package_version, "capable/capable/Cargo.toml") { eprintln!("cargo:warning={}", err); } - if let Err(err) = set_cargo_version(package_version, "capable/capable-ebpf/Cargo.toml") { + if let Err(err) = + set_cargo_version(package_version, "capable/capable-ebpf/Cargo.toml") + { eprintln!("cargo:warning={}", err); } - if let Err(err) = set_cargo_version(package_version, "capable/capable-common/Cargo.toml") { + if let Err(err) = + set_cargo_version(package_version, "capable/capable-common/Cargo.toml") + { eprintln!("cargo:warning={}", err); } } @@ -179,9 +183,9 @@ fn main() { if let Err(err) = set_readme_version(package_version, "README.md") { eprintln!("cargo:warning={}", err); } - if let Err(err) = set_pkgbuild_version(package_version, "PKGBUILD") { - eprintln!("cargo:warning={}", err); - } + //if let Err(err) = set_pkgbuild_version(package_version, "PKGBUILD") { + //eprintln!("cargo:warning={}", err); + //} } Err(err) => { eprintln!("cargo:warning={}", err); @@ -194,6 +198,4 @@ fn main() { // } f.flush().unwrap(); - - } diff --git a/rar-common/src/api.rs b/rar-common/src/api.rs index 75daacc..e000f80 100644 --- a/rar-common/src/api.rs +++ b/rar-common/src/api.rs @@ -7,7 +7,7 @@ use strum::EnumIs; use tracing::debug; #[cfg(feature = "finder")] -use crate::database::finder::{Cred, ExecSettings, TaskMatch, UserMin, FilterMatcher}; +use crate::database::finder::{Cred, ExecSettings, FilterMatcher, TaskMatch, UserMin}; use crate::database::structs::{SActor, SConfig, SRole, STask}; use once_cell::sync::Lazy; diff --git a/rar-common/src/database/finder.rs b/rar-common/src/database/finder.rs index 6f53dd7..d5fb9d1 100644 --- a/rar-common/src/database/finder.rs +++ b/rar-common/src/database/finder.rs @@ -18,15 +18,17 @@ use pcre2::bytes::RegexBuilder; use strum::EnumIs; use tracing::{debug, warn}; -use crate::{api::{PluginManager, PluginResultAction}, as_borrow}; use crate::database::{ - options::{Opt, OptStack}, - structs::{ - SActor, SActorType, SCommand, SCommands, SConfig, SGroups, SRole, STask, - SetBehavior, - }, - }; -use crate::util::{final_path, capabilities_are_exploitable, parse_conf_command}; + options::{Opt, OptStack}, + structs::{ + SActor, SActorType, SCommand, SCommands, SConfig, SGroups, SRole, STask, SetBehavior, + }, +}; +use crate::util::{capabilities_are_exploitable, final_path, parse_conf_command}; +use crate::{ + api::{PluginManager, PluginResultAction}, + as_borrow, +}; use bitflags::bitflags; #[derive(Debug, PartialEq, Eq, Clone, Copy)] @@ -307,8 +309,6 @@ pub trait CredMatcher { fn user_matches(&self, user: &Cred) -> UserMin; } - - fn find_from_envpath(needle: &PathBuf) -> Option { let env_path = std::env::var_os("PATH").unwrap(); for path in std::env::split_paths(&env_path) { diff --git a/rar-common/src/database/mod.rs b/rar-common/src/database/mod.rs index 484b7fa..793ab58 100644 --- a/rar-common/src/database/mod.rs +++ b/rar-common/src/database/mod.rs @@ -11,13 +11,13 @@ use tracing::debug; use self::{migration::Migration, options::EnvKey, structs::SConfig, versionning::Versioning}; -use crate::SettingsFile; use crate::util::warn_if_mutable; +use crate::SettingsFile; +use crate::{open_with_privileges, write_json_config}; use crate::{ + util::{immutable_effective, parse_capset_iter}, RemoteStorageSettings, ROOTASROLE, - util::{parse_capset_iter, immutable_effective}, }; -use crate::{open_with_privileges, write_json_config}; #[cfg(feature = "finder")] pub mod finder; diff --git a/rar-common/src/database/options.rs b/rar-common/src/database/options.rs index 90b67bd..9060591 100644 --- a/rar-common/src/database/options.rs +++ b/rar-common/src/database/options.rs @@ -357,20 +357,21 @@ fn is_regex(_s: &str) -> bool { impl EnvKey { pub fn new(s: String) -> Result { //debug!("Creating env key: {}", s); - if is_valid_env_name(&s) - { + if is_valid_env_name(&s) { Ok(EnvKey { env_type: EnvKeyType::Normal, value: s, }) - } else if is_regex(&s) - { + } else if is_regex(&s) { Ok(EnvKey { env_type: EnvKeyType::Wildcarded, value: s, }) } else { - Err(format!("env key {}, must be a valid env, or a valid regex", s)) + Err(format!( + "env key {}, must be a valid env, or a valid regex", + s + )) } } } @@ -446,11 +447,7 @@ impl EnvSet for HashMap { fn env_matches(&self, wildcarded: &EnvKey) -> bool { match wildcarded.env_type { EnvKeyType::Normal => self.contains_key(&wildcarded.value), - EnvKeyType::Wildcarded => { - self.keys().any(|s| { - check_wildcarded(wildcarded, s) - }) - } + EnvKeyType::Wildcarded => self.keys().any(|s| check_wildcarded(wildcarded, s)), } } } @@ -459,11 +456,7 @@ impl EnvSet for LinkedHashSet { fn env_matches(&self, wildcarded: &EnvKey) -> bool { match wildcarded.env_type { EnvKeyType::Normal => self.contains(wildcarded), - EnvKeyType::Wildcarded => { - self.iter().any(|s| { - check_wildcarded(wildcarded, &s.value) - }) - } + EnvKeyType::Wildcarded => self.iter().any(|s| check_wildcarded(wildcarded, &s.value)), } } } diff --git a/rar-common/src/database/versionning.rs b/rar-common/src/database/versionning.rs index d13182e..f097462 100644 --- a/rar-common/src/database/versionning.rs +++ b/rar-common/src/database/versionning.rs @@ -3,8 +3,8 @@ use serde::{Deserialize, Serialize}; use std::fmt::Debug; use super::migration::Migration; -use crate::SettingsFile; use crate::version; +use crate::SettingsFile; use super::structs::*; diff --git a/rar-common/src/lib.rs b/rar-common/src/lib.rs index 10c03bc..c878e06 100644 --- a/rar-common/src/lib.rs +++ b/rar-common/src/lib.rs @@ -57,17 +57,15 @@ use std::{cell::RefCell, error::Error, ffi::OsStr, path::PathBuf, rc::Rc}; use serde::{Deserialize, Serialize}; use tracing::debug; -pub mod util; -pub mod database; pub mod api; -pub mod version; +pub mod database; pub mod plugin; +pub mod util; +pub mod version; - -use util::{ - dac_override_effective, open_with_privileges, read_effective, - toggle_lock_config, ImmutableLock, - write_json_config, +use util::{ + dac_override_effective, open_with_privileges, read_effective, toggle_lock_config, + write_json_config, ImmutableLock, }; use database::{ diff --git a/rar-common/src/plugin/hashchecker.rs b/rar-common/src/plugin/hashchecker.rs index 9d58be2..7dc13a3 100644 --- a/rar-common/src/plugin/hashchecker.rs +++ b/rar-common/src/plugin/hashchecker.rs @@ -1,13 +1,14 @@ use std::{fs::File, io::Read, os::fd::AsRawFd}; -use nix::unistd::{access, AccessFlags}; -use serde::{Deserialize, Serialize}; -use tracing::{debug, warn}; use crate::{ api::PluginManager, database::structs::SCommand, - open_with_privileges, util::{final_path, parse_conf_command}, + open_with_privileges, + util::{final_path, parse_conf_command}, }; +use nix::unistd::{access, AccessFlags}; +use serde::{Deserialize, Serialize}; +use tracing::{debug, warn}; use libc::FS_IOC_GETFLAGS; use sha2::Digest; @@ -124,7 +125,7 @@ mod tests { use nix::unistd::{Pid, User}; use super::*; - + use crate::finder::{Cred, TaskMatcher}; use crate::{ database::structs::{IdTask, SActor, SCommand, SCommands, SConfig, SRole, STask}, diff --git a/rar-common/src/plugin/ssd.rs b/rar-common/src/plugin/ssd.rs index 8ffe10e..5321b58 100644 --- a/rar-common/src/plugin/ssd.rs +++ b/rar-common/src/plugin/ssd.rs @@ -5,8 +5,8 @@ use nix::unistd::{getgrouplist, Group, User}; use serde_json::Error; use crate::{ - as_borrow, api::{PluginManager, PluginResult}, + as_borrow, database::{ finder::Cred, structs::{SActor, SConfig, SGroups, SRole}, diff --git a/rar-common/src/util.rs b/rar-common/src/util.rs index db0ff3b..f7e98b8 100644 --- a/rar-common/src/util.rs +++ b/rar-common/src/util.rs @@ -1,8 +1,14 @@ use std::{ - env, error::Error, ffi::CString, fs::File, io, os::{ + env, + error::Error, + ffi::CString, + fs::File, + io, + os::{ fd::AsRawFd, unix::fs::{MetadataExt, PermissionsExt}, - }, path::{Path, PathBuf} + }, + path::{Path, PathBuf}, }; use capctl::{prctl, CapState}; @@ -75,12 +81,10 @@ fn immutable_required_privileges(file: &File, effective: bool) -> Result<(), cap fn read_or_dac_override(effective: bool) -> Result<(), capctl::Error> { Ok(match effective { false => { - read_effective(false) - .and(dac_override_effective(false))?; + read_effective(false).and(dac_override_effective(false))?; } true => { - read_effective(true) - .or(dac_override_effective(true))?; + read_effective(true).or(dac_override_effective(true))?; } }) } @@ -89,7 +93,7 @@ fn read_or_dac_override(effective: bool) -> Result<(), capctl::Error> { /// # 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: &P, lock: ImmutableLock) -> io::Result<()> { +pub fn toggle_lock_config>(file: &P, lock: ImmutableLock) -> io::Result<()> { let file = open_with_privileges(file)?; let mut val = 0; let fd = file.as_raw_fd(); diff --git a/resources/rootasrole.json b/resources/rootasrole.json index 73093b0..c590e63 100644 --- a/resources/rootasrole.json +++ b/resources/rootasrole.json @@ -70,6 +70,10 @@ { "type": "user", "name": "ROOTADMINISTRATOR" + }, + { + "type": "user", + "name": "root" } ], "tasks": [ diff --git a/src/chsr/cli/data.rs b/src/chsr/cli/data.rs index b29480b..36fdb95 100644 --- a/src/chsr/cli/data.rs +++ b/src/chsr/cli/data.rs @@ -4,6 +4,7 @@ use capctl::CapSet; use chrono::Duration; use linked_hash_set::LinkedHashSet; +use pest_derive::Parser; use rar_common::database::{ options::{ EnvBehavior, EnvKey, OptType, PathBehavior, SAuthentication, SBounding, SPrivileged, @@ -11,7 +12,6 @@ use rar_common::database::{ }, structs::{IdTask, SActor, SActorType, SGroups, SetBehavior}, }; -use pest_derive::Parser; #[derive(Parser)] #[grammar = "chsr/cli/cli.pest"] diff --git a/src/chsr/cli/mod.rs b/src/chsr/cli/mod.rs index 6c65ee8..e0a5d83 100644 --- a/src/chsr/cli/mod.rs +++ b/src/chsr/cli/mod.rs @@ -13,8 +13,8 @@ use process::process_input; use tracing::debug; use usage::print_usage; -use rar_common::Storage; use crate::util::escape_parser_string_vec; +use rar_common::Storage; pub fn main(storage: &Storage, args: I) -> Result> where @@ -42,7 +42,15 @@ mod tests { use std::{io::Write, rc::Rc}; use rar_common::{ - database::{options::*, read_json_config, structs::{SCredentials, *}, versionning::Versioning}, get_settings, rc_refcell, util::remove_with_privileges, RemoteStorageSettings, SettingsFile, Storage, StorageMethod, ROOTASROLE + database::{ + options::*, + read_json_config, + structs::{SCredentials, *}, + versionning::Versioning, + }, + get_settings, rc_refcell, + util::remove_with_privileges, + RemoteStorageSettings, SettingsFile, Storage, StorageMethod, ROOTASROLE, }; use super::*; @@ -254,9 +262,8 @@ mod tests { #[test] fn test_r_complete_show_actors() { setup("r_complete_show_actors"); - let settings = - get_settings(&format!("{}.{}", ROOTASROLE, "r_complete_show_actors")) - .expect("Failed to get settings"); + let settings = get_settings(&format!("{}.{}", ROOTASROLE, "r_complete_show_actors")) + .expect("Failed to get settings"); let config = read_json_config(settings.clone()).expect("Failed to read json"); assert!(main( &Storage::JSON(config.clone()), diff --git a/src/chsr/cli/pair.rs b/src/chsr/cli/pair.rs index 5c58b50..0d2f79c 100644 --- a/src/chsr/cli/pair.rs +++ b/src/chsr/cli/pair.rs @@ -9,8 +9,7 @@ use tracing::{debug, warn}; use crate::cli::data::{RoleType, TaskType}; use rar_common::database::{ options::{ - EnvBehavior, OptType, PathBehavior, SAuthentication, SBounding, SPrivileged, - TimestampType, + EnvBehavior, OptType, PathBehavior, SAuthentication, SBounding, SPrivileged, TimestampType, }, structs::{IdTask, SActor, SActorType, SGroups, SetBehavior}, }; diff --git a/src/chsr/cli/process.rs b/src/chsr/cli/process.rs index c75bbc5..d681291 100644 --- a/src/chsr/cli/process.rs +++ b/src/chsr/cli/process.rs @@ -7,11 +7,11 @@ use json::*; use tracing::debug; use rar_common::{ - Storage, database::{ options::{Opt, OptType}, structs::IdTask, }, + Storage, }; use super::{ diff --git a/src/chsr/cli/process/json.rs b/src/chsr/cli/process/json.rs index 36ff748..6c7e147 100644 --- a/src/chsr/cli/process/json.rs +++ b/src/chsr/cli/process/json.rs @@ -13,14 +13,13 @@ use crate::cli::data::{InputAction, RoleType, SetListType, TaskType, TimeoutOpt} use rar_common::{ database::{ - options::{ - EnvBehavior, EnvKey, Opt, OptStack, OptType, PathBehavior, SEnvOptions, SPathOptions, - STimeout, + options::{ + EnvBehavior, EnvKey, Opt, OptStack, OptType, PathBehavior, SEnvOptions, SPathOptions, + STimeout, + }, + structs::{IdTask, SCapabilities, SCommand, SRole, STask}, }, - structs::{IdTask, SCapabilities, SCommand, SRole, STask}, - -}, -rc_refcell, + rc_refcell, }; use super::perform_on_target_opt; diff --git a/src/chsr/main.rs b/src/chsr/main.rs index 3c87eb3..4cc3da1 100644 --- a/src/chsr/main.rs +++ b/src/chsr/main.rs @@ -1,14 +1,10 @@ //extern crate sudoers_reader; use rar_common::{ - Storage, database::{read_json_config, save_json}, - util::{ - drop_effective, read_effective, - subsribe, - }, plugin::register_plugins, - + util::{drop_effective, read_effective, subsribe}, + Storage, }; use tracing::{debug, error}; diff --git a/src/sr/main.rs b/src/sr/main.rs index 908747a..45b7b2a 100644 --- a/src/sr/main.rs +++ b/src/sr/main.rs @@ -2,15 +2,15 @@ pub mod pam; mod timeout; use capctl::CapState; -use rar_common::database::finder::{Cred, FilterMatcher, TaskMatch, TaskMatcher}; -use rar_common::database::{options::OptStack, structs::SConfig}; -use rar_common::util::escape_parser_string; use const_format::formatcp; use nix::{ libc::dev_t, sys::stat, unistd::{getgroups, getuid, isatty, Group, User}, }; +use rar_common::database::finder::{Cred, FilterMatcher, TaskMatch, TaskMatcher}; +use rar_common::database::{options::OptStack, structs::SConfig}; +use rar_common::util::escape_parser_string; use pam::PAM_PROMPT; use pty_process::blocking::{Command, Pty}; @@ -21,10 +21,13 @@ use tracing::{debug, error}; use rar_common::plugin::register_plugins; use rar_common::{ - util::{dac_override_effective,activates_no_new_privs, setgid_effective, setpcap_effective, setuid_effective, - drop_effective, read_effective, subsribe, BOLD, RST, UNDERLINE}, - self, Storage, + self, database::{read_json_config, structs::SGroups}, + util::{ + activates_no_new_privs, dac_override_effective, drop_effective, read_effective, + setgid_effective, setpcap_effective, setuid_effective, subsribe, BOLD, RST, UNDERLINE, + }, + Storage, }; //const ABOUT: &str = "Execute privileged commands with a role-based access control system"; @@ -179,7 +182,7 @@ where #[cfg(not(tarpaulin_include))] fn main() -> Result<(), Box> { - use crate::{rar_common::ROOTASROLE, pam::check_auth}; + use crate::{pam::check_auth, rar_common::ROOTASROLE}; subsribe("sr"); drop_effective()?; diff --git a/src/sr/pam/mod.rs b/src/sr/pam/mod.rs index ff2daba..e52827c 100644 --- a/src/sr/pam/mod.rs +++ b/src/sr/pam/mod.rs @@ -8,11 +8,11 @@ use pam_client::{Context, ConversationHandler, ErrorCode, Flag}; use pcre2::bytes::RegexBuilder; use tracing::{debug, error, info, warn}; +use crate::timeout; use rar_common::{ - Storage, database::{finder::Cred, options::OptStack}, + Storage, }; -use crate::timeout; use self::rpassword::Terminal; diff --git a/src/sr/timeout.rs b/src/sr/timeout.rs index 32e53c2..4c27cd3 100644 --- a/src/sr/timeout.rs +++ b/src/sr/timeout.rs @@ -16,11 +16,14 @@ use serde::{Deserialize, Serialize}; use tracing::debug; use rar_common::{ - util::{create_dir_all_with_privileges, create_with_privileges,open_with_privileges, remove_with_privileges}, database::{ finder::Cred, options::{STimeout, TimestampType}, }, + util::{ + create_dir_all_with_privileges, create_with_privileges, open_with_privileges, + remove_with_privileges, + }, }; /// This module checks the validity of a user's credentials diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml index 7192591..3ee76bc 100644 --- a/xtask/Cargo.toml +++ b/xtask/Cargo.toml @@ -24,6 +24,8 @@ strum = { version = "0.26.3", features = ["derive"] } capctl = "0.2.4" nix = { version = "0.29.0", features = ["user","process", "signal", "fs"] } glob = "0.3.1" +tracing = "0.1.40" +tracing-subscriber = "0.3.18" [features] default = ["cli", "ebpf", "deploy"] diff --git a/xtask/src/configure.rs b/xtask/src/configure.rs index 62585b2..337bd88 100644 --- a/xtask/src/configure.rs +++ b/xtask/src/configure.rs @@ -6,11 +6,11 @@ use std::path::Path; use anyhow::Context; use nix::unistd::{getresuid, getuid}; use strum::EnumIs; +use tracing::{info, warn}; -use crate::util::{files_are_equal, toggle_lock_config, ImmutableLock, OsTarget, SettingsFile, ROOTASROLE}; - - - +use crate::util::{ + files_are_equal, toggle_lock_config, ImmutableLock, OsTarget, SettingsFile, ROOTASROLE, +}; const TEMPLATE: &str = include_str!("../../resources/rootasrole.json"); pub const PAM_CONFIG_PATH: &str = "/etc/pam.d/sr"; @@ -57,24 +57,21 @@ pub fn check_filesystem() -> io::Result<()> { if let Some(fs_type) = get_filesystem_type(ROOTASROLE)? { match fs_type.as_str() { "ext2" | "ext3" | "ext4" | "xfs" | "btrfs" | "ocfs2" | "jfs" | "reiserfs" => { - println!( + info!( "{} is compatble for immutability, setting immutable flag", fs_type ); set_immutable(&mut config, true); - toggle_lock_config( - &ROOTASROLE.to_string(), - ImmutableLock::Set, - )?; + toggle_lock_config(&ROOTASROLE.to_string(), ImmutableLock::Set)?; return Ok(()); } - _ => println!( + _ => info!( "{} is not compatible for immutability, removing immutable flag", fs_type ), } } else { - println!("Failed to get filesystem type, removing immutable flag"); + info!("Failed to get filesystem type, removing immutable flag"); } set_immutable(&mut config, false); Ok(()) @@ -86,27 +83,46 @@ fn set_immutable(config: &mut SettingsFile, value: bool) { _immutable = value; } } - + if !value { - let roles = config._extra_fields.as_object_mut().unwrap().get_mut("roles").unwrap().as_object_mut().unwrap(); + let roles = config + ._extra_fields + .as_object_mut() + .unwrap() + .get_mut("roles") + .unwrap() + .as_object_mut() + .unwrap(); for role in roles.values_mut() { let tasks = role.as_object_mut().unwrap().get_mut("tasks"); if let Some(tasks) = tasks { for task in tasks.as_array_mut().unwrap() { - let cred = task.as_object_mut().unwrap().get_mut("cred").unwrap().as_object_mut().unwrap(); - let caps = cred.get_mut("capabilities").unwrap().as_object_mut().unwrap(); + let cred = task + .as_object_mut() + .unwrap() + .get_mut("cred") + .unwrap() + .as_object_mut() + .unwrap(); + let caps = cred + .get_mut("capabilities") + .unwrap() + .as_object_mut() + .unwrap(); if let Some(add) = caps.get_mut("add") { - add.as_array_mut().unwrap().retain(|x| x.as_str().unwrap() != "CAP_LINUX_IMMUTABLE"); + add.as_array_mut() + .unwrap() + .retain(|x| x.as_str().unwrap() != "CAP_LINUX_IMMUTABLE"); } if let Some(sub) = caps.get_mut("sub") { - sub.as_array_mut().unwrap().retain(|x| x.as_str().unwrap() != "CAP_LINUX_IMMUTABLE"); + sub.as_array_mut() + .unwrap() + .retain(|x| x.as_str().unwrap() != "CAP_LINUX_IMMUTABLE"); } } } } } - - } fn get_filesystem_type>(path: P) -> io::Result> { @@ -145,7 +161,7 @@ fn deploy_config_file() -> Result { let mut status = ConfigState::Unchanged; // Check if the target file exists if !Path::new(ROOTASROLE).exists() { - println!("Config file does not exist, deploying default file"); + info!("Config file does not exist, deploying default file"); // If the target file does not exist, copy the default file deploy_config(ROOTASROLE)?; } else { @@ -154,8 +170,8 @@ fn deploy_config_file() -> Result { match status { ConfigState::Unchanged => { - println!("Config file newly created or has not been modified."); - println!("Checking if filesystem allows immutability."); + info!("Config file newly created or has not been modified."); + info!("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 @@ -166,7 +182,7 @@ fn deploy_config_file() -> Result { } } ConfigState::Modified => { - println!("Config file has been modified by the user, skipping immutable configuration"); + info!("Config file has been modified by the user, skipping immutable configuration"); } } Ok(status) @@ -185,7 +201,6 @@ pub fn config_state() -> Result { } fn deploy_config>(config_path: P) -> Result<(), anyhow::Error> { - let mut content = TEMPLATE.to_string(); let user = retrieve_real_user()?; @@ -195,7 +210,7 @@ fn deploy_config>(config_path: P) -> Result<(), anyhow::Error> { content = content.replace("\"ROOTADMINISTRATOR\"", &format!("\"{}\"", user.name)); } None => { - eprintln!("Failed to get the current user from passwd file, using UID instead"); + warn!("Failed to get the current user from passwd file, using UID instead"); content = content.replace("\"ROOTADMINISTRATOR\"", &format!("{}", getuid().as_raw())); } } @@ -221,7 +236,9 @@ fn retrieve_real_user() -> Result, anyhow::Error> { pub fn default_pam_path(os: &OsTarget) -> &'static str { match os { - OsTarget::Debian | OsTarget::Ubuntu => include_str!("../../resources/debian/deb_sr_pam.conf"), + OsTarget::Debian | OsTarget::Ubuntu => { + include_str!("../../resources/debian/deb_sr_pam.conf") + } OsTarget::RedHat | OsTarget::Fedora => include_str!("../../resources/rh/rh_sr_pam.conf"), OsTarget::ArchLinux => include_str!("../../resources/arch/arch_sr_pam.conf"), } @@ -229,7 +246,7 @@ pub fn default_pam_path(os: &OsTarget) -> &'static str { fn deploy_pam_config(os: &OsTarget) -> io::Result { if fs::metadata(PAM_CONFIG_PATH).is_err() { - println!("Deploying PAM configuration file"); + info!("Deploying PAM configuration file"); return fs::copy(default_pam_path(os), PAM_CONFIG_PATH); } Ok(0) @@ -241,7 +258,7 @@ pub fn configure(os: Option) -> Result<(), anyhow::Error> { } else { OsTarget::detect() .and_then(|t| { - println!("Detected OS is : {}", t); + info!("Detected OS is : {}", t); Ok(t) }) .context("Failed to detect the OS")? diff --git a/xtask/src/deploy/arch.rs b/xtask/src/deploy/arch.rs index 46687c9..a26e75d 100644 --- a/xtask/src/deploy/arch.rs +++ b/xtask/src/deploy/arch.rs @@ -1,7 +1,5 @@ use crate::install::Profile; - - -pub fn make_pkg(opts : Profile) -> Result<(), anyhow::Error> { +pub fn make_pkg(opts: Profile) -> Result<(), anyhow::Error> { Ok(()) -} \ No newline at end of file +} diff --git a/xtask/src/deploy/debian.rs b/xtask/src/deploy/debian.rs index 05a183e..dbc94f1 100644 --- a/xtask/src/deploy/debian.rs +++ b/xtask/src/deploy/debian.rs @@ -2,26 +2,38 @@ use std::process::{Command, ExitStatus, Stdio}; use anyhow::Context; -use crate::{install::{self, dependencies::install_dependencies, InstallDependenciesOptions, Profile}, util::OsTarget}; +use crate::{ + install::{self, dependencies::install_dependencies, InstallDependenciesOptions, Profile}, + util::{get_os, OsTarget}, +}; use super::setup_maint_scripts; -pub fn dependencies() -> Result { - install_dependencies(&OsTarget::detect()?, &["upx", "dpkg"]).context("failed to install packaging dependencies") +fn dependencies(os: &OsTarget, priv_bin: Option) -> Result { + install_dependencies(os, &["upx", "dpkg"], priv_bin) + .context("failed to install packaging dependencies") } -pub fn make_deb(profile: Profile) -> Result<(), anyhow::Error> { - dependencies()?; - +pub fn make_deb( + os: Option, + profile: Profile, + priv_bin: &Option, +) -> Result<(), anyhow::Error> { + let os = get_os(os)?; + + dependencies(&os, priv_bin.clone())?; + install::dependencies(InstallDependenciesOptions { - os: None, + os: Some(os), install_dependencies: true, dev: true, + priv_bin: priv_bin.clone(), })?; install::build(&install::BuildOptions { profile, toolchain: install::Toolchain::default(), - clean_before: true, + clean_before: false, + privbin: Some("sudo".to_string()), })?; setup_maint_scripts()?; @@ -31,4 +43,3 @@ pub fn make_deb(profile: Profile) -> Result<(), anyhow::Error> { .status()?; Ok(()) } - diff --git a/xtask/src/deploy/mod.rs b/xtask/src/deploy/mod.rs index 4d47012..8c15fb3 100644 --- a/xtask/src/deploy/mod.rs +++ b/xtask/src/deploy/mod.rs @@ -4,8 +4,8 @@ use clap::Parser; use crate::{install::Profile, util::OsTarget}; -mod debian; mod arch; +mod debian; mod redhat; #[derive(Debug, Parser)] @@ -20,10 +20,16 @@ pub struct MakeOptions { /// The OS target for package generation pub target: Vec, + + /// The binary to elevate privileges + #[clap(long, short = 'p')] + pub priv_bin: Option, } fn all() -> HashSet { - vec![OsTarget::Debian, OsTarget::ArchLinux, OsTarget::RedHat].into_iter().collect() + vec![OsTarget::Debian, OsTarget::ArchLinux, OsTarget::RedHat] + .into_iter() + .collect() } pub fn deploy(opts: &MakeOptions) -> Result<(), anyhow::Error> { @@ -35,13 +41,9 @@ pub fn deploy(opts: &MakeOptions) -> Result<(), anyhow::Error> { for target in targets { match target { - OsTarget::Debian => { - debian::dependencies()?; - debian::make_deb(opts.profile)?; - - }, + OsTarget::Debian => debian::make_deb(opts.os.clone(), opts.profile, &opts.priv_bin)?, OsTarget::ArchLinux => arch::make_pkg(opts.profile)?, - OsTarget::RedHat => redhat::make_rpm(opts.profile)?, + OsTarget::RedHat => redhat::make_rpm(opts.os.clone(), opts.profile, &opts.priv_bin)?, _ => anyhow::bail!("Unsupported OS target"), } } diff --git a/xtask/src/deploy/redhat.rs b/xtask/src/deploy/redhat.rs index fda7d0d..bdc4e68 100644 --- a/xtask/src/deploy/redhat.rs +++ b/xtask/src/deploy/redhat.rs @@ -1,24 +1,31 @@ use std::process::Command; -use crate::install::{self, InstallDependenciesOptions, Profile}; +use crate::{ + install::{self, InstallDependenciesOptions, Profile}, + util::{get_os, OsTarget}, +}; use super::setup_maint_scripts; - -pub fn make_rpm(profile: Profile) -> Result<(), anyhow::Error> { +pub fn make_rpm( + os: Option, + profile: Profile, + exe: &Option, +) -> Result<(), anyhow::Error> { + let os = get_os(os)?; install::dependencies(InstallDependenciesOptions { - os: None, + os: Some(os), install_dependencies: true, dev: true, + priv_bin: exe.clone(), })?; install::build(&install::BuildOptions { profile, toolchain: install::Toolchain::default(), - clean_before: true, + clean_before: false, + privbin: Some("sudo".to_string()), })?; - Command::new("cargo") - .arg("generate-rpm") - .status()?; + Command::new("cargo").arg("generate-rpm").status()?; Ok(()) -} \ No newline at end of file +} diff --git a/xtask/src/install/build.rs b/xtask/src/install/build.rs index c834ff6..813ba21 100644 --- a/xtask/src/install/build.rs +++ b/xtask/src/install/build.rs @@ -1,5 +1,6 @@ use std::process::Command; +use tracing::debug; use super::BuildOptions; @@ -10,6 +11,7 @@ fn build_binary(name: &str, options: &BuildOptions, additionnal_args: Vec<&str>) args.push("--release"); } args.extend(additionnal_args); + debug!("Building {} binary with args: {:?}", name, args); Command::new("cargo") .args(args) .status() @@ -27,4 +29,4 @@ pub fn build(options: &BuildOptions) -> Result<(), anyhow::Error> { build_binary("chsr", options, vec!["--no-default-features"]); Ok(()) -} \ No newline at end of file +} diff --git a/xtask/src/install/dependencies.rs b/xtask/src/install/dependencies.rs index 6022bc3..ae951d2 100644 --- a/xtask/src/install/dependencies.rs +++ b/xtask/src/install/dependencies.rs @@ -1,28 +1,34 @@ use std::process::ExitStatus; use anyhow::Context; +use capctl::CapState; +use nix::unistd::geteuid; +use tracing::info; -use crate::install::OsTarget; +use crate::{install::OsTarget, util::get_os}; use super::InstallDependenciesOptions; -fn update_package_manager() -> Result<(), anyhow::Error> { - let os = OsTarget::detect()?; +fn update_package_manager(os: &OsTarget, priv_bin: &Option) -> Result<(), anyhow::Error> { + let mut command = Vec::new(); + if is_priv_bin_necessary(os)? { + if let Some(priv_bin) = priv_bin { + command.push(priv_bin.as_str()); + } else { + return Err(anyhow::anyhow!("Privileged binary is required")); + } + } match os { - OsTarget::Debian | OsTarget::Ubuntu => { - let _ = std::process::Command::new("apt-get") - .arg("update") - .status()?; - }, - OsTarget::RedHat | OsTarget::Fedora => { - let _ = std::process::Command::new("yum") - .arg("update") - .arg("-y") - .status()?; - }, - OsTarget::ArchLinux => {}, - } + OsTarget::Debian | OsTarget::Ubuntu => command.extend(&["apt-get", "update"]), + OsTarget::RedHat => command.extend(&["yum", "update", "-y"]), + OsTarget::ArchLinux => command.extend(&["pacman", "-Syu"]), + OsTarget::Fedora => command.extend(&["dnf", "update", "-y"]), + }; + std::process::Command::new(command[0]) + .args(&command[1..]) + .status() + .context("Failed to update package manager")?; Ok(()) } @@ -52,56 +58,58 @@ fn get_dependencies(os: &OsTarget, dev: &bool) -> &'static [&'static str] { } } -pub fn install_dependencies(os: &OsTarget, deps:&[&str]) -> Result { +fn is_priv_bin_necessary(os: &OsTarget) -> Result { match os { - OsTarget::Debian | OsTarget::Ubuntu => { - std::process::Command::new("apt-get") - .arg("install") - .arg("-y") - .args(deps) - .status() - }, - OsTarget::RedHat => { - std::process::Command::new("yum") - .arg("install") - .arg("-y") - .args(deps) - .status() - }, - OsTarget::Fedora => { - std::process::Command::new("dnf") - .arg("install") - .arg("-y") - .args(deps) - .status() + OsTarget::ArchLinux => Ok(geteuid().is_root()), + _ => { + let mut state = CapState::get_current()?; + if state.permitted.has(capctl::Cap::DAC_OVERRIDE) + && !state.effective.has(capctl::Cap::DAC_OVERRIDE) + { + state.effective.add(capctl::Cap::DAC_OVERRIDE); + state.set_current()?; + Ok(false) + } else { + Ok(true) + } + } + } +} + +pub fn install_dependencies( + os: &OsTarget, + deps: &[&str], + priv_bin: Option, +) -> Result { + let mut command = Vec::new(); + + if is_priv_bin_necessary(os)? { + if let Some(priv_bin) = &priv_bin { + command.push(priv_bin.as_str()); + } else { + return Err(anyhow::anyhow!("Privileged binary is required")); } - OsTarget::ArchLinux => { - std::process::Command::new("pacman") - .arg("-Syu") - .arg("--noconfirm") - .args(deps) - .status() - }, } + command.extend(match os { + OsTarget::Debian | OsTarget::Ubuntu => ["apt-get", "install", "-y"], + OsTarget::RedHat => ["yum", "install", "-y"], + OsTarget::Fedora => ["dnf", "install", "-y"], + OsTarget::ArchLinux => ["pacman", "-Syu", "--noconfirm"], + }); + command.extend(deps); + Ok(std::process::Command::new(command[0]) + .args(&command[1..]) + .status()?) } pub fn install(opts: InstallDependenciesOptions) -> Result<(), anyhow::Error> { - update_package_manager()?; + let os = get_os(opts.os)?; + update_package_manager(&os, &opts.priv_bin)?; // dependencies are : libpam and libpcre2 - println!("Installing dependencies: libpam.so and libpcre2.so for running the application"); + info!("Installing dependencies: libpam.so and libpcre2.so for running the application"); - let os = if let Some(os) = opts.os { - os - } else { - OsTarget::detect() - .and_then(|t| { - println!("Detected OS is : {}", t); - Ok(t) - }) - .context("Failed to detect the OS")? - }; - install_dependencies(&os, get_dependencies(&os, &opts.dev))?; + install_dependencies(&os, get_dependencies(&os, &opts.dev), opts.priv_bin)?; - println!("Dependencies installed successfully"); + info!("Dependencies installed successfully"); Ok(()) -} \ No newline at end of file +} diff --git a/xtask/src/install/install.rs b/xtask/src/install/install.rs index 7f6547d..68ccf7c 100644 --- a/xtask/src/install/install.rs +++ b/xtask/src/install/install.rs @@ -1,25 +1,52 @@ use std::fs::{self, File}; use std::os::fd::AsRawFd; +use std::path::Path; use capctl::{Cap, CapSet}; +use clap::Command; use nix::sys::stat::{fchmod, Mode}; use nix::unistd::{Gid, Uid}; +use tracing::{debug, error, info}; use crate::install::Profile; -use anyhow::Context; +use crate::util::{BOLD, RED, RST}; +use anyhow::{anyhow, Context}; use super::util::{cap_clear, cap_effective}; use super::{CHSR_DEST, SR_DEST}; fn copy_files(profile: &Profile) -> Result<(), anyhow::Error> { 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)?; - - chmod()?; + let cwd = binding + .to_str() + .context("unable to get current dir as string")?; + info!("Current working directory: {}", cwd); + info!( + "Copying files {}/target/{}/sr to {} and {}", + cwd, profile, SR_DEST, CHSR_DEST + ); + let s_sr = format!("{}/target/{}/sr", cwd, profile); + let sr = Path::new(&s_sr); + let s_chsr = format!("{}/target/{}/chsr", cwd, profile); + let chsr = Path::new(&s_chsr); + if !sr.exists() || !chsr.exists() { + return Err(anyhow!("sr or chsr does not exist in the target directory. + \nYou may need first to do `sudo cargo clean`.\n{}{}Please build the project first using `cargo xtask build`{}", BOLD, RED, RST)); + } + // We can't use fs::copy directly because it will overwrite the destination file + // and it is possible that the destination file is currently under execution. + debug!("Copying sr to sr.tmp"); + fs::copy(sr, format!("{}.tmp", s_sr))?; + debug!("Copying chsr to chsr.tmp"); + fs::copy(chsr, format!("{}.tmp", s_chsr))?; + debug!("Renaming sr to /usr/bin/sr"); + fs::rename(sr, SR_DEST)?; + debug!("Renaming chsr to /usr/bin/chsr"); + fs::rename(chsr, CHSR_DEST)?; + debug!("Renaming sr.tmp to sr"); + fs::rename(format!("{}.tmp", s_sr), sr)?; + debug!("Renaming chsr.tmp to chsr"); + fs::rename(format!("{}.tmp", s_chsr), chsr)?; Ok(()) } @@ -54,52 +81,51 @@ fn setfcap() -> Result<(), anyhow::Error> { pub fn install(profile: Profile, clean_after: bool, copy: bool) -> Result<(), anyhow::Error> { // test if current process has CAP_DAC_OVERRIDE,CAP_CHOWN capabilities let mut state = capctl::CapState::get_current()?; - if state.permitted.has(Cap::DAC_OVERRIDE) - && state.permitted.has(Cap::CHOWN) - && state.permitted.has(Cap::SETFCAP) + if !state.permitted.has(Cap::DAC_OVERRIDE) + || !state.permitted.has(Cap::CHOWN) + || !state.permitted.has(Cap::SETFCAP) { - if copy { - //raise dac_override to copy files - cap_effective(&mut state, Cap::DAC_OVERRIDE).context("Failed to raise DAC_OVERRIDE")?; - - // cp target/{release}/sr,chsr,capable /usr/bin - copy_files( - &profile, - ) - .context("Failed to copy sr and chsr files")?; - - // drop dac_override - cap_clear(&mut state).context("Failed to drop effective DAC_OVERRIDE")?; - } - - - // set file mode to 555 for sr and chsr - chmod().context("Failed to set file mode for sr and chsr")?; - - // raise chown and setfcap to set owner - cap_effective(&mut state, Cap::CHOWN).context("Failed to raise CHOWN")?; - - // chown sr and chsr to root:root - chown().context("Failed to chown sr and chsr")?; - - // drop chown, raise setfcap capabilities - cap_effective(&mut state, Cap::SETFCAP).context("Failed to raise SETFCAP")?; - - // set file capabilities for sr only - setfcap().context("Failed to set file capabilities on /usr/bin/sr")?; - - // drop all capabilities - cap_clear(&mut state).context("Failed to drop effective capabilities")?; - - if clean_after { - fs::remove_dir_all(format!("{:?}/target", std::env::current_dir()?)) - .context("Failed to remove target directory")?; - } - } else { - eprintln!( - "You need to have CAP_DAC_OVERRIDE and CAP_CHOWN capabilities to install rootasrole" - ); - std::process::exit(1); + return Err(anyhow!( + "You need CAP_DAC_OVERRIDE, CAP_CHOWN and CAP_CHOWN capabilities to install rootasrole. + \nConsider using `sr cargo xtask install` or use sudo if sr is currently not installed." + )); + } + if copy { + //raise dac_override to copy files + cap_effective(&mut state, Cap::DAC_OVERRIDE).context("Failed to raise DAC_OVERRIDE")?; + + // cp target/{release}/sr,chsr,capable /usr/bin + copy_files(&profile).context("Failed to copy sr and chsr files")?; + + // drop dac_override + cap_clear(&mut state).context("Failed to drop effective DAC_OVERRIDE")?; + } + + cap_effective(&mut state, Cap::DAC_OVERRIDE).context("Failed to raise CHOWN")?; + + // set file mode to 555 for sr and chsr + chmod().context("Failed to set file mode for sr and chsr")?; + + // raise chown and setfcap to set owner + cap_effective(&mut state, Cap::CHOWN).context("Failed to raise CHOWN")?; + + // chown sr and chsr to root:root + chown().context("Failed to chown sr and chsr")?; + + // drop chown, raise setfcap capabilities + cap_effective(&mut state, Cap::SETFCAP).context("Failed to raise SETFCAP")?; + + // set file capabilities for sr only + setfcap().context("Failed to set file capabilities on /usr/bin/sr")?; + + // drop all capabilities + cap_clear(&mut state).context("Failed to drop effective capabilities")?; + + if clean_after { + std::process::Command::new("cargo") + .args(&["clean"]) + .status() + .context("Failed to clean the project")?; } Ok(()) } diff --git a/xtask/src/install/mod.rs b/xtask/src/install/mod.rs index f62700e..ceca234 100644 --- a/xtask/src/install/mod.rs +++ b/xtask/src/install/mod.rs @@ -1,8 +1,8 @@ -pub(crate) mod install; mod build; +pub(crate) mod dependencies; +pub(crate) mod install; mod uninstall; mod util; -pub(crate) mod dependencies; use std::collections::VecDeque; use std::str::FromStr; @@ -13,28 +13,29 @@ use semver::Version; use strum::{Display, EnumIs, EnumString}; use anyhow::anyhow; +use tracing::debug; -use crate::{configure, util::OsTarget}; - +use crate::{ + configure, + util::{detect_priv_bin, get_os, OsTarget}, +}; pub const SR_DEST: &str = "/usr/bin/sr"; pub const CHSR_DEST: &str = "/usr/bin/chsr"; - #[derive(Debug, Parser, Clone)] pub struct InstallOptions { - #[clap(flatten)] - pub build : BuildOptions, + pub build_opts: BuildOptions, /// The OS target for PAM configuration and dependencies installation (if -i is set) /// By default, it tries to autodetect it #[clap(long, short)] pub os: Option, - /// Do not build the binaries - #[clap(long, short = 'n')] - pub no_build: bool, + /// Build the binaries + #[clap(long, short = 'b')] + pub build: bool, /// Install dependencies before building #[clap(long, short = 'i')] @@ -43,11 +44,14 @@ pub struct InstallOptions { /// Clean the target directory after installing #[clap(long, short = 'a')] pub clean_after: bool, + + /// The binary to elevate privileges + #[clap(long, short = 'p')] + pub priv_bin: Option, } #[derive(Debug, Parser)] pub struct InstallDependenciesOptions { - /// The OS target for PAM configuration and dependencies installation (if -i is set) /// By default, it tries to autodetect it #[clap(long, short)] @@ -60,6 +64,10 @@ pub struct InstallDependenciesOptions { /// Install development dependencies for compiling #[clap(long, short = 'd')] pub dev: bool, + + /// The binary to elevate privileges + #[clap(long, short = 'p')] + pub priv_bin: Option, } #[derive(Debug, Parser)] @@ -86,9 +94,11 @@ pub enum Profile { Debug, } - #[derive(Debug, Parser, Clone)] pub struct BuildOptions { + /// The binary to elevate privileges + pub privbin: Option, + /// Build the target with debug profile (default is release) #[clap(short = 'd', long = "debug", default_value_t = Profile::Release, default_missing_value = "debug", num_args = 0)] pub profile: Profile, @@ -98,9 +108,8 @@ pub struct BuildOptions { pub toolchain: Toolchain, /// Clean the target directory before building - #[clap(long = "clean", short = 'b')] + #[clap(long = "clean")] pub clean_before: bool, - } impl ToString for Toolchain { @@ -205,7 +214,7 @@ impl FromStr for Toolchain { parts.pop_front(); } } - + let host = parts .iter() .fold(String::new(), |acc, x| format!("{}-{}", acc, x)); @@ -226,24 +235,29 @@ pub(crate) fn dependencies(opts: InstallDependenciesOptions) -> Result<(), anyho } pub(crate) fn install(opts: &InstallOptions) -> Result<(), anyhow::Error> { + let os = get_os(opts.os.clone())?; if opts.install_dependencies { + debug!("Installing dependencies"); dependencies(InstallDependenciesOptions { - os: opts.os.clone(), + os: Some(os.clone()), install_dependencies: true, - dev: !opts.no_build, + dev: opts.build, + priv_bin: opts.build_opts.privbin.clone().or(detect_priv_bin()), })?; } - if ! opts.no_build { - build(&opts.build)?; + debug!("AAAAAAAAAAAAAAAaa {:?}", opts.build); + if opts.build { + debug!("Building sr and chsr"); + build(&opts.build_opts)?; } - install::install(opts.build.profile, opts.clean_after, true)?; - configure(opts.os.clone()) + install::install(opts.build_opts.profile, opts.clean_after, true)?; + configure(Some(os)) } pub(crate) fn build(opts: &BuildOptions) -> Result<(), anyhow::Error> { build::build(opts) } -pub(crate) fn uninstall(opts : &UninstallOptions) -> Result<(), anyhow::Error> { +pub(crate) fn uninstall(opts: &UninstallOptions) -> Result<(), anyhow::Error> { uninstall::uninstall(opts) -} \ No newline at end of file +} diff --git a/xtask/src/install/uninstall.rs b/xtask/src/install/uninstall.rs index d1d9eac..70dee4c 100644 --- a/xtask/src/install/uninstall.rs +++ b/xtask/src/install/uninstall.rs @@ -1,5 +1,6 @@ use anyhow::Context; use std::fs; +use tracing::warn; use crate::util::{files_are_equal, toggle_lock_config, ImmutableLock, ROOTASROLE}; @@ -22,18 +23,15 @@ pub fn uninstall(opts: &UninstallOptions) -> Result<(), anyhow::Error> { } if opts.clean_config || config_state()?.is_unchanged() { errors.push( - toggle_lock_config( - &ROOTASROLE.to_string(), - ImmutableLock::Unset, - ) - .context("Error while removing lock from config file"), + toggle_lock_config(&ROOTASROLE.to_string(), ImmutableLock::Unset) + .context("Error while removing lock from config file"), ); errors.push(fs::remove_file(ROOTASROLE).context(ROOTASROLE)); } } for error in errors { if let Err(e) = error { - eprintln!("{}: {}", e.to_string(), e.source().unwrap().to_string()); + warn!("{}: {}", e.to_string(), e.source().unwrap().to_string()); } } Ok(()) diff --git a/xtask/src/install/util.rs b/xtask/src/install/util.rs index 7096cf0..0549f4a 100644 --- a/xtask/src/install/util.rs +++ b/xtask/src/install/util.rs @@ -1,5 +1,3 @@ -use std::{fs, io}; - use capctl::Cap; pub fn cap_clear(state: &mut capctl::CapState) -> Result<(), anyhow::Error> { @@ -13,4 +11,4 @@ pub fn cap_effective(state: &mut capctl::CapState, cap: Cap) -> Result<(), anyho state.effective.add(cap); state.set_current()?; Ok(()) -} \ No newline at end of file +} diff --git a/xtask/src/main.rs b/xtask/src/main.rs index 56deffb..c0e97a1 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -1,11 +1,13 @@ -mod install; mod configure; mod deploy; +mod install; pub mod util; -use std::process::exit; +use std::{ffi::CString, process::exit}; use clap::Parser; +use tracing::{error, warn, Level}; +use tracing_subscriber::util::SubscriberInitExt; use util::OsTarget; #[derive(Debug, Parser)] @@ -31,25 +33,33 @@ enum Command { Uninstall(install::UninstallOptions), #[cfg(feature = "deploy")] Deploy(deploy::MakeOptions), - +} +fn subsribe(tool: &str) { + use std::io; + tracing_subscriber::fmt() + .with_max_level(Level::DEBUG) + .with_file(true) + .with_line_number(true) + .with_writer(io::stdout) + .finish() + .init(); } fn main() { + subsribe("xtask"); let opts = Options::parse(); use Command::*; let ret = match opts.command { Dependencies(opts) => install::dependencies(opts), - Build(opts)=> install::build(&opts), + Build(opts) => install::build(&opts), Install(opts) => install::install(&opts), - Configure{ os} => install::configure(os), + Configure { os } => install::configure(os), Uninstall(opts) => install::uninstall(&opts), Deploy(opts) => deploy::deploy(&opts), - }; - if let Err(e) = ret { - eprintln!("{e:#}"); + error!("{e:#}"); exit(1); } } diff --git a/xtask/src/postinst.rs b/xtask/src/postinst.rs index d491500..62bd8ce 100644 --- a/xtask/src/postinst.rs +++ b/xtask/src/postinst.rs @@ -1,12 +1,12 @@ use std::{env::args, fs::File, io::BufReader}; use configure::check_filesystem; -use install::{BuildOptions, InstallOptions}; +use tracing::warn; use util::{OsTarget, SettingsFile, ROOTASROLE}; -mod util; mod configure; mod install; +mod util; fn main() { let action = args().nth(1); @@ -15,12 +15,12 @@ fn main() { "configure" => { let res = install::install::install(install::Profile::Release, false, false); if let Err(e) = res { - eprintln!("{:#}", e); + warn!("{:#}", e); std::process::exit(1); } let res = configure::configure(Some(OsTarget::Debian)); if let Err(e) = res { - eprintln!("{:#}", e); + warn!("{:#}", e); std::process::exit(1); } } @@ -28,11 +28,16 @@ fn main() { // We replace the immutable flag if it was set in config file if let Ok(f) = File::open(ROOTASROLE) { let config = BufReader::new(f); - let config: SettingsFile = serde_json::from_reader(config).expect("Failed to parse config file"); - if config.storage.settings.is_some_and(|s| s.immutable.unwrap_or(false)) { + let config: SettingsFile = + serde_json::from_reader(config).expect("Failed to parse config file"); + if config + .storage + .settings + .is_some_and(|s| s.immutable.unwrap_or(false)) + { let res = check_filesystem(); if let Err(e) = res { - eprintln!("{:#}", e); + warn!("{:#}", e); std::process::exit(1); } } diff --git a/xtask/src/prerm.rs b/xtask/src/prerm.rs index b54bf3f..9322d86 100644 --- a/xtask/src/prerm.rs +++ b/xtask/src/prerm.rs @@ -5,10 +5,7 @@ use util::{toggle_lock_config, ROOTASROLE}; mod util; fn main() { if File::open(ROOTASROLE).is_ok() { - toggle_lock_config( - &ROOTASROLE.to_string(), - util::ImmutableLock::Unset, - ) - .expect("Error while removing lock from config file"); + toggle_lock_config(&ROOTASROLE.to_string(), util::ImmutableLock::Unset) + .expect("Error while removing lock from config file"); } -} \ No newline at end of file +} diff --git a/xtask/src/util.rs b/xtask/src/util.rs index 8971f87..1648f82 100644 --- a/xtask/src/util.rs +++ b/xtask/src/util.rs @@ -1,18 +1,22 @@ use std::{ - fs::{self, File}, io, os::{ - fd::AsRawFd, - unix::fs::MetadataExt, - }, path::Path + fs::{self, File}, + io, + os::{fd::AsRawFd, unix::fs::MetadataExt}, + path::Path, }; -use capctl::CapState; +use anyhow::{anyhow, Context}; use capctl::Cap; +use capctl::CapState; use clap::ValueEnum; -use nix::libc::{FS_IOC_GETFLAGS, FS_IOC_SETFLAGS}; +use nix::{ + libc::{FS_IOC_GETFLAGS, FS_IOC_SETFLAGS}, + unistd::geteuid, +}; use serde::{Deserialize, Serialize}; use serde_json::Value; use strum::{Display, EnumIs, EnumIter}; -use anyhow::anyhow; +use tracing::debug; #[derive(Debug, Clone, ValueEnum, EnumIs, EnumIter, Display, PartialEq, Eq, Hash)] #[clap(rename_all = "lowercase")] @@ -44,7 +48,7 @@ impl OsTarget { return Ok(OsTarget::ArchLinux); } else if os.contains("redhat") || os.contains("rhel") { return Ok(OsTarget::RedHat); - } + } } Err(anyhow!("Unsupported OS")) } @@ -109,12 +113,10 @@ fn immutable_required_privileges(file: &File, effective: bool) -> Result<(), cap fn read_or_dac_override(effective: bool) -> Result<(), capctl::Error> { Ok(match effective { false => { - read_effective(false) - .and(dac_override_effective(false))?; + read_effective(false).and(dac_override_effective(false))?; } true => { - read_effective(true) - .or(dac_override_effective(true))?; + read_effective(true).or(dac_override_effective(true))?; } }) } @@ -123,7 +125,7 @@ fn read_or_dac_override(effective: bool) -> Result<(), capctl::Error> { /// # 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: &P, lock: ImmutableLock) -> io::Result<()> { +pub fn toggle_lock_config>(file: &P, lock: ImmutableLock) -> io::Result<()> { let file = open_with_privileges(file)?; let mut val = 0; let fd = file.as_raw_fd(); @@ -183,13 +185,38 @@ pub fn files_are_equal(path1: &str, path2: &str) -> io::Result { Ok(file1_content == file2_content) } +pub fn get_os(os: Option) -> Result { + Ok(if let Some(os) = os { + os + } else { + OsTarget::detect() + .and_then(|t| { + debug!("Detected OS is : {}", t); + Ok(t) + }) + .context("Failed to detect the OS")? + }) +} + +pub fn detect_priv_bin() -> Option { + // is /usr/bin/sr exist ? + if std::fs::metadata("/usr/bin/sr").is_ok() { + return Some("/usr/bin/sr".to_string()); + } else if std::fs::metadata("/usr/bin/sudo").is_ok() { + return Some("/usr/bin/sudo".to_string()); + } else if std::fs::metadata("/usr/bin/doas").is_ok() { + return Some("/usr/bin/doas".to_string()); + } else { + return None; + } +} + #[cfg(test)] mod test { use std::{fs, path::PathBuf}; use super::*; - #[test] fn test_toggle_lock_config() { let path = PathBuf::from("/tmp/test");