diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d1845dc..5c35ea0 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -29,7 +29,7 @@ jobs: session requisite pam_permit.so session required pam_permit.so" | tee /etc/pam.d/sr' - name: Install RootAsRole - run: sudo -E cargo xtask install -bia + run: cargo xtask install -bip 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 ff4313d..baf7eda 100644 --- a/.github/workflows/pkg.yml +++ b/.github/workflows/pkg.yml @@ -25,7 +25,7 @@ jobs: override: true - name: Install Dependencies - run: sudo $(command -v cargo) xtask dependencies -i -d + run: cargo xtask dependencies -dip sudo - name: Build run: cargo xtask deploy debian redhat diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml index d45f10a..26608a1 100644 --- a/.github/workflows/quality.yml +++ b/.github/workflows/quality.yml @@ -40,8 +40,8 @@ jobs: components: clippy override: false - - name: Install Dependencies - run: sudo $(command -v cargo) xtask dependencies -di + - name: Install RootAsRole + run: cargo xtask dependencies -dip sudo # if pull request review only - uses: mbrobbel/rustfmt-check@master diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 548ffaf..e8f2c04 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -26,7 +26,7 @@ jobs: run: apt install sudo -y - name: Install Dependencies - run: sudo $(command -v cargo) xtask dependencies -di + run: cargo xtask dependencies -dip sudo - name: run tests with coverage run: cargo +nightly tarpaulin --verbose --all-features --workspace --timeout 120 --bin chsr --bin sr --exclude-files build.rs xtask* --out Xml diff --git a/xtask/src/install/install.rs b/xtask/src/install/install.rs index dff9071..25c69cc 100644 --- a/xtask/src/install/install.rs +++ b/xtask/src/install/install.rs @@ -1,15 +1,18 @@ +use std::env::{self, current_exe}; use std::fs::{self, File}; use std::os::fd::AsRawFd; -use std::path::Path; +use std::path::{Path, PathBuf}; +use std::str::FromStr; -use capctl::{Cap, CapSet}; +use capctl::{Cap, CapSet, CapState}; use clap::Command; use nix::sys::stat::{fchmod, Mode}; use nix::unistd::{Gid, Uid}; +use strum::EnumIs; use tracing::{debug, error, info}; use crate::install::Profile; -use crate::util::{BOLD, RED, RST}; +use crate::util::{detect_priv_bin, BOLD, RED, RST}; use anyhow::{anyhow, Context}; use super::util::{cap_clear, cap_effective}; @@ -78,18 +81,48 @@ fn setfcap() -> Result<(), anyhow::Error> { Ok(()) } -pub fn install(profile: Profile, clean_after: bool, copy: bool) -> Result<(), anyhow::Error> { +#[derive(Debug, EnumIs)] +pub enum Elevated { + Yes, + No, +} + +pub fn install(priv_exe: &Option,profile: Profile, clean_after: bool, copy: bool) -> Result { // 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) { - return Err(anyhow!( - "You need CAP_DAC_OVERRIDE, CAP_CHOWN and CAP_SETFCAP capabilities to install rootasrole. - \nConsider using `sr cargo xtask install` or use sudo if sr is currently not installed." - )); + let bounding = capctl::bounding::probe(); + // get parent process + if !bounding.has(Cap::DAC_OVERRIDE) || + !bounding.has(Cap::CHOWN) || + !bounding.has(Cap::SETFCAP) + { + return Err(anyhow!("The bounding set misses DAC_OVERRIDE, CHOWN or SETFCAP capabilities")); + } else if env::var("ROOTASROLE_INSTALLER_NESTED").is_ok_and(|v| v == "1") { + env::remove_var("ROOTASROLE_INSTALLER_NESTED"); + return Err(anyhow!("Unable to elevate required capabilities, is LSM blocking installation?")); + } + + let priv_bin = detect_priv_bin(); + let priv_exe = priv_exe.as_ref().or(priv_bin.as_ref()).context("Privileged binary is required").map_err(|e|{ + return anyhow::Error::msg(format!("Please run {} as an administrator.", current_exe().unwrap_or(PathBuf::from_str("the command").unwrap()).to_str().unwrap())); + })?; + env::set_var("ROOTASROLE_INSTALLER_NESTED", "1"); + tracing::warn!("Elevating privileges..."); + std::process::Command::new(priv_exe) + .arg(current_exe()?.to_str().context("Failed to get current exe path")?) + .arg("install") + .status() + .context("Failed to run privileged binary").map_err(|e|{ + error!("{}", e); + return anyhow::Error::msg(format!("Failed to run privileged binary. Please run {} as an administrator.", current_exe().unwrap_or(PathBuf::from_str("the command").unwrap()).to_str().unwrap())); + })?; + return Ok(Elevated::Yes); } + env::remove_var("ROOTASROLE_INSTALLER_NESTED"); if copy { //raise dac_override to copy files cap_effective(&mut state, Cap::DAC_OVERRIDE).context("Failed to raise DAC_OVERRIDE")?; @@ -101,7 +134,7 @@ pub fn install(profile: Profile, clean_after: bool, copy: bool) -> Result<(), an cap_clear(&mut state).context("Failed to drop effective DAC_OVERRIDE")?; } - cap_effective(&mut state, Cap::DAC_OVERRIDE).context("Failed to raise CHOWN")?; + cap_effective(&mut state, Cap::FOWNER).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")?; @@ -127,5 +160,5 @@ pub fn install(profile: Profile, clean_after: bool, copy: bool) -> Result<(), an .status() .context("Failed to clean the project")?; } - Ok(()) + Ok(Elevated::No) } diff --git a/xtask/src/install/mod.rs b/xtask/src/install/mod.rs index ceca234..8dd751f 100644 --- a/xtask/src/install/mod.rs +++ b/xtask/src/install/mod.rs @@ -245,13 +245,16 @@ pub(crate) fn install(opts: &InstallOptions) -> Result<(), anyhow::Error> { priv_bin: opts.build_opts.privbin.clone().or(detect_priv_bin()), })?; } - debug!("AAAAAAAAAAAAAAAaa {:?}", opts.build); if opts.build { debug!("Building sr and chsr"); build(&opts.build_opts)?; } - install::install(opts.build_opts.profile, opts.clean_after, true)?; - configure(Some(os)) + if install::install(&opts.priv_bin, opts.build_opts.profile, opts.clean_after, true)?.is_yes(){ + Ok(()) + } else { + configure(Some(os)) + } + } pub(crate) fn build(opts: &BuildOptions) -> Result<(), anyhow::Error> { diff --git a/xtask/src/postinst.rs b/xtask/src/postinst.rs index 62bd8ce..d87a3ff 100644 --- a/xtask/src/postinst.rs +++ b/xtask/src/postinst.rs @@ -13,7 +13,7 @@ fn main() { match action { Some(action) => match action.as_str() { "configure" => { - let res = install::install::install(install::Profile::Release, false, false); + let res = install::install::install(&None,install::Profile::Release, false, false); if let Err(e) = res { warn!("{:#}", e); std::process::exit(1);