From e4b8a85ccd33b06f8f90b5ae23e5cda8de4ab684 Mon Sep 17 00:00:00 2001 From: Xavier Moffett Date: Sat, 14 Sep 2024 21:05:40 -0400 Subject: [PATCH] `--disable-sandbox` for disabling libalpm sandbox - Ability to instruct libalpm to disable its own sandboxing mechanism by engaging `--disable-sandbox` at the command line. - Updated manuals to reflect new command-line options. - Disable sandbox user when disabling libalpm's sandbox --- pacwrap-core/src/exec.rs | 15 ++++++----- pacwrap-core/src/sync.rs | 39 +++++++++++++++++----------- pacwrap-core/src/sync/transaction.rs | 19 +++++++------- pacwrap/src/compose.rs | 1 + pacwrap/src/help/config.rs | 4 +++ pacwrap/src/help/manual.rs | 21 +++++++++++++++ pacwrap/src/remove.rs | 1 + pacwrap/src/sync.rs | 7 ++--- 8 files changed, 73 insertions(+), 34 deletions(-) diff --git a/pacwrap-core/src/exec.rs b/pacwrap-core/src/exec.rs index 34b9e04..5752b04 100644 --- a/pacwrap-core/src/exec.rs +++ b/pacwrap-core/src/exec.rs @@ -19,6 +19,7 @@ use std::{ fmt::{Display, Formatter, Result as FmtResult}, + io::ErrorKind as IOErrorKind, os::{fd::AsRawFd, unix::process::ExitStatusExt}, process::{Child, Command, ExitStatus, Stdio}, }; @@ -70,7 +71,7 @@ lazy_static! { #[derive(Debug, Clone)] pub enum ExecutionError { - InvalidPathVar(String, std::io::ErrorKind), + InvalidPathVar(String, IOErrorKind), ExecutableUnavailable(String), RuntimeArguments, UnabsolutePath(String), @@ -116,9 +117,9 @@ pub enum ExecutionType { #[rustfmt::skip] pub fn fakeroot_container(exec_type: ExecutionType, trap: Option, ins: &ContainerHandle, arguments: Vec<&str>) -> Result<()> { let term_control = TermControl::new(0); - let info_pipe = os_pipe::pipe().unwrap(); - let sec_pipe = os_pipe::pipe().unwrap(); - let sec_fd = provide_bpf_program(vec![Standard, Namespaces], &sec_pipe.0, sec_pipe.1).unwrap(); + let info_pipe = os_pipe::pipe().expect("bwrap pipe"); + let sec_pipe = os_pipe::pipe().expect("eBPF pipe"); + let sec_fd = provide_bpf_program(vec![Standard, Namespaces], &sec_pipe.0, sec_pipe.1).expect("eBPF program"); let info_fd = info_pipe.1.as_raw_fd(); let fd_mappings = vec![ FdMapping { @@ -198,10 +199,10 @@ pub fn fakeroot_container(exec_type: ExecutionType, trap: Option, ins: #[rustfmt::skip] pub fn transaction_agent(ins: &ContainerHandle, params: TransactionParameters, metadata: &TransactionMetadata) -> Result { - let params_pipe = os_pipe::pipe().unwrap(); + let params_pipe = os_pipe::pipe().expect("params pipe"); let params_fd = agent_params(¶ms_pipe.0, ¶ms_pipe.1, ¶ms, metadata)?; - let sec_pipe = os_pipe::pipe().unwrap(); - let sec_fd = provide_bpf_program(vec![Standard, Namespaces], &sec_pipe.0, sec_pipe.1).unwrap(); + let sec_pipe = os_pipe::pipe().expect("eBPF pipe"); + let sec_fd = provide_bpf_program(vec![Standard, Namespaces], &sec_pipe.0, sec_pipe.1).expect("eBPF program"); let fd_mappings = vec![ FdMapping { parent_fd: sec_fd, diff --git a/pacwrap-core/src/sync.rs b/pacwrap-core/src/sync.rs index fb3f6e1..85bba43 100644 --- a/pacwrap-core/src/sync.rs +++ b/pacwrap-core/src/sync.rs @@ -196,23 +196,27 @@ fn alpm_log_callback(level: LogLevel, msg: &str, counter: &mut usize) { } pub fn instantiate_alpm_agent(config: &Global, remotes: &AlpmConfigData, transflags: &TransactionFlags) -> Alpm { - let mut handle = Alpm::new("/mnt/fs", "/mnt/fs/var/lib/pacman/").unwrap(); + let mut handle = Alpm::new("/mnt/fs", "/mnt/fs/var/lib/pacman/").expect("Unable to acquire ALPM handle"); let hook_dirs = vec!["/mnt/fs/usr/share/libalpm/hooks/", "/mnt/fs/etc/pacman.d/hooks/"]; let debug = transflags.intersects(TransactionFlags::DEBUG); + let disable_sandbox = config.alpm().disable_sandbox() || transflags.intersects(TransactionFlags::NO_ALPM_SANDBOX); if debug { handle.set_log_cb(*UNIX_TIMESTAMP as usize, alpm_log_callback); } - handle.set_disable_sandbox(config.alpm().disable_sandbox()); - handle.set_logfile("/mnt/share/pacwrap.log").unwrap(); - handle.set_hookdirs(hook_dirs.iter()).unwrap(); - handle.set_cachedirs(vec!["/mnt/share/cache"].iter()).unwrap(); - handle.set_gpgdir("/mnt/share/gnupg").unwrap(); - handle.set_logfile("/mnt/share/pacwrap.log").unwrap(); - handle.set_check_space(false); - handle.set_disable_dl_timeout(config.alpm().download_timeout()); + if disable_sandbox { + handle.set_disable_sandbox(true); + handle.set_sandbox_user(None::<&str>).expect("set sandbox user"); + } + + handle.set_logfile("/mnt/share/pacwrap.log").expect("set logfile"); + handle.set_hookdirs(hook_dirs.iter()).expect("set hookdirs"); + handle.set_gpgdir("/mnt/share/gnupg").expect("set gpgdir"); + handle.set_cachedirs(vec!["/mnt/share/cache"].iter()).expect("set cachedirs"); handle.set_parallel_downloads(config.alpm().parallel_downloads()); + handle.set_disable_dl_timeout(config.alpm().download_timeout()); + handle.set_check_space(false); handle = register_remote(handle, remotes); handle } @@ -222,20 +226,25 @@ pub fn instantiate_alpm(inshandle: &ContainerHandle, transflags: &TransactionFla } fn alpm_handle(insvars: &ContainerVariables, remotes: &AlpmConfigData, transflags: &TransactionFlags, db_path: String) -> Alpm { - let mut handle = Alpm::new(insvars.root(), &db_path).unwrap(); + let mut handle = Alpm::new(insvars.root(), &db_path).expect("Unable to acquire ALPM handle"); let debug = transflags.intersects(TransactionFlags::DEBUG); + let disable_sandbox = CONFIG.alpm().disable_sandbox() || transflags.intersects(TransactionFlags::NO_ALPM_SANDBOX); if debug { handle.set_log_cb(*UNIX_TIMESTAMP as usize, alpm_log_callback); } - handle.set_disable_sandbox(CONFIG.alpm().disable_sandbox()); - handle.set_cachedirs(vec![format!("{}/pkg", *CACHE_DIR)].iter()).unwrap(); - handle.set_gpgdir(format!("{}/pacman/gnupg", *DATA_DIR)).unwrap(); - handle.set_logfile(format!("{}/pacwrap.log", *DATA_DIR)).unwrap(); + if disable_sandbox { + handle.set_disable_sandbox(true); + handle.set_sandbox_user(None::<&str>).expect("set sandbox user"); + } + + handle.set_logfile(format!("{}/pacwrap.log", *DATA_DIR)).expect("set logfile"); + handle.set_gpgdir(format!("{}/pacman/gnupg", *DATA_DIR)).expect("set gpgdir"); + handle.set_cachedirs(vec![format!("{}/pkg", *CACHE_DIR)].iter()).expect("set cachedirs"); handle.set_parallel_downloads(CONFIG.alpm().parallel_downloads()); - handle.set_check_space(CONFIG.alpm().check_space()); handle.set_disable_dl_timeout(CONFIG.alpm().download_timeout()); + handle.set_check_space(CONFIG.alpm().check_space()); handle = register_remote(handle, remotes); handle } diff --git a/pacwrap-core/src/sync/transaction.rs b/pacwrap-core/src/sync/transaction.rs index fa1d8f7..9b4d220 100644 --- a/pacwrap-core/src/sync/transaction.rs +++ b/pacwrap-core/src/sync/transaction.rs @@ -99,15 +99,16 @@ pub trait Transaction { bitflags! { pub struct TransactionFlags: u16 { const NONE = 0; - const TARGET_ONLY = 0b000000001; - const PREVIEW = 0b000000010; - const NO_CONFIRM = 0b000000100; - const FORCE_DATABASE = 0b000001000; - const DATABASE_ONLY = 0b000010000; - const CREATE = 0b000100000; - const FILESYSTEM_SYNC = 0b001000000; - const LAZY_LOAD_DB = 0b010000000; - const DEBUG = 0b100000000; + const TARGET_ONLY = 0b0000000001; + const PREVIEW = 0b0000000010; + const NO_CONFIRM = 0b0000000100; + const FORCE_DATABASE = 0b0000001000; + const DATABASE_ONLY = 0b0000010000; + const CREATE = 0b0000100000; + const FILESYSTEM_SYNC = 0b0001000000; + const LAZY_LOAD_DB = 0b0010000000; + const DEBUG = 0b0100000000; + const NO_ALPM_SANDBOX = 0b1000000000; } } diff --git a/pacwrap/src/compose.rs b/pacwrap/src/compose.rs index d4c4a11..46cf08a 100644 --- a/pacwrap/src/compose.rs +++ b/pacwrap/src/compose.rs @@ -184,6 +184,7 @@ fn engage_aggregator<'a>(args: &mut Arguments, lock: &'a Lock) -> Result<()> { Op::Long("from-config") => continue, Op::Long("debug") => flags = flags | TransactionFlags::DEBUG, Op::Long("noconfirm") => flags = flags | TransactionFlags::NO_CONFIRM, + Op::Long("disable-sandbox") => flags = flags | TransactionFlags::NO_ALPM_SANDBOX, Op::Long("reinitialize-all") => for instance in cache.registered() { if let Some(handle) = cache.get_instance_option(instance) { diff --git a/pacwrap/src/help/config.rs b/pacwrap/src/help/config.rs index ac3d9ed..8ab06f3 100644 --- a/pacwrap/src/help/config.rs +++ b/pacwrap/src/help/config.rs @@ -148,6 +148,10 @@ This document was generated by the {name} binary with version {} of the program. {tab}{tab}Instructs {bold}libalpm{reset_bold}(3) to parallelise the download queue with a maximum queue amount. Specify an {tab}{tab}{bold}integer{reset_bold} to declare a maximum value. +{sub_bold}disable_sandbox{reset_bold}: false +{tab}{tab}Instructs {bold}libalpm{reset_bold}(3) to disable the landlock and seccomp sandbox for downloads. Specify a +{tab}{tab}{bold}bool{reset_bold} to declare a maximum value. + {head}SEE ALSO{reset} {tab}{tab}{bold}pacman.conf{reset_bold}(5), {bold}libalpm{reset_bold}(3) diff --git a/pacwrap/src/help/manual.rs b/pacwrap/src/help/manual.rs index 3072107..d89a948 100644 --- a/pacwrap/src/help/manual.rs +++ b/pacwrap/src/help/manual.rs @@ -228,6 +228,13 @@ pub fn sync(buf: &mut String, layout: &HelpLayout) -> Result { {sub_bold}--noconfirm{reset_bold} {tab}{tab}Override confirmation prompts and confirm all operations. +{sub_bold}--disable-sandbox{reset_bold} +{tab}{tab}Instruct libalpm to disable its own sandbox, utilizing landlock and seccomp, in order to mitigate potential +{tab}{tab}issues with kernel compatibillity. + +{sub_bold}--debug{reset_bold} +{tab}{tab}Use this option when reporting bugs. + {sub_sect}EXAMPLES{reset_bold} {sub}`$ pacwrap init --base --target base` {tab}{tab}Synchronize remotes and create a base-type container named `base` with no additional packages. @@ -294,6 +301,13 @@ pub fn remove(buf: &mut String, layout: &HelpLayout) -> Result { {sub_bold}--noconfirm{reset_bold} {tab}{tab}Override confirmation prompts and confirm all operations. +{sub_bold}--disable-sandbox{reset_bold} +{tab}{tab}Instruct libalpm to disable its own sandbox, utilizing landlock and seccomp, in order to mitigate potential +{tab}{tab}issues with kernel compatibillity. + +{sub_bold}--debug{reset_bold} +{tab}{tab}Use this option when reporting bugs. + {sub_sect}EXAMPLES{reset_bold} {sub}`$ pacwrap -Rt firefox firefox` {tab}{tab}Remove the target package firefox from target container firefox. @@ -351,6 +365,13 @@ pub fn compose(buf: &mut String, layout: &HelpLayout) -> Result { {sub_bold}--noconfirm{reset_bold} {tab}{tab}Override confirmation prompts and confirm all operations. +{sub_bold}--disable-sandbox{reset_bold} +{tab}{tab}Instruct libalpm to disable its own sandbox, utilizing landlock and seccomp, in order to mitigate potential +{tab}{tab}issues with kernel compatibillity. + +{sub_bold}--debug{reset_bold} +{tab}{tab}Use this option when reporting bugs. + {sub_sect}EXAMPLES{reset_bold} {sub}`$ pacwrap compose -rt element element.yml` {tab}{tab}Reinitialize an existing container named element with its configuration derived diff --git a/pacwrap/src/remove.rs b/pacwrap/src/remove.rs index caaad4a..5bf62ad 100644 --- a/pacwrap/src/remove.rs +++ b/pacwrap/src/remove.rs @@ -98,6 +98,7 @@ fn engage_aggregator<'a>( Op::Long("dbonly") => flags = flags | TransactionFlags::DATABASE_ONLY, Op::Long("noconfirm") => flags = flags | TransactionFlags::NO_CONFIRM, Op::Long("force-foreign") => flags = flags | TransactionFlags::FORCE_DATABASE, + Op::Long("disable-sandbox") => flags = flags | TransactionFlags::NO_ALPM_SANDBOX, Op::Short('p') | Op::Long("preview") => flags = flags | TransactionFlags::PREVIEW, Op::Short('f') | Op::Long("filesystem") => flags = flags | TransactionFlags::FILESYSTEM_SYNC, Op::Short('t') | Op::Long("target") => match args.next() { diff --git a/pacwrap/src/sync.rs b/pacwrap/src/sync.rs index 57708ae..6620b5f 100644 --- a/pacwrap/src/sync.rs +++ b/pacwrap/src/sync.rs @@ -167,10 +167,11 @@ fn engage_aggregator<'a>( while let Some(arg) = args.next() { match arg { Op::Short('y') | Op::Short('u') | Op::Long("refresh") | Op::Long("upgrade") => continue, - Op::Long("dbonly") => flags = flags | TransactionFlags::DATABASE_ONLY, - Op::Long("force-foreign") => flags = flags | TransactionFlags::FORCE_DATABASE, - Op::Long("noconfirm") => flags = flags | TransactionFlags::NO_CONFIRM, Op::Long("debug") => flags = flags | TransactionFlags::DEBUG, + Op::Long("dbonly") => flags = flags | TransactionFlags::DATABASE_ONLY, + Op::Long("noconfirm") => flags = flags | TransactionFlags::NO_CONFIRM, + Op::Long("force-foreign") => flags = flags | TransactionFlags::FORCE_DATABASE, + Op::Long("disable-sandbox") => flags = flags | TransactionFlags::NO_ALPM_SANDBOX, Op::Short('l') | Op::Long("lazy-load") => flags = flags | TransactionFlags::LAZY_LOAD_DB, Op::Short('o') | Op::Long("target-only") => flags = flags | TransactionFlags::TARGET_ONLY, Op::Short('f') | Op::Long("filesystem") => flags = flags | TransactionFlags::FILESYSTEM_SYNC,