1 /* Copyright (c) 2012 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 <stdio.h> 8 #include <stdlib.h> 9 #include <string.h> 10 #include <unistd.h> 11 12 #include "libminijail.h" 13 #include "libsyscalls.h" 14 15 #include "elfparse.h" 16 #include "util.h" 17 18 static 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 } 26 27 if (minijail_change_user(j, arg)) { 28 fprintf(stderr, "Bad user: '%s'\n", arg); 29 exit(1); 30 } 31 } 32 33 static 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 } 41 42 if (minijail_change_group(j, arg)) { 43 fprintf(stderr, "Bad group: '%s'\n", arg); 44 exit(1); 45 } 46 } 47 48 static 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); 58 } 59 60 static void add_binding(struct minijail *j, char *arg) 61 { 62 char *src = strtok(arg, ","); 63 char *dest = strtok(NULL, ","); 64 char *flags = strtok(NULL, ","); 65 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)) { 70 fprintf(stderr, "minijail_bind failed.\n"); 71 exit(1); 72 } 73 } 74 75 static 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 92 static void usage(const char *progn) 93 { 94 size_t i; 95 96 printf("Usage: %s [-GhiInprsvtUl] [-b <src>,<dest>[,<writeable>]] [-f <file>]" 97 "[-c <caps>] [-C <dir>] [-g <group>] [-S <file>] [-u <user>] " 98 "[-k <src>,<dest>,<type>[,<flags>]] " 99 "[-m \"<uid> <loweruid> <count>[,<uid> <loweruid> <count>]\"] " 100 "[-M \"<gid> <lowergid> <count>[,<uid> <loweruid> <count>]\"] " 101 "<program> [args...]\n" 102 " -a <table>: use alternate syscall table <table>\n" 103 " -b: binds <src> to <dest> in chroot. Multiple " 104 "instances allowed\n" 105 " -k: mount <src> to <dest> in chroot. Multiple " 106 "instances allowed, flags are passed to mount(2)\n" 107 " -c <caps>: restrict caps to <caps>\n" 108 " -C <dir>: chroot to <dir>\n" 109 " Not compatible with -P\n" 110 " -e[file]: enter new network namespace, or existing one if 'file' is provided\n" 111 " -f <file>: write the pid of the jailed process to <file>\n" 112 " -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" 116 " -i: exit immediately after fork (do not act as init)\n" 117 " Not compatible with -p\n" 118 " -I: run <program> as init (pid 1) inside a new pid namespace (implies -p)\n" 119 " -l: enter new IPC namespace\n" 120 " -L: report blocked syscalls to syslog when using seccomp filter.\n" 121 " Forces the following syscalls to be allowed:\n" 122 " ", progn); 123 for (i = 0; i < log_syscalls_len; i++) 124 printf("%s ", log_syscalls[i]); 125 126 printf("\n" 127 " -m: set the uid mapping of a user namespace (implies -pU).\n" 128 " Same arguments as newuidmap(1), multiple mappings should be separated by ',' (comma).\n" 129 " Not compatible with -b without writable\n" 130 " -M: set the gid mapping of a user namespace (implies -pU).\n" 131 " Same arguments as newgidmap(1), multiple mappings should be separated by ',' (comma).\n" 132 " Not compatible with -b without writable\n" 133 " -n: set no_new_privs\n" 134 " -p: enter new pid namespace (implies -vr)\n" 135 " -P <dir>: pivot_root to <dir> (implies -v)\n" 136 " Not compatible with -C\n" 137 " -r: remount /proc read-only (implies -v)\n" 138 " -s: use seccomp\n" 139 " -S <file>: set seccomp filter using <file>\n" 140 " E.g., -S /usr/share/filters/<prog>.$(uname -m)\n" 141 " Requires -n when not running as root\n" 142 " -t: mount tmpfs at /tmp inside chroot\n" 143 " -u <user>: change uid to <user>\n" 144 " -U enter new user namespace (implies -p)\n" 145 " -v: enter new mount namespace\n" 146 " -V <file>: enter specified mount namespace\n"); 147 } 148 149 static 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"); 157 } 158 159 static int parse_args(struct minijail *j, int argc, char *argv[], 160 int *exit_immediately) 161 { 162 int opt; 163 int use_seccomp_filter = 0; 164 int binding = 0; 165 int pivot_root = 0, chroot = 0; 166 const size_t path_max = 4096; 167 const char *filter_path; 168 if (argc > 1 && argv[1][0] != '-') 169 return 1; 170 while ((opt = getopt(argc, argv, 171 "u:g:sS:c:C:P:b:V:f:m:M:k:a:e::vrGhHinplLtIU")) 172 != -1) { 173 switch (opt) { 174 case 'u': 175 set_user(j, optarg); 176 break; 177 case 'g': 178 set_group(j, optarg); 179 break; 180 case 'n': 181 minijail_no_new_privs(j); 182 break; 183 case 's': 184 minijail_use_seccomp(j); 185 break; 186 case 'S': 187 minijail_use_seccomp_filter(j); 188 if (strlen(optarg) >= path_max) { 189 fprintf(stderr, "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; 199 break; 200 case 'l': 201 minijail_namespace_ipc(j); 202 break; 203 case 'L': 204 minijail_log_seccomp_filter_failures(j); 205 break; 206 case 'b': 207 add_binding(j, optarg); 208 binding = 1; 209 break; 210 case 'c': 211 use_caps(j, optarg); 212 break; 213 case 'C': 214 if (pivot_root) { 215 fprintf(stderr, "Could not set chroot because " 216 "'-P' was specified.\n"); 217 exit(1); 218 } 219 if (0 != minijail_enter_chroot(j, optarg)) { 220 fprintf(stderr, "Could not set chroot.\n"); 221 exit(1); 222 } 223 chroot = 1; 224 break; 225 case 'k': 226 add_mount(j, optarg); 227 break; 228 case 'P': 229 if (chroot) { 230 fprintf(stderr, 231 "Could not set pivot_root because " 232 "'-C' was specified.\n"); 233 exit(1); 234 } 235 if (0 != minijail_enter_pivot_root(j, optarg)) { 236 fprintf(stderr, "Could not set pivot_root.\n"); 237 exit(1); 238 } 239 minijail_namespace_vfs(j); 240 pivot_root = 1; 241 break; 242 case 'f': 243 if (0 != minijail_write_pid_file(j, optarg)) { 244 fprintf(stderr, 245 "Could not prepare pid file path.\n"); 246 exit(1); 247 } 248 break; 249 case 't': 250 minijail_mount_tmp(j); 251 break; 252 case 'v': 253 minijail_namespace_vfs(j); 254 break; 255 case 'V': 256 minijail_namespace_enter_vfs(j, optarg); 257 break; 258 case 'r': 259 minijail_remount_proc_readonly(j); 260 break; 261 case 'G': 262 minijail_inherit_usergroups(j); 263 break; 264 case 'p': 265 minijail_namespace_pids(j); 266 break; 267 case 'e': 268 if (optarg) 269 minijail_namespace_enter_net(j, optarg); 270 else 271 minijail_namespace_net(j); 272 break; 273 case 'i': 274 *exit_immediately = 1; 275 break; 276 case 'H': 277 seccomp_filter_usage(argv[0]); 278 exit(1); 279 case 'I': 280 minijail_namespace_pids(j); 281 minijail_run_as_init(j); 282 break; 283 case 'U': 284 minijail_namespace_user(j); 285 minijail_namespace_pids(j); 286 break; 287 case 'm': 288 minijail_namespace_user(j); 289 minijail_namespace_pids(j); 290 if (0 != minijail_uidmap(j, optarg)) { 291 fprintf(stderr, "Could not set uidmap.\n"); 292 exit(1); 293 } 294 break; 295 case 'M': 296 minijail_namespace_user(j); 297 minijail_namespace_pids(j); 298 if (0 != minijail_gidmap(j, optarg)) { 299 fprintf(stderr, "Could not set gidmap.\n"); 300 exit(1); 301 } 302 break; 303 case 'a': 304 if (0 != minijail_use_alt_syscall(j, optarg)) { 305 fprintf(stderr, 306 "Could not set alt-syscall table.\n"); 307 exit(1); 308 } 309 break; 310 default: 311 usage(argv[0]); 312 exit(1); 313 } 314 if (optind < argc && argv[optind][0] != '-') 315 break; 316 } 317 318 /* Only allow bind mounts when entering a chroot or using pivot_root. */ 319 if (binding && !(chroot || pivot_root)) { 320 fprintf(stderr, "Can't add bind mounts without chroot or" 321 " pivot_root.\n"); 322 exit(1); 323 } 324 325 /* 326 * We parse seccomp filters here to make sure we've collected all 327 * cmdline options. 328 */ 329 if (use_seccomp_filter) { 330 minijail_parse_seccomp_filters(j, filter_path); 331 free((void*)filter_path); 332 } 333 334 if (argc == optind) { 335 usage(argv[0]); 336 exit(1); 337 } 338 339 return optind; 340 } 341 342 int main(int argc, char *argv[]) 343 { 344 struct minijail *j = minijail_new(); 345 const char *dl_mesg = NULL; 346 int exit_immediately = 0; 347 char *program_path; 348 int consumed = parse_args(j, argc, argv, &exit_immediately); 349 ElfType elftype = ELFERROR; 350 argc -= consumed; 351 argv += consumed; 352 353 /* Get the path to the program adjusted for changing root. */ 354 program_path = minijail_get_original_path(j, argv[0]); 355 356 /* Check that we can access the target program. */ 357 if (access(program_path, X_OK)) { 358 fprintf(stderr, "Target program '%s' is not accessible.\n", 359 argv[0]); 360 return 1; 361 } 362 363 /* Check if target is statically or dynamically linked. */ 364 elftype = get_elf_linkage(program_path); 365 if (elftype == ELFSTATIC) { 366 /* 367 * Target binary is statically linked so we cannot use 368 * libminijailpreload.so. 369 */ 370 minijail_run_no_preload(j, argv[0], argv); 371 } else if (elftype == ELFDYNAMIC) { 372 /* 373 * Target binary is dynamically linked so we can 374 * inject libminijailpreload.so into it. 375 */ 376 377 /* Check that we can dlopen() libminijailpreload.so. */ 378 if (!dlopen(PRELOADPATH, RTLD_LAZY | RTLD_LOCAL)) { 379 dl_mesg = dlerror(); 380 fprintf(stderr, "dlopen(): %s\n", dl_mesg); 381 return 1; 382 } 383 minijail_run(j, argv[0], argv); 384 } else { 385 fprintf(stderr, 386 "Target program '%s' is not a valid ELF file.\n", 387 argv[0]); 388 return 1; 389 } 390 391 free(program_path); 392 393 if (exit_immediately) { 394 info("not running init loop, exiting immediately"); 395 return 0; 396 } 397 return minijail_wait(j); 398 } 399