use libseccomp::ScmpArch;
use nix::{
errno::Errno,
sys::signal::{kill, Signal},
unistd::Pid,
};
use crate::{
error,
kernel::sandbox_path,
lookup::{CanonicalPath, FileMapEntry, FileType, FsFlags},
magic::ProcMagic,
ptrace::{ptrace_get_error, ptrace_syscall_info, ptrace_syscall_info_seccomp},
req::{RemoteProcess, SysArg},
sandbox::{Capability, Sandbox, SandboxGuard},
};
pub(crate) fn sysenter_chdir(
pid: Pid,
sandbox: &SandboxGuard,
arch: ScmpArch,
data: ptrace_syscall_info_seccomp,
) -> Result<(), Errno> {
let mut arg = SysArg {
path: Some(0),
..Default::default()
};
if sandbox.flags.deny_dotdot() {
arg.fsflags.insert(FsFlags::NO_RESOLVE_DOTDOT);
}
let process = RemoteProcess::new(pid);
let (path, _, _) = process.read_path(sandbox, arch, data.args, arg, None)?;
if sandbox.is_chroot() {
return if path.abs().is_root() {
Ok(())
} else {
Err(Errno::ENOENT)
};
}
sandbox_chdir(sandbox, pid, &path, "chdir")?;
Ok(())
}
pub(crate) fn sysenter_fchdir(
pid: Pid,
sandbox: &SandboxGuard,
arch: ScmpArch,
data: ptrace_syscall_info_seccomp,
) -> Result<(), Errno> {
let arg = SysArg {
dirfd: Some(0),
..Default::default()
};
let process = RemoteProcess::new(pid);
let (path, _, _) = process.read_path(sandbox, arch, data.args, arg, None )?;
if sandbox.is_chroot() {
return if path.abs().is_root() {
Ok(())
} else {
Err(Errno::ENOENT)
};
}
sandbox_chdir(sandbox, pid, &path, "fchdir")?;
Ok(())
}
pub(crate) fn sysexit_chdir(
pid: Pid,
info: ptrace_syscall_info,
sandbox: &Sandbox,
) -> Result<(), Errno> {
match ptrace_get_error(pid, info.arch) {
Ok(None) => {
}
Ok(Some(_)) => {
return Ok(());
}
Err(Errno::ESRCH) => return Err(Errno::ESRCH),
Err(_) => {
let _ = kill(pid, Some(Signal::SIGKILL));
return Err(Errno::ESRCH);
}
};
if let Err(errno) = sandbox_chdir_atexit(sandbox, pid) {
error!("ctx": "chdir", "op": "dir_mismatch",
"msg": "dir mismatch detected: assume TOCTTOU!",
"pid": pid.as_raw(), "err": errno as i32);
let _ = kill(pid, Some(Signal::SIGKILL));
Err(Errno::ESRCH)
} else {
Ok(())
}
}
fn sandbox_chdir_atexit(sandbox: &Sandbox, pid: Pid) -> Result<(), Errno> {
let magic = ProcMagic::Cwd { pid };
let (mut entry, _) = FileMapEntry::from_magic_link(magic, true, None, Some(sandbox))?;
let path = CanonicalPath {
abs: entry.target.take().ok_or(Errno::ENOENT)??,
base_offset: 0,
dir: Some(entry.fd),
typ: Some(FileType::Dir),
};
sandbox_chdir(sandbox, pid, &path, "chdir")
}
fn sandbox_chdir(
sandbox: &Sandbox,
pid: Pid,
path: &CanonicalPath,
sysname: &str,
) -> Result<(), Errno> {
let mut caps = Capability::empty();
if let Some(typ) = path.typ.as_ref() {
if typ.is_dir() {
caps.insert(Capability::CAP_CHDIR);
}
} else {
return Err(Errno::ENOENT);
}
sandbox_path(None, sandbox, pid, path.abs(), caps, sysname)?;
if !caps.contains(Capability::CAP_CHDIR) {
return Err(Errno::ENOTDIR);
}
Ok(())
}