Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix install_t again #284

Merged
merged 3 commits into from
Jan 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion lib/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,9 @@ pub(crate) async fn prepare_for_write() -> Result<()> {
}
ensure_self_unshared_mount_namespace().await?;
if crate::lsm::selinux_enabled()? {
crate::lsm::selinux_ensure_install()?;
if !crate::lsm::selinux_ensure_install()? {
tracing::warn!("Do not have install_t capabilities");
}
}
Ok(())
}
Expand Down
21 changes: 17 additions & 4 deletions lib/src/install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -873,9 +873,22 @@ fn ensure_var() -> Result<()> {
/// We can't bind mount though - we need to symlink it so that each calling process
/// will traverse the link.
#[context("Linking tmp mounts to host")]
pub(crate) fn propagate_tmp_mounts_to_host() -> Result<()> {
// Point our /tmp and /var/tmp at the host, via the /proc/1/root magic link
for path in ["/tmp", "/var/tmp"].map(Utf8Path::new) {
pub(crate) fn setup_tmp_mounts() -> Result<()> {
let st = rustix::fs::statfs("/tmp")?;
if st.f_type == libc::TMPFS_MAGIC {
tracing::trace!("Already have tmpfs /tmp")
} else {
// Note we explicitly also don't want a "nosuid" tmp, because that
// suppresses our install_t transition
Task::new_and_run(
"Mounting tmpfs /tmp",
"mount",
["tmpfs", "-t", "tmpfs", "/tmp"],
)?;
}

// Point our /var/tmp at the host, via the /proc/1/root magic link
for path in ["/var/tmp"].map(Utf8Path::new) {
if path.try_exists()? {
let st = rustix::fs::statfs(path.as_std_path()).context(path)?;
if st.f_type != libc::OVERLAYFS_SUPER_MAGIC {
Expand Down Expand Up @@ -999,7 +1012,7 @@ async fn prepare_install(
}

ensure_var()?;
propagate_tmp_mounts_to_host()?;
setup_tmp_mounts()?;

// Even though we require running in a container, the mounts we create should be specific
// to this process, so let's enter a private mountns to avoid leaking them.
Expand Down
35 changes: 20 additions & 15 deletions lib/src/lsm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,16 +33,18 @@ fn get_current_security_context() -> Result<String> {
std::fs::read_to_string(SELF_CURRENT).with_context(|| format!("Reading {SELF_CURRENT}"))
}

/// Determine if a security context is the "install_t" type which can
/// write arbitrary labels.
fn context_is_install_t(context: &str) -> bool {
// TODO: we shouldn't actually hardcode this...it's just ugly though
// to figure out whether we really can gain CAP_MAC_ADMIN.
context.contains(":install_t:")
#[context("Testing install_t")]
fn test_install_t() -> Result<bool> {
let tmpf = tempfile::NamedTempFile::new()?;
let st = Command::new("chcon")
.args(["-t", "invalid_bootcinstall_testlabel_t"])
.arg(tmpf.path())
.status()?;
Ok(st.success())
}

#[context("Ensuring selinux install_t type")]
pub(crate) fn selinux_ensure_install() -> Result<()> {
pub(crate) fn selinux_ensure_install() -> Result<bool> {
let guardenv = "_bootc_selinuxfs_mounted";
let current = get_current_security_context()?;
tracing::debug!("Current security context is {current}");
Expand All @@ -54,9 +56,13 @@ pub(crate) fn selinux_ensure_install() -> Result<()> {
} else {
tracing::debug!("Assuming we now have a privileged (e.g. install_t) label");
}
return Ok(());
return test_install_t();
}
if test_install_t()? {
tracing::debug!("We have install_t");
return Ok(true);
}
tracing::debug!("Copying self to temporary file for re-exec");
tracing::debug!("Lacking install_t capabilities; copying self to temporary file for re-exec");
// OK now, we always copy our binary to a tempfile, set its security context
// to match that of /usr/bin/ostree, and then re-exec. This is really a gross
// hack; we can't always rely on https://github.com/fedora-selinux/selinux-policy/pull/1500/commits/67eb283c46d35a722636d749e5b339615fe5e7f5
Expand Down Expand Up @@ -98,18 +104,17 @@ impl Drop for SetEnforceGuard {
#[cfg(feature = "install")]
pub(crate) fn selinux_ensure_install_or_setenforce() -> Result<Option<SetEnforceGuard>> {
// If the process already has install_t, exit early
let current = get_current_security_context()?;
if context_is_install_t(&current) {
// Note that this may re-exec the entire process
if selinux_ensure_install()? {
return Ok(None);
}
// Note that this will re-exec the entire process
selinux_ensure_install()?;
let g = if !context_is_install_t(&current) {
let g = if std::env::var_os("BOOTC_SETENFORCE0_FALLBACK").is_some() {
tracing::warn!("Failed to enter install_t; temporarily setting permissive mode");
selinux_set_permissive(true)?;
Some(SetEnforceGuard)
} else {
None
let current = get_current_security_context()?;
anyhow::bail!("Failed to enter install_t (running as {current}) - use BOOTC_SETENFORCE0_FALLBACK=1 to override");
};
Ok(g)
}
Expand Down
Loading