add -T option to minijail0 for specifying ELF linkage type

This removes the need for minijail0 to access the program file before
trying to launch it, which allows it to be used for running programs
that are only accessible within a mount namespace specified with the -V
option.

It would still be preferable if -V worked correctly on its own (like
how -b, -k, and -C already do), but this is a less intrusive and
simpler immediate fix.

TEST=Use minijail0 -V -T to run program in pivot_root'd mount namespace

Bug: 26947503
Change-Id: I923ca87683b7fd8a60530946fad58018cfcd5125
diff --git a/minijail0.1 b/minijail0.1
index dbec74d..ae53ce0 100644
--- a/minijail0.1
+++ b/minijail0.1
@@ -92,6 +92,10 @@
 system calls defined in the policy file.  Note that system calls often change
 names based on the architecture or mode. (uname -m is your friend.)
 .TP
+\fB-T <type>\fR
+Assume program's ELF linkage type is \fItype\fR,
+which should be either 'static' or 'dynamic'.
+.TP
 \fB-u <user>\fR
 Change users to \fIuser\fR, which may be either a user name or a numeric user
 ID.
diff --git a/minijail0.c b/minijail0.c
index 9b7260d..0be0b6a 100644
--- a/minijail0.c
+++ b/minijail0.c
@@ -95,7 +95,7 @@
 
 	printf("Usage: %s [-GhiInprsvtUl] [-b <src>,<dest>[,<writeable>]] [-f <file>]"
 	       "[-c <caps>] [-C <dir>] [-g <group>] [-S <file>] [-u <user>] "
-	       "[-k <src>,<dest>,<type>[,<flags>]] "
+	       "[-k <src>,<dest>,<type>[,<flags>]] [-T <type>] "
 	       "[-m \"<uid> <loweruid> <count>[,<uid> <loweruid> <count>]\"] "
 	       "[-M \"<gid> <lowergid> <count>[,<uid> <loweruid> <count>]\"] "
 	       "<program> [args...]\n"
@@ -140,6 +140,8 @@
 	       "              E.g., -S /usr/share/filters/<prog>.$(uname -m)\n"
 	       "              Requires -n when not running as root\n"
 	       "  -t:         mount tmpfs at /tmp inside chroot\n"
+	       "  -T <type>:  assume <program> is a <type> ELF binary.\n"
+	       "              Must be 'static' or 'dynamic'.\n"
 	       "  -u <user>:  change uid to <user>\n"
 	       "  -U          enter new user namespace (implies -p)\n"
 	       "  -v:         enter new mount namespace\n"
@@ -157,7 +159,7 @@
 }
 
 static int parse_args(struct minijail *j, int argc, char *argv[],
-		      int *exit_immediately)
+		      int *exit_immediately, ElfType *elftype)
 {
 	int opt;
 	int use_seccomp_filter = 0;
@@ -168,7 +170,7 @@
 	if (argc > 1 && argv[1][0] != '-')
 		return 1;
 	while ((opt = getopt(argc, argv,
-			     "u:g:sS:c:C:P:b:V:f:m:M:k:a:e::vrGhHinplLtIU"))
+			     "u:g:sS:c:C:P:b:V:f:m:M:k:a:e::T:vrGhHinplLtIU"))
 	       != -1) {
 		switch (opt) {
 		case 'u':
@@ -307,6 +309,16 @@
 				exit(1);
 			}
 			break;
+		case 'T':
+			if (!strcmp(optarg, "static"))
+				*elftype = ELFSTATIC;
+			else if (!strcmp(optarg, "dynamic"))
+				*elftype = ELFDYNAMIC;
+			else {
+				fprintf(stderr, "ELF type must be 'static' or 'dynamic'.\n");
+				exit(1);
+			}
+			break;
 		default:
 			usage(argv[0]);
 			exit(1);
@@ -344,24 +356,31 @@
 	struct minijail *j = minijail_new();
 	const char *dl_mesg = NULL;
 	int exit_immediately = 0;
-	char *program_path;
-	int consumed = parse_args(j, argc, argv, &exit_immediately);
 	ElfType elftype = ELFERROR;
+	int consumed = parse_args(j, argc, argv, &exit_immediately, &elftype);
 	argc -= consumed;
 	argv += consumed;
 
-	/* Get the path to the program adjusted for changing root. */
-	program_path = minijail_get_original_path(j, argv[0]);
+	if (elftype == ELFERROR) {
+		/*
+		 * -T was not specified.
+		 * Get the path to the program adjusted for changing root.
+		 */
+		char *program_path = minijail_get_original_path(j, argv[0]);
 
-	/* Check that we can access the target program. */
-	if (access(program_path, X_OK)) {
-		fprintf(stderr, "Target program '%s' is not accessible.\n",
-			argv[0]);
-		return 1;
+		/* Check that we can access the target program. */
+		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. */
+		elftype = get_elf_linkage(program_path);
+		free(program_path);
 	}
 
-	/* Check if target is statically or dynamically linked. */
-	elftype = get_elf_linkage(program_path);
 	if (elftype == ELFSTATIC) {
 		/*
 		 * Target binary is statically linked so we cannot use
@@ -376,9 +395,9 @@
 
 		/* Check that we can dlopen() libminijailpreload.so. */
 		if (!dlopen(PRELOADPATH, RTLD_LAZY | RTLD_LOCAL)) {
-			    dl_mesg = dlerror();
-			    fprintf(stderr, "dlopen(): %s\n", dl_mesg);
-			    return 1;
+			dl_mesg = dlerror();
+			fprintf(stderr, "dlopen(): %s\n", dl_mesg);
+			return 1;
 		}
 		minijail_run(j, argv[0], argv);
 	} else {
@@ -388,8 +407,6 @@
 		return 1;
 	}
 
-	free(program_path);
-
 	if (exit_immediately) {
 		info("not running init loop, exiting immediately");
 		return 0;