diff --git a/programs/contain/src/chroot.rs b/programs/contain/src/chroot.rs new file mode 100644 index 0000000..edce9ea --- /dev/null +++ b/programs/contain/src/chroot.rs @@ -0,0 +1,172 @@ +use syscall; +use syscall::data::{Stat, StatVfs}; +use syscall::error::{Error, EBADF, EINVAL, EPERM, Result}; +use syscall::scheme::Scheme; + +use std::str; +use std::path::PathBuf; + +pub struct ChrootScheme { + root: PathBuf +} + +impl ChrootScheme { + pub fn new(root: PathBuf) -> ChrootScheme { + ChrootScheme { + root: root + } + } + + fn translate(&self, path: &[u8]) -> Result { + let path = str::from_utf8(path).or(Err(Error::new(EINVAL)))?; + let mut translated = self.root.clone(); + translated.push(path.trim_left_matches('/')); + if translated.starts_with(&self.root) { + translated.into_os_string().into_string().or(Err(Error::new(EINVAL))) + } else { + println!("escaped chroot"); + Err(Error::new(EPERM)) + } + } +} + +impl Scheme for ChrootScheme { + fn open(&self, path: &[u8], flags: usize, uid: u32, gid: u32) -> Result { + if uid != 0 { + syscall::setreuid(0, uid as usize)?; + } + if gid != 0 { + syscall::setregid(0, gid as usize)?; + } + let res = syscall::open(&self.translate(path)?, flags); + if uid != 0 { + syscall::setreuid(0, 0).unwrap(); + } + if gid != 0 { + syscall::setregid(0, 0).unwrap(); + } + res + } + + fn chmod(&self, path: &[u8], mode: u16, uid: u32, gid: u32) -> Result { + if uid != 0 { + syscall::setreuid(0, uid as usize)?; + } + if gid != 0 { + syscall::setregid(0, gid as usize)?; + } + let res = syscall::chmod(&self.translate(path)?, mode as usize); + if uid != 0 { + syscall::setreuid(0, 0).unwrap(); + } + if gid != 0 { + syscall::setregid(0, 0).unwrap(); + } + res + } + + fn rmdir(&self, path: &[u8], uid: u32, gid: u32) -> Result { + if uid != 0 { + syscall::setreuid(0, uid as usize)?; + } + if gid != 0 { + syscall::setregid(0, gid as usize)?; + } + let res = syscall::rmdir(&self.translate(path)?); + if uid != 0 { + syscall::setreuid(0, 0).unwrap(); + } + if gid != 0 { + syscall::setregid(0, 0).unwrap(); + } + res + } + + fn unlink(&self, path: &[u8], uid: u32, gid: u32) -> Result { + if uid != 0 { + syscall::setreuid(0, uid as usize)?; + } + if gid != 0 { + syscall::setregid(0, gid as usize)?; + } + let res = syscall::unlink(&self.translate(path)?); + if uid != 0 { + syscall::setreuid(0, 0).unwrap(); + } + if gid != 0 { + syscall::setregid(0, 0).unwrap(); + } + res + } + + /* Resource operations */ + fn dup(&self, old_id: usize, buf: &[u8]) -> Result { + syscall::dup(old_id, buf) + } + + fn read(&self, id: usize, buf: &mut [u8]) -> Result { + syscall::read(id, buf) + } + + fn write(&self, id: usize, buf: &[u8]) -> Result { + syscall::write(id, buf) + } + + fn seek(&self, id: usize, pos: usize, whence: usize) -> Result { + syscall::lseek(id, pos as isize, whence) + } + + fn fcntl(&self, id: usize, cmd: usize, arg: usize) -> Result { + syscall::fcntl(id, cmd, arg) + } + + fn fevent(&self, _id: usize, _flags: usize) -> Result { + //TODO + Err(Error::new(EBADF)) + } + + fn fmap(&self, _id: usize, _offset: usize, _size: usize) -> Result { + //TODO + Err(Error::new(EBADF)) + } + + fn fpath(&self, id: usize, buf: &mut [u8]) -> Result { + let count = syscall::fpath(id, buf)?; + + let translated = { + let path = str::from_utf8(&buf[.. count]).or(Err(Error::new(EINVAL)))?; + let translated = path.to_string().replace(self.root.to_str().ok_or(Error::new(EINVAL))?, ""); + format!("file:{}", translated.trim_left_matches('/')) + }; + + let path = translated.as_bytes(); + + let mut i = 0; + while i < buf.len() && i < path.len() { + buf[i] = path[i]; + i += 1; + } + + Ok(i) + } + + fn fstat(&self, id: usize, stat: &mut Stat) -> Result { + syscall::fstat(id, stat) + } + + fn fstatvfs(&self, id: usize, stat: &mut StatVfs) -> Result { + syscall::fstatvfs(id, stat) + } + + fn fsync(&self, id: usize) -> Result { + syscall::fsync(id) + } + + fn ftruncate(&self, id: usize, len: usize) -> Result { + syscall::ftruncate(id, len) + } + + fn close(&self, id: usize) -> Result { + syscall::close(id) + } +} diff --git a/programs/contain/src/main.rs b/programs/contain/src/main.rs index c660b30..8e22e23 100644 --- a/programs/contain/src/main.rs +++ b/programs/contain/src/main.rs @@ -1,9 +1,15 @@ extern crate syscall; -use std::{env, thread}; +use syscall::scheme::Scheme; + +use std::{env, fs, thread}; use std::os::unix::process::CommandExt; use std::process::Command; +use self::chroot::ChrootScheme; + +mod chroot; + pub fn main() { let mut args = env::args().skip(1); @@ -34,12 +40,14 @@ pub fn main() { let scheme_fd = syscall::open(":file", syscall::O_CREAT | syscall::O_RDWR | syscall::O_CLOEXEC).unwrap(); syscall::setrens(-1isize as usize, syscall::getns().unwrap()).unwrap(); + let chroot_scheme = ChrootScheme::new(fs::canonicalize(root).unwrap()); loop { let mut packet = syscall::Packet::default(); if syscall::read(scheme_fd, &mut packet).unwrap() == 0 { break; } - println!("{:?}", packet); + chroot_scheme.handle(&mut packet); + syscall::write(scheme_fd, &packet).unwrap(); } let _ = syscall::close(scheme_fd); @@ -58,6 +66,7 @@ pub fn main() { for arg in args { command.arg(&arg); } + command.current_dir("/"); let err = command.exec(); diff --git a/rust b/rust index 3a1bb2b..d73d32f 160000 --- a/rust +++ b/rust @@ -1 +1 @@ -Subproject commit 3a1bb2ba26a85bbea4c9be813a9a13d48ab448ac +Subproject commit d73d32f58d477ca1562e3fc0e966efc88e81409e