blob: a43495347cc50e9844bdcf36804d921ebdc0e7a9 [file] [log] [blame]
Elly Jonese58176c2012-01-23 11:46:17 -05001/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
Elly Jonescd7a9042011-07-22 13:56:51 -04002 * Use of this source code is governed by a BSD-style license that can be
Will Drewry32ac9f52011-08-18 21:36:27 -05003 * found in the LICENSE file.
4 */
Elly Jonescd7a9042011-07-22 13:56:51 -04005
Jorge Lucangeli Obes4b2d5ee2014-01-09 15:47:47 -08006#include <dlfcn.h>
Stephen Barber5dd5b1b2017-10-16 23:02:39 -07007#include <errno.h>
Jorge Lucangeli Obes0b208772017-04-19 14:15:46 -04008#include <getopt.h>
Elly Jonescd7a9042011-07-22 13:56:51 -04009#include <stdio.h>
10#include <stdlib.h>
11#include <string.h>
Luis Hector Chavez71323552017-09-05 09:17:22 -070012#include <sys/capability.h>
13#include <sys/types.h>
Elly Jonescd7a9042011-07-22 13:56:51 -040014#include <unistd.h>
15
16#include "libminijail.h"
Will Drewry32ac9f52011-08-18 21:36:27 -050017#include "libsyscalls.h"
Elly Jonescd7a9042011-07-22 13:56:51 -040018
Lee Campbell1e4fc6a2014-06-06 17:40:02 -070019#include "elfparse.h"
Luis Hector Chavez71323552017-09-05 09:17:22 -070020#include "system.h"
Jorge Lucangeli Obesbda833c2012-07-31 16:25:56 -070021#include "util.h"
22
Jorge Lucangeli Obesab6fa6f2016-08-04 15:42:48 -040023#define IDMAP_LEN 32U
Luis Hector Chavezd45fc422017-10-25 15:11:53 -070024#define DEFAULT_TMP_SIZE (64 * 1024 * 1024)
Jorge Lucangeli Obesab6fa6f2016-08-04 15:42:48 -040025
Luis Hector Chavez71323552017-09-05 09:17:22 -070026static void set_user(struct minijail *j, const char *arg, uid_t *out_uid,
27 gid_t *out_gid)
Elly Jonese1749eb2011-10-07 13:54:59 -040028{
29 char *end = NULL;
30 int uid = strtod(arg, &end);
31 if (!*end && *arg) {
Luis Hector Chavez71323552017-09-05 09:17:22 -070032 *out_uid = uid;
Elly Jonese1749eb2011-10-07 13:54:59 -040033 minijail_change_uid(j, uid);
34 return;
35 }
Elly Jonescd7a9042011-07-22 13:56:51 -040036
Luis Hector Chavez71323552017-09-05 09:17:22 -070037 if (lookup_user(arg, out_uid, out_gid)) {
38 fprintf(stderr, "Bad user: '%s'\n", arg);
39 exit(1);
40 }
41
Elly Jonese1749eb2011-10-07 13:54:59 -040042 if (minijail_change_user(j, arg)) {
43 fprintf(stderr, "Bad user: '%s'\n", arg);
44 exit(1);
45 }
Elly Jonescd7a9042011-07-22 13:56:51 -040046}
47
Luis Hector Chavez71323552017-09-05 09:17:22 -070048static void set_group(struct minijail *j, const char *arg, gid_t *out_gid)
Elly Jonese1749eb2011-10-07 13:54:59 -040049{
50 char *end = NULL;
51 int gid = strtod(arg, &end);
52 if (!*end && *arg) {
Luis Hector Chavez71323552017-09-05 09:17:22 -070053 *out_gid = gid;
Elly Jonese1749eb2011-10-07 13:54:59 -040054 minijail_change_gid(j, gid);
55 return;
56 }
Elly Jonescd7a9042011-07-22 13:56:51 -040057
Luis Hector Chavez71323552017-09-05 09:17:22 -070058 if (lookup_group(arg, out_gid)) {
59 fprintf(stderr, "Bad group: '%s'\n", arg);
60 exit(1);
61 }
62
Elly Jonese1749eb2011-10-07 13:54:59 -040063 if (minijail_change_group(j, arg)) {
64 fprintf(stderr, "Bad group: '%s'\n", arg);
65 exit(1);
66 }
Elly Jonescd7a9042011-07-22 13:56:51 -040067}
68
Luis Hector Chavezec0a2c12017-06-29 20:29:57 -070069static void skip_securebits(struct minijail *j, const char *arg)
70{
71 uint64_t securebits_skip_mask;
72 char *end = NULL;
73 securebits_skip_mask = strtoull(arg, &end, 16);
74 if (*end) {
75 fprintf(stderr, "Invalid securebit mask: '%s'\n", arg);
76 exit(1);
77 }
78 minijail_skip_setting_securebits(j, securebits_skip_mask);
79}
80
Elly Jonese1749eb2011-10-07 13:54:59 -040081static void use_caps(struct minijail *j, const char *arg)
82{
83 uint64_t caps;
84 char *end = NULL;
85 caps = strtoull(arg, &end, 16);
86 if (*end) {
87 fprintf(stderr, "Invalid cap set: '%s'\n", arg);
88 exit(1);
89 }
90 minijail_use_caps(j, caps);
Elly Jonescd7a9042011-07-22 13:56:51 -040091}
92
Jorge Lucangeli Obesc8b21e12014-06-13 14:26:16 -070093static void add_binding(struct minijail *j, char *arg)
94{
Mike Frysingerfea05c62018-01-17 16:56:48 -050095 char *src = tokenize(&arg, ",");
96 char *dest = tokenize(&arg, ",");
97 char *flags = tokenize(&arg, ",");
98 if (!src || src[0] == '\0' || arg != NULL) {
Elly Jones51a5b6c2011-10-12 19:09:26 -040099 fprintf(stderr, "Bad binding: %s %s\n", src, dest);
100 exit(1);
101 }
Mike Frysingerfea05c62018-01-17 16:56:48 -0500102 if (dest == NULL || dest[0] == '\0')
Mike Frysinger8f0665b2018-01-17 14:26:09 -0500103 dest = src;
Mike Frysingerfea05c62018-01-17 16:56:48 -0500104 if (flags == NULL || flags[0] == '\0')
105 flags = "0";
106 if (minijail_bind(j, src, dest, atoi(flags))) {
Jorge Lucangeli Obes2b12ba42016-01-26 10:37:51 -0800107 fprintf(stderr, "minijail_bind failed.\n");
Elly Jones51a5b6c2011-10-12 19:09:26 -0400108 exit(1);
109 }
110}
111
Dylan Reid0f72ef42017-06-06 15:42:49 -0700112static void add_rlimit(struct minijail *j, char *arg)
113{
Mike Frysingerfea05c62018-01-17 16:56:48 -0500114 char *type = tokenize(&arg, ",");
115 char *cur = tokenize(&arg, ",");
116 char *max = tokenize(&arg, ",");
117 if (!type || type[0] == '\0' || !cur || cur[0] == '\0' ||
118 !max || max[0] == '\0' || arg != NULL) {
Dylan Reid0f72ef42017-06-06 15:42:49 -0700119 fprintf(stderr, "Bad rlimit '%s'.\n", arg);
120 exit(1);
121 }
122 if (minijail_rlimit(j, atoi(type), atoi(cur), atoi(max))) {
Luis Hector Chavez71323552017-09-05 09:17:22 -0700123 fprintf(stderr, "minijail_rlimit '%s,%s,%s' failed.\n", type,
124 cur, max);
Dylan Reid0f72ef42017-06-06 15:42:49 -0700125 exit(1);
126 }
127}
128
Dylan Reid648b2202015-10-23 00:50:00 -0700129static void add_mount(struct minijail *j, char *arg)
130{
Mike Frysingerfea05c62018-01-17 16:56:48 -0500131 char *src = tokenize(&arg, ",");
132 char *dest = tokenize(&arg, ",");
133 char *type = tokenize(&arg, ",");
134 char *flags = tokenize(&arg, ",");
135 char *data = tokenize(&arg, ",");
136 if (!src || src[0] == '\0' || !dest || dest[0] == '\0' ||
137 !type || type[0] == '\0' || arg != NULL) {
Dylan Reid648b2202015-10-23 00:50:00 -0700138 fprintf(stderr, "Bad mount: %s %s %s\n", src, dest, type);
139 exit(1);
140 }
Dylan Reid81e23972016-05-18 14:06:35 -0700141 if (minijail_mount_with_data(j, src, dest, type,
Jorge Lucangeli Obesab6fa6f2016-08-04 15:42:48 -0400142 flags ? strtoul(flags, NULL, 16) : 0,
143 data)) {
Dylan Reid648b2202015-10-23 00:50:00 -0700144 fprintf(stderr, "minijail_mount failed.\n");
145 exit(1);
146 }
147}
148
Jorge Lucangeli Obesab6fa6f2016-08-04 15:42:48 -0400149static char *build_idmap(id_t id, id_t lowerid)
150{
151 int ret;
152 char *idmap = malloc(IDMAP_LEN);
153 ret = snprintf(idmap, IDMAP_LEN, "%d %d 1", id, lowerid);
154 if (ret < 0 || (size_t)ret >= IDMAP_LEN) {
155 free(idmap);
156 fprintf(stderr, "Could not build id map.\n");
157 exit(1);
158 }
159 return idmap;
160}
161
Luis Hector Chavez71323552017-09-05 09:17:22 -0700162static int has_cap_setgid()
163{
164 cap_t caps;
165 cap_flag_value_t cap_value;
166
167 if (!CAP_IS_SUPPORTED(CAP_SETGID))
168 return 0;
169
170 caps = cap_get_proc();
171 if (!caps) {
172 fprintf(stderr, "Could not get process' capabilities: %m\n");
173 exit(1);
174 }
175
176 if (cap_get_flag(caps, CAP_SETGID, CAP_EFFECTIVE, &cap_value)) {
177 fprintf(stderr, "Could not get the value of CAP_SETGID: %m\n");
178 exit(1);
179 }
180
181 if (cap_free(caps)) {
182 fprintf(stderr, "Could not free capabilities: %m\n");
183 exit(1);
184 }
185
186 return cap_value == CAP_SET;
187}
188
189static void set_ugid_mapping(struct minijail *j, int set_uidmap, uid_t uid,
190 char *uidmap, int set_gidmap, gid_t gid,
191 char *gidmap)
192{
193 if (set_uidmap) {
194 minijail_namespace_user(j);
195 minijail_namespace_pids(j);
196
197 if (!uidmap) {
198 /*
199 * If no map is passed, map the current uid to the
200 * chosen uid in the target namespace (or root, if none
201 * was chosen).
202 */
203 uidmap = build_idmap(uid, getuid());
204 }
205 if (0 != minijail_uidmap(j, uidmap)) {
206 fprintf(stderr, "Could not set uid map.\n");
207 exit(1);
208 }
209 free(uidmap);
210 }
211 if (set_gidmap) {
212 minijail_namespace_user(j);
213 minijail_namespace_pids(j);
214
215 if (!gidmap) {
216 /*
217 * If no map is passed, map the current gid to the
218 * chosen gid in the target namespace.
219 */
220 gidmap = build_idmap(gid, getgid());
221 }
222 if (!has_cap_setgid()) {
223 /*
224 * This means that we are not running as root,
225 * so we also have to disable setgroups(2) to
226 * be able to set the gid map.
227 * See
228 * http://man7.org/linux/man-pages/man7/user_namespaces.7.html
229 */
230 minijail_namespace_user_disable_setgroups(j);
231 }
232 if (0 != minijail_gidmap(j, gidmap)) {
233 fprintf(stderr, "Could not set gid map.\n");
234 exit(1);
235 }
236 free(gidmap);
237 }
238}
239
Luis Hector Chavezd45fc422017-10-25 15:11:53 -0700240static void use_chroot(struct minijail *j, const char *path, int *chroot,
241 int pivot_root)
242{
243 if (pivot_root) {
244 fprintf(stderr, "Could not set chroot because "
245 "'-P' was specified.\n");
246 exit(1);
247 }
248 if (minijail_enter_chroot(j, path)) {
249 fprintf(stderr, "Could not set chroot.\n");
250 exit(1);
251 }
252 *chroot = 1;
253}
254
255static void use_pivot_root(struct minijail *j, const char *path,
256 int *pivot_root, int chroot)
257{
258 if (chroot) {
259 fprintf(stderr, "Could not set pivot_root because "
260 "'-C' was specified.\n");
261 exit(1);
262 }
263 if (minijail_enter_pivot_root(j, path)) {
264 fprintf(stderr, "Could not set pivot_root.\n");
265 exit(1);
266 }
267 minijail_namespace_vfs(j);
268 *pivot_root = 1;
269}
270
271static void use_profile(struct minijail *j, const char *profile,
272 int *pivot_root, int chroot, size_t *tmp_size)
273{
274 if (!strcmp(profile, "minimalistic-mountns")) {
275 minijail_namespace_vfs(j);
276 if (minijail_bind(j, "/", "/", 0)) {
277 fprintf(stderr, "minijail_bind failed.\n");
278 exit(1);
279 }
280 if (minijail_bind(j, "/proc", "/proc", 0)) {
281 fprintf(stderr, "minijail_bind failed.\n");
282 exit(1);
283 }
284 minijail_mount_dev(j);
285 if (!*tmp_size) {
286 /* Avoid clobbering |tmp_size| if it was already set. */
287 *tmp_size = DEFAULT_TMP_SIZE;
288 }
289 minijail_remount_proc_readonly(j);
290 use_pivot_root(j, "/var/empty", pivot_root, chroot);
291 } else {
292 fprintf(stderr, "Unrecognized profile name '%s'\n", profile);
293 exit(1);
294 }
295}
296
Elly Jonese1749eb2011-10-07 13:54:59 -0400297static void usage(const char *progn)
298{
Jorge Lucangeli Obesbda833c2012-07-31 16:25:56 -0700299 size_t i;
Luis Hector Chavezfb449ab2016-10-14 09:49:22 -0700300 /* clang-format off */
Mike Frysinger33ffef32017-01-13 19:53:19 -0500301 printf("Usage: %s [-dGhHiIKlLnNprRstUvyYz]\n"
Jorge Lucangeli Obesa32e8392016-09-01 16:52:40 -0400302 " [-a <table>]\n"
Mike Frysingerfea05c62018-01-17 16:56:48 -0500303 " [-b <src>[,<dest>[,<writeable>]]] [-k <src>,<dest>,<type>[,<flags>[,<data>]]]\n"
Jorge Lucangeli Obesa32e8392016-09-01 16:52:40 -0400304 " [-c <caps>] [-C <dir>] [-P <dir>] [-e[file]] [-f <file>] [-g <group>]\n"
Luis Hector Chavezd45fc422017-10-25 15:11:53 -0700305 " [-m[<uid> <loweruid> <count>]*] [-M[<gid> <lowergid> <count>]*] [--profile <name>]\n"
Dylan Reid0f72ef42017-06-06 15:42:49 -0700306 " [-R <type,cur,max>] [-S <file>] [-t[size]] [-T <type>] [-u <user>] [-V <file>]\n"
Jorge Lucangeli Obes9dd256e2016-02-16 15:31:02 -0800307 " <program> [args...]\n"
Mike Frysingerb9a7b162017-05-30 15:25:49 -0400308 " -a <table>: Use alternate syscall table <table>.\n"
Mike Frysingereaab4202017-08-14 14:57:21 -0400309 " -b <...>: Bind <src> to <dest> in chroot.\n"
Mike Frysingerb9a7b162017-05-30 15:25:49 -0400310 " Multiple instances allowed.\n"
Mike Frysingereaab4202017-08-14 14:57:21 -0400311 " -B <mask>: Skip setting securebits in <mask> when restricting capabilities (-c).\n"
Luis Hector Chavezec0a2c12017-06-29 20:29:57 -0700312 " By default, SECURE_NOROOT, SECURE_NO_SETUID_FIXUP, and \n"
313 " SECURE_KEEP_CAPS (together with their respective locks) are set.\n"
Mike Frysingereaab4202017-08-14 14:57:21 -0400314 " -k <...>: Mount <src> at <dest> in chroot.\n"
Mike Frysingerb9a7b162017-05-30 15:25:49 -0400315 " <flags> and <data> can be specified as in mount(2).\n"
316 " Multiple instances allowed.\n"
317 " -c <caps>: Restrict caps to <caps>.\n"
318 " -C <dir>: chroot(2) to <dir>.\n"
319 " Not compatible with -P.\n"
320 " -P <dir>: pivot_root(2) to <dir> (implies -v).\n"
321 " Not compatible with -C.\n"
Mike Frysinger33ffef32017-01-13 19:53:19 -0500322 " --mount-dev, Create a new /dev with a minimal set of device nodes (implies -v).\n"
Luis Hector Chavezd775f4c2017-10-25 11:36:57 -0700323 " -d: See the minijail0(1) man page for the exact set.\n"
Mike Frysingerb9a7b162017-05-30 15:25:49 -0400324 " -e[file]: Enter new network namespace, or existing one if |file| is provided.\n"
325 " -f <file>: Write the pid of the jailed process to <file>.\n"
326 " -g <group>: Change gid to <group>.\n"
327 " -G: Inherit supplementary groups from uid.\n"
328 " Not compatible with -y.\n"
329 " -y: Keep uid's supplementary groups.\n"
330 " Not compatible with -G.\n"
331 " -h: Help (this message).\n"
332 " -H: Seccomp filter help message.\n"
333 " -i: Exit immediately after fork (do not act as init).\n"
334 " -I: Run <program> as init (pid 1) inside a new pid namespace (implies -p).\n"
335 " -K: Don't mark all existing mounts as MS_PRIVATE.\n"
336 " -l: Enter new IPC namespace.\n"
337 " -L: Report blocked syscalls to syslog when using seccomp filter.\n"
338 " Forces the following syscalls to be allowed:\n"
Kees Cook03b2af22014-12-18 17:11:13 -0800339 " ", progn);
Luis Hector Chavezfb449ab2016-10-14 09:49:22 -0700340 /* clang-format on */
Jorge Lucangeli Obesbda833c2012-07-31 16:25:56 -0700341 for (i = 0; i < log_syscalls_len; i++)
342 printf("%s ", log_syscalls[i]);
343
Luis Hector Chavezfb449ab2016-10-14 09:49:22 -0700344 /* clang-format off */
Jorge Lucangeli Obesbda833c2012-07-31 16:25:56 -0700345 printf("\n"
Mike Frysingerb9a7b162017-05-30 15:25:49 -0400346 " -m[map]: Set the uid map of a user namespace (implies -pU).\n"
347 " Same arguments as newuidmap(1), multiple mappings should be separated by ',' (comma).\n"
348 " With no mapping, map the current uid to root inside the user namespace.\n"
349 " Not compatible with -b without the 'writable' option.\n"
350 " -M[map]: Set the gid map of a user namespace (implies -pU).\n"
351 " Same arguments as newgidmap(1), multiple mappings should be separated by ',' (comma).\n"
352 " With no mapping, map the current gid to root inside the user namespace.\n"
353 " Not compatible with -b without the 'writable' option.\n"
354 " -n: Set no_new_privs.\n"
355 " -N: Enter a new cgroup namespace.\n"
356 " -p: Enter new pid namespace (implies -vr).\n"
357 " -r: Remount /proc read-only (implies -v).\n"
Dylan Reid0f72ef42017-06-06 15:42:49 -0700358 " -R: Set rlimits, can be specified multiple times.\n"
Mike Frysingere61fd662017-06-20 14:07:41 -0400359 " -s: Use seccomp mode 1 (not the same as -S).\n"
Mike Frysingerb9a7b162017-05-30 15:25:49 -0400360 " -S <file>: Set seccomp filter using <file>.\n"
361 " E.g., '-S /usr/share/filters/<prog>.$(uname -m)'.\n"
362 " Requires -n when not running as root.\n"
363 " -t[size]: Mount tmpfs at /tmp (implies -v).\n"
364 " Optional argument specifies size (default \"64M\").\n"
Graziano Misuraca58602a82017-08-28 17:33:15 -0700365 " -T <type>: Assume <program> is a <type> ELF binary; <type> can be 'static' or 'dynamic'.\n"
366 " This will avoid accessing <program> binary before execve(2).\n"
367 " Type 'static' will avoid preload hooking.\n"
Mike Frysingerb9a7b162017-05-30 15:25:49 -0400368 " -u <user>: Change uid to <user>.\n"
369 " -U: Enter new user namespace (implies -p).\n"
370 " -v: Enter new mount namespace.\n"
371 " -V <file>: Enter specified mount namespace.\n"
372 " -w: Create and join a new anonymous session keyring.\n"
373 " -Y: Synchronize seccomp filters across thread group.\n"
374 " -z: Don't forward signals to jailed process.\n"
375 " --ambient: Raise ambient capabilities. Requires -c.\n"
Luis Hector Chavez114a9302017-09-05 20:36:58 -0700376 " --uts[=name]: Enter a new UTS namespace (and set hostname).\n"
377 " --logging=<s>:Use <s> as the logging system.\n"
Luis Hector Chavezd45fc422017-10-25 15:11:53 -0700378 " <s> must be 'syslog' (default) or 'stderr'.\n"
379 " --profile <p>,Configure minijail0 to run with the <p> sandboxing profile,\n"
380 " which is a convenient way to express multiple flags\n"
381 " that are typically used together.\n"
382 " See the minijail0(1) man page for the full list.\n");
Luis Hector Chavezfb449ab2016-10-14 09:49:22 -0700383 /* clang-format on */
Elly Jonescd7a9042011-07-22 13:56:51 -0400384}
385
Elly Jonese1749eb2011-10-07 13:54:59 -0400386static void seccomp_filter_usage(const char *progn)
387{
388 const struct syscall_entry *entry = syscall_table;
389 printf("Usage: %s -S <policy.file> <program> [args...]\n\n"
Jorge Lucangeli Obes0b208772017-04-19 14:15:46 -0400390 "System call names supported:\n",
391 progn);
Elly Jonese1749eb2011-10-07 13:54:59 -0400392 for (; entry->name && entry->nr >= 0; ++entry)
393 printf(" %s [%d]\n", entry->name, entry->nr);
394 printf("\nSee minijail0(5) for example policies.\n");
Will Drewry32ac9f52011-08-18 21:36:27 -0500395}
396
Christopher Wiley88f76a72013-11-01 14:12:56 -0700397static int parse_args(struct minijail *j, int argc, char *argv[],
Matthew Dempsky2ed09122016-02-11 09:43:37 -0800398 int *exit_immediately, ElfType *elftype)
Elly Jonese1749eb2011-10-07 13:54:59 -0400399{
Elly Jonese1749eb2011-10-07 13:54:59 -0400400 int opt;
Jorge Lucangeli Obes482cb9d2014-07-23 15:16:04 -0700401 int use_seccomp_filter = 0;
Jorge Lucangeli Obesdba62092017-05-18 17:10:23 -0400402 int forward = 1;
Jorge Lucangeli Obes2b12ba42016-01-26 10:37:51 -0800403 int binding = 0;
Jorge Lucangeli Obes0b208772017-04-19 14:15:46 -0400404 int chroot = 0, pivot_root = 0;
Jorge Lucangeli Obesa521bee2016-03-03 13:47:57 -0800405 int mount_ns = 0, skip_remount = 0;
Lutz Justen13807cb2017-01-03 17:11:55 +0100406 int inherit_suppl_gids = 0, keep_suppl_gids = 0;
Jorge Lucangeli Obesa6eb21a2017-04-20 10:44:00 -0400407 int caps = 0, ambient_caps = 0;
Mike Frysingere61fd662017-06-20 14:07:41 -0400408 int seccomp = -1;
Jorge Lucangeli Obes482cb9d2014-07-23 15:16:04 -0700409 const size_t path_max = 4096;
Luis Hector Chavez71323552017-09-05 09:17:22 -0700410 uid_t uid = 0;
411 gid_t gid = 0;
412 char *uidmap = NULL, *gidmap = NULL;
413 int set_uidmap = 0, set_gidmap = 0;
Luis Hector Chavezd45fc422017-10-25 15:11:53 -0700414 size_t tmp_size = 0;
Luis Hector Chavez95582e72017-09-05 09:13:25 -0700415 const char *filter_path = NULL;
Luis Hector Chavez114a9302017-09-05 20:36:58 -0700416 int log_to_stderr = 0;
Jorge Lucangeli Obesab6fa6f2016-08-04 15:42:48 -0400417
418 const char *optstring =
Mike Frysinger33ffef32017-01-13 19:53:19 -0500419 "+u:g:sS:c:C:P:b:B:V:f:m::M::k:a:e::R:T:vrGhHinNplLt::IUKwyYzd";
Jorge Lucangeli Obesa6eb21a2017-04-20 10:44:00 -0400420 /* clang-format off */
421 const struct option long_options[] = {
Mike Frysinger227c2912017-10-04 14:27:04 -0400422 {"help", no_argument, 0, 'h'},
Mike Frysinger33ffef32017-01-13 19:53:19 -0500423 {"mount-dev", no_argument, 0, 'd'},
Jorge Lucangeli Obesa6eb21a2017-04-20 10:44:00 -0400424 {"ambient", no_argument, 0, 128},
Mike Frysingerb9a7b162017-05-30 15:25:49 -0400425 {"uts", optional_argument, 0, 129},
Luis Hector Chavez114a9302017-09-05 20:36:58 -0700426 {"logging", required_argument, 0, 130},
Luis Hector Chavezd45fc422017-10-25 15:11:53 -0700427 {"profile", required_argument, 0, 131},
Jorge Lucangeli Obesa6eb21a2017-04-20 10:44:00 -0400428 {0, 0, 0, 0},
429 };
430 /* clang-format on */
Jorge Lucangeli Obes0b208772017-04-19 14:15:46 -0400431
Mike Frysinger99becbd2017-10-04 14:26:52 -0400432 while ((opt = getopt_long(argc, argv, optstring, long_options, NULL)) !=
433 -1) {
Elly Jonese1749eb2011-10-07 13:54:59 -0400434 switch (opt) {
435 case 'u':
Luis Hector Chavez71323552017-09-05 09:17:22 -0700436 set_user(j, optarg, &uid, &gid);
Elly Jonese1749eb2011-10-07 13:54:59 -0400437 break;
438 case 'g':
Luis Hector Chavez71323552017-09-05 09:17:22 -0700439 set_group(j, optarg, &gid);
Elly Jonese1749eb2011-10-07 13:54:59 -0400440 break;
Jorge Lucangeli Obesc2c9bcc2012-05-01 09:30:24 -0700441 case 'n':
442 minijail_no_new_privs(j);
Jorge Lucangeli Obes0341d6c2012-07-16 15:27:31 -0700443 break;
Elly Jonese1749eb2011-10-07 13:54:59 -0400444 case 's':
Mike Frysingere61fd662017-06-20 14:07:41 -0400445 if (seccomp != -1 && seccomp != 1) {
Luis Hector Chavez71323552017-09-05 09:17:22 -0700446 fprintf(stderr,
447 "Do not use -s & -S together.\n");
Mike Frysingere61fd662017-06-20 14:07:41 -0400448 exit(1);
449 }
450 seccomp = 1;
Elly Jonese1749eb2011-10-07 13:54:59 -0400451 minijail_use_seccomp(j);
452 break;
453 case 'S':
Mike Frysingere61fd662017-06-20 14:07:41 -0400454 if (seccomp != -1 && seccomp != 2) {
Luis Hector Chavez71323552017-09-05 09:17:22 -0700455 fprintf(stderr,
456 "Do not use -s & -S together.\n");
Mike Frysingere61fd662017-06-20 14:07:41 -0400457 exit(1);
458 }
459 seccomp = 2;
Elly Jonese1749eb2011-10-07 13:54:59 -0400460 minijail_use_seccomp_filter(j);
Jorge Lucangeli Obes482cb9d2014-07-23 15:16:04 -0700461 if (strlen(optarg) >= path_max) {
Jorge Lucangeli Obes2b12ba42016-01-26 10:37:51 -0800462 fprintf(stderr, "Filter path is too long.\n");
Jorge Lucangeli Obes482cb9d2014-07-23 15:16:04 -0700463 exit(1);
464 }
465 filter_path = strndup(optarg, path_max);
466 if (!filter_path) {
467 fprintf(stderr,
468 "Could not strndup(3) filter path.\n");
469 exit(1);
470 }
471 use_seccomp_filter = 1;
Elly Jonese1749eb2011-10-07 13:54:59 -0400472 break;
Dylan Reidf7942472015-11-18 17:55:26 -0800473 case 'l':
474 minijail_namespace_ipc(j);
475 break;
Jorge Lucangeli Obesbda833c2012-07-31 16:25:56 -0700476 case 'L':
477 minijail_log_seccomp_filter_failures(j);
478 break;
Elly Jones51a5b6c2011-10-12 19:09:26 -0400479 case 'b':
480 add_binding(j, optarg);
Jorge Lucangeli Obes2b12ba42016-01-26 10:37:51 -0800481 binding = 1;
Elly Jones51a5b6c2011-10-12 19:09:26 -0400482 break;
Luis Hector Chavezec0a2c12017-06-29 20:29:57 -0700483 case 'B':
484 skip_securebits(j, optarg);
485 break;
Elly Jonese1749eb2011-10-07 13:54:59 -0400486 case 'c':
Jorge Lucangeli Obesa6eb21a2017-04-20 10:44:00 -0400487 caps = 1;
Elly Jonese1749eb2011-10-07 13:54:59 -0400488 use_caps(j, optarg);
489 break;
Elly Jones51a5b6c2011-10-12 19:09:26 -0400490 case 'C':
Luis Hector Chavezd45fc422017-10-25 15:11:53 -0700491 use_chroot(j, optarg, &chroot, pivot_root);
Yu-Hsi Chiang64d65a72015-08-13 17:43:27 +0800492 break;
Dylan Reid648b2202015-10-23 00:50:00 -0700493 case 'k':
494 add_mount(j, optarg);
495 break;
Jorge Lucangeli Obesa521bee2016-03-03 13:47:57 -0800496 case 'K':
497 minijail_skip_remount_private(j);
498 skip_remount = 1;
499 break;
Yu-Hsi Chiang64d65a72015-08-13 17:43:27 +0800500 case 'P':
Luis Hector Chavezd45fc422017-10-25 15:11:53 -0700501 use_pivot_root(j, optarg, &pivot_root, chroot);
Lee Campbell11af0622014-05-22 12:36:04 -0700502 break;
Yu-Hsi Chiang3cc05ea2015-08-11 11:23:17 +0800503 case 'f':
504 if (0 != minijail_write_pid_file(j, optarg)) {
Jorge Lucangeli Obes2b12ba42016-01-26 10:37:51 -0800505 fprintf(stderr,
506 "Could not prepare pid file path.\n");
Yu-Hsi Chiang3cc05ea2015-08-11 11:23:17 +0800507 exit(1);
508 }
509 break;
Lee Campbell11af0622014-05-22 12:36:04 -0700510 case 't':
Mike Frysingerec7def22017-01-13 18:44:45 -0500511 minijail_namespace_vfs(j);
Luis Hector Chavezd45fc422017-10-25 15:11:53 -0700512 if (!tmp_size) {
513 /*
514 * Avoid clobbering |tmp_size| if it was already
515 * set.
516 */
517 tmp_size = DEFAULT_TMP_SIZE;
518 }
519 if (optarg != NULL &&
520 0 != parse_size(&tmp_size, optarg)) {
Martin Pelikánab9eb442017-01-25 11:53:58 +1100521 fprintf(stderr, "Invalid /tmp tmpfs size.\n");
522 exit(1);
523 }
Elly Jones51a5b6c2011-10-12 19:09:26 -0400524 break;
Elly Jonese1749eb2011-10-07 13:54:59 -0400525 case 'v':
526 minijail_namespace_vfs(j);
Jorge Lucangeli Obesa521bee2016-03-03 13:47:57 -0800527 mount_ns = 1;
Elly Jonese1749eb2011-10-07 13:54:59 -0400528 break;
Jorge Lucangeli Obes1563b5b2014-07-10 07:01:53 -0700529 case 'V':
530 minijail_namespace_enter_vfs(j, optarg);
531 break;
Elly Jonese1749eb2011-10-07 13:54:59 -0400532 case 'r':
Dylan Reid791f5772015-09-14 20:02:42 -0700533 minijail_remount_proc_readonly(j);
Elly Jonese1749eb2011-10-07 13:54:59 -0400534 break;
535 case 'G':
Lutz Justen13807cb2017-01-03 17:11:55 +0100536 if (keep_suppl_gids) {
537 fprintf(stderr,
538 "-y and -G are not compatible.\n");
539 exit(1);
540 }
Elly Jonese1749eb2011-10-07 13:54:59 -0400541 minijail_inherit_usergroups(j);
Lutz Justen13807cb2017-01-03 17:11:55 +0100542 inherit_suppl_gids = 1;
543 break;
544 case 'y':
545 if (inherit_suppl_gids) {
546 fprintf(stderr,
547 "-y and -G are not compatible.\n");
548 exit(1);
549 }
550 minijail_keep_supplementary_gids(j);
551 keep_suppl_gids = 1;
Elly Jonese1749eb2011-10-07 13:54:59 -0400552 break;
Dylan Reid4cbc2a52016-06-17 19:06:07 -0700553 case 'N':
554 minijail_namespace_cgroups(j);
555 break;
Elly Jonese1749eb2011-10-07 13:54:59 -0400556 case 'p':
557 minijail_namespace_pids(j);
558 break;
Elly Fong-Jones6c086302013-03-20 17:15:28 -0400559 case 'e':
Dylan Reid1102f5a2015-09-15 11:52:20 -0700560 if (optarg)
561 minijail_namespace_enter_net(j, optarg);
562 else
563 minijail_namespace_net(j);
Elly Fong-Jones6c086302013-03-20 17:15:28 -0400564 break;
Christopher Wiley88f76a72013-11-01 14:12:56 -0700565 case 'i':
Christopher Wiley88f76a72013-11-01 14:12:56 -0700566 *exit_immediately = 1;
567 break;
Elly Jonese1749eb2011-10-07 13:54:59 -0400568 case 'H':
569 seccomp_filter_usage(argv[0]);
Mike Frysinger5b67e0b2018-01-22 18:52:06 -0500570 exit(0);
Yu-Hsi Chiang3e954ec2015-07-28 16:48:14 +0800571 case 'I':
572 minijail_namespace_pids(j);
573 minijail_run_as_init(j);
574 break;
Yu-Hsi Chiang10e91232015-08-05 14:40:45 +0800575 case 'U':
576 minijail_namespace_user(j);
577 minijail_namespace_pids(j);
578 break;
579 case 'm':
Luis Hector Chavez71323552017-09-05 09:17:22 -0700580 set_uidmap = 1;
581 if (uidmap) {
582 free(uidmap);
583 uidmap = NULL;
Jorge Lucangeli Obesab6fa6f2016-08-04 15:42:48 -0400584 }
Luis Hector Chavez71323552017-09-05 09:17:22 -0700585 if (optarg)
586 uidmap = strdup(optarg);
Yu-Hsi Chiang10e91232015-08-05 14:40:45 +0800587 break;
588 case 'M':
Luis Hector Chavez71323552017-09-05 09:17:22 -0700589 set_gidmap = 1;
590 if (gidmap) {
591 free(gidmap);
592 gidmap = NULL;
Jorge Lucangeli Obes200299c2016-09-23 15:21:57 -0400593 }
Luis Hector Chavez71323552017-09-05 09:17:22 -0700594 if (optarg)
595 gidmap = strdup(optarg);
Yu-Hsi Chiang10e91232015-08-05 14:40:45 +0800596 break;
Andrew Brestickereac28942015-11-11 16:04:46 -0800597 case 'a':
598 if (0 != minijail_use_alt_syscall(j, optarg)) {
Jorge Lucangeli Obes2b12ba42016-01-26 10:37:51 -0800599 fprintf(stderr,
600 "Could not set alt-syscall table.\n");
Andrew Brestickereac28942015-11-11 16:04:46 -0800601 exit(1);
602 }
603 break;
Dylan Reid0f72ef42017-06-06 15:42:49 -0700604 case 'R':
605 add_rlimit(j, optarg);
606 break;
Matthew Dempsky2ed09122016-02-11 09:43:37 -0800607 case 'T':
608 if (!strcmp(optarg, "static"))
609 *elftype = ELFSTATIC;
610 else if (!strcmp(optarg, "dynamic"))
611 *elftype = ELFDYNAMIC;
612 else {
Jorge Lucangeli Obes768d42b2016-02-17 10:28:25 -0800613 fprintf(stderr, "ELF type must be 'static' or "
614 "'dynamic'.\n");
Matthew Dempsky2ed09122016-02-11 09:43:37 -0800615 exit(1);
616 }
617 break;
Chirantan Ekbote866bb3a2017-02-07 12:26:42 -0800618 case 'w':
619 minijail_new_session_keyring(j);
620 break;
Jorge Lucangeli Obes13650612016-09-02 11:27:29 -0400621 case 'Y':
622 minijail_set_seccomp_filter_tsync(j);
623 break;
Jorge Lucangeli Obesdba62092017-05-18 17:10:23 -0400624 case 'z':
625 forward = 0;
626 break;
Mike Frysinger33ffef32017-01-13 19:53:19 -0500627 case 'd':
628 minijail_namespace_vfs(j);
629 minijail_mount_dev(j);
630 break;
Jorge Lucangeli Obesa6eb21a2017-04-20 10:44:00 -0400631 /* Long options. */
632 case 128: /* Ambient caps. */
633 ambient_caps = 1;
634 minijail_set_ambient_caps(j);
635 break;
Mike Frysingerb9a7b162017-05-30 15:25:49 -0400636 case 129: /* UTS/hostname namespace. */
637 minijail_namespace_uts(j);
638 if (optarg)
639 minijail_namespace_set_hostname(j, optarg);
640 break;
Luis Hector Chavez114a9302017-09-05 20:36:58 -0700641 case 130: /* Logging. */
642 if (!strcmp(optarg, "syslog"))
643 log_to_stderr = 0;
644 else if (!strcmp(optarg, "stderr")) {
645 log_to_stderr = 1;
646 } else {
647 fprintf(stderr, "--logger must be 'syslog' or "
648 "'stderr'.\n");
649 exit(1);
650 }
651 break;
Luis Hector Chavezd45fc422017-10-25 15:11:53 -0700652 case 131: /* Profile */
653 use_profile(j, optarg, &pivot_root, chroot, &tmp_size);
654 break;
Elly Jonese1749eb2011-10-07 13:54:59 -0400655 default:
656 usage(argv[0]);
Mike Frysinger227c2912017-10-04 14:27:04 -0400657 exit(opt == 'h' ? 0 : 1);
Elly Jonese1749eb2011-10-07 13:54:59 -0400658 }
659 }
Elly Jonescd7a9042011-07-22 13:56:51 -0400660
Luis Hector Chavez114a9302017-09-05 20:36:58 -0700661 if (log_to_stderr) {
662 init_logging(LOG_TO_FD, STDERR_FILENO, LOG_INFO);
663 /*
664 * When logging to stderr, ensure the FD survives the jailing.
665 */
666 if (0 !=
667 minijail_preserve_fd(j, STDERR_FILENO, STDERR_FILENO)) {
668 fprintf(stderr, "Could not preserve stderr.\n");
669 exit(1);
670 }
671 }
672
Luis Hector Chavez71323552017-09-05 09:17:22 -0700673 /* Set up uid/gid mapping. */
674 if (set_uidmap || set_gidmap) {
675 set_ugid_mapping(j, set_uidmap, uid, uidmap, set_gidmap, gid,
676 gidmap);
677 }
678
Jorge Lucangeli Obesa6eb21a2017-04-20 10:44:00 -0400679 /* Can only set ambient caps when using regular caps. */
680 if (ambient_caps && !caps) {
681 fprintf(stderr, "Can't set ambient capabilities (--ambient) "
682 "without actually using capabilities (-c).\n");
683 exit(1);
684 }
685
Jorge Lucangeli Obesdba62092017-05-18 17:10:23 -0400686 /* Set up signal handlers in minijail unless asked not to. */
687 if (forward)
688 minijail_forward_signals(j);
689
Mike Frysingerac08a682017-10-10 02:04:50 -0400690 /*
691 * Only allow bind mounts when entering a chroot, using pivot_root, or
692 * a new mount namespace.
693 */
694 if (binding && !(chroot || pivot_root || mount_ns)) {
695 fprintf(stderr, "Bind mounts require a chroot, pivot_root, or "
696 " new mount namespace.\n");
Jorge Lucangeli Obes2b12ba42016-01-26 10:37:51 -0800697 exit(1);
698 }
699
Jorge Lucangeli Obes482cb9d2014-07-23 15:16:04 -0700700 /*
Jorge Lucangeli Obesa521bee2016-03-03 13:47:57 -0800701 * Remounting / as MS_PRIVATE only happens when entering a new mount
702 * namespace, so skipping it only applies in that case.
703 */
704 if (skip_remount && !mount_ns) {
705 fprintf(stderr, "Can't skip marking mounts as MS_PRIVATE"
706 " without mount namespaces.\n");
707 exit(1);
708 }
709
710 /*
Jorge Lucangeli Obes482cb9d2014-07-23 15:16:04 -0700711 * We parse seccomp filters here to make sure we've collected all
712 * cmdline options.
713 */
714 if (use_seccomp_filter) {
715 minijail_parse_seccomp_filters(j, filter_path);
Jorge Lucangeli Obes0b208772017-04-19 14:15:46 -0400716 free((void *)filter_path);
Jorge Lucangeli Obes482cb9d2014-07-23 15:16:04 -0700717 }
718
Luis Hector Chavezd45fc422017-10-25 15:11:53 -0700719 /* Mount a tmpfs under /tmp and set its size. */
720 if (tmp_size)
721 minijail_mount_tmp_size(j, tmp_size);
722
Luis Hector Chavezfe5fb8e2017-06-29 10:41:27 -0700723 /*
724 * There should be at least one additional unparsed argument: the
725 * executable name.
726 */
Elly Jonese1749eb2011-10-07 13:54:59 -0400727 if (argc == optind) {
728 usage(argv[0]);
729 exit(1);
730 }
Lee Campbell11af0622014-05-22 12:36:04 -0700731
Luis Hector Chavezfe5fb8e2017-06-29 10:41:27 -0700732 if (*elftype == ELFERROR) {
733 /*
734 * -T was not specified.
735 * Get the path to the program adjusted for changing root.
736 */
737 char *program_path =
738 minijail_get_original_path(j, argv[optind]);
739
740 /* Check that we can access the target program. */
741 if (access(program_path, X_OK)) {
742 fprintf(stderr,
743 "Target program '%s' is not accessible.\n",
744 argv[optind]);
745 exit(1);
746 }
747
748 /* Check if target is statically or dynamically linked. */
749 *elftype = get_elf_linkage(program_path);
750 free(program_path);
751 }
752
753 /*
754 * Setting capabilities need either a dynamically-linked binary, or the
755 * use of ambient capabilities for them to be able to survive an
756 * execve(2).
757 */
758 if (caps && *elftype == ELFSTATIC && !ambient_caps) {
759 fprintf(stderr, "Can't run statically-linked binaries with "
760 "capabilities (-c) without also setting "
761 "ambient capabilities. Try passing "
762 "--ambient.\n");
763 exit(1);
764 }
765
Elly Fong-Jonesf65c9fe2013-01-22 13:55:02 -0500766 return optind;
767}
Elly Jonescd7a9042011-07-22 13:56:51 -0400768
Elly Fong-Jonesf65c9fe2013-01-22 13:55:02 -0500769int main(int argc, char *argv[])
770{
771 struct minijail *j = minijail_new();
Jorge Lucangeli Obesd99a40d2016-01-26 13:50:44 -0800772 const char *dl_mesg = NULL;
Christopher Wiley88f76a72013-11-01 14:12:56 -0700773 int exit_immediately = 0;
Lee Campbell1e4fc6a2014-06-06 17:40:02 -0700774 ElfType elftype = ELFERROR;
Matthew Dempsky2ed09122016-02-11 09:43:37 -0800775 int consumed = parse_args(j, argc, argv, &exit_immediately, &elftype);
Elly Fong-Jonesf65c9fe2013-01-22 13:55:02 -0500776 argc -= consumed;
777 argv += consumed;
Jorge Lucangeli Obes482cb9d2014-07-23 15:16:04 -0700778
Stephen Barber5dd5b1b2017-10-16 23:02:39 -0700779 /*
780 * Make the process group ID of this process equal to its PID.
781 * In the non-interactive case (e.g. when minijail0 is started from
782 * init) this ensures the parent process and the jailed process
783 * can be killed together.
784 *
785 * Don't fail on EPERM, since setpgid(0, 0) can only EPERM when
786 * the process is already a process group leader.
787 */
788 if (setpgid(0 /* use calling PID */, 0 /* make PGID = PID */)) {
789 if (errno != EPERM) {
790 fprintf(stderr, "setpgid(0, 0) failed\n");
791 exit(1);
792 }
793 }
794
Lee Campbell1e4fc6a2014-06-06 17:40:02 -0700795 if (elftype == ELFSTATIC) {
Jorge Lucangeli Obes54714502015-09-30 10:08:45 -0700796 /*
797 * Target binary is statically linked so we cannot use
798 * libminijailpreload.so.
799 */
800 minijail_run_no_preload(j, argv[0], argv);
Lee Campbell1e4fc6a2014-06-06 17:40:02 -0700801 } else if (elftype == ELFDYNAMIC) {
802 /*
803 * Target binary is dynamically linked so we can
804 * inject libminijailpreload.so into it.
805 */
806
807 /* Check that we can dlopen() libminijailpreload.so. */
808 if (!dlopen(PRELOADPATH, RTLD_LAZY | RTLD_LOCAL)) {
Matthew Dempsky2ed09122016-02-11 09:43:37 -0800809 dl_mesg = dlerror();
810 fprintf(stderr, "dlopen(): %s\n", dl_mesg);
811 return 1;
Lee Campbell1e4fc6a2014-06-06 17:40:02 -0700812 }
813 minijail_run(j, argv[0], argv);
814 } else {
Jorge Lucangeli Obes2f61ee42014-06-16 11:08:18 -0700815 fprintf(stderr,
816 "Target program '%s' is not a valid ELF file.\n",
817 argv[0]);
Jorge Lucangeli Obes4b2d5ee2014-01-09 15:47:47 -0800818 return 1;
819 }
Lee Campbell1e4fc6a2014-06-06 17:40:02 -0700820
Christopher Wiley88f76a72013-11-01 14:12:56 -0700821 if (exit_immediately) {
Luis Hector Chavez114a9302017-09-05 20:36:58 -0700822 info("not running init loop, exiting immediately\n");
Christopher Wiley88f76a72013-11-01 14:12:56 -0700823 return 0;
824 }
lhchavez6c8d8202017-09-01 03:55:11 +0000825 int ret = minijail_wait(j);
826#if defined(__SANITIZE_ADDRESS__)
827 minijail_destroy(j);
828#endif /* __SANITIZE_ADDRESS__ */
829 return ret;
Elly Jonescd7a9042011-07-22 13:56:51 -0400830}