Minijail: allow writing to the child process' standard input.

BUG=chromium-os:33983
TEST=libminijail_unittest
TEST=security_Minijail0

Change-Id: Ic2373127b3bca6a4a4a05ffcbc48b486cb5eb4a6
Reviewed-on: https://gerrit.chromium.org/gerrit/31779
Tested-by: Jorge Lucangeli Obes <jorgelo@chromium.org>
Reviewed-by: Jorge Lucangeli Obes <jorgelo@chromium.org>
Commit-Ready: Jorge Lucangeli Obes <jorgelo@chromium.org>
diff --git a/Makefile b/Makefile
index 5983e3a..b953b66 100644
--- a/Makefile
+++ b/Makefile
@@ -3,7 +3,8 @@
 # found in the LICENSE file.
 
 LIBDIR = lib
-PRELOADPATH = \"/$(LIBDIR)/libminijailpreload.so\"
+PRELOADNAME = libminijailpreload.so
+PRELOADPATH = \"/$(LIBDIR)/$(PRELOADNAME)\"
 CFLAGS += -fPIC -Wall -Wextra -Werror -DPRELOADPATH="$(PRELOADPATH)"
 CFLAGS += -fvisibility=internal
 
@@ -26,6 +27,8 @@
 	$(MAKE) $(MAKEARGS) test-clean
 
 libminijail_unittest : CFLAGS := $(filter-out -fvisibility=%,$(CFLAGS))
+libminijail_unittest : CFLAGS := $(filter-out -DPRELOADPATH=%,$(CFLAGS))
+libminijail_unittest : CFLAGS := $(CFLAGS) -DPRELOADPATH=\"./$(PRELOADNAME)\"
 libminijail_unittest : libminijail_unittest.o libminijail.o \
 		syscall_filter.o signal.o bpf.o util.o libsyscalls.gen.o
 	$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(filter-out $(CFLAGS_FILE),$^) -lcap
diff --git a/libminijail.c b/libminijail.c
index fdf6f29..bf8c163 100644
--- a/libminijail.c
+++ b/libminijail.c
@@ -835,15 +835,29 @@
 int API minijail_run(struct minijail *j, const char *filename,
 		     char *const argv[])
 {
-	return minijail_run_pid(j, filename, argv, NULL);
+	return minijail_run_pid_pipe(j, filename, argv, NULL, NULL);
 }
 
 int API minijail_run_pid(struct minijail *j, const char *filename,
 			 char *const argv[], pid_t *pchild_pid)
 {
+	return minijail_run_pid_pipe(j, filename, argv, pchild_pid, NULL);
+}
+
+int API minijail_run_pipe(struct minijail *j, const char *filename,
+			 char *const argv[], int *pstdin_fd)
+{
+	return minijail_run_pid_pipe(j, filename, argv, NULL, pstdin_fd);
+}
+
+int API minijail_run_pid_pipe(struct minijail *j, const char *filename,
+			       char *const argv[], pid_t *pchild_pid,
+			       int *pstdin_fd)
+{
 	char *oldenv, *oldenv_copy = NULL;
 	pid_t child_pid;
 	int pipe_fds[2];
+	int stdin_fds[2];
 	int ret;
 	/* We need to remember this across the minijail_preexec() call. */
 	int pid_namespace = j->flags.pids;
@@ -865,6 +879,15 @@
 	if (setup_pipe(pipe_fds))
 		return -EFAULT;
 
+	/*
+	 * If we want to write to the child process' standard input,
+	 * create the pipe(2) now.
+	 */
+	if (pstdin_fd) {
+		if (pipe(stdin_fds))
+			return -EFAULT;
+	}
+
 	/* Use sys_clone() if and only if we're creating a pid namespace.
 	 *
 	 * tl;dr: WARNING: do not mix pid namespaces and multithreading.
@@ -924,7 +947,10 @@
 			unsetenv(kLdPreloadEnvVar);
 		}
 		unsetenv(kFdEnvVar);
+
 		j->initpid = child_pid;
+
+		/* Send marshalled minijail. */
 		close(pipe_fds[0]);	/* read endpoint */
 		ret = minijail_to_fd(j, pipe_fds[1]);
 		close(pipe_fds[1]);	/* write endpoint */
@@ -932,12 +958,34 @@
 			kill(j->initpid, SIGKILL);
 			die("failed to send marshalled minijail");
 		}
