minijail: Allow static binaries in a bind mount to run

A previous commit placed a restriction on running static binaries and
using bind mounts.  Remove that restriction by checking if the binary
path is in a bind mount and rebasing the path on to the bind mount
source path so that the executable can be accessed from outside the
chroot.  This is needed so bind mounts can be specified when running a
statically linked init program for Android.

BUG=b/25192613
TEST=security_Minijail0, run a static init with bind mounts.

Change-Id: I801909df67c1bf18d48efcfd54c11aafe4c75e54
Signed-off-by: Dylan Reid <dgreid@google.com>
diff --git a/libminijail.c b/libminijail.c
index bebdb8c..47b4b3f 100644
--- a/libminijail.c
+++ b/libminijail.c
@@ -387,20 +387,60 @@
 	return 0;
 }
 
-char *minijail_get_original_path(struct minijail *j, const char *chroot_path)
+static char *append_external_path(const char *external_path,
+				  const char *path_inside_chroot)
 {
-	char *external_path;
+	char *path;
 	size_t pathlen;
 
-	if (!j->chrootdir)
-		return strdup(chroot_path);
-
 	/* One extra char for '/' and one for '\0', hence + 2. */
-	pathlen = strlen(chroot_path) + strlen(j->chrootdir) + 2;
-	external_path = malloc(pathlen);
-	snprintf(external_path, pathlen, "%s/%s", j->chrootdir, chroot_path);
+	pathlen = strlen(path_inside_chroot) + strlen(external_path) + 2;
+	path = malloc(pathlen);
+	snprintf(path, pathlen, "%s/%s", external_path, path_inside_chroot);
 
-	return external_path;
+	return path;
+}
+
+char API *minijail_get_original_path(struct minijail *j,
+				     const char *path_inside_chroot)
+{
+	struct binding *b;
+
+	b = j->bindings_head;
+	while (b) {
+		/*
+		 * If |path_inside_chroot| is the exact destination of a
+		 * bind mount, then the original path is exactly the source of
+		 * the bind mount.
+		 *  for example: "-b /some/path/exe,/chroot/path/exe"
+		 *    bind source = /some/path/exe, bind dest = /chroot/path/exe
+		 *    Then when getting the original path of "/chroot/path/exe",
+		 *    the source of that bind mount, "/some/path/exe" is what
+		 *    should be returned.
+		 */
+		if (!strcmp(b->dest, path_inside_chroot))
+			return strdup(b->src);
+
+		/*
+		 * If |path_inside_chroot| is within the destination path of a
+		 * bind mount, take the suffix of the chroot path relative to
+		 * the bind mount destination path, and append it to the bind
+		 * mount source path.
+		 */
+		if (!strncmp(b->dest, path_inside_chroot, strlen(b->dest))) {
+			const char *relative_path =
+				path_inside_chroot + strlen(b->dest);
+			return append_external_path(b->src, relative_path);
+		}
+		b = b->next;
+	}
+
+	/* If there is a chroot path, append |path_inside_chroot| to that. */
+	if (j->chrootdir)
+		return append_external_path(j->chrootdir, path_inside_chroot);
+
+	/* No chroot, so the path outside is the same as it is inside. */
+	return strdup(path_inside_chroot);
 }
 
 void API minijail_mount_tmp(struct minijail *j)
diff --git a/minijail0.c b/minijail0.c
index e272702..d1260bd 100644
--- a/minijail0.c
+++ b/minijail0.c
@@ -308,23 +308,14 @@
 	program_path = minijail_get_original_path(j, argv[0]);
 
 	/* Check that we can access the target program. */
-	if (!minijail_has_bind_mounts(j) && access(program_path, X_OK)) {
+	if (access(program_path, X_OK)) {
 		fprintf(stderr, "Target program '%s' is not accessible.\n",
 			argv[0]);
 		return 1;
 	}
 
 	/* Check if target is statically or dynamically linked. */
-	if (minijail_has_bind_mounts(j)) {
-		/* We can't tell what the internal path to the binary is so
-		 * assume it's dynamically linked.
-		 */
-		elftype = ELFDYNAMIC;
-		warn("assuming program '%s' is dynamically linked\n", argv[0]);
-	} else {
-		elftype = get_elf_linkage(program_path);
-	}
-
+	elftype = get_elf_linkage(program_path);
 	if (elftype == ELFSTATIC) {
 		/*
 		 * Target binary is statically linked so we cannot use