Compare commits

..

1 commit

Author SHA1 Message Date
atagen
513ecde2da expand permissions system, allow escapes 2025-11-03 15:16:44 +11:00

View file

@ -1,6 +1,6 @@
mod parse;
mod types;
use std::{collections::VecDeque, path::PathBuf, str::FromStr};
use std::collections::VecDeque;
use anyhow::{Context, Result, anyhow};
use elb_dl::{DependencyTree, DynamicLoader, glibc};
@ -12,7 +12,6 @@ use landlock::{
fn main() -> Result<()> {
let opts = parse::parse_args()?;
if opts.exec.is_empty() {
// print help
eprintln!(
"
yoke -- simple command sandboxer
@ -69,26 +68,17 @@ examples
);
std::process::exit(1);
}
// set up scope of our intial ruleset
let mut preempt = Ruleset::default();
// TODO FIXME set up for lesser ABI versions
preempt = preempt.set_compatibility(landlock::CompatLevel::HardRequirement);
// disallow signals to other processes
if !opts.signals {
preempt = preempt.scope(Scope::Signal).context("scoping signals")?;
}
// disallow connections to unix domain sockets
if !opts.sockets {
preempt = preempt
.scope(Scope::AbstractUnixSocket)
.context("scoping sockets")?;
}
// lock down fs access
preempt = if !opts.unsandbox.fs {
preempt
.handle_access(AccessFs::from_all(ABI::V6))
@ -96,8 +86,6 @@ examples
} else {
preempt
};
// lock down tcp access
preempt = if !opts.unsandbox.tcp {
preempt
.handle_access(AccessNet::BindTcp)
@ -107,11 +95,7 @@ examples
} else {
preempt
};
// create ruleset and begin inserting rules
let mut ruleset = preempt.create().context("creating ruleset")?;
// allow each path specified, grouped by access specifier
for (perms, paths) in opts.fs {
let mut access = BitFlags::empty();
if perms.read {
@ -147,8 +131,6 @@ examples
.add_rules(path_beneath_rules(paths, access))
.context("adding fs rule")?;
}
// allow each tcp action specified, grouped by access specifier
for (dir, ports) in opts.tcp {
let mut access = BitFlags::empty();
if dir.inbound {
@ -163,24 +145,14 @@ examples
.context("adding tcp rule")?;
}
}
// locate our executable
let fullpath = which::which(&opts.exec[0]).context("finding executable")?;
// add executeable as read+execute
if !opts.unsandbox.fs {
ruleset = ruleset.add_rules(path_beneath_rules(
std::slice::from_ref(&fullpath),
AccessFs::from_read(ABI::V6),
))?;
}
// if requested, trace dependencies and add as read+execute
ruleset = ruleset.add_rules(path_beneath_rules(
std::slice::from_ref(&fullpath),
AccessFs::from_read(ABI::V6),
))?;
if opts.ldd {
let loader = DynamicLoader::options()
.search_dirs(glibc::get_hard_coded_search_dirs(None)?)
.search_dirs(glibc::get_search_dirs(
PathBuf::from_str("/").context("finding root")?,
)?)
.new_loader();
let mut tree = DependencyTree::new();
let mut queue = VecDeque::new();
@ -199,17 +171,11 @@ examples
))
.context("tracking dependencies")?;
}
// enforce the ruleset on ourselves
ruleset.restrict_self().context("enforcing ruleset")?;
// construct a command for the target program
let mut cmd = exec::Command::new(fullpath);
if opts.exec.len() > 1 {
cmd.args(&opts.exec[1..]);
}
// clear env unless retention is requested
if !opts.retain_env {
for (k, _) in std::env::vars() {
unsafe {
@ -217,8 +183,6 @@ examples
}
}
}
// add specified env vars
if !opts.env.is_empty() {
for (k, v) in opts.env {
unsafe {
@ -226,8 +190,6 @@ examples
}
}
}
// execute and hopefully never return
let err = cmd.exec();
eprintln!("failed to run process: {}", err);
Ok(())