#![deny(
missing_copy_implementations,
missing_debug_implementations,
missing_docs,
rust_2018_idioms,
unstable_features
)]
#![warn(clippy::pedantic)]
#![allow(clippy::must_use_candidate, clippy::needless_doctest_main)]
#[cfg(not(any(target_os = "linux", target_os = "android")))]
compile_error!("pentacle only works on linux or android");
mod syscall;
use crate::syscall::{fcntl_add_seals, fcntl_get_seals, memfd_create};
use libc::{F_SEAL_GROW, F_SEAL_SEAL, F_SEAL_SHRINK, F_SEAL_WRITE, MFD_ALLOW_SEALING, MFD_CLOEXEC};
use std::ffi::CStr;
use std::fmt::{self, Debug};
use std::fs::File;
use std::io::{self, Read, Result, Write};
use std::ops::{Deref, DerefMut};
use std::os::unix::io::AsRawFd;
use std::os::unix::process::CommandExt;
use std::process::Command;
const MEMFD_SEALS: libc::c_int = F_SEAL_SEAL | F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE;
pub fn ensure_sealed() -> Result<()> {
let mut file = File::open("/proc/self/exe")?;
if is_sealed_inner(&file) {
Ok(())
} else {
let mut command = SealedCommand::new(&mut file)?;
let mut args = std::env::args_os().fuse();
if let Some(arg0) = args.next() {
command.arg0(arg0);
}
command.args(args);
Err(command.exec())
}
}
pub fn is_sealed() -> bool {
File::open("/proc/self/exe")
.map(|f| is_sealed_inner(&f))
.unwrap_or(false)
}
fn is_sealed_inner(file: &File) -> bool {
fcntl_get_seals(file) == MEMFD_SEALS
}
pub struct SealedCommand {
inner: Command,
_memfd: File,
}
impl SealedCommand {
pub fn new<R: Read>(program: &mut R) -> Result<Self> {
let mut memfd_flags = MFD_ALLOW_SEALING;
let mut buf = [0; 8192];
let n = program.read(&mut buf)?;
if !(n >= 2 && &buf[..2] == b"#!") {
memfd_flags |= MFD_CLOEXEC;
}
let memfd_name = unsafe { CStr::from_bytes_with_nul_unchecked(b"pentacle_sealed\0") };
let mut memfd = memfd_create(memfd_name, memfd_flags)?;
memfd.write_all(&buf[..n])?;
io::copy(program, &mut memfd)?;
fcntl_add_seals(&memfd, MEMFD_SEALS)?;
Ok(Self {
inner: Command::new(format!("/proc/self/fd/{}", memfd.as_raw_fd())),
_memfd: memfd,
})
}
}
impl Debug for SealedCommand {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.inner.fmt(f)
}
}
impl Deref for SealedCommand {
type Target = Command;
fn deref(&self) -> &Command {
&self.inner
}
}
impl DerefMut for SealedCommand {
fn deref_mut(&mut self) -> &mut Command {
&mut self.inner
}
}