#![forbid(unsafe_code)]
use libseccomp::ScmpNotifResp;
use nix::{errno::Errno, NixPath};
use crate::{
cookie::safe_symlinkat,
kernel::sandbox_path,
lookup::FsFlags,
req::{RemoteProcess, SysArg, UNotifyEventRequest},
sandbox::Capability,
};
pub(crate) fn sys_symlink(request: UNotifyEventRequest) -> ScmpNotifResp {
syscall_handler!(request, |request: UNotifyEventRequest| {
let arg = SysArg {
path: Some(1),
fsflags: FsFlags::NO_FOLLOW_LAST | FsFlags::MISS_LAST | FsFlags::DOTLAST_EEXIST,
..Default::default()
};
syscall_symlink_handler(request, arg)
})
}
pub(crate) fn sys_symlinkat(request: UNotifyEventRequest) -> ScmpNotifResp {
syscall_handler!(request, |request: UNotifyEventRequest| {
let arg = SysArg {
dirfd: Some(1),
path: Some(2),
fsflags: FsFlags::NO_FOLLOW_LAST | FsFlags::MISS_LAST | FsFlags::DOTLAST_EEXIST,
..Default::default()
};
syscall_symlink_handler(request, arg)
})
}
fn syscall_symlink_handler(
request: UNotifyEventRequest,
arg: SysArg,
) -> Result<ScmpNotifResp, Errno> {
let req = request.scmpreq;
let process = RemoteProcess::new(request.scmpreq.pid());
let target = process.remote_path(req.data.arch, req.data.args[0], Some(&request))?;
if target.is_empty() {
return Err(Errno::ENOENT);
}
let sandbox = request.get_sandbox();
let (path, _, _) = request.read_path(&sandbox, arg)?;
let name = if arg.dirfd.is_some() {
"symlinkat"
} else {
"symlink"
};
sandbox_path(
Some(&request),
&sandbox,
request.scmpreq.pid(), path.abs(),
Capability::CAP_SYMLINK,
name,
)?;
drop(sandbox);
if path.typ.is_some() {
return Err(Errno::EEXIST);
}
request.cache.add_sys_block(req, false)?;
let result = safe_symlinkat(&target, path.dir(), path.base());
request.cache.del_sys_block(req.id)?;
result.map(|_| request.return_syscall(0))
}