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 <getopt.h> 8 #include <stdio.h> 9 #include <stdlib.h> 10 #include <string.h> 11 #include <unistd.h> 12 13 #include "libminijail.h" 14 #include "libsyscalls.h" 15 16 #include "elfparse.h" 17 #include "util.h" 18 19 #define IDMAP_LEN 32U 20 21 static void set_user(struct minijail *j, const char *arg) 22 { 23 char *end = NULL; 24 int uid = strtod(arg, &end); 25 if (!*end && *arg) { 26 minijail_change_uid(j, uid); 27 return; 28 } 29 30 if (minijail_change_user(j, arg)) { 31 fprintf(stderr, "Bad user: '%s'\n", arg); 32 exit(1); 33 } 34 } 35 36 static void set_group(struct minijail *j, const char *arg) 37 { 38 char *end = NULL; 39 int gid = strtod(arg, &end); 40 if (!*end && *arg) { 41 minijail_change_gid(j, gid); 42 return; 43 } 44 45 if (minijail_change_group(j, arg)) { 46 fprintf(stderr, "Bad group: '%s'\n", arg); 47 exit(1); 48 } 49 } 50 51 static void skip_securebits(struct minijail *j, const char *arg) 52 { 53 uint64_t securebits_skip_mask; 54 char *end = NULL; 55 securebits_skip_mask = strtoull(arg, &end, 16); 56 if (*end) { 57 fprintf(stderr, "Invalid securebit mask: '%s'\n", arg); 58 exit(1); 59 } 60 minijail_skip_setting_securebits(j, securebits_skip_mask); 61 } 62 63 static void use_caps(struct minijail *j, const char *arg) 64 { 65 uint64_t caps; 66 char *end = NULL; 67 caps = strtoull(arg, &end, 16); 68 if (*end) { 69 fprintf(stderr, "Invalid cap set: '%s'\n", arg); 70 exit(1); 71 } 72 minijail_use_caps(j, caps); 73 } 74 75 static void add_binding(struct minijail *j, char *arg) 76 { 77 char *src = strtok(arg, ","); 78 char *dest = strtok(NULL, ","); 79 char *flags = strtok(NULL, ","); 80 if (!src || !dest) { 81 fprintf(stderr, "Bad binding: %s %s\n", src, dest); 82 exit(1); 83 } 84 if (minijail_bind(j, src, dest, flags ? atoi(flags) : 0)) { 85 fprintf(stderr, "minijail_bind failed.\n"); 86 exit(1); 87 } 88 } 89 90 static void add_rlimit(struct minijail *j, char *arg) 91 { 92 char *type = strtok(arg, ","); 93 char *cur = strtok(NULL, ","); 94 char *max = strtok(NULL, ","); 95 if (!type || !cur || !max) { 96 fprintf(stderr, "Bad rlimit '%s'.\n", arg); 97 exit(1); 98 } 99 if (minijail_rlimit(j, atoi(type), atoi(cur), atoi(max))) { 100 fprintf(stderr, "minijail_rlimit '%s,%s,%s' failed.\n", 101 type, cur, max); 102 exit(1); 103 } 104 } 105 106 static void add_mount(struct minijail *j, char *arg) 107 { 108 char *src = strtok(arg, ","); 109 char *dest = strtok(NULL, ","); 110 char *type = strtok(NULL, ","); 111 char *flags = strtok(NULL, ","); 112 char *data = strtok(NULL, ","); 113 if (!src || !dest || !type) { 114 fprintf(stderr, "Bad mount: %s %s %s\n", src, dest, type); 115 exit(1); 116 } 117 if (minijail_mount_with_data(j, src, dest, type, 118 flags ? strtoul(flags, NULL, 16) : 0, 119 data)) { 120 fprintf(stderr, "minijail_mount failed.\n"); 121 exit(1); 122 } 123 } 124 125 static char *build_idmap(id_t id, id_t lowerid) 126 { 127 int ret; 128 char *idmap = malloc(IDMAP_LEN); 129 ret = snprintf(idmap, IDMAP_LEN, "%d %d 1", id, lowerid); 130 if (ret < 0 || (size_t)ret >= IDMAP_LEN) { 131 free(idmap); 132 fprintf(stderr, "Could not build id map.\n"); 133 exit(1); 134 } 135 return idmap; 136 } 137 138 static void usage(const char *progn) 139 { 140 size_t i; 141 /* clang-format off */ 142 printf("Usage: %s [-GhHiIKlLnNprRstUvyYz]\n" 143 " [-a <table>]\n" 144 " [-b <src>,<dest>[,<writeable>]] [-k <src>,<dest>,<type>[,<flags>][,<data>]]\n" 145 " [-c <caps>] [-C <dir>] [-P <dir>] [-e[file]] [-f <file>] [-g <group>]\n" 146 " [-m[<uid> <loweruid> <count>]*] [-M[<gid> <lowergid> <count>]*]\n" 147 " [-R <type,cur,max>] [-S <file>] [-t[size]] [-T <type>] [-u <user>] [-V <file>]\n" 148 " <program> [args...]\n" 149 " -a <table>: Use alternate syscall table <table>.\n" 150 " -b: Bind <src> to <dest> in chroot.\n" 151 " Multiple instances allowed.\n" 152 " -B <mask> Skip setting securebits in <mask> when restricting capabilities (-c).\n" 153 " By default, SECURE_NOROOT, SECURE_NO_SETUID_FIXUP, and \n" 154 " SECURE_KEEP_CAPS (together with their respective locks) are set.\n" 155 " -k: Mount <src> at <dest> in chroot.\n" 156 " <flags> and <data> can be specified as in mount(2).\n" 157 " Multiple instances allowed.\n" 158 " -c <caps>: Restrict caps to <caps>.\n" 159 " -C <dir>: chroot(2) to <dir>.\n" 160 " Not compatible with -P.\n" 161 " -P <dir>: pivot_root(2) to <dir> (implies -v).\n" 162 " Not compatible with -C.\n" 163 " -e[file]: Enter new network namespace, or existing one if |file| is provided.\n" 164 " -f <file>: Write the pid of the jailed process to <file>.\n" 165 " -g <group>: Change gid to <group>.\n" 166 " -G: Inherit supplementary groups from uid.\n" 167 " Not compatible with -y.\n" 168 " -y: Keep uid's supplementary groups.\n" 169 " Not compatible with -G.\n" 170 " -h: Help (this message).\n" 171 " -H: Seccomp filter help message.\n" 172 " -i: Exit immediately after fork (do not act as init).\n" 173 " -I: Run <program> as init (pid 1) inside a new pid namespace (implies -p).\n" 174 " -K: Don't mark all existing mounts as MS_PRIVATE.\n" 175 " -l: Enter new IPC namespace.\n" 176 " -L: Report blocked syscalls to syslog when using seccomp filter.\n" 177 " Forces the following syscalls to be allowed:\n" 178 " ", progn); 179 /* clang-format on */ 180 for (i = 0; i < log_syscalls_len; i++) 181 printf("%s ", log_syscalls[i]); 182 183 /* clang-format off */ 184 printf("\n" 185 " -m[map]: Set the uid map of a user namespace (implies -pU).\n" 186 " Same arguments as newuidmap(1), multiple mappings should be separated by ',' (comma).\n" 187 " With no mapping, map the current uid to root inside the user namespace.\n" 188 " Not compatible with -b without the 'writable' option.\n" 189 " -M[map]: Set the gid map of a user namespace (implies -pU).\n" 190 " Same arguments as newgidmap(1), multiple mappings should be separated by ',' (comma).\n" 191 " With no mapping, map the current gid to root inside the user namespace.\n" 192 " Not compatible with -b without the 'writable' option.\n" 193 " -n: Set no_new_privs.\n" 194 " -N: Enter a new cgroup namespace.\n" 195 " -p: Enter new pid namespace (implies -vr).\n" 196 " -r: Remount /proc read-only (implies -v).\n" 197 " -R: Set rlimits, can be specified multiple times.\n" 198 " -s: Use seccomp mode 1 (not the same as -S).\n" 199 " -S <file>: Set seccomp filter using <file>.\n" 200 " E.g., '-S /usr/share/filters/<prog>.$(uname -m)'.\n" 201 " Requires -n when not running as root.\n" 202 " -t[size]: Mount tmpfs at /tmp (implies -v).\n" 203 " Optional argument specifies size (default \"64M\").\n" 204 " -T <type>: Don't access <program> before execve(2), assume <type> ELF binary.\n" 205 " <type> must be 'static' or 'dynamic'.\n" 206 " -u <user>: Change uid to <user>.\n" 207 " -U: Enter new user namespace (implies -p).\n" 208 " -v: Enter new mount namespace.\n" 209 " -V <file>: Enter specified mount namespace.\n" 210 " -w: Create and join a new anonymous session keyring.\n" 211 " -Y: Synchronize seccomp filters across thread group.\n" 212 " -z: Don't forward signals to jailed process.\n" 213 " --ambient: Raise ambient capabilities. Requires -c.\n" 214 " --uts[=name]: Enter a new UTS namespace (and set hostname).\n"); 215 /* clang-format on */ 216 } 217 218 static void seccomp_filter_usage(const char *progn) 219 { 220 const struct syscall_entry *entry = syscall_table; 221 printf("Usage: %s -S <policy.file> <program> [args...]\n\n" 222 "System call names supported:\n", 223 progn); 224 for (; entry->name && entry->nr >= 0; ++entry) 225 printf(" %s [%d]\n", entry->name, entry->nr); 226 printf("\nSee minijail0(5) for example policies.\n"); 227 } 228 229 static int parse_args(struct minijail *j, int argc, char *argv[], 230 int *exit_immediately, ElfType *elftype) 231 { 232 int opt; 233 int use_seccomp_filter = 0; 234 int forward = 1; 235 int binding = 0; 236 int chroot = 0, pivot_root = 0; 237 int mount_ns = 0, skip_remount = 0; 238 int inherit_suppl_gids = 0, keep_suppl_gids = 0; 239 int caps = 0, ambient_caps = 0; 240 int seccomp = -1; 241 const size_t path_max = 4096; 242 char *map; 243 size_t size; 244 const char *filter_path; 245 246 const char *optstring = 247 "+u:g:sS:c:C:P:b:B:V:f:m::M::k:a:e::R:T:vrGhHinNplLt::IUKwyYz"; 248 int longoption_index = 0; 249 /* clang-format off */ 250 const struct option long_options[] = { 251 {"ambient", no_argument, 0, 128}, 252 {"uts", optional_argument, 0, 129}, 253 {0, 0, 0, 0}, 254 }; 255 /* clang-format on */ 256 257 while ((opt = getopt_long(argc, argv, optstring, long_options, 258 &longoption_index)) != -1) { 259 switch (opt) { 260 case 'u': 261 set_user(j, optarg); 262 break; 263 case 'g': 264 set_group(j, optarg); 265 break; 266 case 'n': 267 minijail_no_new_privs(j); 268 break; 269 case 's': 270 if (seccomp != -1 && seccomp != 1) { 271 fprintf(stderr, "Do not use -s & -S together.\n"); 272 exit(1); 273 } 274 seccomp = 1; 275 minijail_use_seccomp(j); 276 break; 277 case 'S': 278 if (seccomp != -1 && seccomp != 2) { 279 fprintf(stderr, "Do not use -s & -S together.\n"); 280 exit(1); 281 } 282 seccomp = 2; 283 minijail_use_seccomp_filter(j); 284 if (strlen(optarg) >= path_max) { 285 fprintf(stderr, "Filter path is too long.\n"); 286 exit(1); 287 } 288 filter_path = strndup(optarg, path_max); 289 if (!filter_path) { 290 fprintf(stderr, 291 "Could not strndup(3) filter path.\n"); 292 exit(1); 293 } 294 use_seccomp_filter = 1; 295 break; 296 case 'l': 297 minijail_namespace_ipc(j); 298 break; 299 case 'L': 300 minijail_log_seccomp_filter_failures(j); 301 break; 302 case 'b': 303 add_binding(j, optarg); 304 binding = 1; 305 break; 306 case 'B': 307 skip_securebits(j, optarg); 308 break; 309 case 'c': 310 caps = 1; 311 use_caps(j, optarg); 312 break; 313 case 'C': 314 if (pivot_root) { 315 fprintf(stderr, "Could not set chroot because " 316 "'-P' was specified.\n"); 317 exit(1); 318 } 319 if (0 != minijail_enter_chroot(j, optarg)) { 320 fprintf(stderr, "Could not set chroot.\n"); 321 exit(1); 322 } 323 chroot = 1; 324 break; 325 case 'k': 326 add_mount(j, optarg); 327 break; 328 case 'K': 329 minijail_skip_remount_private(j); 330 skip_remount = 1; 331 break; 332 case 'P': 333 if (chroot) { 334 fprintf(stderr, 335 "Could not set pivot_root because " 336 "'-C' was specified.\n"); 337 exit(1); 338 } 339 if (0 != minijail_enter_pivot_root(j, optarg)) { 340 fprintf(stderr, "Could not set pivot_root.\n"); 341 exit(1); 342 } 343 minijail_namespace_vfs(j); 344 pivot_root = 1; 345 break; 346 case 'f': 347 if (0 != minijail_write_pid_file(j, optarg)) { 348 fprintf(stderr, 349 "Could not prepare pid file path.\n"); 350 exit(1); 351 } 352 break; 353 case 't': 354 minijail_namespace_vfs(j); 355 size = 64 * 1024 * 1024; 356 if (optarg != NULL && 0 != parse_size(&size, optarg)) { 357 fprintf(stderr, "Invalid /tmp tmpfs size.\n"); 358 exit(1); 359 } 360 minijail_mount_tmp_size(j, size); 361 break; 362 case 'v': 363 minijail_namespace_vfs(j); 364 mount_ns = 1; 365 break; 366 case 'V': 367 minijail_namespace_enter_vfs(j, optarg); 368 break; 369 case 'r': 370 minijail_remount_proc_readonly(j); 371 break; 372 case 'G': 373 if (keep_suppl_gids) { 374 fprintf(stderr, 375 "-y and -G are not compatible.\n"); 376 exit(1); 377 } 378 minijail_inherit_usergroups(j); 379 inherit_suppl_gids = 1; 380 break; 381 case 'y': 382 if (inherit_suppl_gids) { 383 fprintf(stderr, 384 "-y and -G are not compatible.\n"); 385 exit(1); 386 } 387 minijail_keep_supplementary_gids(j); 388 keep_suppl_gids = 1; 389 break; 390 case 'N': 391 minijail_namespace_cgroups(j); 392 break; 393 case 'p': 394 minijail_namespace_pids(j); 395 break; 396 case 'e': 397 if (optarg) 398 minijail_namespace_enter_net(j, optarg); 399 else 400 minijail_namespace_net(j); 401 break; 402 case 'i': 403 *exit_immediately = 1; 404 break; 405 case 'H': 406 seccomp_filter_usage(argv[0]); 407 exit(1); 408 case 'I': 409 minijail_namespace_pids(j); 410 minijail_run_as_init(j); 411 break; 412 case 'U': 413 minijail_namespace_user(j); 414 minijail_namespace_pids(j); 415 break; 416 case 'm': 417 minijail_namespace_user(j); 418 minijail_namespace_pids(j); 419 420 if (optarg) { 421 map = strdup(optarg); 422 } else { 423 /* 424 * If no map is passed, map the current uid to 425 * root. 426 */ 427 map = build_idmap(0, getuid()); 428 } 429 if (0 != minijail_uidmap(j, map)) { 430 fprintf(stderr, "Could not set uid map.\n"); 431 exit(1); 432 } 433 free(map); 434 break; 435 case 'M': 436 minijail_namespace_user(j); 437 minijail_namespace_pids(j); 438 439 if (optarg) { 440 map = strdup(optarg); 441 } else { 442 /* 443 * If no map is passed, map the current gid to 444 * root. 445 * This means that we're likely *not* running as 446 * root, so we also have to disable 447 * setgroups(2) to be able to set the gid map. 448 * See 449 * http://man7.org/linux/man-pages/man7/user_namespaces.7.html 450 */ 451 minijail_namespace_user_disable_setgroups(j); 452 453 map = build_idmap(0, getgid()); 454 } 455 if (0 != minijail_gidmap(j, map)) { 456 fprintf(stderr, "Could not set gid map.\n"); 457 exit(1); 458 } 459 free(map); 460 break; 461 case 'a': 462 if (0 != minijail_use_alt_syscall(j, optarg)) { 463 fprintf(stderr, 464 "Could not set alt-syscall table.\n"); 465 exit(1); 466 } 467 break; 468 case 'R': 469 add_rlimit(j, optarg); 470 break; 471 case 'T': 472 if (!strcmp(optarg, "static")) 473 *elftype = ELFSTATIC; 474 else if (!strcmp(optarg, "dynamic")) 475 *elftype = ELFDYNAMIC; 476 else { 477 fprintf(stderr, "ELF type must be 'static' or " 478 "'dynamic'.\n"); 479 exit(1); 480 } 481 break; 482 case 'w': 483 minijail_new_session_keyring(j); 484 break; 485 case 'Y': 486 minijail_set_seccomp_filter_tsync(j); 487 break; 488 case 'z': 489 forward = 0; 490 break; 491 /* Long options. */ 492 case 128: /* Ambient caps. */ 493 ambient_caps = 1; 494 minijail_set_ambient_caps(j); 495 break; 496 case 129: /* UTS/hostname namespace. */ 497 minijail_namespace_uts(j); 498 if (optarg) 499 minijail_namespace_set_hostname(j, optarg); 500 break; 501 default: 502 usage(argv[0]); 503 exit(1); 504 } 505 } 506 507 /* Can only set ambient caps when using regular caps. */ 508 if (ambient_caps && !caps) { 509 fprintf(stderr, "Can't set ambient capabilities (--ambient) " 510 "without actually using capabilities (-c).\n"); 511 exit(1); 512 } 513 514 /* Set up signal handlers in minijail unless asked not to. */ 515 if (forward) 516 minijail_forward_signals(j); 517 518 /* Only allow bind mounts when entering a chroot or using pivot_root. */ 519 if (binding && !(chroot || pivot_root)) { 520 fprintf(stderr, "Can't add bind mounts without chroot or" 521 " pivot_root.\n"); 522 exit(1); 523 } 524 525 /* 526 * Remounting / as MS_PRIVATE only happens when entering a new mount 527 * namespace, so skipping it only applies in that case. 528 */ 529 if (skip_remount && !mount_ns) { 530 fprintf(stderr, "Can't skip marking mounts as MS_PRIVATE" 531 " without mount namespaces.\n"); 532 exit(1); 533 } 534 535 /* 536 * We parse seccomp filters here to make sure we've collected all 537 * cmdline options. 538 */ 539 if (use_seccomp_filter) { 540 minijail_parse_seccomp_filters(j, filter_path); 541 free((void *)filter_path); 542 } 543 544 /* 545 * There should be at least one additional unparsed argument: the 546 * executable name. 547 */ 548 if (argc == optind) { 549 usage(argv[0]); 550 exit(1); 551 } 552 553 if (*elftype == ELFERROR) { 554 /* 555 * -T was not specified. 556 * Get the path to the program adjusted for changing root. 557 */ 558 char *program_path = 559 minijail_get_original_path(j, argv[optind]); 560 561 /* Check that we can access the target program. */ 562 if (access(program_path, X_OK)) { 563 fprintf(stderr, 564 "Target program '%s' is not accessible.\n", 565 argv[optind]); 566 exit(1); 567 } 568 569 /* Check if target is statically or dynamically linked. */ 570 *elftype = get_elf_linkage(program_path); 571 free(program_path); 572 } 573 574 /* 575 * Setting capabilities need either a dynamically-linked binary, or the 576 * use of ambient capabilities for them to be able to survive an 577 * execve(2). 578 */ 579 if (caps && *elftype == ELFSTATIC && !ambient_caps) { 580 fprintf(stderr, "Can't run statically-linked binaries with " 581 "capabilities (-c) without also setting " 582 "ambient capabilities. Try passing " 583 "--ambient.\n"); 584 exit(1); 585 } 586 587 return optind; 588 } 589 590 int main(int argc, char *argv[]) 591 { 592 struct minijail *j = minijail_new(); 593 const char *dl_mesg = NULL; 594 int exit_immediately = 0; 595 ElfType elftype = ELFERROR; 596 int consumed = parse_args(j, argc, argv, &exit_immediately, &elftype); 597 argc -= consumed; 598 argv += consumed; 599 600 if (elftype == ELFSTATIC) { 601 /* 602 * Target binary is statically linked so we cannot use 603 * libminijailpreload.so. 604 */ 605 minijail_run_no_preload(j, argv[0], argv); 606 } else if (elftype == ELFDYNAMIC) { 607 /* 608 * Target binary is dynamically linked so we can 609 * inject libminijailpreload.so into it. 610 */ 611 612 /* Check that we can dlopen() libminijailpreload.so. */ 613 if (!dlopen(PRELOADPATH, RTLD_LAZY | RTLD_LOCAL)) { 614 dl_mesg = dlerror(); 615 fprintf(stderr, "dlopen(): %s\n", dl_mesg); 616 return 1; 617 } 618 minijail_run(j, argv[0], argv); 619 } else { 620 fprintf(stderr, 621 "Target program '%s' is not a valid ELF file.\n", 622 argv[0]); 623 return 1; 624 } 625 626 if (exit_immediately) { 627 info("not running init loop, exiting immediately"); 628 return 0; 629 } 630 return minijail_wait(j); 631 } 632