Skip to content

Commit

Permalink
install: Fall back to setenforce 0 if needed
Browse files Browse the repository at this point in the history
I've opened #83
to track a better fix.

Signed-off-by: Colin Walters <walters@verbum.org>
  • Loading branch information
cgwalters committed Mar 21, 2023
1 parent a581429 commit 600c64d
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 9 deletions.
12 changes: 8 additions & 4 deletions lib/src/install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<crate::lsm::SetEnforceGuard>,
pub(crate) config_opts: InstallConfigOpts,
pub(crate) target_opts: InstallTargetOpts,
pub(crate) install_config: config::InstallConfiguration,
Expand Down Expand Up @@ -609,9 +611,10 @@ impl RootSetup {
pub(crate) fn reexecute_self_for_selinux_if_needed(
srcdata: &SourceInfo,
override_disable_selinux: bool,
) -> Result<bool> {
) -> Result<(bool, Option<crate::lsm::SetEnforceGuard>)> {
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}");
Expand All @@ -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")
Expand All @@ -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;
Expand Down Expand Up @@ -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()?;
Expand All @@ -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,
Expand Down
46 changes: 41 additions & 5 deletions lib/src/lsm.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -31,6 +31,9 @@ pub(crate) fn selinux_enabled() -> Result<bool> {
#[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() {
Expand Down Expand Up @@ -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<Option<SetEnforceGuard>> {
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")]
Expand All @@ -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(())
}
Expand All @@ -111,6 +146,7 @@ fn selinux_label_for_path(target: &str) -> Result<String> {
#[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"))
Expand Down

0 comments on commit 600c64d

Please sign in to comment.