blob: 442934caf17d594b0f844af2678172ea24d03592 [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>
Elly Jonescd7a9042011-07-22 13:56:51 -04007#include <stdio.h>
8#include <stdlib.h>
9#include <string.h>
10#include <unistd.h>
11
12#include "libminijail.h"
Will Drewry32ac9f52011-08-18 21:36:27 -050013#include "libsyscalls.h"
Elly Jonescd7a9042011-07-22 13:56:51 -040014
Lee Campbell1e4fc6a2014-06-06 17:40:02 -070015#include "elfparse.h"
Jorge Lucangeli Obesbda833c2012-07-31 16:25:56 -070016#include "util.h"
17
Elly Jonese1749eb2011-10-07 13:54:59 -040018static void set_user(struct minijail *j, const char *arg)
19{
20 char *end = NULL;
21 int uid = strtod(arg, &end);
22 if (!*end && *arg) {
23 minijail_change_uid(j, uid);
24 return;
25 }
Elly Jonescd7a9042011-07-22 13:56:51 -040026
Elly Jonese1749eb2011-10-07 13:54:59 -040027 if (minijail_change_user(j, arg)) {
28 fprintf(stderr, "Bad user: '%s'\n", arg);
29 exit(1);
30 }
Elly Jonescd7a9042011-07-22 13:56:51 -040031}
32
Elly Jonese1749eb2011-10-07 13:54:59 -040033static void set_group(struct minijail *j, const char *arg)
34{
35 char *end = NULL;
36 int gid = strtod(arg, &end);
37 if (!*end && *arg) {
38 minijail_change_gid(j, gid);
39 return;
40 }
Elly Jonescd7a9042011-07-22 13:56:51 -040041
Elly Jonese1749eb2011-10-07 13:54:59 -040042 if (minijail_change_group(j, arg)) {
43 fprintf(stderr, "Bad group: '%s'\n", arg);
44 exit(1);
45 }
Elly Jonescd7a9042011-07-22 13:56:51 -040046}
47
Elly Jonese1749eb2011-10-07 13:54:59 -040048static void use_caps(struct minijail *j, const char *arg)
49{
50 uint64_t caps;
51 char *end = NULL;
52 caps = strtoull(arg, &end, 16);
53 if (*end) {
54 fprintf(stderr, "Invalid cap set: '%s'\n", arg);
55 exit(1);
56 }
57 minijail_use_caps(j, caps);
Elly Jonescd7a9042011-07-22 13:56:51 -040058}
59
Jorge Lucangeli Obesc8b21e12014-06-13 14:26:16 -070060static void add_binding(struct minijail *j, char *arg)
61{
Elly Jones51a5b6c2011-10-12 19:09:26 -040062 char *src = strtok(arg, ",");
Elly Jones5ba42b52011-12-07 13:31:43 -050063 char *dest = strtok(NULL, ",");
64 char *flags = strtok(NULL, ",");
Elly Jones51a5b6c2011-10-12 19:09:26 -040065 if (!src || !dest) {
66 fprintf(stderr, "Bad binding: %s %s\n", src, dest);
67 exit(1);
68 }
69 if (minijail_bind(j, src, dest, flags ? atoi(flags) : 0)) {
Jorge Lucangeli Obes2f61ee42014-06-16 11:08:18 -070070 fprintf(stderr, "Bind failure.\n");
Elly Jones51a5b6c2011-10-12 19:09:26 -040071 exit(1);
72 }
73}
74
Dylan Reid648b2202015-10-23 00:50:00 -070075static void add_mount(struct minijail *j, char *arg)
76{
77 char *src = strtok(arg, ",");
78 char *dest = strtok(NULL, ",");
79 char *type = strtok(NULL, ",");
80 char *flags = strtok(NULL, ",");
81 if (!src || !dest || !type) {
82 fprintf(stderr, "Bad mount: %s %s %s\n", src, dest, type);
83 exit(1);
84 }
85 if (minijail_mount(j, src, dest, type,
86 flags ? strtoul(flags, NULL, 16) : 0)) {
87 fprintf(stderr, "minijail_mount failed.\n");
88 exit(1);
89 }
90}
91
Elly Jonese1749eb2011-10-07 13:54:59 -040092static void usage(const char *progn)
93{
Jorge Lucangeli Obesbda833c2012-07-31 16:25:56 -070094 size_t i;
95
Dylan Reidf7942472015-11-18 17:55:26 -080096 printf("Usage: %s [-GhiInprsvtUl] [-b <src>,<dest>[,<writeable>]] [-f <file>]"
Jorge Lucangeli Obesc2c9bcc2012-05-01 09:30:24 -070097 "[-c <caps>] [-C <dir>] [-g <group>] [-S <file>] [-u <user>] "
Dylan Reid648b2202015-10-23 00:50:00 -070098 "[-k <src>,<dest>,<type>[,<flags>]] "
Yu-Hsi Chiang1912c5b2015-08-31 18:59:49 +080099 "[-m \"<uid> <loweruid> <count>[,<uid> <loweruid> <count>]\"] "
100 "[-M \"<gid> <lowergid> <count>[,<uid> <loweruid> <count>]\"] "
Jorge Lucangeli Obesc2c9bcc2012-05-01 09:30:24 -0700101 "<program> [args...]\n"
Andrew Brestickereac28942015-11-11 16:04:46 -0800102 " -a <table>: use alternate syscall table <table>\n"
Elly Jonesa8d1e1b2011-10-21 15:38:00 -0400103 " -b: binds <src> to <dest> in chroot. Multiple "
104 "instances allowed\n"
Dylan Reid648b2202015-10-23 00:50:00 -0700105 " -k: mount <src> to <dest> in chroot. Multiple "
106 "instances allowed, flags are passed to mount(2)\n"
Elly Jonese1749eb2011-10-07 13:54:59 -0400107 " -c <caps>: restrict caps to <caps>\n"
Elly Jonesa8d1e1b2011-10-21 15:38:00 -0400108 " -C <dir>: chroot to <dir>\n"
Yu-Hsi Chiang64d65a72015-08-13 17:43:27 +0800109 " Not compatible with -P\n"
Dylan Reid1102f5a2015-09-15 11:52:20 -0700110 " -e[file]: enter new network namespace, or existing one if 'file' is provided\n"
Yu-Hsi Chiang3cc05ea2015-08-11 11:23:17 +0800111 " -f <file>: write the pid of the jailed process to <file>\n"
Elly Jonese1749eb2011-10-07 13:54:59 -0400112 " -G: inherit secondary groups from uid\n"
113 " -g <group>: change gid to <group>\n"
114 " -h: help (this message)\n"
115 " -H: seccomp filter help message\n"
Christopher Wiley88f76a72013-11-01 14:12:56 -0700116 " -i: exit immediately after fork (do not act as init)\n"
117 " Not compatible with -p\n"
Yu-Hsi Chiang3e954ec2015-07-28 16:48:14 +0800118 " -I: run <program> as init (pid 1) inside a new pid namespace (implies -p)\n"
Dylan Reidf7942472015-11-18 17:55:26 -0800119 " -l: enter new IPC namespace\n"
Kees Cook03b2af22014-12-18 17:11:13 -0800120 " -L: report blocked syscalls to syslog when using seccomp filter.\n"
121 " Forces the following syscalls to be allowed:\n"
122 " ", progn);
Jorge Lucangeli Obesbda833c2012-07-31 16:25:56 -0700123 for (i = 0; i < log_syscalls_len; i++)
124 printf("%s ", log_syscalls[i]);
125
126 printf("\n"
Yu-Hsi Chiang10e91232015-08-05 14:40:45 +0800127 " -m: set the uid mapping of a user namespace (implies -pU).\n"
Yu-Hsi Chiang1912c5b2015-08-31 18:59:49 +0800128 " Same arguments as newuidmap(1), multiple mappings should be separated by ',' (comma).\n"
Yu-Hsi Chiang10e91232015-08-05 14:40:45 +0800129 " Not compatible with -b without writable\n"
130 " -M: set the gid mapping of a user namespace (implies -pU).\n"
Yu-Hsi Chiang1912c5b2015-08-31 18:59:49 +0800131 " Same arguments as newgidmap(1), multiple mappings should be separated by ',' (comma).\n"
Yu-Hsi Chiang10e91232015-08-05 14:40:45 +0800132 " Not compatible with -b without writable\n"
Jorge Lucangeli Obesc2c9bcc2012-05-01 09:30:24 -0700133 " -n: set no_new_privs\n"
Jorge Lucangeli Obes1563b5b2014-07-10 07:01:53 -0700134 " -p: enter new pid namespace (implies -vr)\n"
Yu-Hsi Chiang64d65a72015-08-13 17:43:27 +0800135 " -P <dir>: pivot_root to <dir> (implies -v)\n"
136 " Not compatible with -C\n"
Jorge Lucangeli Obes1563b5b2014-07-10 07:01:53 -0700137 " -r: remount /proc read-only (implies -v)\n"
Elly Jonese1749eb2011-10-07 13:54:59 -0400138 " -s: use seccomp\n"
Jorge Lucangeli Obesbda833c2012-07-31 16:25:56 -0700139 " -S <file>: set seccomp filter using <file>\n"
Elly Jonese1749eb2011-10-07 13:54:59 -0400140 " E.g., -S /usr/share/filters/<prog>.$(uname -m)\n"
Kees Cook03b2af22014-12-18 17:11:13 -0800141 " Requires -n when not running as root\n"
Jorge Lucangeli Obesc8b21e12014-06-13 14:26:16 -0700142 " -t: mount tmpfs at /tmp inside chroot\n"
Elly Jonese1749eb2011-10-07 13:54:59 -0400143 " -u <user>: change uid to <user>\n"
Yu-Hsi Chiang10e91232015-08-05 14:40:45 +0800144 " -U enter new user namespace (implies -p)\n"
Jorge Lucangeli Obes1563b5b2014-07-10 07:01:53 -0700145 " -v: enter new mount namespace\n"
146 " -V <file>: enter specified mount namespace\n");
Elly Jonescd7a9042011-07-22 13:56:51 -0400147}
148
Elly Jonese1749eb2011-10-07 13:54:59 -0400149static void seccomp_filter_usage(const char *progn)
150{
151 const struct syscall_entry *entry = syscall_table;
152 printf("Usage: %s -S <policy.file> <program> [args...]\n\n"
153 "System call names supported:\n", progn);
154 for (; entry->name && entry->nr >= 0; ++entry)
155 printf(" %s [%d]\n", entry->name, entry->nr);
156 printf("\nSee minijail0(5) for example policies.\n");
Will Drewry32ac9f52011-08-18 21:36:27 -0500157}
158
Christopher Wiley88f76a72013-11-01 14:12:56 -0700159static int parse_args(struct minijail *j, int argc, char *argv[],
160 int *exit_immediately)
Elly Jonese1749eb2011-10-07 13:54:59 -0400161{
Elly Jonese1749eb2011-10-07 13:54:59 -0400162 int opt;
Jorge Lucangeli Obes482cb9d2014-07-23 15:16:04 -0700163 int use_seccomp_filter = 0;
Yu-Hsi Chiang64d65a72015-08-13 17:43:27 +0800164 int pivot_root = 0, chroot = 0;
Jorge Lucangeli Obes482cb9d2014-07-23 15:16:04 -0700165 const size_t path_max = 4096;
166 const char *filter_path;
Elly Fong-Jonesf65c9fe2013-01-22 13:55:02 -0500167 if (argc > 1 && argv[1][0] != '-')
168 return 1;
Jorge Lucangeli Obes54714502015-09-30 10:08:45 -0700169 while ((opt = getopt(argc, argv,
Dylan Reidf7942472015-11-18 17:55:26 -0800170 "u:g:sS:c:C:P:b:V:f:m:M:k:a:e::vrGhHinplLtIU"))
Dylan Reid648b2202015-10-23 00:50:00 -0700171 != -1) {
Elly Jonese1749eb2011-10-07 13:54:59 -0400172 switch (opt) {
173 case 'u':
174 set_user(j, optarg);
175 break;
176 case 'g':
177 set_group(j, optarg);
178 break;
Jorge Lucangeli Obesc2c9bcc2012-05-01 09:30:24 -0700179 case 'n':
180 minijail_no_new_privs(j);
Jorge Lucangeli Obes0341d6c2012-07-16 15:27:31 -0700181 break;
Elly Jonese1749eb2011-10-07 13:54:59 -0400182 case 's':
183 minijail_use_seccomp(j);
184 break;
185 case 'S':
Elly Jonese1749eb2011-10-07 13:54:59 -0400186 minijail_use_seccomp_filter(j);
Jorge Lucangeli Obes482cb9d2014-07-23 15:16:04 -0700187 if (strlen(optarg) >= path_max) {
188 fprintf(stderr,
189 "Filter path is too long.\n");
190 exit(1);
191 }
192 filter_path = strndup(optarg, path_max);
193 if (!filter_path) {
194 fprintf(stderr,
195 "Could not strndup(3) filter path.\n");
196 exit(1);
197 }
198 use_seccomp_filter = 1;
Elly Jonese1749eb2011-10-07 13:54:59 -0400199 break;
Dylan Reidf7942472015-11-18 17:55:26 -0800200 case 'l':
201 minijail_namespace_ipc(j);
202 break;
Jorge Lucangeli Obesbda833c2012-07-31 16:25:56 -0700203 case 'L':
204 minijail_log_seccomp_filter_failures(j);
205 break;
Elly Jones51a5b6c2011-10-12 19:09:26 -0400206 case 'b':
207 add_binding(j, optarg);
208 break;
Elly Jonese1749eb2011-10-07 13:54:59 -0400209 case 'c':
210 use_caps(j, optarg);
211 break;
Elly Jones51a5b6c2011-10-12 19:09:26 -0400212 case 'C':
Yu-Hsi Chiang64d65a72015-08-13 17:43:27 +0800213 if (pivot_root) {
214 fprintf(stderr, "Could not set chroot because "
215 "'-P' was specified.\n");
216 exit(1);
217 }
Jorge Lucangeli Obes1563b5b2014-07-10 07:01:53 -0700218 if (0 != minijail_enter_chroot(j, optarg)) {
219 fprintf(stderr, "Could not set chroot.\n");
Lee Campbell11af0622014-05-22 12:36:04 -0700220 exit(1);
Jorge Lucangeli Obes1563b5b2014-07-10 07:01:53 -0700221 }
Yu-Hsi Chiang64d65a72015-08-13 17:43:27 +0800222 chroot = 1;
223 break;
Dylan Reid648b2202015-10-23 00:50:00 -0700224 case 'k':
225 add_mount(j, optarg);
226 break;
Yu-Hsi Chiang64d65a72015-08-13 17:43:27 +0800227 case 'P':
228 if (chroot) {
229 fprintf(stderr, "Could not set pivot_root because "
230 "'-C' was specified.\n");
231 exit(1);
232 }
233 if (0 != minijail_enter_pivot_root(j, optarg)) {
234 fprintf(stderr, "Could not set pivot_root.\n");
235 exit(1);
236 }
237 minijail_namespace_vfs(j);
238 pivot_root = 1;
Lee Campbell11af0622014-05-22 12:36:04 -0700239 break;
Yu-Hsi Chiang3cc05ea2015-08-11 11:23:17 +0800240 case 'f':
241 if (0 != minijail_write_pid_file(j, optarg)) {
242 fprintf(stderr, "Could not prepare pid file path.\n");
243 exit(1);
244 }
245 break;
Lee Campbell11af0622014-05-22 12:36:04 -0700246 case 't':
247 minijail_mount_tmp(j);
Elly Jones51a5b6c2011-10-12 19:09:26 -0400248 break;
Elly Jonese1749eb2011-10-07 13:54:59 -0400249 case 'v':
250 minijail_namespace_vfs(j);
251 break;
Jorge Lucangeli Obes1563b5b2014-07-10 07:01:53 -0700252 case 'V':
253 minijail_namespace_enter_vfs(j, optarg);
254 break;
Elly Jonese1749eb2011-10-07 13:54:59 -0400255 case 'r':
Dylan Reid791f5772015-09-14 20:02:42 -0700256 minijail_remount_proc_readonly(j);
Elly Jonese1749eb2011-10-07 13:54:59 -0400257 break;
258 case 'G':
259 minijail_inherit_usergroups(j);
260 break;
261 case 'p':
262 minijail_namespace_pids(j);
263 break;
Elly Fong-Jones6c086302013-03-20 17:15:28 -0400264 case 'e':
Dylan Reid1102f5a2015-09-15 11:52:20 -0700265 if (optarg)
266 minijail_namespace_enter_net(j, optarg);
267 else
268 minijail_namespace_net(j);
Elly Fong-Jones6c086302013-03-20 17:15:28 -0400269 break;
Christopher Wiley88f76a72013-11-01 14:12:56 -0700270 case 'i':
Christopher Wiley88f76a72013-11-01 14:12:56 -0700271 *exit_immediately = 1;
272 break;
Elly Jonese1749eb2011-10-07 13:54:59 -0400273 case 'H':
274 seccomp_filter_usage(argv[0]);
275 exit(1);
Yu-Hsi Chiang3e954ec2015-07-28 16:48:14 +0800276 case 'I':
277 minijail_namespace_pids(j);
278 minijail_run_as_init(j);
279 break;
Yu-Hsi Chiang10e91232015-08-05 14:40:45 +0800280 case 'U':
281 minijail_namespace_user(j);
282 minijail_namespace_pids(j);
283 break;
284 case 'm':
285 minijail_namespace_user(j);
286 minijail_namespace_pids(j);
287 if (0 != minijail_uidmap(j, optarg)) {
288 fprintf(stderr, "Could not set uidmap\n");
289 exit(1);
290 }
291 break;
292 case 'M':
293 minijail_namespace_user(j);
294 minijail_namespace_pids(j);
295 if (0 != minijail_gidmap(j, optarg)) {
296 fprintf(stderr, "Could not set gidmap\n");
297 exit(1);
298 }
299 break;
Andrew Brestickereac28942015-11-11 16:04:46 -0800300 case 'a':
301 if (0 != minijail_use_alt_syscall(j, optarg)) {
302 fprintf(stderr, "Could not set alt-syscall table\n");
303 exit(1);
304 }
305 break;
Elly Jonese1749eb2011-10-07 13:54:59 -0400306 default:
307 usage(argv[0]);
308 exit(1);
309 }
Elly Fong-Jonesf65c9fe2013-01-22 13:55:02 -0500310 if (optind < argc && argv[optind][0] != '-')
Lee Campbell11af0622014-05-22 12:36:04 -0700311 break;
Elly Jonese1749eb2011-10-07 13:54:59 -0400312 }
Elly Jonescd7a9042011-07-22 13:56:51 -0400313
Jorge Lucangeli Obes482cb9d2014-07-23 15:16:04 -0700314 /*
315 * We parse seccomp filters here to make sure we've collected all
316 * cmdline options.
317 */
318 if (use_seccomp_filter) {
319 minijail_parse_seccomp_filters(j, filter_path);
320 free((void*)filter_path);
321 }
322
Elly Jonese1749eb2011-10-07 13:54:59 -0400323 if (argc == optind) {
324 usage(argv[0]);
325 exit(1);
326 }
Lee Campbell11af0622014-05-22 12:36:04 -0700327
Elly Fong-Jonesf65c9fe2013-01-22 13:55:02 -0500328 return optind;
329}
Elly Jonescd7a9042011-07-22 13:56:51 -0400330
Elly Fong-Jonesf65c9fe2013-01-22 13:55:02 -0500331int main(int argc, char *argv[])
332{
333 struct minijail *j = minijail_new();
Jorge Lucangeli Obesd99a40d2016-01-26 13:50:44 -0800334 const char *dl_mesg = NULL;
Christopher Wiley88f76a72013-11-01 14:12:56 -0700335 int exit_immediately = 0;
Dylan Reid08946cc2015-09-16 19:10:57 -0700336 char *program_path;
Christopher Wiley88f76a72013-11-01 14:12:56 -0700337 int consumed = parse_args(j, argc, argv, &exit_immediately);
Lee Campbell1e4fc6a2014-06-06 17:40:02 -0700338 ElfType elftype = ELFERROR;
Elly Fong-Jonesf65c9fe2013-01-22 13:55:02 -0500339 argc -= consumed;
340 argv += consumed;
Jorge Lucangeli Obes482cb9d2014-07-23 15:16:04 -0700341
Dylan Reid08946cc2015-09-16 19:10:57 -0700342 /* Get the path to the program adjusted for changing root. */
343 program_path = minijail_get_original_path(j, argv[0]);
344
Jorge Lucangeli Obes4b2d5ee2014-01-09 15:47:47 -0800345 /* Check that we can access the target program. */
Dylan Reida14e08d2015-10-22 21:05:29 -0700346 if (access(program_path, X_OK)) {
Jorge Lucangeli Obes2f61ee42014-06-16 11:08:18 -0700347 fprintf(stderr, "Target program '%s' is not accessible.\n",
Jorge Lucangeli Obesc8b21e12014-06-13 14:26:16 -0700348 argv[0]);
Elly Fong-Jones6d717852013-03-19 16:29:03 -0400349 return 1;
350 }
Jorge Lucangeli Obes482cb9d2014-07-23 15:16:04 -0700351
Lee Campbell1e4fc6a2014-06-06 17:40:02 -0700352 /* Check if target is statically or dynamically linked. */
Dylan Reida14e08d2015-10-22 21:05:29 -0700353 elftype = get_elf_linkage(program_path);
Lee Campbell1e4fc6a2014-06-06 17:40:02 -0700354 if (elftype == ELFSTATIC) {
Jorge Lucangeli Obes54714502015-09-30 10:08:45 -0700355 /*
356 * Target binary is statically linked so we cannot use
357 * libminijailpreload.so.
358 */
359 minijail_run_no_preload(j, argv[0], argv);
Lee Campbell1e4fc6a2014-06-06 17:40:02 -0700360 } else if (elftype == ELFDYNAMIC) {
361 /*
362 * Target binary is dynamically linked so we can
363 * inject libminijailpreload.so into it.
364 */
365
366 /* Check that we can dlopen() libminijailpreload.so. */
367 if (!dlopen(PRELOADPATH, RTLD_LAZY | RTLD_LOCAL)) {
368 dl_mesg = dlerror();
369 fprintf(stderr, "dlopen(): %s\n", dl_mesg);
370 return 1;
371 }
372 minijail_run(j, argv[0], argv);
373 } else {
Jorge Lucangeli Obes2f61ee42014-06-16 11:08:18 -0700374 fprintf(stderr,
375 "Target program '%s' is not a valid ELF file.\n",
376 argv[0]);
Jorge Lucangeli Obes4b2d5ee2014-01-09 15:47:47 -0800377 return 1;
378 }
Lee Campbell1e4fc6a2014-06-06 17:40:02 -0700379
Dylan Reid08946cc2015-09-16 19:10:57 -0700380 free(program_path);
381
Christopher Wiley88f76a72013-11-01 14:12:56 -0700382 if (exit_immediately) {
383 info("not running init loop, exiting immediately");
384 return 0;
385 }
Elly Jonese1749eb2011-10-07 13:54:59 -0400386 return minijail_wait(j);
Elly Jonescd7a9042011-07-22 13:56:51 -0400387}