From 4157ef25c473e506c17d00027f9c16e8dcfccd4d Mon Sep 17 00:00:00 2001
From: Jeremy Soller <jackpot51@gmail.com>
Date: Fri, 25 Nov 2016 20:42:46 -0700
Subject: [PATCH] Add chroot to container

---
 programs/contain/src/chroot.rs | 172 +++++++++++++++++++++++++++++++++
 programs/contain/src/main.rs   |  13 ++-
 rust                           |   2 +-
 3 files changed, 184 insertions(+), 3 deletions(-)
 create mode 100644 programs/contain/src/chroot.rs

diff --git a/programs/contain/src/chroot.rs b/programs/contain/src/chroot.rs
new file mode 100644
index 000000000..edce9ea46
--- /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<String> {
+        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<usize> {
+        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<usize> {
+        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<usize> {
+        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<usize> {
+        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<usize> {
+        syscall::dup(old_id, buf)
+    }
+
+    fn read(&self, id: usize, buf: &mut [u8]) -> Result<usize> {
+        syscall::read(id, buf)
+    }
+
+    fn write(&self, id: usize, buf: &[u8]) -> Result<usize> {
+        syscall::write(id, buf)
+    }
+
+    fn seek(&self, id: usize, pos: usize, whence: usize) -> Result<usize> {
+        syscall::lseek(id, pos as isize, whence)
+    }
+
+    fn fcntl(&self, id: usize, cmd: usize, arg: usize) -> Result<usize> {
+        syscall::fcntl(id, cmd, arg)
+    }
+
+    fn fevent(&self, _id: usize, _flags: usize) -> Result<usize> {
+        //TODO
+        Err(Error::new(EBADF))
+    }
+
+    fn fmap(&self, _id: usize, _offset: usize, _size: usize) -> Result<usize> {
+        //TODO
+        Err(Error::new(EBADF))
+    }
+
+    fn fpath(&self, id: usize, buf: &mut [u8]) -> Result<usize> {
+        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<usize> {
+        syscall::fstat(id, stat)
+    }
+
+    fn fstatvfs(&self, id: usize, stat: &mut StatVfs) -> Result<usize> {
+        syscall::fstatvfs(id, stat)
+    }
+
+    fn fsync(&self, id: usize) -> Result<usize> {
+        syscall::fsync(id)
+    }
+
+    fn ftruncate(&self, id: usize, len: usize) -> Result<usize> {
+        syscall::ftruncate(id, len)
+    }
+
+    fn close(&self, id: usize) -> Result<usize> {
+        syscall::close(id)
+    }
+}
diff --git a/programs/contain/src/main.rs b/programs/contain/src/main.rs
index c660b30f1..8e22e2343 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 3a1bb2ba2..d73d32f58 160000
--- a/rust
+++ b/rust
@@ -1 +1 @@
-Subproject commit 3a1bb2ba26a85bbea4c9be813a9a13d48ab448ac
+Subproject commit d73d32f58d477ca1562e3fc0e966efc88e81409e
-- 
GitLab