use libseccomp::ScmpArch;
use nix::{errno::Errno, unistd::Pid};
use crate::{
cookie::safe_kill,
error,
fd::PROC_FILE,
kernel::sandbox_path,
lookup::{CanonicalPath, FileInfo, FsFlags},
path::XPathBuf,
ptrace::{ptrace_get_error, ptrace_syscall_info, ptrace_syscall_info_seccomp},
req::{RemoteProcess, SysArg},
sandbox::{Capability, SandboxGuard},
};
pub(crate) fn sysenter_chdir(
pid: Pid,
sandbox: &SandboxGuard,
arch: ScmpArch,
data: ptrace_syscall_info_seccomp,
) -> Result<FileInfo, 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() {
if !path.abs().is_root() {
return Err(Errno::ENOENT);
}
return FileInfo::from_fd(&path.dir);
}
sandbox_chdir(sandbox, pid, &path, "chdir")?;
FileInfo::from_fd(&path.dir)
}
pub(crate) fn sysenter_fchdir(
pid: Pid,
sandbox: &SandboxGuard,
arch: ScmpArch,
data: ptrace_syscall_info_seccomp,
) -> Result<FileInfo, 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() {
if !path.abs().is_root() {
return Err(Errno::ENOENT);
}
return FileInfo::from_fd(&path.dir);
}
sandbox_chdir(sandbox, pid, &path, "fchdir")?;
FileInfo::from_fd(&path.dir)
}
pub(crate) fn sysexit_chdir(
pid: Pid,
info: ptrace_syscall_info,
file_info: FileInfo,
) -> Result<(), Errno> {
match ptrace_get_error(pid, info.arch) {
Ok(None) => {
}
Ok(Some(_)) => {
return Ok(());
}
Err(Errno::ESRCH) => return Err(Errno::ESRCH),
Err(_) => {
let _ = safe_kill(pid, libc::SIGKILL);
return Err(Errno::ESRCH);
}
};
let cwd_sym = XPathBuf::from_cwd(pid)?;
let file_info_new = FileInfo::from_path(PROC_FILE(), &cwd_sym, true)?;
if file_info_new != file_info {
error!("ctx": "chdir", "op": "dir_mismatch",
"msg": "dir mismatch detected: assume TOCTTOU!",
"pid": pid.as_raw(),
"mnt": [file_info.mnt, file_info_new.mnt],
"ino": [file_info.ino, file_info_new.ino]);
let _ = safe_kill(pid, libc::SIGKILL);
Err(Errno::ESRCH)
} else {
Ok(())
}
}
fn sandbox_chdir(
sandbox: &SandboxGuard,
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(())
}