From 600c64d1645f17a1602d10e5a96bafcabf5be21b Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Tue, 21 Mar 2023 08:32:34 -0400 Subject: [PATCH] install: Fall back to `setenforce 0` if needed I've opened https://github.com/containers/bootc/issues/83 to track a better fix. Signed-off-by: Colin Walters --- lib/src/install.rs | 12 ++++++++---- lib/src/lsm.rs | 46 +++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 49 insertions(+), 9 deletions(-) diff --git a/lib/src/install.rs b/lib/src/install.rs index ba8bb46a..220d223b 100644 --- a/lib/src/install.rs +++ b/lib/src/install.rs @@ -178,6 +178,8 @@ pub(crate) struct State { pub(crate) source: SourceInfo, /// Force SELinux off in target system pub(crate) override_disable_selinux: bool, + #[allow(dead_code)] + pub(crate) setenforce_guard: Option, pub(crate) config_opts: InstallConfigOpts, pub(crate) target_opts: InstallTargetOpts, pub(crate) install_config: config::InstallConfiguration, @@ -609,9 +611,10 @@ impl RootSetup { pub(crate) fn reexecute_self_for_selinux_if_needed( srcdata: &SourceInfo, override_disable_selinux: bool, -) -> Result { +) -> Result<(bool, Option)> { let mut ret_did_override = false; // If the target state has SELinux enabled, we need to check the host state. + let mut g = None; if srcdata.selinux { let host_selinux = crate::lsm::selinux_enabled()?; tracing::debug!("Target has SELinux, host={host_selinux}"); @@ -623,7 +626,7 @@ pub(crate) fn reexecute_self_for_selinux_if_needed( // so let's just fall through to that. crate::lsm::container_setup_selinux()?; // This will re-execute the current process (once). - crate::lsm::selinux_ensure_install()?; + g = crate::lsm::selinux_ensure_install_or_setenforce()?; } else if override_disable_selinux { ret_did_override = true; println!("notice: Target has SELinux enabled, overriding to disable") @@ -635,7 +638,7 @@ pub(crate) fn reexecute_self_for_selinux_if_needed( } else { tracing::debug!("Target does not enable SELinux"); } - Ok(ret_did_override) + Ok((ret_did_override, g)) } /// Trim, flush outstanding writes, and freeze/thaw the target mounted filesystem; @@ -744,7 +747,7 @@ async fn prepare_install( } // Now, deal with SELinux state. - let override_disable_selinux = + let (override_disable_selinux, setenforce_guard) = reexecute_self_for_selinux_if_needed(&source, config_opts.disable_selinux)?; let install_config = config::load_config()?; @@ -754,6 +757,7 @@ async fn prepare_install( // combines our command line options along with some bind mounts from the host. let state = Arc::new(State { override_disable_selinux, + setenforce_guard, source, config_opts, target_opts, diff --git a/lib/src/lsm.rs b/lib/src/lsm.rs index f37d1824..a37f8f71 100644 --- a/lib/src/lsm.rs +++ b/lib/src/lsm.rs @@ -1,4 +1,4 @@ -use std::fs::File; +#[cfg(feature = "install")] use std::io::Write; use std::os::unix::process::CommandExt; use std::path::Path; @@ -31,6 +31,9 @@ pub(crate) fn selinux_enabled() -> Result { #[context("Ensuring selinux install_t type")] pub(crate) fn selinux_ensure_install() -> Result<()> { let guardenv = "_bootc_selinuxfs_mounted"; + let current = std::fs::read_to_string("/proc/self/attr/current") + .context("Reading /proc/self/attr/current")?; + tracing::debug!("Current security context is {current}"); if let Some(p) = std::env::var_os(guardenv) { let p = Path::new(&p); if p.exists() { @@ -59,10 +62,42 @@ pub(crate) fn selinux_ensure_install() -> Result<()> { let mut cmd = Command::new(&tmpf); cmd.env(guardenv, tmpf); cmd.args(std::env::args_os().skip(1)); - tracing::debug!("Re-executing"); + tracing::debug!("Re-executing {cmd:?}"); Err(anyhow::Error::msg(cmd.exec()).context("execve")) } +/// A type which will reset SELinux back to enforcing mode when dropped. +/// This is a workaround for the deep difficulties in trying to reliably +/// gain the `mac_admin` permission (install_t). +#[cfg(feature = "install")] +#[must_use] +pub(crate) struct SetEnforceGuard; + +#[cfg(feature = "install")] +impl Drop for SetEnforceGuard { + fn drop(&mut self) { + let _ = selinux_set_permissive(false); + } +} + +/// Try to enter the install_t domain, but if we can't do that, then +/// just setenforce 0. +#[context("Ensuring selinux install_t type")] +#[cfg(feature = "install")] +pub(crate) fn selinux_ensure_install_or_setenforce() -> Result> { + selinux_ensure_install()?; + let current = std::fs::read_to_string("/proc/self/attr/current") + .context("Reading /proc/self/attr/current")?; + let g = if !current.contains("install_t") { + tracing::warn!("Failed to enter install_t; temporarily setting permissive mode"); + selinux_set_permissive(true)?; + Some(SetEnforceGuard) + } else { + None + }; + Ok(g) +} + /// Ensure that /sys/fs/selinux is mounted, and ensure we're running /// as install_t. #[context("Ensuring selinux mount")] @@ -84,13 +119,13 @@ pub(crate) fn container_setup_selinux() -> Result<()> { #[context("Setting SELinux permissive mode")] #[allow(dead_code)] #[cfg(feature = "install")] -pub(crate) fn selinux_set_permissive() -> Result<()> { +pub(crate) fn selinux_set_permissive(permissive: bool) -> Result<()> { let enforce_path = &Utf8Path::new(SELINUXFS).join("enforce"); if !enforce_path.exists() { return Ok(()); } - let mut f = File::open(enforce_path)?; - f.write_all(b"0")?; + let mut f = std::fs::File::options().write(true).open(enforce_path)?; + f.write_all(if permissive { b"0" } else { b"1" })?; tracing::debug!("Set SELinux permissive mode"); Ok(()) } @@ -111,6 +146,7 @@ fn selinux_label_for_path(target: &str) -> Result { #[context("Labeling {as_path}")] pub(crate) fn lsm_label(target: &Utf8Path, as_path: &Utf8Path, recurse: bool) -> Result<()> { let label = selinux_label_for_path(as_path.as_str())?; + tracing::debug!("Label for {target} is {label}"); let st = Command::new("chcon") .arg("-h") .args(recurse.then_some("-R"))