blob: ea66d7cda5f7dfca30fa0f7d07d49a83bda880e9 [file] [log] [blame]
Mike Frysinger5ef22ca2018-01-20 13:42:10 -05001/* 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 <stdio.h>
10#include <stdlib.h>
11#include <string.h>
12#include <sys/capability.h>
Mike Frysinger785b1c32018-02-23 15:47:24 -050013#include <sys/mount.h>
Mike Frysinger5ef22ca2018-01-20 13:42:10 -050014#include <sys/types.h>
15#include <unistd.h>
16
17#include "libminijail.h"
18#include "libsyscalls.h"
19
20#include "elfparse.h"
21#include "minijail0_cli.h"
22#include "system.h"
23#include "util.h"
24
25#define IDMAP_LEN 32U
26#define DEFAULT_TMP_SIZE (64 * 1024 * 1024)
27
28static void set_user(struct minijail *j, const char *arg, uid_t *out_uid,
29 gid_t *out_gid)
30{
31 char *end = NULL;
32 int uid = strtod(arg, &end);
33 if (!*end && *arg) {
34 *out_uid = uid;
35 minijail_change_uid(j, uid);
36 return;
37 }
38
39 if (lookup_user(arg, out_uid, out_gid)) {
40 fprintf(stderr, "Bad user: '%s'\n", arg);
41 exit(1);
42 }
43
44 if (minijail_change_user(j, arg)) {
45 fprintf(stderr, "Bad user: '%s'\n", arg);
46 exit(1);
47 }
48}
49
50static void set_group(struct minijail *j, const char *arg, gid_t *out_gid)
51{
52 char *end = NULL;
53 int gid = strtod(arg, &end);
54 if (!*end && *arg) {
55 *out_gid = gid;
56 minijail_change_gid(j, gid);
57 return;
58 }
59
60 if (lookup_group(arg, out_gid)) {
61 fprintf(stderr, "Bad group: '%s'\n", arg);
62 exit(1);
63 }
64
65 if (minijail_change_group(j, arg)) {
66 fprintf(stderr, "Bad group: '%s'\n", arg);
67 exit(1);
68 }
69}
70
71static void skip_securebits(struct minijail *j, const char *arg)
72{
73 uint64_t securebits_skip_mask;
74 char *end = NULL;
75 securebits_skip_mask = strtoull(arg, &end, 16);
76 if (*end) {
77 fprintf(stderr, "Invalid securebit mask: '%s'\n", arg);
78 exit(1);
79 }
80 minijail_skip_setting_securebits(j, securebits_skip_mask);
81}
82
83static void use_caps(struct minijail *j, const char *arg)
84{
Luis Hector Chavezdabc4302018-09-21 09:21:47 -070085 uint64_t caps = 0;
86 cap_t parsed_caps = cap_from_text(arg);
87
88 if (parsed_caps != NULL) {
89 unsigned int i;
90 const uint64_t one = 1;
91 cap_flag_value_t cap_value;
92 unsigned int last_valid_cap = get_last_valid_cap();
93
94 for (i = 0; i <= last_valid_cap; ++i) {
95 if (cap_get_flag(parsed_caps, i, CAP_EFFECTIVE,
96 &cap_value)) {
97 fprintf(stderr,
98 "Could not get the value of "
99 "the %d-th capability: %m\n",
100 i);
101 exit(1);
102 }
103 if (cap_value == CAP_SET)
104 caps |= (one << i);
105 }
106 cap_free(parsed_caps);
107 } else {
108 char *end = NULL;
109 caps = strtoull(arg, &end, 16);
110 if (*end) {
111 fprintf(stderr, "Invalid cap set: '%s'\n", arg);
112 exit(1);
113 }
Mike Frysinger5ef22ca2018-01-20 13:42:10 -0500114 }
Luis Hector Chavezdabc4302018-09-21 09:21:47 -0700115
Mike Frysinger5ef22ca2018-01-20 13:42:10 -0500116 minijail_use_caps(j, caps);
117}
118
119static void add_binding(struct minijail *j, char *arg)
120{
121 char *src = tokenize(&arg, ",");
122 char *dest = tokenize(&arg, ",");
123 char *flags = tokenize(&arg, ",");
124 if (!src || src[0] == '\0' || arg != NULL) {
125 fprintf(stderr, "Bad binding: %s %s\n", src, dest);
126 exit(1);
127 }
128 if (dest == NULL || dest[0] == '\0')
129 dest = src;
130 if (flags == NULL || flags[0] == '\0')
131 flags = "0";
132 if (minijail_bind(j, src, dest, atoi(flags))) {
133 fprintf(stderr, "minijail_bind failed.\n");
134 exit(1);
135 }
136}
137
138static void add_rlimit(struct minijail *j, char *arg)
139{
140 char *type = tokenize(&arg, ",");
141 char *cur = tokenize(&arg, ",");
142 char *max = tokenize(&arg, ",");
Luis Hector Chavez7058a2d2018-01-29 08:41:34 -0800143 char *end;
Mike Frysinger5ef22ca2018-01-20 13:42:10 -0500144 if (!type || type[0] == '\0' || !cur || cur[0] == '\0' ||
145 !max || max[0] == '\0' || arg != NULL) {
146 fprintf(stderr, "Bad rlimit '%s'.\n", arg);
147 exit(1);
148 }
Luis Hector Chavez7058a2d2018-01-29 08:41:34 -0800149 rlim_t cur_rlim;
150 rlim_t max_rlim;
151 if (!strcmp(cur, "unlimited")) {
152 cur_rlim = RLIM_INFINITY;
153 } else {
154 end = NULL;
Mike Frysingere34d7fe2018-05-23 04:18:30 -0400155 cur_rlim = strtoul(cur, &end, 0);
Luis Hector Chavez7058a2d2018-01-29 08:41:34 -0800156 if (*end) {
157 fprintf(stderr, "Bad soft limit: '%s'.\n", cur);
158 exit(1);
159 }
160 }
161 if (!strcmp(max, "unlimited")) {
162 max_rlim = RLIM_INFINITY;
163 } else {
164 end = NULL;
Mike Frysingere34d7fe2018-05-23 04:18:30 -0400165 max_rlim = strtoul(max, &end, 0);
Luis Hector Chavez7058a2d2018-01-29 08:41:34 -0800166 if (*end) {
167 fprintf(stderr, "Bad hard limit: '%s'.\n", max);
168 exit(1);
169 }
170 }
Mike Frysingere34d7fe2018-05-23 04:18:30 -0400171
172 end = NULL;
173 int resource = parse_single_constant(type, &end);
174 if (type == end) {
175 fprintf(stderr, "Bad rlimit: '%s'.\n", type);
176 exit(1);
177 }
178
179 if (minijail_rlimit(j, resource, cur_rlim, max_rlim)) {
Mike Frysinger5ef22ca2018-01-20 13:42:10 -0500180 fprintf(stderr, "minijail_rlimit '%s,%s,%s' failed.\n", type,
181 cur, max);
182 exit(1);
183 }
184}
185
186static void add_mount(struct minijail *j, char *arg)
187{
188 char *src = tokenize(&arg, ",");
189 char *dest = tokenize(&arg, ",");
190 char *type = tokenize(&arg, ",");
191 char *flags = tokenize(&arg, ",");
192 char *data = tokenize(&arg, ",");
Mike Frysinger6f4e93d2018-05-23 05:05:35 -0400193 char *end;
Mike Frysinger5ef22ca2018-01-20 13:42:10 -0500194 if (!src || src[0] == '\0' || !dest || dest[0] == '\0' ||
Mike Frysinger4f3e09f2018-01-24 18:01:16 -0500195 !type || type[0] == '\0') {
Mike Frysinger5ef22ca2018-01-20 13:42:10 -0500196 fprintf(stderr, "Bad mount: %s %s %s\n", src, dest, type);
197 exit(1);
198 }
Mike Frysinger4f3e09f2018-01-24 18:01:16 -0500199
200 /*
201 * Fun edge case: the data option itself is comma delimited. If there
202 * were no more options, then arg would be set to NULL. But if we had
203 * more pending, it'll be pointing to the next token. Back up and undo
204 * the null byte so it'll be merged back.
205 * An example:
206 * none,/tmp,tmpfs,0xe,mode=0755,uid=10,gid=10
207 * The tokenize calls above will turn this memory into:
208 * none\0/tmp\0tmpfs\00xe\0mode=0755\0uid=10,gid=10
209 * With data pointing at mode=0755 and arg pointing at uid=10,gid=10.
210 */
211 if (arg != NULL)
212 arg[-1] = ',';
213
Mike Frysinger6f4e93d2018-05-23 05:05:35 -0400214 unsigned long mountflags;
215 if (flags == NULL || flags[0] == '\0') {
216 mountflags = 0;
217 } else {
218 end = NULL;
219 mountflags = parse_constant(flags, &end);
220 if (flags == end) {
221 fprintf(stderr, "Bad mount flags: %s\n", flags);
222 exit(1);
223 }
224 }
225
Mike Frysinger5ef22ca2018-01-20 13:42:10 -0500226 if (minijail_mount_with_data(j, src, dest, type,
Mike Frysinger6f4e93d2018-05-23 05:05:35 -0400227 mountflags, data)) {
Mike Frysinger5ef22ca2018-01-20 13:42:10 -0500228 fprintf(stderr, "minijail_mount failed.\n");
229 exit(1);
230 }
231}
232
233static char *build_idmap(id_t id, id_t lowerid)
234{
235 int ret;
236 char *idmap = malloc(IDMAP_LEN);
237 ret = snprintf(idmap, IDMAP_LEN, "%d %d 1", id, lowerid);
238 if (ret < 0 || (size_t)ret >= IDMAP_LEN) {
239 free(idmap);
240 fprintf(stderr, "Could not build id map.\n");
241 exit(1);
242 }
243 return idmap;
244}
245
246static int has_cap_setgid(void)
247{
248 cap_t caps;
249 cap_flag_value_t cap_value;
250
251 if (!CAP_IS_SUPPORTED(CAP_SETGID))
252 return 0;
253
254 caps = cap_get_proc();
255 if (!caps) {
256 fprintf(stderr, "Could not get process' capabilities: %m\n");
257 exit(1);
258 }
259
260 if (cap_get_flag(caps, CAP_SETGID, CAP_EFFECTIVE, &cap_value)) {
261 fprintf(stderr, "Could not get the value of CAP_SETGID: %m\n");
262 exit(1);
263 }
264
265 if (cap_free(caps)) {
266 fprintf(stderr, "Could not free capabilities: %m\n");
267 exit(1);
268 }
269
270 return cap_value == CAP_SET;
271}
272
273static void set_ugid_mapping(struct minijail *j, int set_uidmap, uid_t uid,
274 char *uidmap, int set_gidmap, gid_t gid,
275 char *gidmap)
276{
277 if (set_uidmap) {
278 minijail_namespace_user(j);
279 minijail_namespace_pids(j);
280
281 if (!uidmap) {
282 /*
283 * If no map is passed, map the current uid to the
284 * chosen uid in the target namespace (or root, if none
285 * was chosen).
286 */
287 uidmap = build_idmap(uid, getuid());
288 }
289 if (0 != minijail_uidmap(j, uidmap)) {
290 fprintf(stderr, "Could not set uid map.\n");
291 exit(1);
292 }
293 free(uidmap);
294 }
295 if (set_gidmap) {
296 minijail_namespace_user(j);
297 minijail_namespace_pids(j);
298
299 if (!gidmap) {
300 /*
301 * If no map is passed, map the current gid to the
302 * chosen gid in the target namespace.
303 */
304 gidmap = build_idmap(gid, getgid());
305 }
306 if (!has_cap_setgid()) {
307 /*
308 * This means that we are not running as root,
309 * so we also have to disable setgroups(2) to
310 * be able to set the gid map.
311 * See
312 * http://man7.org/linux/man-pages/man7/user_namespaces.7.html
313 */
314 minijail_namespace_user_disable_setgroups(j);
315 }
316 if (0 != minijail_gidmap(j, gidmap)) {
317 fprintf(stderr, "Could not set gid map.\n");
318 exit(1);
319 }
320 free(gidmap);
321 }
322}
323
324static void use_chroot(struct minijail *j, const char *path, int *chroot,
325 int pivot_root)
326{
327 if (pivot_root) {
328 fprintf(stderr, "Could not set chroot because "
329 "'-P' was specified.\n");
330 exit(1);
331 }
332 if (minijail_enter_chroot(j, path)) {
333 fprintf(stderr, "Could not set chroot.\n");
334 exit(1);
335 }
336 *chroot = 1;
337}
338
339static void use_pivot_root(struct minijail *j, const char *path,
340 int *pivot_root, int chroot)
341{
342 if (chroot) {
343 fprintf(stderr, "Could not set pivot_root because "
344 "'-C' was specified.\n");
345 exit(1);
346 }
347 if (minijail_enter_pivot_root(j, path)) {
348 fprintf(stderr, "Could not set pivot_root.\n");
349 exit(1);
350 }
351 minijail_namespace_vfs(j);
352 *pivot_root = 1;
353}
354
355static void use_profile(struct minijail *j, const char *profile,
356 int *pivot_root, int chroot, size_t *tmp_size)
357{
Mike Frysinger4d2a81e2018-01-22 16:43:33 -0500358 /* Note: New profiles should be added in minijail0_cli_unittest.cc. */
359
Mike Frysinger5ef22ca2018-01-20 13:42:10 -0500360 if (!strcmp(profile, "minimalistic-mountns")) {
361 minijail_namespace_vfs(j);
362 if (minijail_bind(j, "/", "/", 0)) {
363 fprintf(stderr, "minijail_bind failed.\n");
364 exit(1);
365 }
366 if (minijail_bind(j, "/proc", "/proc", 0)) {
367 fprintf(stderr, "minijail_bind failed.\n");
368 exit(1);
369 }
370 minijail_mount_dev(j);
371 if (!*tmp_size) {
372 /* Avoid clobbering |tmp_size| if it was already set. */
373 *tmp_size = DEFAULT_TMP_SIZE;
374 }
375 minijail_remount_proc_readonly(j);
376 use_pivot_root(j, "/var/empty", pivot_root, chroot);
377 } else {
378 fprintf(stderr, "Unrecognized profile name '%s'\n", profile);
379 exit(1);
380 }
381}
382
Mike Frysinger785b1c32018-02-23 15:47:24 -0500383static void set_remount_mode(struct minijail *j, const char *mode)
384{
385 unsigned long msmode;
386 if (!strcmp(mode, "shared"))
387 msmode = MS_SHARED;
388 else if (!strcmp(mode, "private"))
389 msmode = MS_PRIVATE;
390 else if (!strcmp(mode, "slave"))
391 msmode = MS_SLAVE;
392 else if (!strcmp(mode, "unbindable"))
393 msmode = MS_UNBINDABLE;
394 else {
395 fprintf(stderr, "Unknown remount mode: '%s'\n", mode);
396 exit(1);
397 }
398 minijail_remount_mode(j, msmode);
399}
400
Mike Frysinger5ef22ca2018-01-20 13:42:10 -0500401static void usage(const char *progn)
402{
403 size_t i;
404 /* clang-format off */
405 printf("Usage: %s [-dGhHiIKlLnNprRstUvyYz]\n"
406 " [-a <table>]\n"
407 " [-b <src>[,<dest>[,<writeable>]]] [-k <src>,<dest>,<type>[,<flags>[,<data>]]]\n"
408 " [-c <caps>] [-C <dir>] [-P <dir>] [-e[file]] [-f <file>] [-g <group>]\n"
409 " [-m[<uid> <loweruid> <count>]*] [-M[<gid> <lowergid> <count>]*] [--profile <name>]\n"
410 " [-R <type,cur,max>] [-S <file>] [-t[size]] [-T <type>] [-u <user>] [-V <file>]\n"
411 " <program> [args...]\n"
412 " -a <table>: Use alternate syscall table <table>.\n"
413 " -b <...>: Bind <src> to <dest> in chroot.\n"
414 " Multiple instances allowed.\n"
415 " -B <mask>: Skip setting securebits in <mask> when restricting capabilities (-c).\n"
416 " By default, SECURE_NOROOT, SECURE_NO_SETUID_FIXUP, and \n"
417 " SECURE_KEEP_CAPS (together with their respective locks) are set.\n"
Jorge Lucangeli Obes54234212018-04-26 11:52:15 -0400418 " There are eight securebits in total.\n"
Mike Frysinger5ef22ca2018-01-20 13:42:10 -0500419 " -k <...>: Mount <src> at <dest> in chroot.\n"
420 " <flags> and <data> can be specified as in mount(2).\n"
421 " Multiple instances allowed.\n"
422 " -c <caps>: Restrict caps to <caps>.\n"
423 " -C <dir>: chroot(2) to <dir>.\n"
424 " Not compatible with -P.\n"
425 " -P <dir>: pivot_root(2) to <dir> (implies -v).\n"
426 " Not compatible with -C.\n"
427 " --mount-dev, Create a new /dev with a minimal set of device nodes (implies -v).\n"
428 " -d: See the minijail0(1) man page for the exact set.\n"
429 " -e[file]: Enter new network namespace, or existing one if |file| is provided.\n"
430 " -f <file>: Write the pid of the jailed process to <file>.\n"
431 " -g <group>: Change gid to <group>.\n"
432 " -G: Inherit supplementary groups from uid.\n"
433 " Not compatible with -y.\n"
434 " -y: Keep uid's supplementary groups.\n"
435 " Not compatible with -G.\n"
436 " -h: Help (this message).\n"
437 " -H: Seccomp filter help message.\n"
Luis Hector Chavez9dd13fd2018-04-19 20:14:47 -0700438 " -i: Exit immediately after fork(2). The jailed process will run\n"
439 " in the background.\n"
Mike Frysinger5ef22ca2018-01-20 13:42:10 -0500440 " -I: Run <program> as init (pid 1) inside a new pid namespace (implies -p).\n"
Mike Frysinger785b1c32018-02-23 15:47:24 -0500441 " -K: Do not change share mode of any existing mounts.\n"
442 " -K<mode>: Mark all existing mounts as <mode> instead of MS_PRIVATE.\n"
Mike Frysinger5ef22ca2018-01-20 13:42:10 -0500443 " -l: Enter new IPC namespace.\n"
444 " -L: Report blocked syscalls to syslog when using seccomp filter.\n"
445 " Forces the following syscalls to be allowed:\n"
446 " ", progn);
447 /* clang-format on */
448 for (i = 0; i < log_syscalls_len; i++)
449 printf("%s ", log_syscalls[i]);
450
451 /* clang-format off */
452 printf("\n"
453 " -m[map]: Set the uid map of a user namespace (implies -pU).\n"
454 " Same arguments as newuidmap(1), multiple mappings should be separated by ',' (comma).\n"
455 " With no mapping, map the current uid to root inside the user namespace.\n"
456 " Not compatible with -b without the 'writable' option.\n"
457 " -M[map]: Set the gid map of a user namespace (implies -pU).\n"
458 " Same arguments as newgidmap(1), multiple mappings should be separated by ',' (comma).\n"
459 " With no mapping, map the current gid to root inside the user namespace.\n"
460 " Not compatible with -b without the 'writable' option.\n"
461 " -n: Set no_new_privs.\n"
462 " -N: Enter a new cgroup namespace.\n"
463 " -p: Enter new pid namespace (implies -vr).\n"
464 " -r: Remount /proc read-only (implies -v).\n"
465 " -R: Set rlimits, can be specified multiple times.\n"
466 " -s: Use seccomp mode 1 (not the same as -S).\n"
467 " -S <file>: Set seccomp filter using <file>.\n"
468 " E.g., '-S /usr/share/filters/<prog>.$(uname -m)'.\n"
469 " Requires -n when not running as root.\n"
470 " -t[size]: Mount tmpfs at /tmp (implies -v).\n"
471 " Optional argument specifies size (default \"64M\").\n"
472 " -T <type>: Assume <program> is a <type> ELF binary; <type> can be 'static' or 'dynamic'.\n"
473 " This will avoid accessing <program> binary before execve(2).\n"
474 " Type 'static' will avoid preload hooking.\n"
475 " -u <user>: Change uid to <user>.\n"
476 " -U: Enter new user namespace (implies -p).\n"
477 " -v: Enter new mount namespace.\n"
478 " -V <file>: Enter specified mount namespace.\n"
479 " -w: Create and join a new anonymous session keyring.\n"
480 " -Y: Synchronize seccomp filters across thread group.\n"
481 " -z: Don't forward signals to jailed process.\n"
482 " --ambient: Raise ambient capabilities. Requires -c.\n"
483 " --uts[=name]: Enter a new UTS namespace (and set hostname).\n"
484 " --logging=<s>:Use <s> as the logging system.\n"
485 " <s> must be 'syslog' (default) or 'stderr'.\n"
486 " --profile <p>,Configure minijail0 to run with the <p> sandboxing profile,\n"
487 " which is a convenient way to express multiple flags\n"
488 " that are typically used together.\n"
489 " See the minijail0(1) man page for the full list.\n");
490 /* clang-format on */
491}
492
493static void seccomp_filter_usage(const char *progn)
494{
495 const struct syscall_entry *entry = syscall_table;
496 printf("Usage: %s -S <policy.file> <program> [args...]\n\n"
497 "System call names supported:\n",
498 progn);
499 for (; entry->name && entry->nr >= 0; ++entry)
500 printf(" %s [%d]\n", entry->name, entry->nr);
501 printf("\nSee minijail0(5) for example policies.\n");
502}
503
504int parse_args(struct minijail *j, int argc, char * const argv[],
505 int *exit_immediately, ElfType *elftype)
506{
507 int opt;
508 int use_seccomp_filter = 0;
509 int forward = 1;
510 int binding = 0;
511 int chroot = 0, pivot_root = 0;
512 int mount_ns = 0, skip_remount = 0;
513 int inherit_suppl_gids = 0, keep_suppl_gids = 0;
514 int caps = 0, ambient_caps = 0;
515 int seccomp = -1;
516 const size_t path_max = 4096;
517 uid_t uid = 0;
518 gid_t gid = 0;
519 char *uidmap = NULL, *gidmap = NULL;
520 int set_uidmap = 0, set_gidmap = 0;
521 size_t tmp_size = 0;
522 const char *filter_path = NULL;
523 int log_to_stderr = 0;
524
525 const char *optstring =
Mike Frysinger785b1c32018-02-23 15:47:24 -0500526 "+u:g:sS:c:C:P:b:B:V:f:m::M::k:a:e::R:T:vrGhHinNplLt::IUK::wyYzd";
Mike Frysinger5ef22ca2018-01-20 13:42:10 -0500527 /* clang-format off */
528 const struct option long_options[] = {
529 {"help", no_argument, 0, 'h'},
530 {"mount-dev", no_argument, 0, 'd'},
531 {"ambient", no_argument, 0, 128},
532 {"uts", optional_argument, 0, 129},
533 {"logging", required_argument, 0, 130},
534 {"profile", required_argument, 0, 131},
535 {0, 0, 0, 0},
536 };
537 /* clang-format on */
538
539 while ((opt = getopt_long(argc, argv, optstring, long_options, NULL)) !=
540 -1) {
541 switch (opt) {
542 case 'u':
543 set_user(j, optarg, &uid, &gid);
544 break;
545 case 'g':
546 set_group(j, optarg, &gid);
547 break;
548 case 'n':
549 minijail_no_new_privs(j);
550 break;
551 case 's':
552 if (seccomp != -1 && seccomp != 1) {
553 fprintf(stderr,
554 "Do not use -s & -S together.\n");
555 exit(1);
556 }
557 seccomp = 1;
558 minijail_use_seccomp(j);
559 break;
560 case 'S':
561 if (seccomp != -1 && seccomp != 2) {
562 fprintf(stderr,
563 "Do not use -s & -S together.\n");
564 exit(1);
565 }
566 seccomp = 2;
567 minijail_use_seccomp_filter(j);
568 if (strlen(optarg) >= path_max) {
569 fprintf(stderr, "Filter path is too long.\n");
570 exit(1);
571 }
572 filter_path = strndup(optarg, path_max);
573 if (!filter_path) {
574 fprintf(stderr,
575 "Could not strndup(3) filter path.\n");
576 exit(1);
577 }
578 use_seccomp_filter = 1;
579 break;
580 case 'l':
581 minijail_namespace_ipc(j);
582 break;
583 case 'L':
584 minijail_log_seccomp_filter_failures(j);
585 break;
586 case 'b':
587 add_binding(j, optarg);
588 binding = 1;
589 break;
590 case 'B':
591 skip_securebits(j, optarg);
592 break;
593 case 'c':
594 caps = 1;
595 use_caps(j, optarg);
596 break;
597 case 'C':
598 use_chroot(j, optarg, &chroot, pivot_root);
599 break;
600 case 'k':
601 add_mount(j, optarg);
602 break;
603 case 'K':
Mike Frysinger785b1c32018-02-23 15:47:24 -0500604 if (optarg)
605 set_remount_mode(j, optarg);
606 else
607 minijail_skip_remount_private(j);
Mike Frysinger5ef22ca2018-01-20 13:42:10 -0500608 skip_remount = 1;
609 break;
610 case 'P':
611 use_pivot_root(j, optarg, &pivot_root, chroot);
612 break;
613 case 'f':
614 if (0 != minijail_write_pid_file(j, optarg)) {
615 fprintf(stderr,
616 "Could not prepare pid file path.\n");
617 exit(1);
618 }
619 break;
620 case 't':
621 minijail_namespace_vfs(j);
622 if (!tmp_size) {
623 /*
624 * Avoid clobbering |tmp_size| if it was already
625 * set.
626 */
627 tmp_size = DEFAULT_TMP_SIZE;
628 }
629 if (optarg != NULL &&
630 0 != parse_size(&tmp_size, optarg)) {
631 fprintf(stderr, "Invalid /tmp tmpfs size.\n");
632 exit(1);
633 }
634 break;
635 case 'v':
636 minijail_namespace_vfs(j);
637 mount_ns = 1;
638 break;
639 case 'V':
640 minijail_namespace_enter_vfs(j, optarg);
641 break;
642 case 'r':
643 minijail_remount_proc_readonly(j);
644 break;
645 case 'G':
646 if (keep_suppl_gids) {
647 fprintf(stderr,
648 "-y and -G are not compatible.\n");
649 exit(1);
650 }
651 minijail_inherit_usergroups(j);
652 inherit_suppl_gids = 1;
653 break;
654 case 'y':
655 if (inherit_suppl_gids) {
656 fprintf(stderr,
657 "-y and -G are not compatible.\n");
658 exit(1);
659 }
660 minijail_keep_supplementary_gids(j);
661 keep_suppl_gids = 1;
662 break;
663 case 'N':
664 minijail_namespace_cgroups(j);
665 break;
666 case 'p':
667 minijail_namespace_pids(j);
668 break;
669 case 'e':
670 if (optarg)
671 minijail_namespace_enter_net(j, optarg);
672 else
673 minijail_namespace_net(j);
674 break;
675 case 'i':
676 *exit_immediately = 1;
677 break;
678 case 'H':
679 seccomp_filter_usage(argv[0]);
680 exit(0);
681 case 'I':
682 minijail_namespace_pids(j);
683 minijail_run_as_init(j);
684 break;
685 case 'U':
686 minijail_namespace_user(j);
687 minijail_namespace_pids(j);
688 break;
689 case 'm':
690 set_uidmap = 1;
691 if (uidmap) {
692 free(uidmap);
693 uidmap = NULL;
694 }
695 if (optarg)
696 uidmap = strdup(optarg);
697 break;
698 case 'M':
699 set_gidmap = 1;
700 if (gidmap) {
701 free(gidmap);
702 gidmap = NULL;
703 }
704 if (optarg)
705 gidmap = strdup(optarg);
706 break;
707 case 'a':
708 if (0 != minijail_use_alt_syscall(j, optarg)) {
709 fprintf(stderr,
710 "Could not set alt-syscall table.\n");
711 exit(1);
712 }
713 break;
714 case 'R':
715 add_rlimit(j, optarg);
716 break;
717 case 'T':
718 if (!strcmp(optarg, "static"))
719 *elftype = ELFSTATIC;
720 else if (!strcmp(optarg, "dynamic"))
721 *elftype = ELFDYNAMIC;
722 else {
723 fprintf(stderr, "ELF type must be 'static' or "
724 "'dynamic'.\n");
725 exit(1);
726 }
727 break;
728 case 'w':
729 minijail_new_session_keyring(j);
730 break;
731 case 'Y':
732 minijail_set_seccomp_filter_tsync(j);
733 break;
734 case 'z':
735 forward = 0;
736 break;
737 case 'd':
738 minijail_namespace_vfs(j);
739 minijail_mount_dev(j);
740 break;
741 /* Long options. */
742 case 128: /* Ambient caps. */
743 ambient_caps = 1;
744 minijail_set_ambient_caps(j);
745 break;
746 case 129: /* UTS/hostname namespace. */
747 minijail_namespace_uts(j);
748 if (optarg)
749 minijail_namespace_set_hostname(j, optarg);
750 break;
751 case 130: /* Logging. */
752 if (!strcmp(optarg, "syslog"))
753 log_to_stderr = 0;
754 else if (!strcmp(optarg, "stderr")) {
755 log_to_stderr = 1;
756 } else {
757 fprintf(stderr, "--logger must be 'syslog' or "
758 "'stderr'.\n");
759 exit(1);
760 }
761 break;
762 case 131: /* Profile */
763 use_profile(j, optarg, &pivot_root, chroot, &tmp_size);
764 break;
765 default:
766 usage(argv[0]);
767 exit(opt == 'h' ? 0 : 1);
768 }
769 }
770
771 if (log_to_stderr) {
772 init_logging(LOG_TO_FD, STDERR_FILENO, LOG_INFO);
773 /*
774 * When logging to stderr, ensure the FD survives the jailing.
775 */
776 if (0 !=
777 minijail_preserve_fd(j, STDERR_FILENO, STDERR_FILENO)) {
778 fprintf(stderr, "Could not preserve stderr.\n");
779 exit(1);
780 }
781 }
782
783 /* Set up uid/gid mapping. */
784 if (set_uidmap || set_gidmap) {
785 set_ugid_mapping(j, set_uidmap, uid, uidmap, set_gidmap, gid,
786 gidmap);
787 }
788
789 /* Can only set ambient caps when using regular caps. */
790 if (ambient_caps && !caps) {
791 fprintf(stderr, "Can't set ambient capabilities (--ambient) "
792 "without actually using capabilities (-c).\n");
793 exit(1);
794 }
795
796 /* Set up signal handlers in minijail unless asked not to. */
797 if (forward)
798 minijail_forward_signals(j);
799
800 /*
801 * Only allow bind mounts when entering a chroot, using pivot_root, or
802 * a new mount namespace.
803 */
804 if (binding && !(chroot || pivot_root || mount_ns)) {
805 fprintf(stderr, "Bind mounts require a chroot, pivot_root, or "
806 " new mount namespace.\n");
807 exit(1);
808 }
809
810 /*
811 * Remounting / as MS_PRIVATE only happens when entering a new mount
812 * namespace, so skipping it only applies in that case.
813 */
814 if (skip_remount && !mount_ns) {
815 fprintf(stderr, "Can't skip marking mounts as MS_PRIVATE"
816 " without mount namespaces.\n");
817 exit(1);
818 }
819
820 /*
821 * We parse seccomp filters here to make sure we've collected all
822 * cmdline options.
823 */
824 if (use_seccomp_filter) {
825 minijail_parse_seccomp_filters(j, filter_path);
826 free((void *)filter_path);
827 }
828
829 /* Mount a tmpfs under /tmp and set its size. */
830 if (tmp_size)
831 minijail_mount_tmp_size(j, tmp_size);
832
833 /*
834 * There should be at least one additional unparsed argument: the
835 * executable name.
836 */
837 if (argc == optind) {
838 usage(argv[0]);
839 exit(1);
840 }
841
842 if (*elftype == ELFERROR) {
843 /*
844 * -T was not specified.
845 * Get the path to the program adjusted for changing root.
846 */
847 char *program_path =
848 minijail_get_original_path(j, argv[optind]);
849
850 /* Check that we can access the target program. */
851 if (access(program_path, X_OK)) {
852 fprintf(stderr,
853 "Target program '%s' is not accessible.\n",
854 argv[optind]);
855 exit(1);
856 }
857
858 /* Check if target is statically or dynamically linked. */
859 *elftype = get_elf_linkage(program_path);
860 free(program_path);
861 }
862
863 /*
864 * Setting capabilities need either a dynamically-linked binary, or the
865 * use of ambient capabilities for them to be able to survive an
866 * execve(2).
867 */
868 if (caps && *elftype == ELFSTATIC && !ambient_caps) {
869 fprintf(stderr, "Can't run statically-linked binaries with "
870 "capabilities (-c) without also setting "
871 "ambient capabilities. Try passing "
872 "--ambient.\n");
873 exit(1);
874 }
875
876 return optind;
877}