libminijail: move over to using marshalled binary for preload

Move libminijail and libminijailpreload over to using the marshalling
helper functions and add to/from_fd.  The format itself is not terribly
robust, but we can change it underneath the functions in the future
(or move struct minijail to a protobuf :).

These changes lay the groundwork for sending seccomp_filter policy. A
subsequent change will implement that and disable use in the parent.

BUG=chromium-os:19459
TEST=tested as per previous commits:
     minijail0 -[pvrcuGg] -- /bin/cat /proc/self/status
     .. /bin/ps aux
     .. /bin/bash -c 'env'

Change-Id: I565816611b31ce49f85fee2241c55a3328d7b770
Reviewed-on: http://gerrit.chromium.org/gerrit/7892
Reviewed-by: Elly Jones <ellyjones@chromium.org>
Tested-by: Will Drewry <wad@chromium.org>
diff --git a/libminijail.c b/libminijail.c
index 46df149..938a8d7 100644
--- a/libminijail.c
+++ b/libminijail.c
@@ -9,6 +9,7 @@
 #include <errno.h>
 #include <grp.h>
 #include <inttypes.h>
+#include <limits.h>
 #include <linux/capability.h>
 #include <linux/securebits.h>
 #include <pwd.h>
@@ -249,13 +250,6 @@
   return bytes;
 }
 
-void minijail_preenter(struct minijail *j) {
-  /* Strip out options which are minijail_run() only. */
-  j->flags.pids = 0;
-  j->flags.vfs = 0;
-  j->flags.readonly = 0;
-}
-
 int minijail_marshal(const struct minijail *j, char *buf, size_t available) {
   size_t total = sizeof(*j);
   if (available < total)
@@ -288,16 +282,25 @@
   return 0;
 }
 
-void minijail_prefork(struct minijail *j) {
-  j->flags.uid = 0;
-  j->flags.caps = 0;
-  j->flags.seccomp = 0;
-  j->flags.usergroups = 0;
-  j->flags.ptrace = 0;
-  j->flags.seccomp_filter = 0;
+void minijail_preenter(struct minijail *j) {
+  /* Strip out options which are minijail_run() only. */
+  j->flags.vfs = 0;
+  j->flags.readonly = 0;
+  j->flags.pids = 0;
+}
+
+void minijail_preexec(struct minijail *j) {
+  int vfs = j->flags.vfs;
+  int readonly = j->flags.readonly;
   if (j->user)
     free(j->user);
   j->user = NULL;
+
+  memset(&j->flags, 0, sizeof(j->flags));
+  /* Now restore anything we meant to keep. */
+  j->flags.vfs = vfs;
+  j->flags.readonly = readonly;
+  /* Note, pidns will already have been used before this call. */
 }
 
 static int remount_readonly(void) {
@@ -468,50 +471,57 @@
   _exit(WEXITSTATUS(init_exitstatus));
 }
 
-static int write_cmd(int fd, const char *fmt, ...) {
-  char cmd[MINIJAIL_MAX_ARG_LINE];
-  ssize_t written;
+int minijail_from_fd(int fd, struct minijail *j) {
+  size_t sz = 0;
+  size_t bytes = read(fd, &sz, sizeof(sz));
+  char *buf;
   int r;
-  va_list ap;
-
-  va_start(ap, fmt);
-  r = vsnprintf(cmd, sizeof(cmd), fmt, ap);
-  va_end(ap);
-
-  if (r <= 0)
-    return -EFAULT;
-  if ((size_t) r >= sizeof(cmd))
+  if (sizeof(sz) != bytes)
+    return -EINVAL;
+  if (sz > USHRT_MAX)  /* Arbitrary sanity check */
     return -E2BIG;
-
-  written = write(fd, cmd, r);
-  if (written != r)
-    return -EFAULT;
-  return 0;
+  buf = malloc(sz);
+  if (!buf)
+    return -ENOMEM;
+  bytes = read(fd, buf, sz);
+  if (bytes != sz) {
+    free(buf);
+    return -EINVAL;
+  }
+  r = minijail_unmarshal(j, buf, sz);
+  free(buf);
+  return r;
 }
 
-/** @brief Move any commands that need to be done post-exec into an environment
- *         variable
- *  @param j Jail to move commands from.
- *
- *  Serializes post-exec() commands into a string, removes them from the jail,
- *  and adds them to the environment; they will be deserialized later (see
- *  __minijail_preloaded) and executed inside the execve()'d process.
- */
-static int send_commands_to_child(struct minijail *j, int fd) {
-  if (j->flags.caps && write_cmd(fd, "caps=%" PRIx64 "\n", j->caps))
-    return -EFAULT;
-  if (j->flags.uid && write_cmd(fd, "uid=%d\n", j->uid))
-    return -EFAULT;
-  if (j->flags.ptrace && write_cmd(fd, "ptrace\n"))
-    return -EFAULT;
-  if (j->flags.seccomp && write_cmd(fd, "seccomp\n"))
-    return -EFAULT;
+int minijail_to_fd(struct minijail *j, int fd) {
+  char *buf;
+  size_t sz = minijail_size(j);
+  ssize_t written;
+  int r;
 
   if (j->flags.seccomp_filter)
     warn("TODO(wad) seccomp_filter is installed in the parent which "
          "requires overly permissive rules for execve(2)ing.");
-
-  return write_cmd(fd, "eom\n");
+  if (!sz)
+    return -EINVAL;
+  buf = malloc(sz);
+  if ((r = minijail_marshal(j, buf, sz))) {
+    free(buf);
+    return r;
+  }
+  /* Sends [size][minijail]. */
+  written = write(fd, &sz, sizeof(sz));
+  if (written != sizeof(sz)) {
+    free(buf);
+    return -EFAULT;
+  }
+  written = write(fd, buf, sz);
+  if (written < 0 || (size_t) written != sz) {
+    free(buf);
+    return -EFAULT;
+  }
+  free(buf);
+  return 0;
 }
 
 static int setup_preload(void) {
@@ -568,7 +578,7 @@
     unsetenv(kFdEnvVar);
     j->initpid = r;
     close(pipe_fds[0]);
-    r = send_commands_to_child(j, pipe_fds[1]);
+    r = minijail_to_fd(j, pipe_fds[1]);
     close(pipe_fds[1]);
     if (r) {
       kill(j->initpid, SIGKILL);
@@ -582,16 +592,8 @@
   if (r < 0)
     return r;
 
-  j->flags.uid = 0;
-  /* TODO(wad) gid should be sent over preload and not done in advance.
-   * j->flags.gid = 0;
-   */
-  j->flags.usergroups = 0;
-  j->flags.caps = 0;
-  j->flags.ptrace = 0;
-  j->flags.seccomp = 0;
-
-  j->flags.pids = 0;
+  /* Drop everything that cannot be inherited across execve. */
+  minijail_preexec(j);
 
   /* Jail this process and its descendants... */
   minijail_enter(j);