From 59bd207a4b8372b53b551e7f7745e3f1da7a1b89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20Sch=C3=B6lling?= Date: Sun, 2 Oct 2016 17:58:31 +0200 Subject: [PATCH] Add std{in,out} for communication with child process --- Cargo.toml | 5 +++++ platform/linux/namespace.rs | 29 ++++++++++++++++++++++--- platform/unix/process.rs | 33 +++++++++++++++++++++++++++- tests/stdio.rs | 43 +++++++++++++++++++++++++++++++++++++ 4 files changed, 106 insertions(+), 4 deletions(-) create mode 100644 tests/stdio.rs diff --git a/Cargo.toml b/Cargo.toml index 7cfb782..9c9bcb7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,3 +38,8 @@ name = "system-info-read" path = "tests/system-info-read.rs" harness = false +[[test]] +name = "stdio" +path = "tests/stdio.rs" +harness = false + diff --git a/platform/linux/namespace.rs b/platform/linux/namespace.rs index c5ebe89..2423513 100644 --- a/platform/linux/namespace.rs +++ b/platform/linux/namespace.rs @@ -17,6 +17,8 @@ use profile::{Operation, PathPattern, Profile}; use sandbox::Command; use libc::{self, c_char, c_int, c_ulong, c_void, gid_t, pid_t, size_t, ssize_t, uid_t}; +use libc::dup2; +use libc::{STDIN_FILENO, STDOUT_FILENO}; use std::env; use std::ffi::{CString, OsStr, OsString}; use std::fs::{self, File}; @@ -26,6 +28,7 @@ use std::mem; use std::os::unix::prelude::OsStrExt; use std::path::{Path, PathBuf}; use std::ptr; +use std::os::unix::io::FromRawFd; /// Creates a namespace and sets up a chroot jail. pub fn activate(profile: &Profile) -> Result<(),c_int> { @@ -227,11 +230,17 @@ pub fn start(profile: &Profile, command: &mut Command) -> io::Result { unsafe { // Create a pipe so we can communicate the PID of our grandchild back. let mut pipe_fds = [0, 0]; - assert!(libc::pipe(&mut pipe_fds[0]) == 0); + assert_eq!(libc::pipe(&mut pipe_fds[0]), 0); + + // Create two other pipes for stdin and stdout + let mut io1_fds = [0, 0]; + let mut io2_fds = [0, 0]; + assert_eq!(libc::pipe(&mut io1_fds[0]), 0); + assert_eq!(libc::pipe(&mut io2_fds[0]), 0); // Set this `prctl` flag so that we can wait on our grandchild. (Otherwise it'll be // reparented to init.) - assert!(seccomp::prctl(PR_SET_CHILD_SUBREAPER, 1, 0, 0, 0) == 0); + assert_eq!(seccomp::prctl(PR_SET_CHILD_SUBREAPER, 1, 0, 0, 0), 0); // Fork so that we can unshare without removing our ability to create threads. if fork() == 0 { @@ -246,7 +255,16 @@ pub fn start(profile: &Profile, command: &mut Command) -> io::Result { match fork() { 0 => { // Enter the auxiliary namespaces. - assert!(unshare(unshare_flags) == 0); + assert_eq!(unshare(unshare_flags), 0); + + assert_eq!(libc::close(io1_fds[1]), 0); + assert_eq!(libc::close(io2_fds[0]), 0); + + assert_eq!(dup2(io1_fds[0], STDIN_FILENO), STDIN_FILENO); + assert_eq!(libc::close(io1_fds[0]), 0); + + assert_eq!(dup2(io2_fds[1], STDOUT_FILENO), STDOUT_FILENO); + assert_eq!(libc::close(io2_fds[1]), 0); // Go ahead and start the command. drop(unix::process::exec(command)); @@ -266,6 +284,9 @@ pub fn start(profile: &Profile, command: &mut Command) -> io::Result { // Grandparent execution continues here. First, close the writing end of the pipe. libc::close(pipe_fds[1]); + libc::close(io1_fds[0]); + libc::close(io2_fds[1]); + // Retrieve our grandchild's PID. let mut grandchild_pid: pid_t = 0; assert!(libc::read(pipe_fds[0], @@ -274,6 +295,8 @@ pub fn start(profile: &Profile, command: &mut Command) -> io::Result { mem::size_of::() as ssize_t); Ok(Process { pid: grandchild_pid, + stdin: File::from_raw_fd(io1_fds[1]), + stdout: File::from_raw_fd(io2_fds[0]), }) } } diff --git a/platform/unix/process.rs b/platform/unix/process.rs index 21e69d3..3c1bcb7 100644 --- a/platform/unix/process.rs +++ b/platform/unix/process.rs @@ -12,11 +12,16 @@ use sandbox::Command; -use libc::{c_char, c_int, pid_t}; +use libc::{self, c_char, c_int, pid_t}; +use libc::pipe; +use libc::dup2; +use libc::{STDIN_FILENO, STDOUT_FILENO}; use std::ffi::CString; use std::io; +use std::fs::File; use std::ptr; use std::str; +use std::os::unix::io::FromRawFd; pub fn exec(command: &Command) -> io::Error { let mut args: Vec<_> = vec![command.module_path.as_ptr()]; @@ -43,15 +48,39 @@ pub fn exec(command: &Command) -> io::Error { } pub fn spawn(command: &Command) -> io::Result { + let mut fd1 = [0 as c_int; 2]; + let mut fd2 = [0 as c_int; 2]; + + if unsafe { pipe(&mut fd1[0]) } < 0 { + return Err(io::Error::last_os_error()); + } + if unsafe { pipe(&mut fd2[0]) } < 0 { + return Err(io::Error::last_os_error()); + } + unsafe { match fork() { 0 => { + libc::close(fd1[1]); + libc::close(fd2[0]); + + assert_eq!(dup2(fd1[0], STDIN_FILENO), STDIN_FILENO); + libc::close(fd1[0]); + + assert_eq!(dup2(fd2[1], STDOUT_FILENO), STDOUT_FILENO); + libc::close(fd2[1]); + drop(exec(command)); panic!() } pid => { + libc::close(fd1[0]); + libc::close(fd2[1]); + Ok(Process { pid: pid, + stdin: File::from_raw_fd(fd1[1]), + stdout: File::from_raw_fd(fd2[0]), }) } } @@ -61,6 +90,8 @@ pub fn spawn(command: &Command) -> io::Result { #[allow(missing_copy_implementations)] pub struct Process { pub pid: pid_t, + pub stdin: File, + pub stdout: File, } impl Process { diff --git a/tests/stdio.rs b/tests/stdio.rs new file mode 100644 index 0000000..0c9e949 --- /dev/null +++ b/tests/stdio.rs @@ -0,0 +1,43 @@ +// Any copyright is dedicated to the Public Domain. +// http://creativecommons.org/publicdomain/zero/1.0/ + +extern crate gaol; + +//use gaol::profile::{AddressPattern, Operation, OperationSupport, OperationSupportLevel}; +//use gaol::profile::{PathPattern, Profile}; +use gaol::profile::Profile; +use gaol::sandbox::{ChildSandbox, ChildSandboxMethods, Command, Sandbox, SandboxMethods}; +use std::env; +use std::io; +use std::io::{Read, Write}; + +fn main() { + match env::args().skip(1).next() { + Some(ref arg) if arg == "child" => { + // This is the child process. + ChildSandbox::new(Profile::new(vec![]).unwrap()).activate().unwrap(); + + let mut buf = vec![0]; + io::stdin().read_exact(&mut buf[..]).unwrap(); + assert_eq!(buf, b"A"); + + io::stdout().write_all(b"B").unwrap(); + io::stdout().flush().unwrap(); + } + _ => { + // This is the parent process. + let mut command = Command::me().unwrap(); + let mut cmd = Sandbox::new(Profile::new(vec![]).unwrap()).start(command.arg("child")).unwrap(); + + cmd.stdin.write_all(b"A").unwrap(); + cmd.stdin.flush().unwrap(); + + let mut buf = vec![0]; + cmd.stdout.read_exact(&mut buf[..]).unwrap(); + assert_eq!(buf, b"B"); + + cmd.wait().unwrap(); + } + } +} +