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