minijail0: move ld_preload communication to a pipe

Moves minijail0 communication over to using a file descriptor instead
of packing it in an environment variable. The primary reasoning is to
allow seccomp filter policies to be passed to a child process.

However, this will make it easier for minijail behavior to stay
consistent across minijail_run and minijail_enter if serialization can
be made more generic.  For instance, -g does not properly traverse a
preload instead relying on inheritance which is inconsistent depending
on pidns usage.

BUG=chromium-os:19459
TEST=tested -[pvrcu] with /bin/cat /proc/self/status

Change-Id: Id1845b86517ce0a6a9d6bcd85f700ea459d7c8f4
Reviewed-on: http://gerrit.chromium.org/gerrit/7890
Reviewed-by: Elly Jones <[email protected]>
Tested-by: Will Drewry <[email protected]>
diff --git a/libminijailpreload.c b/libminijailpreload.c
index 40a4c8c..a4970bc 100644
--- a/libminijailpreload.c
+++ b/libminijailpreload.c
@@ -12,6 +12,7 @@
 #include "libminijail-private.h"
 
 #include <dlfcn.h>
+#include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/types.h>
@@ -40,57 +41,68 @@
 
 /** @brief Fake main(), spliced in before the real call to main() by
  *         __libc_start_main (see below).
- *  We get serialized commands from our invoking process in an environment
- *  variable (kCommandEnvVar). The environment variable is a list of key=value
- *  pairs (see move_commands_to_env); we use them to construct a jail, then
- *  enter it.
+ *  We get serialized commands from our invoking process over an fd specified
+ *  by an environment variable (kFdEnvVar). The environment variable is a list
+ *  of key=value pairs (see move_commands_to_env); we use them to construct a
+ *  jail, then enter it.
  */
 static int fake_main(int argc, char **argv, char **envp) {
-  char *args = getenv(kCommandEnvVar);
-  char *copy, *oldcopy;
-  char *arg;
+  char *fd_name = getenv(kFdEnvVar);
+  char *arg = NULL;
+  size_t arg_len;
+  int fd = -1;
+  FILE *args;
   struct minijail *j;
   if (geteuid() != getuid() || getegid() != getgid())
-    /* If we didn't do this check, an attacker could set kCommandEnvVar for
+    /* If we didn't do this check, an attacker could set kFdEnvVar for
      * any setuid program that uses libminijail to cause it to get capabilities
      * or a uid it did not expect. */
+    /* TODO(wad) why would libminijail interact here? */
     return MINIJAIL_ERR_PRELOAD;
+  if (!fd_name)
+    return MINIJAIL_ERR_PRELOAD;
+  fd = atoi(fd_name);
+  if (fd < 0)
+    return MINIJAIL_ERR_PRELOAD;
+  args = fdopen(fd, "r");
   if (!args)
     return MINIJAIL_ERR_PRELOAD;
-  if (!(copy = strdup(args)))
-    die("preload: out of memory");
-  oldcopy = copy;
+
   j = minijail_new();
   if (!j)
     die("preload: out of memory");
-  while ((arg = strsep(&copy, " "))) {
+  while (getline(&arg, &arg_len, args) > 0) {
     char *key, *val;
     unsigned long v;
     splitarg(arg, &key, &val);
-    if (!strcmp(key, "caps")) {
+    if (!strcmp(arg, "eom\n")) {
+      break;
+    } else if (!strcmp(key, "caps")) {
       v = strtoul(val, NULL, 16);
       minijail_use_caps(j, v);
-    }
-    else if (!strcmp(key, "ptrace"))
+    } else if (!strcmp(key, "ptrace")) {
       minijail_disable_ptrace(j);
-    else if (!strcmp(key, "uid")) {
+    } else if (!strcmp(key, "uid")) {
       v = atoi(val);
       minijail_change_uid(j, v);
-    }
-    else if (!strcmp(key, "gid")) {
+    } else if (!strcmp(key, "gid")) {
       v = atoi(val);
       minijail_change_gid(j, v);
-    }
-    else if (!strcmp(key, "seccomp"))
+    } else if (!strcmp(key, "seccomp")) {
       minijail_use_seccomp(j);
+    }
+    free(arg);
+    arg = NULL;
   }
+  if (!feof(args) && ferror(args))
+    die("preload: unexpected failure during unmarshalling");
+  fclose(args);
   /* TODO(ellyjones): this trashes existing preloads, so one can't do:
    * LD_PRELOAD="/tmp/test.so libminijailpreload.so" prog; the descendants of
    * prog will have no LD_PRELOAD set at all. */
   unset_in_env(envp, kLdPreloadEnvVar);
   minijail_enter(j);
   minijail_destroy(j);
-  free(oldcopy);
   dlclose(libc_handle);
   return real_main(argc, argv, envp);
 }