code-intelligence.com/cifuzz@v0.40.0/third-party/minijail/minijail0_cli.c (about)

     1  /* Copyright 2018 The Chromium OS Authors. All rights reserved.
     2   * Use of this source code is governed by a BSD-style license that can be
     3   * found in the LICENSE file.
     4   */
     5  
     6  #include <dlfcn.h>
     7  #include <errno.h>
     8  #include <getopt.h>
     9  #include <inttypes.h>
    10  #include <stdbool.h>
    11  #include <stdio.h>
    12  #include <stdlib.h>
    13  #include <string.h>
    14  #include <sys/capability.h>
    15  #include <sys/mount.h>
    16  #include <sys/types.h>
    17  #include <unistd.h>
    18  
    19  #include <linux/filter.h>
    20  
    21  #include "libminijail.h"
    22  #include "libsyscalls.h"
    23  
    24  #include "elfparse.h"
    25  #include "minijail0_cli.h"
    26  #include "system.h"
    27  #include "util.h"
    28  
    29  #define IDMAP_LEN 32U
    30  #define DEFAULT_TMP_SIZE (64 * 1024 * 1024)
    31  
    32  /*
    33   * A malloc() that aborts on failure.  We only implement this in the CLI as
    34   * the library should return ENOMEM errors when allocations fail.
    35   */
    36  static void *xmalloc(size_t size)
    37  {
    38  	void *ret = malloc(size);
    39  	if (!ret) {
    40  		perror("malloc() failed");
    41  		exit(1);
    42  	}
    43  	return ret;
    44  }
    45  
    46  static char *xstrdup(const char *s)
    47  {
    48  	char *ret = strdup(s);
    49  	if (!ret) {
    50  		perror("strdup() failed");
    51  		exit(1);
    52  	}
    53  	return ret;
    54  }
    55  
    56  static void set_user(struct minijail *j, const char *arg, uid_t *out_uid,
    57  		     gid_t *out_gid)
    58  {
    59  	char *end = NULL;
    60  	int uid = strtod(arg, &end);
    61  	if (!*end && *arg) {
    62  		*out_uid = uid;
    63  		minijail_change_uid(j, uid);
    64  		return;
    65  	}
    66  
    67  	int ret = lookup_user(arg, out_uid, out_gid);
    68  	if (ret) {
    69  		fprintf(stderr, "Bad user '%s': %s\n", arg, strerror(-ret));
    70  		exit(1);
    71  	}
    72  
    73  	ret = minijail_change_user(j, arg);
    74  	if (ret) {
    75  		fprintf(stderr, "minijail_change_user('%s') failed: %s\n", arg,
    76  			strerror(-ret));
    77  		exit(1);
    78  	}
    79  }
    80  
    81  static void set_group(struct minijail *j, const char *arg, gid_t *out_gid)
    82  {
    83  	char *end = NULL;
    84  	int gid = strtod(arg, &end);
    85  	if (!*end && *arg) {
    86  		*out_gid = gid;
    87  		minijail_change_gid(j, gid);
    88  		return;
    89  	}
    90  
    91  	int ret = lookup_group(arg, out_gid);
    92  	if (ret) {
    93  		fprintf(stderr, "Bad group '%s': %s\n", arg, strerror(-ret));
    94  		exit(1);
    95  	}
    96  
    97  	minijail_change_gid(j, *out_gid);
    98  }
    99  
   100  /*
   101   * Helper function used by --add-suppl-group (possibly more than once),
   102   * to build the supplementary gids array.
   103   */
   104  static void suppl_group_add(size_t *suppl_gids_count, gid_t **suppl_gids,
   105                              char *arg) {
   106  	char *end = NULL;
   107  	int groupid = strtod(arg, &end);
   108  	gid_t gid;
   109  	int ret;
   110  	if (!*end && *arg) {
   111  		/* A gid number has been specified, proceed. */
   112  		gid = groupid;
   113  	} else if ((ret = lookup_group(arg, &gid))) {
   114  		/*
   115  		 * A group name has been specified,
   116  		 * but doesn't exist: we bail out.
   117  		 */
   118  		fprintf(stderr, "Bad group '%s': %s\n", arg, strerror(-ret));
   119  		exit(1);
   120  	}
   121  
   122  	/*
   123  	 * From here, gid is guaranteed to be set and valid,
   124  	 * we add it to our supplementary gids array.
   125  	 */
   126  	*suppl_gids = realloc(*suppl_gids,
   127  			      sizeof(gid_t) * ++(*suppl_gids_count));
   128  	if (!suppl_gids) {
   129  		fprintf(stderr, "failed to allocate memory.\n");
   130  		exit(1);
   131  	}
   132  
   133  	(*suppl_gids)[*suppl_gids_count - 1] = gid;
   134  }
   135  
   136  static void skip_securebits(struct minijail *j, const char *arg)
   137  {
   138  	uint64_t securebits_skip_mask;
   139  	char *end = NULL;
   140  	securebits_skip_mask = strtoull(arg, &end, 16);
   141  	if (*end) {
   142  		fprintf(stderr, "Invalid securebit mask: '%s'\n", arg);
   143  		exit(1);
   144  	}
   145  	minijail_skip_setting_securebits(j, securebits_skip_mask);
   146  }
   147  
   148  static void use_caps(struct minijail *j, const char *arg)
   149  {
   150  	uint64_t caps = 0;
   151  	cap_t parsed_caps = cap_from_text(arg);
   152  
   153  	if (parsed_caps != NULL) {
   154  		unsigned int i;
   155  		const uint64_t one = 1;
   156  		cap_flag_value_t cap_value;
   157  		unsigned int last_valid_cap = get_last_valid_cap();
   158  
   159  		for (i = 0; i <= last_valid_cap; ++i) {
   160  			if (cap_get_flag(parsed_caps, i, CAP_EFFECTIVE,
   161  					 &cap_value)) {
   162  				if (errno == EINVAL) {
   163  					/*
   164  					 * Some versions of libcap reject any
   165  					 * capabilities they were not compiled
   166  					 * with by returning EINVAL.
   167  					 */
   168  					continue;
   169  				}
   170  				fprintf(stderr,
   171  					"Could not get the value of "
   172  					"the %d-th capability: %m\n",
   173  					i);
   174  				exit(1);
   175  			}
   176  			if (cap_value == CAP_SET)
   177  				caps |= (one << i);
   178  		}
   179  		cap_free(parsed_caps);
   180  	} else {
   181  		char *end = NULL;
   182  		caps = strtoull(arg, &end, 16);
   183  		if (*end) {
   184  			fprintf(stderr, "Invalid cap set: '%s'\n", arg);
   185  			exit(1);
   186  		}
   187  	}
   188  
   189  	minijail_use_caps(j, caps);
   190  }
   191  
   192  static void add_binding(struct minijail *j, char *arg)
   193  {
   194  	char *src = tokenize(&arg, ",");
   195  	char *dest = tokenize(&arg, ",");
   196  	char *flags = tokenize(&arg, ",");
   197  	if (!src || src[0] == '\0' || arg != NULL) {
   198  		fprintf(stderr, "Bad binding: %s %s\n", src, dest);
   199  		exit(1);
   200  	}
   201  	if (dest == NULL || dest[0] == '\0')
   202  		dest = src;
   203  	int writable;
   204  	if (flags == NULL || flags[0] == '\0' || !strcmp(flags, "0"))
   205  		writable = 0;
   206  	else if (!strcmp(flags, "1"))
   207  		writable = 1;
   208  	else {
   209  		fprintf(stderr, "Bad value for <writable>: %s\n", flags);
   210  		exit(1);
   211  	}
   212  	if (minijail_bind(j, src, dest, writable)) {
   213  		fprintf(stderr, "minijail_bind failed.\n");
   214  		exit(1);
   215  	}
   216  }
   217  
   218  static void add_rlimit(struct minijail *j, char *arg)
   219  {
   220  	char *type = tokenize(&arg, ",");
   221  	char *cur = tokenize(&arg, ",");
   222  	char *max = tokenize(&arg, ",");
   223  	char *end;
   224  	if (!type || type[0] == '\0' || !cur || cur[0] == '\0' ||
   225  	    !max || max[0] == '\0' || arg != NULL) {
   226  		fprintf(stderr, "Bad rlimit '%s'.\n", arg);
   227  		exit(1);
   228  	}
   229  	rlim_t cur_rlim;
   230  	rlim_t max_rlim;
   231  	if (!strcmp(cur, "unlimited")) {
   232  		cur_rlim = RLIM_INFINITY;
   233  	} else {
   234  		end = NULL;
   235  		cur_rlim = strtoul(cur, &end, 0);
   236  		if (*end) {
   237  			fprintf(stderr, "Bad soft limit: '%s'.\n", cur);
   238  			exit(1);
   239  		}
   240  	}
   241  	if (!strcmp(max, "unlimited")) {
   242  		max_rlim = RLIM_INFINITY;
   243  	} else {
   244  		end = NULL;
   245  		max_rlim = strtoul(max, &end, 0);
   246  		if (*end) {
   247  			fprintf(stderr, "Bad hard limit: '%s'.\n", max);
   248  			exit(1);
   249  		}
   250  	}
   251  
   252  	end = NULL;
   253  	int resource = parse_single_constant(type, &end);
   254  	if (type == end) {
   255  		fprintf(stderr, "Bad rlimit: '%s'.\n", type);
   256  		exit(1);
   257  	}
   258  
   259  	if (minijail_rlimit(j, resource, cur_rlim, max_rlim)) {
   260  		fprintf(stderr, "minijail_rlimit '%s,%s,%s' failed.\n", type,
   261  			cur, max);
   262  		exit(1);
   263  	}
   264  }
   265  
   266  static void add_mount(struct minijail *j, char *arg)
   267  {
   268  	char *src = tokenize(&arg, ",");
   269  	char *dest = tokenize(&arg, ",");
   270  	char *type = tokenize(&arg, ",");
   271  	char *flags = tokenize(&arg, ",");
   272  	char *data = tokenize(&arg, ",");
   273  	char *end;
   274  	if (!src || src[0] == '\0' || !dest || dest[0] == '\0' ||
   275  	    !type || type[0] == '\0') {
   276  		fprintf(stderr, "Bad mount: %s %s %s\n", src, dest, type);
   277  		exit(1);
   278  	}
   279  
   280  	/*
   281  	 * Fun edge case: the data option itself is comma delimited.  If there
   282  	 * were no more options, then arg would be set to NULL.  But if we had
   283  	 * more pending, it'll be pointing to the next token.  Back up and undo
   284  	 * the null byte so it'll be merged back.
   285  	 * An example:
   286  	 *   none,/tmp,tmpfs,0xe,mode=0755,uid=10,gid=10
   287  	 * The tokenize calls above will turn this memory into:
   288  	 *   none\0/tmp\0tmpfs\00xe\0mode=0755\0uid=10,gid=10
   289  	 * With data pointing at mode=0755 and arg pointing at uid=10,gid=10.
   290  	 */
   291  	if (arg != NULL)
   292  		arg[-1] = ',';
   293  
   294  	unsigned long mountflags;
   295  	if (flags == NULL || flags[0] == '\0') {
   296  		mountflags = 0;
   297  	} else {
   298  		end = NULL;
   299  		mountflags = parse_constant(flags, &end);
   300  		if (flags == end) {
   301  			fprintf(stderr, "Bad mount flags: %s\n", flags);
   302  			exit(1);
   303  		}
   304  	}
   305  
   306  	if (minijail_mount_with_data(j, src, dest, type,
   307  				     mountflags, data)) {
   308  		fprintf(stderr, "minijail_mount failed.\n");
   309  		exit(1);
   310  	}
   311  }
   312  
   313  static char *build_idmap(id_t id, id_t lowerid)
   314  {
   315  	int ret;
   316  	char *idmap = xmalloc(IDMAP_LEN);
   317  	ret = snprintf(idmap, IDMAP_LEN, "%d %d 1", id, lowerid);
   318  	if (ret < 0 || (size_t)ret >= IDMAP_LEN) {
   319  		free(idmap);
   320  		fprintf(stderr, "Could not build id map.\n");
   321  		exit(1);
   322  	}
   323  	return idmap;
   324  }
   325  
   326  static int has_cap_setgid(void)
   327  {
   328  	cap_t caps;
   329  	cap_flag_value_t cap_value;
   330  
   331  	if (!CAP_IS_SUPPORTED(CAP_SETGID))
   332  		return 0;
   333  
   334  	caps = cap_get_proc();
   335  	if (!caps) {
   336  		fprintf(stderr, "Could not get process' capabilities: %m\n");
   337  		exit(1);
   338  	}
   339  
   340  	if (cap_get_flag(caps, CAP_SETGID, CAP_EFFECTIVE, &cap_value)) {
   341  		fprintf(stderr, "Could not get the value of CAP_SETGID: %m\n");
   342  		exit(1);
   343  	}
   344  
   345  	if (cap_free(caps)) {
   346  		fprintf(stderr, "Could not free capabilities: %m\n");
   347  		exit(1);
   348  	}
   349  
   350  	return cap_value == CAP_SET;
   351  }
   352  
   353  static void set_ugid_mapping(struct minijail *j, int set_uidmap, uid_t uid,
   354  			     char *uidmap, int set_gidmap, gid_t gid,
   355  			     char *gidmap)
   356  {
   357  	if (set_uidmap) {
   358  		minijail_namespace_user(j);
   359  		minijail_namespace_pids(j);
   360  
   361  		if (!uidmap) {
   362  			/*
   363  			 * If no map is passed, map the current uid to the
   364  			 * chosen uid in the target namespace (or root, if none
   365  			 * was chosen).
   366  			 */
   367  			uidmap = build_idmap(uid, getuid());
   368  		}
   369  		if (0 != minijail_uidmap(j, uidmap)) {
   370  			fprintf(stderr, "Could not set uid map.\n");
   371  			exit(1);
   372  		}
   373  		free(uidmap);
   374  	}
   375  	if (set_gidmap) {
   376  		minijail_namespace_user(j);
   377  		minijail_namespace_pids(j);
   378  
   379  		if (!gidmap) {
   380  			/*
   381  			 * If no map is passed, map the current gid to the
   382  			 * chosen gid in the target namespace.
   383  			 */
   384  			gidmap = build_idmap(gid, getgid());
   385  		}
   386  		if (!has_cap_setgid()) {
   387  			/*
   388  			 * This means that we are not running as root,
   389  			 * so we also have to disable setgroups(2) to
   390  			 * be able to set the gid map.
   391  			 * See
   392  			 * http://man7.org/linux/man-pages/man7/user_namespaces.7.html
   393  			 */
   394  			minijail_namespace_user_disable_setgroups(j);
   395  		}
   396  		if (0 != minijail_gidmap(j, gidmap)) {
   397  			fprintf(stderr, "Could not set gid map.\n");
   398  			exit(1);
   399  		}
   400  		free(gidmap);
   401  	}
   402  }
   403  
   404  static void use_chroot(struct minijail *j, const char *path, int *chroot,
   405  		       int pivot_root)
   406  {
   407  	if (pivot_root) {
   408  		fprintf(stderr, "Could not set chroot because "
   409  				"'-P' was specified.\n");
   410  		exit(1);
   411  	}
   412  	if (minijail_enter_chroot(j, path)) {
   413  		fprintf(stderr, "Could not set chroot.\n");
   414  		exit(1);
   415  	}
   416  	*chroot = 1;
   417  }
   418  
   419  static void use_pivot_root(struct minijail *j, const char *path,
   420  			   int *pivot_root, int chroot)
   421  {
   422  	if (chroot) {
   423  		fprintf(stderr, "Could not set pivot_root because "
   424  				"'-C' was specified.\n");
   425  		exit(1);
   426  	}
   427  	if (minijail_enter_pivot_root(j, path)) {
   428  		fprintf(stderr, "Could not set pivot_root.\n");
   429  		exit(1);
   430  	}
   431  	minijail_namespace_vfs(j);
   432  	*pivot_root = 1;
   433  }
   434  
   435  static void use_profile(struct minijail *j, const char *profile,
   436  			int *pivot_root, int chroot, size_t *tmp_size)
   437  {
   438  	/* Note: New profiles should be added in minijail0_cli_unittest.cc. */
   439  
   440  	if (!strcmp(profile, "minimalistic-mountns") ||
   441  	    !strcmp(profile, "minimalistic-mountns-nodev")) {
   442  		minijail_namespace_vfs(j);
   443  		if (minijail_bind(j, "/", "/", 0)) {
   444  			fprintf(stderr, "minijail_bind(/) failed.\n");
   445  			exit(1);
   446  		}
   447  		if (minijail_bind(j, "/proc", "/proc", 0)) {
   448  			fprintf(stderr, "minijail_bind(/proc) failed.\n");
   449  			exit(1);
   450  		}
   451  		if (!strcmp(profile, "minimalistic-mountns")) {
   452  			if (minijail_bind(j, "/dev/log", "/dev/log", 0)) {
   453  				fprintf(stderr, "minijail_bind(/dev/log) failed.\n");
   454  				exit(1);
   455  			}
   456  			minijail_mount_dev(j);
   457  		}
   458  		if (!*tmp_size) {
   459  			/* Avoid clobbering |tmp_size| if it was already set. */
   460  			*tmp_size = DEFAULT_TMP_SIZE;
   461  		}
   462  		minijail_remount_proc_readonly(j);
   463  		use_pivot_root(j, DEFAULT_PIVOT_ROOT, pivot_root, chroot);
   464  	} else {
   465  		fprintf(stderr, "Unrecognized profile name '%s'\n", profile);
   466  		exit(1);
   467  	}
   468  }
   469  
   470  static void set_remount_mode(struct minijail *j, const char *mode)
   471  {
   472  	unsigned long msmode;
   473  	if (!strcmp(mode, "shared"))
   474  		msmode = MS_SHARED;
   475  	else if (!strcmp(mode, "private"))
   476  		msmode = MS_PRIVATE;
   477  	else if (!strcmp(mode, "slave"))
   478  		msmode = MS_SLAVE;
   479  	else if (!strcmp(mode, "unbindable"))
   480  		msmode = MS_UNBINDABLE;
   481  	else {
   482  		fprintf(stderr, "Unknown remount mode: '%s'\n", mode);
   483  		exit(1);
   484  	}
   485  	minijail_remount_mode(j, msmode);
   486  }
   487  
   488  static void read_seccomp_filter(const char *filter_path,
   489  				struct sock_fprog *filter)
   490  {
   491  	attribute_cleanup_fp FILE *f = fopen(filter_path, "re");
   492  	if (!f) {
   493  		fprintf(stderr, "failed to open %s: %m", filter_path);
   494  		exit(1);
   495  	}
   496  	off_t filter_size = 0;
   497  	if (fseeko(f, 0, SEEK_END) == -1 || (filter_size = ftello(f)) == -1) {
   498  		fprintf(stderr, "failed to get file size of %s: %m",
   499  			filter_path);
   500  		exit(1);
   501  	}
   502  	if (filter_size % sizeof(struct sock_filter) != 0) {
   503  		fprintf(stderr,
   504  			"filter size (%" PRId64
   505  			") of %s is not a multiple of %zu: %m",
   506  			filter_size, filter_path, sizeof(struct sock_filter));
   507  		exit(1);
   508  	}
   509  	rewind(f);
   510  
   511  	filter->len = filter_size / sizeof(struct sock_filter);
   512  	filter->filter = xmalloc(filter_size);
   513  	if (fread(filter->filter, sizeof(struct sock_filter), filter->len, f) !=
   514  	    filter->len) {
   515  		fprintf(stderr, "failed read %s: %m", filter_path);
   516  		exit(1);
   517  	}
   518  }
   519  
   520  static void usage(const char *progn)
   521  {
   522  	size_t i;
   523  	/* clang-format off */
   524  	printf("Usage: %s [-dGhHiIKlLnNprRstUvyYz]\n"
   525  	       "  [-a <table>]\n"
   526  	       "  [-b <src>[,[dest][,<writeable>]]] [-k <src>,<dest>,<type>[,<flags>[,<data>]]]\n"
   527  	       "  [-c <caps>] [-C <dir>] [-P <dir>] [-e[file]] [-f <file>] [-g <group>]\n"
   528  	       "  [-m[<uid> <loweruid> <count>]*] [-M[<gid> <lowergid> <count>]*] [--profile <name>]\n"
   529  	       "  [-R <type,cur,max>] [-S <file>] [-t[size]] [-T <type>] [-u <user>] [-V <file>]\n"
   530  	       "  <program> [args...]\n"
   531  	       "  -a <table>:   Use alternate syscall table <table>.\n"
   532  	       "  -b <...>:     Bind <src> to <dest> in chroot.\n"
   533  	       "                Multiple instances allowed.\n"
   534  	       "  -B <mask>:    Skip setting securebits in <mask> when restricting capabilities (-c).\n"
   535  	       "                By default, SECURE_NOROOT, SECURE_NO_SETUID_FIXUP, and \n"
   536  	       "                SECURE_KEEP_CAPS (together with their respective locks) are set.\n"
   537  	       "                There are eight securebits in total.\n"
   538  	       "  -k <...>:     Mount <src> at <dest> in chroot.\n"
   539  	       "                <flags> and <data> can be specified as in mount(2).\n"
   540  	       "                Multiple instances allowed.\n"
   541  	       "  -c <caps>:    Restrict caps to <caps>.\n"
   542  	       "  -C <dir>:     chroot(2) to <dir>.\n"
   543  	       "                Not compatible with -P.\n"
   544  	       "  -P <dir>:     pivot_root(2) to <dir> (implies -v).\n"
   545  	       "                Not compatible with -C.\n"
   546  	       "  --mount-dev,  Create a new /dev with a minimal set of device nodes (implies -v).\n"
   547  	       "           -d:  See the minijail0(1) man page for the exact set.\n"
   548  	       "  -e[file]:     Enter new network namespace, or existing one if |file| is provided.\n"
   549  	       "  -f <file>:    Write the pid of the jailed process to <file>.\n"
   550  	       "  -g <group>:   Change gid to <group>.\n"
   551  	       "  -G:           Inherit supplementary groups from new uid.\n"
   552  	       "                Not compatible with -y or --add-suppl-group.\n"
   553  	       "  -y:           Keep original uid's supplementary groups.\n"
   554  	       "                Not compatible with -G or --add-suppl-group.\n"
   555  	       "  --add-suppl-group <g>:Add <g> to the proccess' supplementary groups,\n"
   556  	       "                can be specified multiple times to add several groups.\n"
   557  	       "                Not compatible with -y or -G.\n"
   558  	       "  -h:           Help (this message).\n"
   559  	       "  -H:           Seccomp filter help message.\n"
   560  	       "  -i:           Exit immediately after fork(2). The jailed process will run\n"
   561  	       "                in the background.\n"
   562  	       "  -I:           Run <program> as init (pid 1) inside a new pid namespace (implies -p).\n"
   563  	       "  -K:           Do not change share mode of any existing mounts.\n"
   564  	       "  -K<mode>:     Mark all existing mounts as <mode> instead of MS_PRIVATE.\n"
   565  	       "  -l:           Enter new IPC namespace.\n"
   566  	       "  -L:           Report blocked syscalls when using seccomp filter.\n"
   567  	       "                If the kernel does not support SECCOMP_RET_LOG,\n"
   568  	       "                forces the following syscalls to be allowed:\n"
   569  	       "                  ", progn);
   570  	/* clang-format on */
   571  	for (i = 0; i < log_syscalls_len; i++)
   572  		printf("%s ", log_syscalls[i]);
   573  
   574  	/* clang-format off */
   575  	printf("\n"
   576  	       "  -m[map]:      Set the uid map of a user namespace (implies -pU).\n"
   577  	       "                Same arguments as newuidmap(1), multiple mappings should be separated by ',' (comma).\n"
   578  	       "                With no mapping, map the current uid to root inside the user namespace.\n"
   579  	       "                Not compatible with -b without the 'writable' option.\n"
   580  	       "  -M[map]:      Set the gid map of a user namespace (implies -pU).\n"
   581  	       "                Same arguments as newgidmap(1), multiple mappings should be separated by ',' (comma).\n"
   582  	       "                With no mapping, map the current gid to root inside the user namespace.\n"
   583  	       "                Not compatible with -b without the 'writable' option.\n"
   584  	       "  -n:           Set no_new_privs.\n"
   585  	       "  -N:           Enter a new cgroup namespace.\n"
   586  	       "  -p:           Enter new pid namespace (implies -vr).\n"
   587  	       "  -r:           Remount /proc read-only (implies -v).\n"
   588  	       "  -R:           Set rlimits, can be specified multiple times.\n"
   589  	       "  -s:           Use seccomp mode 1 (not the same as -S).\n"
   590  	       "  -S <file>:    Set seccomp filter using <file>.\n"
   591  	       "                E.g., '-S /usr/share/filters/<prog>.$(uname -m)'.\n"
   592  	       "                Requires -n when not running as root.\n"
   593  	       "  -t[size]:     Mount tmpfs at /tmp (implies -v).\n"
   594  	       "                Optional argument specifies size (default \"64M\").\n"
   595  	       "  -T <type>:    Assume <program> is a <type> ELF binary; <type> can be 'static' or 'dynamic'.\n"
   596  	       "                This will avoid accessing <program> binary before execve(2).\n"
   597  	       "                Type 'static' will avoid preload hooking.\n"
   598  	       "  -u <user>:    Change uid to <user>.\n"
   599  	       "  -U:           Enter new user namespace (implies -p).\n"
   600  	       "  -v:           Enter new mount namespace.\n"
   601  	       "  -V <file>:    Enter specified mount namespace.\n"
   602  	       "  -w:           Create and join a new anonymous session keyring.\n"
   603  	       "  -Y:           Synchronize seccomp filters across thread group.\n"
   604  	       "  -z:           Don't forward signals to jailed process.\n"
   605  	       "  --ambient:    Raise ambient capabilities. Requires -c.\n"
   606  	       "  --uts[=name]: Enter a new UTS namespace (and set hostname).\n"
   607  	       "  --logging=<s>:Use <s> as the logging system.\n"
   608  	       "                <s> must be 'auto' (default), 'syslog', or 'stderr'.\n"
   609  	       "  --profile <p>:Configure minijail0 to run with the <p> sandboxing profile,\n"
   610  	       "                which is a convenient way to express multiple flags\n"
   611  	       "                that are typically used together.\n"
   612  	       "                See the minijail0(1) man page for the full list.\n"
   613  	       "  --preload-library=<f>:Overrides the path to \"" PRELOADPATH "\".\n"
   614  	       "                This is only really useful for local testing.\n"
   615  	       "  --seccomp-bpf-binary=<f>:Set a pre-compiled seccomp filter using <f>.\n"
   616  	       "                E.g., '-S /usr/share/filters/<prog>.$(uname -m).bpf'.\n"
   617  	       "                Requires -n when not running as root.\n"
   618  	       "                The user is responsible for ensuring that the binary\n"
   619  	       "                was compiled for the correct architecture / kernel version.\n"
   620  	       "  --allow-speculative-execution:Allow speculative execution and disable\n"
   621  	       "                mitigations for speculative execution attacks.\n");
   622  	/* clang-format on */
   623  }
   624  
   625  static void seccomp_filter_usage(const char *progn)
   626  {
   627  	const struct syscall_entry *entry = syscall_table;
   628  	printf("Usage: %s -S <policy.file> <program> [args...]\n\n"
   629  	       "System call names supported:\n",
   630  	       progn);
   631  	for (; entry->name && entry->nr >= 0; ++entry)
   632  		printf("  %s [%d]\n", entry->name, entry->nr);
   633  	printf("\nSee minijail0(5) for example policies.\n");
   634  }
   635  
   636  int parse_args(struct minijail *j, int argc, char *const argv[],
   637  	       int *exit_immediately, ElfType *elftype,
   638  	       const char **preload_path)
   639  {
   640  	int opt;
   641  	int use_seccomp_filter = 0, use_seccomp_filter_binary = 0;
   642  	int forward = 1;
   643  	int binding = 0;
   644  	int chroot = 0, pivot_root = 0;
   645  	int mount_ns = 0, change_remount = 0;
   646  	const char *remount_mode = NULL;
   647  	int inherit_suppl_gids = 0, keep_suppl_gids = 0;
   648  	int caps = 0, ambient_caps = 0;
   649  	int seccomp = -1;
   650  	bool use_uid = false, use_gid = false;
   651  	uid_t uid = 0;
   652  	gid_t gid = 0;
   653  	gid_t *suppl_gids = NULL;
   654  	size_t suppl_gids_count = 0;
   655  	char *uidmap = NULL, *gidmap = NULL;
   656  	int set_uidmap = 0, set_gidmap = 0;
   657  	size_t tmp_size = 0;
   658  	const char *filter_path = NULL;
   659  	int log_to_stderr = -1;
   660  
   661  	const char *optstring =
   662  	    "+u:g:sS:c:C:P:b:B:V:f:m::M::k:a:e::R:T:vrGhHinNplLt::IUK::wyYzd";
   663  	/* clang-format off */
   664  	const struct option long_options[] = {
   665  		{"help", no_argument, 0, 'h'},
   666  		{"mount-dev", no_argument, 0, 'd'},
   667  		{"ambient", no_argument, 0, 128},
   668  		{"uts", optional_argument, 0, 129},
   669  		{"logging", required_argument, 0, 130},
   670  		{"profile", required_argument, 0, 131},
   671  		{"preload-library", required_argument, 0, 132},
   672  		{"seccomp-bpf-binary", required_argument, 0, 133},
   673  		{"add-suppl-group", required_argument, 0, 134},
   674  		{"allow-speculative-execution", no_argument, 0, 135},
   675  		{0, 0, 0, 0},
   676  	};
   677  	/* clang-format on */
   678  
   679  	while ((opt = getopt_long(argc, argv, optstring, long_options, NULL)) !=
   680  	       -1) {
   681  		switch (opt) {
   682  		case 'u':
   683  			if (use_uid) {
   684  				fprintf(stderr,
   685  					"-u provided multiple times.\n");
   686  				exit(1);
   687  			}
   688  			use_uid = true;
   689  			set_user(j, optarg, &uid, &gid);
   690  			break;
   691  		case 'g':
   692  			if (use_gid) {
   693  				fprintf(stderr,
   694  					"-g provided multiple times.\n");
   695  				exit(1);
   696  			}
   697  			use_gid = true;
   698  			set_group(j, optarg, &gid);
   699  			break;
   700  		case 'n':
   701  			minijail_no_new_privs(j);
   702  			break;
   703  		case 's':
   704  			if (seccomp != -1 && seccomp != 1) {
   705  				fprintf(stderr,
   706  					"Do not use -s, -S, or "
   707  					"--seccomp-bpf-binary together.\n");
   708  				exit(1);
   709  			}
   710  			seccomp = 1;
   711  			minijail_use_seccomp(j);
   712  			break;
   713  		case 'S':
   714  			if (seccomp != -1 && seccomp != 2) {
   715  				fprintf(stderr,
   716  					"Do not use -s, -S, or "
   717  					"--seccomp-bpf-binary together.\n");
   718  				exit(1);
   719  			}
   720  			seccomp = 2;
   721  			minijail_use_seccomp_filter(j);
   722  			filter_path = optarg;
   723  			use_seccomp_filter = 1;
   724  			break;
   725  		case 'l':
   726  			minijail_namespace_ipc(j);
   727  			break;
   728  		case 'L':
   729  			minijail_log_seccomp_filter_failures(j);
   730  			break;
   731  		case 'b':
   732  			add_binding(j, optarg);
   733  			binding = 1;
   734  			break;
   735  		case 'B':
   736  			skip_securebits(j, optarg);
   737  			break;
   738  		case 'c':
   739  			caps = 1;
   740  			use_caps(j, optarg);
   741  			break;
   742  		case 'C':
   743  			use_chroot(j, optarg, &chroot, pivot_root);
   744  			break;
   745  		case 'k':
   746  			add_mount(j, optarg);
   747  			break;
   748  		case 'K':
   749  			remount_mode = optarg;
   750  			change_remount = 1;
   751  			break;
   752  		case 'P':
   753  			use_pivot_root(j, optarg, &pivot_root, chroot);
   754  			break;
   755  		case 'f':
   756  			if (0 != minijail_write_pid_file(j, optarg)) {
   757  				fprintf(stderr,
   758  					"Could not prepare pid file path.\n");
   759  				exit(1);
   760  			}
   761  			break;
   762  		case 't':
   763  			minijail_namespace_vfs(j);
   764  			if (!tmp_size) {
   765  				/*
   766  				 * Avoid clobbering |tmp_size| if it was already
   767  				 * set.
   768  				 */
   769  				tmp_size = DEFAULT_TMP_SIZE;
   770  			}
   771  			if (optarg != NULL &&
   772  			    0 != parse_size(&tmp_size, optarg)) {
   773  				fprintf(stderr, "Invalid /tmp tmpfs size.\n");
   774  				exit(1);
   775  			}
   776  			break;
   777  		case 'v':
   778  			minijail_namespace_vfs(j);
   779  			/*
   780  			 * Set the default mount propagation in the command-line
   781  			 * tool to MS_SLAVE.
   782  			 *
   783  			 * When executing the sandboxed program in a new mount
   784  			 * namespace the Minijail library will by default
   785  			 * remount all mounts with the MS_PRIVATE flag. While
   786  			 * this is an appropriate, safe default for the library,
   787  			 * MS_PRIVATE can be problematic: unmount events will
   788  			 * not propagate into mountpoints marked as MS_PRIVATE.
   789  			 * This means that if a mount is unmounted in the root
   790  			 * mount namespace, it will not be unmounted in the
   791  			 * non-root mount namespace.
   792  			 * This in turn can be problematic because activity in
   793  			 * the non-root mount namespace can now directly
   794  			 * influence the root mount namespace (e.g. preventing
   795  			 * re-mounts of said mount), which would be a privilege
   796  			 * inversion.
   797  			 *
   798  			 * Setting the default in the command-line to MS_SLAVE
   799  			 * will still prevent mounts from leaking out of the
   800  			 * non-root mount namespace but avoid these
   801  			 * privilege-inversion issues.
   802  			 * For cases where mounts should not flow *into* the
   803  			 * namespace either, the user can pass -Kprivate.
   804  			 * Note that mounts are marked as MS_PRIVATE by default
   805  			 * by the kernel, so unless the init process (like
   806  			 * systemd) or something else marks them as shared, this
   807  			 * won't do anything.
   808  			 */
   809  			minijail_remount_mode(j, MS_SLAVE);
   810  			mount_ns = 1;
   811  			break;
   812  		case 'V':
   813  			minijail_namespace_enter_vfs(j, optarg);
   814  			break;
   815  		case 'r':
   816  			minijail_remount_proc_readonly(j);
   817  			break;
   818  		case 'G':
   819  			if (keep_suppl_gids) {
   820  				fprintf(stderr,
   821  					"-y and -G are not compatible.\n");
   822  				exit(1);
   823  			}
   824  			minijail_inherit_usergroups(j);
   825  			inherit_suppl_gids = 1;
   826  			break;
   827  		case 'y':
   828  			if (inherit_suppl_gids) {
   829  				fprintf(stderr,
   830  					"-y and -G are not compatible.\n");
   831  				exit(1);
   832  			}
   833  			minijail_keep_supplementary_gids(j);
   834  			keep_suppl_gids = 1;
   835  			break;
   836  		case 'N':
   837  			minijail_namespace_cgroups(j);
   838  			break;
   839  		case 'p':
   840  			minijail_namespace_pids(j);
   841  			break;
   842  		case 'e':
   843  			if (optarg)
   844  				minijail_namespace_enter_net(j, optarg);
   845  			else
   846  				minijail_namespace_net(j);
   847  			break;
   848  		case 'i':
   849  			*exit_immediately = 1;
   850  			break;
   851  		case 'H':
   852  			seccomp_filter_usage(argv[0]);
   853  			exit(0);
   854  		case 'I':
   855  			minijail_namespace_pids(j);
   856  			minijail_run_as_init(j);
   857  			break;
   858  		case 'U':
   859  			minijail_namespace_user(j);
   860  			minijail_namespace_pids(j);
   861  			break;
   862  		case 'm':
   863  			set_uidmap = 1;
   864  			if (uidmap) {
   865  				free(uidmap);
   866  				uidmap = NULL;
   867  			}
   868  			if (optarg)
   869  				uidmap = xstrdup(optarg);
   870  			break;
   871  		case 'M':
   872  			set_gidmap = 1;
   873  			if (gidmap) {
   874  				free(gidmap);
   875  				gidmap = NULL;
   876  			}
   877  			if (optarg)
   878  				gidmap = xstrdup(optarg);
   879  			break;
   880  		case 'a':
   881  			if (0 != minijail_use_alt_syscall(j, optarg)) {
   882  				fprintf(stderr,
   883  					"Could not set alt-syscall table.\n");
   884  				exit(1);
   885  			}
   886  			break;
   887  		case 'R':
   888  			add_rlimit(j, optarg);
   889  			break;
   890  		case 'T':
   891  			if (!strcmp(optarg, "static"))
   892  				*elftype = ELFSTATIC;
   893  			else if (!strcmp(optarg, "dynamic"))
   894  				*elftype = ELFDYNAMIC;
   895  			else {
   896  				fprintf(stderr, "ELF type must be 'static' or "
   897  						"'dynamic'.\n");
   898  				exit(1);
   899  			}
   900  			break;
   901  		case 'w':
   902  			minijail_new_session_keyring(j);
   903  			break;
   904  		case 'Y':
   905  			minijail_set_seccomp_filter_tsync(j);
   906  			break;
   907  		case 'z':
   908  			forward = 0;
   909  			break;
   910  		case 'd':
   911  			minijail_namespace_vfs(j);
   912  			minijail_mount_dev(j);
   913  			break;
   914  		/* Long options. */
   915  		case 128: /* Ambient caps. */
   916  			ambient_caps = 1;
   917  			minijail_set_ambient_caps(j);
   918  			break;
   919  		case 129: /* UTS/hostname namespace. */
   920  			minijail_namespace_uts(j);
   921  			if (optarg)
   922  				minijail_namespace_set_hostname(j, optarg);
   923  			break;
   924  		case 130: /* Logging. */
   925  			if (!strcmp(optarg, "auto")) {
   926  				log_to_stderr = -1;
   927  			} else if (!strcmp(optarg, "syslog")) {
   928  				log_to_stderr = 0;
   929  			} else if (!strcmp(optarg, "stderr")) {
   930  				log_to_stderr = 1;
   931  			} else {
   932  				fprintf(stderr, "--logger must be 'syslog' or "
   933  						"'stderr'.\n");
   934  				exit(1);
   935  			}
   936  			break;
   937  		case 131: /* Profile */
   938  			use_profile(j, optarg, &pivot_root, chroot, &tmp_size);
   939  			break;
   940  		case 132: /* PRELOADPATH */
   941  			*preload_path = optarg;
   942  			break;
   943  		case 133: /* seccomp-bpf binary. */
   944  			if (seccomp != -1 && seccomp != 3) {
   945  				fprintf(stderr,
   946  					"Do not use -s, -S, or "
   947  					"--seccomp-bpf-binary together.\n");
   948  				exit(1);
   949  			}
   950  			seccomp = 3;
   951  			minijail_use_seccomp_filter(j);
   952  			filter_path = optarg;
   953  			use_seccomp_filter_binary = 1;
   954  			break;
   955  		case 134:
   956  			suppl_group_add(&suppl_gids_count, &suppl_gids,
   957  			                optarg);
   958  			break;
   959  		case 135:
   960  			minijail_set_seccomp_filter_allow_speculation(j);
   961  			break;
   962  		default:
   963  			usage(argv[0]);
   964  			exit(opt == 'h' ? 0 : 1);
   965  		}
   966  	}
   967  
   968  	if (log_to_stderr == -1) {
   969  		/* Autodetect default logging output. */
   970  		log_to_stderr = isatty(STDIN_FILENO) ? 1 : 0;
   971  	}
   972  	if (log_to_stderr) {
   973  		init_logging(LOG_TO_FD, STDERR_FILENO, LOG_INFO);
   974  		/*
   975  		 * When logging to stderr, ensure the FD survives the jailing.
   976  		 */
   977  		if (0 !=
   978  		    minijail_preserve_fd(j, STDERR_FILENO, STDERR_FILENO)) {
   979  			fprintf(stderr, "Could not preserve stderr.\n");
   980  			exit(1);
   981  		}
   982  	}
   983  
   984  	/* Set up uid/gid mapping. */
   985  	if (set_uidmap || set_gidmap) {
   986  		set_ugid_mapping(j, set_uidmap, uid, uidmap, set_gidmap, gid,
   987  				 gidmap);
   988  	}
   989  
   990  	/* Can only set ambient caps when using regular caps. */
   991  	if (ambient_caps && !caps) {
   992  		fprintf(stderr, "Can't set ambient capabilities (--ambient) "
   993  				"without actually using capabilities (-c).\n");
   994  		exit(1);
   995  	}
   996  
   997  	/* Set up signal handlers in minijail unless asked not to. */
   998  	if (forward)
   999  		minijail_forward_signals(j);
  1000  
  1001  	/*
  1002  	 * Only allow bind mounts when entering a chroot, using pivot_root, or
  1003  	 * a new mount namespace.
  1004  	 */
  1005  	if (binding && !(chroot || pivot_root || mount_ns)) {
  1006  		fprintf(stderr, "Bind mounts require a chroot, pivot_root, or "
  1007  				" new mount namespace.\n");
  1008  		exit(1);
  1009  	}
  1010  
  1011  	/*
  1012  	 * / is only remounted when entering a new mount namespace, so unless
  1013  	 * that's set there is no need for the -K/-K<mode> flags.
  1014  	 */
  1015  	if (change_remount && !mount_ns) {
  1016  		fprintf(stderr, "No need to use -K (skip remounting '/') or "
  1017  				"-K<mode> (remount '/' as <mode>)\n"
  1018  				"without -v (new mount namespace).\n"
  1019  				"Do you need to add '-v' explicitly?\n");
  1020  		exit(1);
  1021  	}
  1022  
  1023  	/* Configure the remount flag here to avoid having -v override it. */
  1024  	if (change_remount) {
  1025  		if (remount_mode != NULL) {
  1026  			set_remount_mode(j, remount_mode);
  1027  		} else {
  1028  			minijail_skip_remount_private(j);
  1029  		}
  1030  	}
  1031  
  1032  	/*
  1033  	 * Proceed in setting the supplementary gids specified on the
  1034  	 * cmdline options.
  1035  	 */
  1036  	if (suppl_gids_count) {
  1037  		minijail_set_supplementary_gids(j, suppl_gids_count,
  1038  		                                suppl_gids);
  1039  		free(suppl_gids);
  1040  	}
  1041  
  1042  	/*
  1043  	 * We parse seccomp filters here to make sure we've collected all
  1044  	 * cmdline options.
  1045  	 */
  1046  	if (use_seccomp_filter) {
  1047  		minijail_parse_seccomp_filters(j, filter_path);
  1048  	} else if (use_seccomp_filter_binary) {
  1049  		struct sock_fprog filter;
  1050  		read_seccomp_filter(filter_path, &filter);
  1051  		minijail_set_seccomp_filters(j, &filter);
  1052  		free((void *)filter.filter);
  1053  	}
  1054  
  1055  	/* Mount a tmpfs under /tmp and set its size. */
  1056  	if (tmp_size)
  1057  		minijail_mount_tmp_size(j, tmp_size);
  1058  
  1059  	/*
  1060  	 * There should be at least one additional unparsed argument: the
  1061  	 * executable name.
  1062  	 */
  1063  	if (argc == optind) {
  1064  		usage(argv[0]);
  1065  		exit(1);
  1066  	}
  1067  
  1068  	if (*elftype == ELFERROR) {
  1069  		/*
  1070  		 * -T was not specified.
  1071  		 * Get the path to the program adjusted for changing root.
  1072  		 */
  1073  		char *program_path =
  1074  		    minijail_get_original_path(j, argv[optind]);
  1075  
  1076  		/* Check that we can access the target program. */
  1077  		if (access(program_path, X_OK)) {
  1078  			fprintf(stderr,
  1079  				"Target program '%s' is not accessible.\n",
  1080  				argv[optind]);
  1081  			exit(1);
  1082  		}
  1083  
  1084  		/* Check if target is statically or dynamically linked. */
  1085  		*elftype = get_elf_linkage(program_path);
  1086  		free(program_path);
  1087  	}
  1088  
  1089  	/*
  1090  	 * Setting capabilities need either a dynamically-linked binary, or the
  1091  	 * use of ambient capabilities for them to be able to survive an
  1092  	 * execve(2).
  1093  	 */
  1094  	if (caps && *elftype == ELFSTATIC && !ambient_caps) {
  1095  		fprintf(stderr, "Can't run statically-linked binaries with "
  1096  				"capabilities (-c) without also setting "
  1097  				"ambient capabilities. Try passing "
  1098  				"--ambient.\n");
  1099  		exit(1);
  1100  	}
  1101  
  1102  	return optind;
  1103  }