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) } }