+
 		if (pchild_pid)
 			*pchild_pid = child_pid;
+
+		/*
+		 * If we want to write to the child process' standard input,
+		 * set up the write end of the pipe.
+		 */
+		if (pstdin_fd) {
+			close(stdin_fds[0]);	/* read endpoint */
+			*pstdin_fd = stdin_fds[1];
+		}
+
 		return 0;
 	}
 	free(oldenv_copy);
 
+	/*
+	 * If we want to write to the jailed process' standard input,
+	 * set up the read end of the pipe.
+	 */
+	if (pstdin_fd) {
+		close(stdin_fds[1]);	/* write endpoint */
+		/* dup2(2) the read end of the pipe into stdin. */
+		if (dup2(stdin_fds[0], 0))
+			die("failed to set up stdin pipe");
+	}
+
 	/* Drop everything that cannot be inherited across execve. */
 	minijail_preexec(j);
 	/* Jail this process and its descendants... */
diff --git a/libminijail.h b/libminijail.h
index d83dc21..c231572 100644
--- a/libminijail.h
+++ b/libminijail.h
@@ -102,6 +102,22 @@
 int minijail_run_pid(struct minijail *j, const char *filename,
 		     char *const argv[], pid_t *pchild_pid);
 
+/* Run the specified command in the given minijail, execve(3)-style.
+ * Update |*pstdin_fd| with a fd that allows writing to the child's
+ * standard input.
+ */
+int minijail_run_pipe(struct minijail *j, const char *filename,
+		      char *const argv[], int *pstdin_fd);
+
+/* Run the specified command in the given minijail, execve(3)-style.
+ * Update |*pchild_pid| with the pid of the child.
+ * Update |*pstdin_fd| with a fd that allows writing to the child's
+ * standard input.
+ */
+int minijail_run_pid_pipe(struct minijail *j, const char *filename,
+			  char *const argv[], pid_t *pchild_pid,
+			  int *pstdin_fd);
+
 /* Kill the specified minijail. The minijail must have been created with pid
  * namespacing; if it was, all processes inside it are atomically killed.
  */
diff --git a/libminijail_unittest.c b/libminijail_unittest.c
index 9d420c1..a2304a2 100644
--- a/libminijail_unittest.c
+++ b/libminijail_unittest.c
@@ -8,6 +8,9 @@
 
 #include <errno.h>
 
+#include <sys/types.h>
+#include <sys/wait.h>
+
 #include "test_harness.h"
 
 #include "libminijail.h"
@@ -140,9 +143,26 @@
   EXPECT_EQ(-EINVAL, minijail_unmarshal(self->j, self->buf, sizeof(self->buf)));
 }
 
-TEST_F(marshal, short) {
-  ASSERT_EQ(0, minijail_marshal(self->m, self->buf, sizeof(self->buf)));
-  EXPECT_NE(0, minijail_unmarshal(self->j, self->buf, self->size / 2));
+TEST(test_minijail_run_pid_pipe) {
+  pid_t pid;
+  int child_stdin;
+  int mj_run_ret;
+  int write_ret;
+  int status;
+
+  struct minijail *j = minijail_new();
+  mj_run_ret = minijail_run_pid_pipe(j, "test/read_stdin",
+                                     NULL, &pid, &child_stdin);
+  EXPECT_EQ(mj_run_ret, 0);
+  write_ret = write(child_stdin, "test\n", strlen("test\n"));
+  EXPECT_GT(write_ret, -1);
+
+  waitpid(pid, &status, 0);
+
+  ASSERT_TRUE(WIFEXITED(status));
+  EXPECT_EQ(WEXITSTATUS(status), 0);
+
+  minijail_destroy(j);
 }
 
 TEST_HARNESS_MAIN
diff --git a/test/read_stdin b/test/read_stdin
new file mode 100755
index 0000000..29578a6
--- /dev/null
+++ b/test/read_stdin
@@ -0,0 +1,13 @@
+#!/bin/bash
+
+# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+read line
+
+if [ "$line" == "test" ]; then
+	exit 0
+else
+	exit 1
+fi