1 /* 2 * Copyright (C) 2015 John Crispin <blogic@openwrt.org> 3 * Copyright (C) 2015 Etienne Champetier <champetier.etienne@gmail.com> 4 * Copyright (C) 2020 Daniel Golle <daniel@makrotopia.org> 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU Lesser General Public License version 2.1 8 * as published by the Free Software Foundation 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 */ 15 16 #define _GNU_SOURCE 17 18 #include <assert.h> 19 #include <elf.h> 20 #include <errno.h> 21 #include <sys/syscall.h> 22 #include <fcntl.h> 23 #include <linux/limits.h> 24 #include <linux/mount.h> 25 #include <sched.h> 26 #include <signal.h> 27 #include <stdlib.h> 28 #include <stdio.h> 29 #include <string.h> 30 #include <sys/socket.h> 31 #include <sys/stat.h> 32 #include <sys/mman.h> 33 #include <sys/syscall.h> 34 #include <sys/wait.h> 35 #include <unistd.h> 36 #include <libgen.h> 37 #include <dirent.h> 38 39 #include <libubox/avl.h> 40 #include <libubox/avl-cmp.h> 41 #include <libubox/blobmsg.h> 42 #include <libubox/list.h> 43 #include <libubox/utils.h> 44 45 #include "elf.h" 46 #include "fs.h" 47 #include "jail.h" 48 #include "log.h" 49 50 #define UJAIL_NOAFILE "/tmp/.ujailnoafile" 51 52 /* 53 * mnt_already_visible() requires a new mount's atime class to match 54 * the host's existing instance or the kernel refuses it with EPERM 55 * ("Mount too revealing"). Detect the mountpoint's actual atime class 56 * instead of hardcoding one, so the new mount can never conflict. 57 */ 58 unsigned long detect_atime_flag(const char *mountpoint) 59 { 60 FILE *f; 61 char *line = NULL; 62 size_t linelen = 0; 63 unsigned long ret = MS_RELATIME; /* kernel default if nothing else is known */ 64 size_t mplen = strlen(mountpoint); 65 66 f = fopen("/proc/self/mountinfo", "r"); 67 if (!f) 68 return ret; 69 70 while (getline(&line, &linelen, f) != -1) { 71 /* mountinfo(5): field 5 is the mountpoint, field 6 its options */ 72 char *saveptr = NULL; 73 char *field; 74 int idx = 0; 75 char *mp_field = NULL, *opts_field = NULL; 76 77 for (field = strtok_r(line, " \t\n", &saveptr); field; 78 field = strtok_r(NULL, " \t\n", &saveptr), idx++) { 79 if (idx == 4) 80 mp_field = field; 81 else if (idx == 5) { 82 opts_field = field; 83 break; 84 } 85 } 86 87 if (!mp_field || !opts_field) 88 continue; 89 90 if (strlen(mp_field) != mplen || strcmp(mp_field, mountpoint)) 91 continue; 92 93 /* last matching entry wins: it's the topmost/currently-effective one */ 94 if (strstr(opts_field, "noatime")) 95 ret = MS_NOATIME; 96 else if (strstr(opts_field, "relatime")) 97 ret = MS_RELATIME; 98 else 99 /* strictatime is the absence of noatime/relatime, not a token */ 100 ret = MS_STRICTATIME; 101 } 102 103 free(line); 104 fclose(f); 105 106 return ret; 107 } 108 109 #ifndef MOUNT_ATTR_IDMAP 110 #define MOUNT_ATTR_IDMAP 0x00100000 111 #endif 112 113 #ifndef MOUNT_ATTR__ATIME 114 #define MOUNT_ATTR__ATIME 0x00000070 115 #endif 116 #ifndef MOUNT_ATTR_RELATIME 117 #define MOUNT_ATTR_RELATIME 0x00000000 118 #endif 119 #ifndef MOUNT_ATTR_NOATIME 120 #define MOUNT_ATTR_NOATIME 0x00000010 121 #endif 122 #ifndef MOUNT_ATTR_STRICTATIME 123 #define MOUNT_ATTR_STRICTATIME 0x00000020 124 #endif 125 #ifndef MOUNT_ATTR_NODIRATIME 126 #define MOUNT_ATTR_NODIRATIME 0x00000080 127 #endif 128 129 /* MS_* -> MOUNT_ATTR_* for fsmount()/mount_setattr() */ 130 static unsigned mountflags_to_attr(unsigned long mountflags) 131 { 132 unsigned attr = 0; 133 134 if (mountflags & MS_RDONLY) 135 attr |= MOUNT_ATTR_RDONLY; 136 if (mountflags & MS_NOSUID) 137 attr |= MOUNT_ATTR_NOSUID; 138 if (mountflags & MS_NODEV) 139 attr |= MOUNT_ATTR_NODEV; 140 if (mountflags & MS_NOEXEC) 141 attr |= MOUNT_ATTR_NOEXEC; 142 if (mountflags & MS_NODIRATIME) 143 attr |= MOUNT_ATTR_NODIRATIME; 144 if (mountflags & MS_NOATIME) 145 attr |= MOUNT_ATTR_NOATIME; 146 else if (mountflags & MS_STRICTATIME) 147 attr |= MOUNT_ATTR_STRICTATIME; 148 else 149 attr |= MOUNT_ATTR_RELATIME; 150 151 return attr; 152 } 153 154 int sys_openat2(int dfd, const char *path, struct open_how *how, size_t size) 155 { 156 return syscall(SYS_openat2, dfd, path, how, size); 157 } 158 159 static int jailroot_dirfd = -1; 160 161 static unsigned int idmap_host_offset; 162 163 void jail_set_idmap_offset(unsigned int offset) 164 { 165 idmap_host_offset = offset; 166 } 167 168 static int write_mappings_file(pid_t pid, const char *which, struct blob_attr *mappings) 169 { 170 enum { 171 OCI_LINUX_UIDGIDMAP_CONTAINERID, 172 OCI_LINUX_UIDGIDMAP_HOSTID, 173 OCI_LINUX_UIDGIDMAP_SIZE, 174 __OCI_LINUX_UIDGIDMAP_MAX, 175 }; 176 static const struct blobmsg_policy policy[] = { 177 [OCI_LINUX_UIDGIDMAP_CONTAINERID] = { "containerID", BLOBMSG_TYPE_INT32 }, 178 [OCI_LINUX_UIDGIDMAP_HOSTID] = { "hostID", BLOBMSG_TYPE_INT32 }, 179 [OCI_LINUX_UIDGIDMAP_SIZE] = { "size", BLOBMSG_TYPE_INT32 }, 180 }; 181 struct blob_attr *tb[__OCI_LINUX_UIDGIDMAP_MAX]; 182 struct blob_attr *cur; 183 char path[64]; 184 char *buf = NULL; 185 size_t buflen = 0; 186 FILE *mem; 187 ssize_t w; 188 int rem, fd, ret = 0, saved_err; 189 190 mem = open_memstream(&buf, &buflen); 191 if (!mem) 192 return errno; 193 194 blobmsg_for_each_attr(cur, mappings, rem) { 195 blobmsg_parse(policy, __OCI_LINUX_UIDGIDMAP_MAX, tb, 196 blobmsg_data(cur), blobmsg_len(cur)); 197 if (!tb[OCI_LINUX_UIDGIDMAP_CONTAINERID] || 198 !tb[OCI_LINUX_UIDGIDMAP_HOSTID] || 199 !tb[OCI_LINUX_UIDGIDMAP_SIZE]) { 200 fclose(mem); 201 free(buf); 202 return EINVAL; 203 } 204 fprintf(mem, "%u %u %u\n", 205 blobmsg_get_u32(tb[OCI_LINUX_UIDGIDMAP_CONTAINERID]), 206 blobmsg_get_u32(tb[OCI_LINUX_UIDGIDMAP_HOSTID]) + idmap_host_offset, 207 blobmsg_get_u32(tb[OCI_LINUX_UIDGIDMAP_SIZE])); 208 } 209 fclose(mem); 210 211 if (!buflen) { 212 free(buf); 213 return 0; 214 } 215 216 snprintf(path, sizeof(path), "/proc/%d/%s", pid, which); 217 fd = open(path, O_WRONLY | O_CLOEXEC); 218 if (fd < 0) { 219 ret = errno; 220 free(buf); 221 return ret; 222 } 223 224 w = write(fd, buf, buflen); 225 if (w < 0) 226 ret = errno; 227 else if ((size_t)w != buflen) 228 ret = EIO; 229 230 saved_err = ret; 231 close(fd); 232 free(buf); 233 return saved_err; 234 } 235 236 int build_userns_fd(struct blob_attr *uidmappings, struct blob_attr *gidmappings) 237 { 238 int sync[2]; 239 pid_t pid; 240 char path[64]; 241 char buf; 242 int fd = -1; 243 int ret, saved_err = 0; 244 245 if (socketpair(AF_UNIX, SOCK_STREAM, 0, sync) < 0) 246 return -errno; 247 248 pid = fork(); 249 if (pid < 0) { 250 ret = -errno; 251 close(sync[0]); 252 close(sync[1]); 253 return ret; 254 } 255 256 if (pid == 0) { 257 close(sync[0]); 258 if (unshare(CLONE_NEWUSER) < 0) 259 _exit(EXIT_FAILURE); 260 if (send(sync[1], "R", 1, MSG_NOSIGNAL) != 1) 261 _exit(EXIT_FAILURE); 262 if (read(sync[1], &buf, 1) < 0) { 263 } 264 _exit(EXIT_SUCCESS); 265 } 266 267 close(sync[1]); 268 if (read(sync[0], &buf, 1) != 1 || buf != 'R') { 269 saved_err = EIO; 270 goto out; 271 } 272 273 if (uidmappings && (ret = write_mappings_file(pid, "uid_map", uidmappings))) { 274 saved_err = ret; 275 goto out; 276 } 277 if (gidmappings) { 278 int gfd; 279 snprintf(path, sizeof(path), "/proc/%d/setgroups", pid); 280 gfd = open(path, O_WRONLY | O_CLOEXEC); 281 if (gfd >= 0) { 282 (void)!write(gfd, "deny", 4); 283 close(gfd); 284 } 285 if ((ret = write_mappings_file(pid, "gid_map", gidmappings))) { 286 saved_err = ret; 287 goto out; 288 } 289 } 290 291 snprintf(path, sizeof(path), "/proc/%d/ns/user", pid); 292 fd = open(path, O_RDONLY | O_CLOEXEC); 293 if (fd < 0) 294 saved_err = errno; 295 296 out: 297 (void)send(sync[0], "X", 1, MSG_NOSIGNAL); 298 close(sync[0]); 299 waitpid(pid, NULL, 0); 300 if (fd < 0) { 301 errno = saved_err ? saved_err : EIO; 302 return -errno; 303 } 304 return fd; 305 } 306 307 struct mount { 308 struct avl_node avl; 309 struct list_head list; 310 const char *source; 311 const char *target; 312 const char *filesystemtype; 313 unsigned long mountflags; 314 unsigned long propflags; 315 const char *optstr; 316 int error; 317 bool inner; 318 int source_fd; 319 bool idmap; 320 bool idmap_recursive; 321 bool volume; 322 bool mounted; 323 int idmap_treefd; 324 struct blob_attr *uidmappings; 325 struct blob_attr *gidmappings; 326 }; 327 328 /* open_tree()/move_mount()/mount_setattr() have no glibc wrappers yet; 329 * struct ujail_mount_attr is declared in fs.h */ 330 int sys_open_tree(int dfd, const char *path, unsigned flags) 331 { 332 return syscall(SYS_open_tree, dfd, path, flags); 333 } 334 335 int sys_move_mount(int from_dfd, const char *from_path, int to_dfd, const char *to_path, unsigned flags) 336 { 337 return syscall(SYS_move_mount, from_dfd, from_path, to_dfd, to_path, flags); 338 } 339 340 int sys_fsopen(const char *fsname, unsigned flags) 341 { 342 return syscall(SYS_fsopen, fsname, flags); 343 } 344 345 int sys_fsconfig(int fd, unsigned cmd, const char *key, const void *value, int aux) 346 { 347 return syscall(SYS_fsconfig, fd, cmd, key, value, aux); 348 } 349 350 int sys_fsmount(int fd, unsigned flags, unsigned attr_flags) 351 { 352 return syscall(SYS_fsmount, fd, flags, attr_flags); 353 } 354 355 int sys_mount_setattr(int dfd, const char *path, unsigned flags, struct ujail_mount_attr *attr, size_t size) 356 { 357 return syscall(SYS_mount_setattr, dfd, path, flags, attr, size); 358 } 359 360 struct avl_tree mounts; 361 static LIST_HEAD(mounts_order); 362 363 /* same masking as do_mount()'s is_mask branch, applied immediately 364 * against an absolute path instead of queued through jail_root. 365 * 366 * UJAIL_NOAFILE lives under the pre-pivot root, gone by the time this 367 * runs; JAIL_NOAFILE, a locked bind of procd's read-only noafile 368 * queued for deferred-userns jails, is used instead. */ 369 int mask_path_now(const char *path) 370 { 371 struct stat s; 372 373 if (stat(path, &s)) 374 return 0; /* doesn't exist, nothing to mask */ 375 376 if (S_ISDIR(s.st_mode)) { 377 if (mount("none", path, "tmpfs", MS_RDONLY | MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_RELATIME, "size=0,mode=000")) 378 return -1; 379 } else { 380 if (mount(JAIL_NOAFILE, path, "bind", MS_BIND, NULL)) 381 return -1; 382 if (remount_readonly(path, MS_NOSUID | MS_NOEXEC | MS_NODEV)) 383 return -1; 384 } 385 386 DEBUG("masked path %s\n", path); 387 return 0; 388 } 389 390 static void mountinfo_unescape(char *s) 391 { 392 char *r = s, *w = s; 393 394 while (*r) { 395 if (r[0] == '\\' && r[1] >= '' && r[1] <= '7' && 396 r[2] >= '' && r[2] <= '7' && r[3] >= '' && r[3] <= '7') { 397 *w++ = (char)(((r[1] - '') << 6) | ((r[2] - '') << 3) | (r[3] - '')); 398 r += 4; 399 } else { 400 *w++ = *r++; 401 } 402 } 403 *w = '\0'; 404 } 405 406 /* 407 * The flags a locked mount will not let a remount clear. The atime class is 408 * deliberately absent: path_mount() preserves it when the remount names none. 409 */ 410 static unsigned long mountinfo_current_flags(const char *path) 411 { 412 unsigned long flags = 0; 413 bool found = false; 414 FILE *f; 415 char *line = NULL; 416 size_t linecap = 0; 417 418 f = fopen("/proc/self/mountinfo", "re"); 419 if (!f) 420 return flags; 421 422 while (getline(&line, &linecap, f) >= 0) { 423 char *mp, *opts, *save = NULL, *optsave = NULL; 424 char *tok; 425 unsigned long this_flags; 426 427 strtok_r(line, " ", &save); 428 strtok_r(NULL, " ", &save); 429 strtok_r(NULL, " ", &save); 430 strtok_r(NULL, " ", &save); 431 mp = strtok_r(NULL, " ", &save); 432 opts = strtok_r(NULL, " ", &save); 433 if (!mp || !opts) 434 continue; 435 mountinfo_unescape(mp); 436 if (strcmp(mp, path)) 437 continue; 438 439 this_flags = 0; 440 for (tok = strtok_r(opts, ",", &optsave); tok; 441 tok = strtok_r(NULL, ",", &optsave)) { 442 if (!strcmp(tok, "ro")) 443 this_flags |= MS_RDONLY; 444 else if (!strcmp(tok, "nosuid")) 445 this_flags |= MS_NOSUID; 446 else if (!strcmp(tok, "nodev")) 447 this_flags |= MS_NODEV; 448 else if (!strcmp(tok, "noexec")) 449 this_flags |= MS_NOEXEC; 450 } 451 452 /* last match wins: it's the topmost/effective entry */ 453 flags = this_flags; 454 found = true; 455 } 456 free(line); 457 fclose(f); 458 459 if (!found) 460 return 0; 461 462 return flags; 463 } 464 465 /* 466 * Self-bind @path and remount it read-only, preserving the flags already in 467 * effect. Mounts copied in by unshare(CLONE_NEWNS) under a userns that does 468 * not own them are MNT_LOCK_{NOSUID,NODEV,NOEXEC,ATIME}; a remount clearing 469 * any of those fails with EPERM. 470 */ 471 int remount_readonly(const char *path, unsigned long flags) 472 { 473 flags |= MS_REMOUNT | MS_BIND | MS_RDONLY | mountinfo_current_flags(path); 474 475 return mount(NULL, path, NULL, flags, NULL); 476 } 477 478 int bind_remount_readonly(const char *path, unsigned long flags) 479 { 480 if (mount(path, path, "bind", MS_BIND | (flags & MS_REC), NULL)) 481 return -1; 482 483 return remount_readonly(path, flags); 484 } 485 486 static bool fs_userns; 487 488 void jail_fs_set_userns(bool enabled) 489 { 490 fs_userns = enabled; 491 } 492 493 static bool mount_opts_has(const char *opts, const char *needle); 494 static bool mount_opts_gid_unmapped(const char *opts); 495 static void mount_opts_drop(const char *opts, const char *key, char *buf, size_t len); 496 497 static int do_mount(const char *root, const char *orig_source, const char *target, const char *filesystemtype, 498 unsigned long orig_mountflags, unsigned long propflags, const char *optstr, int error, bool inner, 499 int source_fd) 500 { 501 struct stat s; 502 char new[PATH_MAX]; 503 char tmpfs_data[512]; 504 char devpts_data[512]; 505 const char *mount_data; 506 char *source = (char *)orig_source; 507 int fd, err, ret = 0; 508 bool is_bind = (orig_mountflags & MS_BIND); 509 bool is_mask = (source == (void *)(-1)); 510 bool use_fd = false; 511 unsigned long mountflags = orig_mountflags; 512 513 assert(!(inner && is_mask)); 514 assert(!(inner && !orig_source)); 515 516 if (source && is_bind && stat(source, &s)) { 517 if (source_fd >= 0 && !fstatat(source_fd, "", &s, AT_EMPTY_PATH)) { 518 use_fd = true; 519 } else { 520 if (error) 521 ERROR("stat(%s) failed: %m\n", source); 522 return error; 523 } 524 } 525 526 if (inner) 527 if (asprintf(&source, "%s%s", root, orig_source) < 0) 528 return ENOMEM; 529 530 snprintf(new, sizeof(new), "%s%s", root, target?target:source); 531 532 if (is_mask) { 533 if (stat(new, &s)) 534 return 0; /* doesn't exists, nothing to mask */ 535 536 if (S_ISDIR(s.st_mode)) {/* use empty 0-sized tmpfs for directories */ 537 err = mount("none", new, "tmpfs", MS_RDONLY | MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_RELATIME, "size=0,mode=000"); 538 } else { 539 /* mount-bind 0-sized file having mode 000 */ 540 err = mount(UJAIL_NOAFILE, new, "bind", MS_BIND, NULL); 541 if (!err) 542 err = remount_readonly(new, MS_NOSUID | MS_NOEXEC | MS_NODEV); 543 } 544 545 if (err) { 546 if (error) 547 ERROR("failed to mask %s: %m\n", new); 548 else 549 WARNING("could not mask optional path %s: %m\n", 550 new); 551 return error; 552 } 553 554 DEBUG("masked path %s\n", new); 555 return 0; 556 } 557 558 559 if (!is_bind || (source && S_ISDIR(s.st_mode))) { 560 mkdir_p(new, 0755); 561 } else if (is_bind && source) { 562 const char *target_rel = target ? target : source; 563 struct open_how how = { 564 .flags = O_CREAT | O_WRONLY | O_TRUNC | O_EXCL | O_CLOEXEC, 565 .mode = 0644, 566 .resolve = RESOLVE_BENEATH | RESOLVE_NO_MAGICLINKS, 567 }; 568 569 assert(target_rel); 570 mkdir_p(dirname(new), 0755); 571 snprintf(new, sizeof(new), "%s%s", root, target?target:source); 572 while (*target_rel == '/') 573 ++target_rel; 574 fd = (jailroot_dirfd >= 0) 575 ? sys_openat2(jailroot_dirfd, target_rel, &how, sizeof(how)) 576 : open(new, O_CREAT|O_WRONLY|O_TRUNC|O_EXCL|O_CLOEXEC, 0644); 577 if (fd >= 0) 578 close(fd); 579 580 if (error && fd < 0 && errno != EEXIST) { 581 ERROR("failed to create mount target %s: %m\n", new); 582 583 ret = errno; 584 goto free_source_out; 585 } 586 } 587 588 if (is_bind) { 589 if (use_fd) { 590 if (sys_move_mount(source_fd, "", AT_FDCWD, new, MOVE_MOUNT_F_EMPTY_PATH) < 0) { 591 if (error) 592 ERROR("move_mount(%s -> %s): %m\n", source, new); 593 ret = error; 594 goto free_source_out; 595 } 596 } else if (mount(source?:new, new, filesystemtype?:"bind", MS_BIND | (mountflags & MS_REC), optstr)) { 597 if (error) 598 ERROR("failed to mount -B %s %s: %m\n", source, new); 599 600 ret = error; 601 goto free_source_out; 602 } 603 mountflags |= MS_REMOUNT; 604 } 605 606 mount_data = optstr; 607 if (filesystemtype && !strcmp(filesystemtype, "devpts") && fs_userns && 608 mount_opts_gid_unmapped(optstr)) { 609 mount_opts_drop(optstr, "gid=", devpts_data, sizeof(devpts_data)); 610 mount_data = devpts_data; 611 } 612 613 if (filesystemtype && !strcmp(filesystemtype, "tmpfs") && !fs_userns && 614 !mount_opts_has(optstr ?: "", "swap") && !mount_opts_has(optstr ?: "", "noswap")) { 615 if (optstr && *optstr) 616 snprintf(tmpfs_data, sizeof(tmpfs_data), "%s,noswap", optstr); 617 else 618 snprintf(tmpfs_data, sizeof(tmpfs_data), "noswap"); 619 mount_data = tmpfs_data; 620 } 621 622 const char *hack_fstype = ((!filesystemtype || strcmp(filesystemtype, "cgroup"))?filesystemtype:"cgroup2"); 623 if (mount(source?:(is_bind?new:NULL), new, hack_fstype?:"none", mountflags, mount_data)) { 624 int mount_errno = errno; 625 626 if ((mountflags & MS_REMOUNT) && mount_errno == EPERM) { 627 /* Not a heuristic: re-read the kernel-enforced flags from 628 * mountinfo and only proceed once the security-relevant 629 * ones (ro/nosuid/nodev/noexec) are confirmed in effect. */ 630 unsigned long retry_flags = mountflags | mountinfo_current_flags(new); 631 632 if (retry_flags != mountflags && 633 !mount(source?:(is_bind?new:NULL), new, hack_fstype?:"none", retry_flags, mount_data)) 634 goto mount_ok; 635 636 unsigned long lockable_flags = MS_RDONLY | MS_NOSUID | MS_NODEV | MS_NOEXEC; 637 unsigned long wanted = orig_mountflags & lockable_flags; 638 unsigned long got = mountinfo_current_flags(new) & lockable_flags; 639 640 if ((wanted & ~got) == 0) { 641 WARNING("remount(%s) to apply flags failed: %s (tolerated - " 642 "the flags actually in effect already satisfy the " 643 "request; expected under CLONE_NEWUSER for host-owned " 644 "bind sources)\n", new, strerror(mount_errno)); 645 goto mount_ok; 646 } 647 648 if (error) { 649 errno = mount_errno; 650 ERROR("failed to enforce mount restrictions on %s %s: %m " 651 "(missing flags: %s%s%s%s)\n", source, new, 652 (wanted & ~got & MS_RDONLY) ? "ro " : "", 653 (wanted & ~got & MS_NOSUID) ? "nosuid " : "", 654 (wanted & ~got & MS_NODEV) ? "nodev " : "", 655 (wanted & ~got & MS_NOEXEC) ? "noexec " : ""); 656 ret = error; 657 goto free_source_out; 658 } 659 660 WARNING("remount(%s) to apply mount restrictions failed and could not " 661 "be verified in effect; continuing best-effort since " 662 "this mount was not marked as critical\n", new); 663 goto mount_ok; 664 } 665 666 errno = mount_errno; 667 if (error) 668 ERROR("failed to mount %s %s: %m\n", source, new); 669 670 ret = error; 671 goto free_source_out; 672 } 673 674 mount_ok: 675 DEBUG("mount %s%s %s (%s)\n", (mountflags & MS_BIND)?"-B ":"", source, new, 676 (mountflags & MS_RDONLY)?"ro":"rw"); 677 678 if (propflags && mount("none", new, "none", propflags, NULL)) { 679 if (error) 680 ERROR("failed to mount --make-... %s \n", new); 681 682 ret = error; 683 } 684 685 free_source_out: 686 if (inner) 687 free(source); 688 689 return ret; 690 } 691 692 static bool nullable_str_eq(const char *a, const char *b) 693 { 694 if (a == b) 695 return true; 696 if (!a || !b) 697 return false; 698 return !strcmp(a, b); 699 } 700 701 static int _add_mount(const char *source, const char *target, const char *filesystemtype, 702 unsigned long mountflags, unsigned long propflags, const char *optstr, 703 int error, bool inner) 704 { 705 struct mount *m; 706 707 assert(target != NULL); 708 709 m = avl_find_element(&mounts, target, m, avl); 710 if (m) { 711 bool source_match; 712 if (m->source == (void *)(-1) || source == (void *)(-1)) 713 source_match = (m->source == source); 714 else 715 source_match = nullable_str_eq(m->source, source); 716 717 if (source_match && 718 nullable_str_eq(m->filesystemtype, filesystemtype) && 719 nullable_str_eq(m->optstr, optstr) && 720 m->mountflags == mountflags && 721 m->propflags == propflags && 722 m->error == error && 723 m->inner == inner) 724 return 0; 725 726 return EEXIST; 727 } 728 729 m = calloc(1, sizeof(struct mount)); 730 if (!m) 731 return ENOMEM; 732 733 m->idmap_treefd = -1; 734 m->source_fd = -1; 735 m->avl.key = m->target = strdup(target); 736 if (source) { 737 if (source != (void*)(-1)) 738 m->source = strdup(source); 739 else 740 m->source = (void*)(-1); 741 } 742 if (filesystemtype) 743 m->filesystemtype = strdup(filesystemtype); 744 745 if (optstr) 746 m->optstr = strdup(optstr); 747 748 m->mountflags = mountflags; 749 m->propflags = propflags; 750 m->error = error; 751 m->inner = inner; 752 m->source_fd = -1; 753 754 avl_insert(&mounts, &m->avl); 755 list_add_tail(&m->list, &mounts_order); 756 DEBUG("adding mount %s %s bind(%d) ro(%d) err(%d)\n", (m->source == (void*)(-1))?"mask":m->source, m->target, 757 !!(m->mountflags & MS_BIND), !!(m->mountflags & MS_RDONLY), m->error != 0); 758 759 return 0; 760 } 761 762 int add_mount(const char *source, const char *target, const char *filesystemtype, 763 unsigned long mountflags, unsigned long propflags, const char *optstr, int error) 764 { 765 return _add_mount(source, target, filesystemtype, mountflags, propflags, optstr, error, false); 766 } 767 768 int add_mount_inner(const char *source, const char *target, const char *filesystemtype, 769 unsigned long mountflags, unsigned long propflags, const char *optstr, int error) 770 { 771 return _add_mount(source, target, filesystemtype, mountflags, propflags, optstr, error, true); 772 } 773 774 static int _add_mount_bind(const char *path, const char *path2, int readonly, int error) 775 { 776 unsigned long mountflags = MS_BIND; 777 778 if (readonly) 779 mountflags |= MS_RDONLY; 780 781 return add_mount(path, path2, NULL, mountflags, 0, NULL, error); 782 } 783 784 int add_mount_bind(const char *path, int readonly, int error) 785 { 786 return _add_mount_bind(path, path, readonly, error); 787 } 788 789 int add_mount_fd(int fd, const char *target, int error) 790 { 791 struct mount *m; 792 793 if (avl_find(&mounts, target)) 794 return 1; 795 796 m = calloc(1, sizeof(struct mount)); 797 if (!m) 798 return ENOMEM; 799 800 m->avl.key = m->target = strdup(target); 801 m->mountflags = MS_BIND; 802 m->error = error; 803 m->source_fd = fd; 804 805 avl_insert(&mounts, &m->avl); 806 list_add_tail(&m->list, &mounts_order); 807 DEBUG("adding mount fd:%d %s bind(1) ro(?) err(%d)\n", fd, target, error != 0); 808 809 return 0; 810 } 811 812 /* 813 * sysfs needs CAP_SYS_ADMIN in the userns owning the netns. fsmount() the 814 * queued sysfs entries here, before clone(), for do_mount_fd(). 815 */ 816 int premount_sysfs(void) 817 { 818 struct mount *m; 819 int fsfd, mfd; 820 821 list_for_each_entry(m, &mounts_order, list) { 822 if (!m->filesystemtype || strcmp(m->filesystemtype, "sysfs") || 823 m->source_fd >= 0) 824 continue; 825 826 fsfd = sys_fsopen("sysfs", FSOPEN_CLOEXEC); 827 if (fsfd < 0) 828 return -1; 829 if (sys_fsconfig(fsfd, FSCONFIG_CMD_CREATE, NULL, NULL, 0)) { 830 close(fsfd); 831 return -1; 832 } 833 mfd = sys_fsmount(fsfd, FSMOUNT_CLOEXEC, mountflags_to_attr(m->mountflags)); 834 close(fsfd); 835 if (mfd < 0) 836 return -1; 837 838 m->source_fd = mfd; 839 DEBUG("pre-mounted sysfs for %s as fd:%d\n", m->target, mfd); 840 } 841 842 return 0; 843 } 844 845 int add_mount_volume(const char *source, const char *target, int error) 846 { 847 struct mount *m; 848 int ret; 849 850 ret = add_mount(source, target, NULL, 851 MS_BIND | MS_NOEXEC | MS_NOSUID | MS_NODEV, 0, NULL, error); 852 if (ret && ret != EEXIST) 853 return ret; 854 855 m = avl_find_element(&mounts, target, m, avl); 856 if (m) 857 m->volume = true; 858 859 return ret; 860 } 861 862 char *resolve_mount_source(const char *source) 863 { 864 char *real; 865 866 if (!source) 867 return NULL; 868 869 if (source[0] != '/') 870 return strdup(source); 871 872 real = realpath(source, NULL); 873 return real ? real : strdup(source); 874 } 875 876 enum { 877 OCI_MOUNT_SOURCE, 878 OCI_MOUNT_DESTINATION, 879 OCI_MOUNT_TYPE, 880 OCI_MOUNT_OPTIONS, 881 OCI_MOUNT_UIDMAPPINGS, 882 OCI_MOUNT_GIDMAPPINGS, 883 __OCI_MOUNT_MAX, 884 }; 885 886 static const struct blobmsg_policy oci_mount_policy[] = { 887 [OCI_MOUNT_SOURCE] = { "source", BLOBMSG_TYPE_STRING }, 888 [OCI_MOUNT_DESTINATION] = { "destination", BLOBMSG_TYPE_STRING }, 889 [OCI_MOUNT_TYPE] = { "type", BLOBMSG_TYPE_STRING }, 890 [OCI_MOUNT_OPTIONS] = { "options", BLOBMSG_TYPE_ARRAY }, 891 [OCI_MOUNT_UIDMAPPINGS] = { "uidMappings", BLOBMSG_TYPE_ARRAY }, 892 [OCI_MOUNT_GIDMAPPINGS] = { "gidMappings", BLOBMSG_TYPE_ARRAY }, 893 }; 894 895 struct mount_opt { 896 struct list_head list; 897 char *optstr; 898 }; 899 900 #ifndef MS_LAZYTIME 901 #define MS_LAZYTIME (1 << 25) 902 #endif 903 904 bool jail_propagation_flags_add(const char *opt, unsigned long *flags) 905 { 906 if (!strcmp("private", opt)) 907 *flags |= MS_PRIVATE; 908 else if (!strcmp("rprivate", opt)) 909 *flags |= MS_PRIVATE | MS_REC; 910 else if (!strcmp("slave", opt)) 911 *flags |= MS_SLAVE; 912 else if (!strcmp("rslave", opt)) 913 *flags |= MS_SLAVE | MS_REC; 914 else if (!strcmp("shared", opt)) 915 *flags |= MS_SHARED; 916 else if (!strcmp("rshared", opt)) 917 *flags |= MS_SHARED | MS_REC; 918 else if (!strcmp("unbindable", opt)) 919 *flags |= MS_UNBINDABLE; 920 else if (!strcmp("runbindable", opt)) 921 *flags |= MS_UNBINDABLE | MS_REC; 922 else 923 return false; 924 925 return true; 926 } 927 928 static int parseOCImountopts(struct blob_attr *msg, unsigned long *mount_flags, unsigned long *propagation_flags, char **mount_data, int *error, bool *idmap, bool *idmap_recursive) 929 { 930 struct blob_attr *cur; 931 int rem; 932 unsigned long mf = 0; 933 unsigned long pf = 0; 934 char *tmp; 935 struct list_head fsopts = LIST_HEAD_INIT(fsopts); 936 size_t len = 0; 937 struct mount_opt *opt, *tmpopt; 938 939 *idmap = false; 940 *idmap_recursive = false; 941 942 blobmsg_for_each_attr(cur, msg, rem) { 943 tmp = blobmsg_get_string(cur); 944 if (!strcmp("idmap", tmp)) { 945 *idmap = true; 946 continue; 947 } else if (!strcmp("ridmap", tmp)) { 948 *idmap = true; 949 *idmap_recursive = true; 950 continue; 951 } else if (jail_propagation_flags_add(tmp, &pf)) 952 continue; 953 else if (!strcmp("ro", tmp)) 954 mf |= MS_RDONLY; 955 else if (!strcmp("rw", tmp)) 956 mf &= ~MS_RDONLY; 957 else if (!strcmp("bind", tmp)) 958 mf = MS_BIND; 959 else if (!strcmp("rbind", tmp)) 960 mf |= MS_BIND | MS_REC; 961 else if (!strcmp("sync", tmp)) 962 mf |= MS_SYNCHRONOUS; 963 else if (!strcmp("async", tmp)) 964 mf &= ~MS_SYNCHRONOUS; 965 else if (!strcmp("atime", tmp)) 966 mf &= ~MS_NOATIME; 967 else if (!strcmp("noatime", tmp)) 968 mf |= MS_NOATIME; 969 else if (!strcmp("defaults", tmp)) 970 mf = 0; /* rw, suid, dev, exec, auto, nouser, and async */ 971 else if (!strcmp("dev", tmp)) 972 mf &= ~MS_NODEV; 973 else if (!strcmp("nodev", tmp)) 974 mf |= MS_NODEV; 975 else if (!strcmp("iversion", tmp)) 976 mf |= MS_I_VERSION; 977 else if (!strcmp("noiversion", tmp)) 978 mf &= ~MS_I_VERSION; 979 else if (!strcmp("diratime", tmp)) 980 mf &= ~MS_NODIRATIME; 981 else if (!strcmp("nodiratime", tmp)) 982 mf |= MS_NODIRATIME; 983 else if (!strcmp("dirsync", tmp)) 984 mf |= MS_DIRSYNC; 985 else if (!strcmp("exec", tmp)) 986 mf &= ~MS_NOEXEC; 987 else if (!strcmp("noexec", tmp)) 988 mf |= MS_NOEXEC; 989 else if (!strcmp("mand", tmp)) 990 mf |= MS_MANDLOCK; 991 else if (!strcmp("nomand", tmp)) 992 mf &= ~MS_MANDLOCK; 993 else if (!strcmp("relatime", tmp)) 994 mf |= MS_RELATIME; 995 else if (!strcmp("norelatime", tmp)) 996 mf &= ~MS_RELATIME; 997 else if (!strcmp("strictatime", tmp)) 998 mf |= MS_STRICTATIME; 999 else if (!strcmp("nostrictatime", tmp)) 1000 mf &= ~MS_STRICTATIME; 1001 else if (!strcmp("lazytime", tmp)) 1002 mf |= MS_LAZYTIME; 1003 else if (!strcmp("nolazytime", tmp)) 1004 mf &= ~MS_LAZYTIME; 1005 else if (!strcmp("suid", tmp)) 1006 mf &= ~MS_NOSUID; 1007 else if (!strcmp("nosuid", tmp)) 1008 mf |= MS_NOSUID; 1009 else if (!strcmp("remount", tmp)) 1010 mf |= MS_REMOUNT; 1011 /* special case: 'nofail' */ 1012 else if(!strcmp("nofail", tmp)) 1013 *error = 0; 1014 else if (!strcmp("auto", tmp) || 1015 !strcmp("noauto", tmp) || 1016 !strcmp("user", tmp) || 1017 !strcmp("group", tmp) || 1018 !strcmp("_netdev", tmp)) 1019 DEBUG("ignoring built-in mount option %s\n", tmp); 1020 else { 1021 /* filesystem-specific free-form option */ 1022 opt = calloc(1, sizeof(*opt)); 1023 opt->optstr = tmp; 1024 list_add_tail(&opt->list, &fsopts); 1025 } 1026 }; 1027 1028 *mount_flags = mf; 1029 *propagation_flags = pf; 1030 1031 list_for_each_entry(opt, &fsopts, list) { 1032 if (len) 1033 ++len; 1034 1035 len += strlen(opt->optstr); 1036 }; 1037 1038 if (len) { 1039 *mount_data = calloc(len + 1, sizeof(char)); 1040 if (!(*mount_data)) 1041 return ENOMEM; 1042 1043 len = 0; 1044 list_for_each_entry(opt, &fsopts, list) { 1045 if (len) 1046 strcat(*mount_data, ","); 1047 1048 strcat(*mount_data, opt->optstr); 1049 ++len; 1050 } 1051 1052 list_for_each_entry_safe(opt, tmpopt, &fsopts, list) { 1053 list_del(&opt->list); 1054 free(opt); 1055 } 1056 } 1057 1058 DEBUG("mount flags(%08lx) propagation(%08lx) fsopts(\"%s\")\n", mf, pf, *mount_data?:""); 1059 1060 return 0; 1061 } 1062 1063 static bool is_proc_or_sys_path(const char *path) 1064 { 1065 if (!strcmp(path, "/proc") || !strcmp(path, "/sys")) 1066 return true; 1067 1068 if (!strncmp(path, "/proc/", 6)) 1069 return true; 1070 1071 if (!strncmp(path, "/sys/", 5)) 1072 return true; 1073 1074 return false; 1075 } 1076 1077 static bool id_is_mapped(const char *mapfile, unsigned long id) 1078 { 1079 unsigned long inside, outside, count; 1080 char line[128]; 1081 bool mapped = false; 1082 FILE *f; 1083 1084 f = fopen(mapfile, "r"); 1085 if (!f) 1086 return true; 1087 1088 while (fgets(line, sizeof(line), f)) { 1089 if (sscanf(line, "%lu %lu %lu", &inside, &outside, &count) != 3) 1090 continue; 1091 1092 if (id >= inside && id - inside < count) { 1093 mapped = true; 1094 break; 1095 } 1096 } 1097 1098 fclose(f); 1099 1100 return mapped; 1101 } 1102 1103 static bool mount_opts_gid_unmapped(const char *opts) 1104 { 1105 const char *p; 1106 1107 if (!opts) 1108 return false; 1109 1110 for (p = opts; p; p = strchr(p, ',')) { 1111 if (*p == ',') 1112 ++p; 1113 1114 if (!strncmp(p, "gid=", 4)) 1115 return !id_is_mapped("/proc/self/gid_map", strtoul(p + 4, NULL, 10)); 1116 } 1117 1118 return false; 1119 } 1120 1121 static void mount_opts_drop(const char *opts, const char *key, char *buf, size_t len) 1122 { 1123 size_t klen = strlen(key); 1124 const char *p, *end; 1125 size_t used = 0; 1126 1127 buf[0] = '\0'; 1128 1129 for (p = opts; p && *p; p = end) { 1130 end = strchr(p, ','); 1131 if (end) 1132 ++end; 1133 1134 if (!strncmp(p, key, klen)) 1135 continue; 1136 1137 while (*p && used + 1 < len) { 1138 buf[used++] = *p; 1139 if (*p++ == ',') 1140 break; 1141 } 1142 buf[used] = '\0'; 1143 } 1144 1145 if (used && buf[used - 1] == ',') 1146 buf[used - 1] = '\0'; 1147 } 1148 1149 static bool mount_opts_has(const char *opts, const char *needle) 1150 { 1151 size_t nlen = strlen(needle); 1152 const char *p = opts; 1153 1154 while (p && *p) { 1155 const char *end = strchr(p, ','); 1156 size_t plen = end ? (size_t)(end - p) : strlen(p); 1157 1158 if (plen == nlen && !strncmp(p, needle, nlen)) 1159 return true; 1160 if (!end) 1161 break; 1162 p = end + 1; 1163 } 1164 return false; 1165 } 1166 1167 int parseOCImount(struct blob_attr *msg) 1168 { 1169 struct blob_attr *tb[__OCI_MOUNT_MAX]; 1170 unsigned long mount_flags = 0; 1171 unsigned long propagation_flags = 0; 1172 char *mount_data = NULL; 1173 char *destination, *abs_destination = NULL; 1174 char *rsrc = NULL; 1175 bool idmap = false, idmap_recursive = false; 1176 int ret, err = -1; 1177 1178 blobmsg_parse(oci_mount_policy, __OCI_MOUNT_MAX, tb, blobmsg_data(msg), blobmsg_len(msg)); 1179 1180 if (!tb[OCI_MOUNT_DESTINATION]) 1181 return EINVAL; 1182 1183 if (tb[OCI_MOUNT_OPTIONS]) { 1184 ret = parseOCImountopts(tb[OCI_MOUNT_OPTIONS], &mount_flags, &propagation_flags, &mount_data, &err, &idmap, &idmap_recursive); 1185 if (ret) 1186 return ret; 1187 } 1188 1189 destination = blobmsg_get_string(tb[OCI_MOUNT_DESTINATION]); 1190 if (destination[0] != '/') { 1191 if (asprintf(&abs_destination, "/%s", destination) < 0) { 1192 free(mount_data); 1193 return ENOMEM; 1194 } 1195 destination = abs_destination; 1196 } 1197 1198 if (is_proc_or_sys_path(destination) && 1199 ((mount_flags & MS_BIND) || 1200 (tb[OCI_MOUNT_TYPE] && !strcmp(blobmsg_get_string(tb[OCI_MOUNT_TYPE]), "bind"))) && 1201 !(mount_flags & MS_RDONLY)) { 1202 ERROR("OCI mount config requests a writable bind mount onto %s; " 1203 "refusing to allow write access to /proc or /sys\n", 1204 destination); 1205 free(abs_destination); 1206 if (mount_data) 1207 free(mount_data); 1208 return EPERM; 1209 } 1210 1211 if (tb[OCI_MOUNT_SOURCE]) 1212 rsrc = resolve_mount_source(blobmsg_get_string(tb[OCI_MOUNT_SOURCE])); 1213 1214 ret = add_mount(rsrc, 1215 destination, 1216 tb[OCI_MOUNT_TYPE] ? blobmsg_get_string(tb[OCI_MOUNT_TYPE]) : NULL, 1217 mount_flags, propagation_flags, mount_data, err); 1218 1219 free(rsrc); 1220 1221 if (!ret && (idmap || tb[OCI_MOUNT_UIDMAPPINGS] || tb[OCI_MOUNT_GIDMAPPINGS])) { 1222 struct mount *m = avl_find_element(&mounts, destination, m, avl); 1223 if (m) { 1224 m->idmap = idmap || tb[OCI_MOUNT_UIDMAPPINGS] || tb[OCI_MOUNT_GIDMAPPINGS]; 1225 m->idmap_recursive = idmap_recursive; 1226 if (tb[OCI_MOUNT_UIDMAPPINGS]) { 1227 free(m->uidmappings); 1228 m->uidmappings = blob_memdup(tb[OCI_MOUNT_UIDMAPPINGS]); 1229 if (!m->uidmappings) { 1230 free(abs_destination); 1231 free(mount_data); 1232 return ENOMEM; 1233 } 1234 } 1235 if (tb[OCI_MOUNT_GIDMAPPINGS]) { 1236 free(m->gidmappings); 1237 m->gidmappings = blob_memdup(tb[OCI_MOUNT_GIDMAPPINGS]); 1238 if (!m->gidmappings) { 1239 free(abs_destination); 1240 free(mount_data); 1241 return ENOMEM; 1242 } 1243 } 1244 } 1245 } 1246 1247 free(abs_destination); 1248 if (mount_data) 1249 free(mount_data); 1250 1251 return ret; 1252 } 1253 1254 bool mount_is_defined(const char *target) 1255 { 1256 struct mount *m; 1257 1258 m = avl_find_element(&mounts, target, m, avl); 1259 1260 return m != NULL; 1261 } 1262 1263 static struct blob_attr *single_idmap(uint32_t container_id) 1264 { 1265 struct blob_buf b = {}; 1266 void *arr, *tbl; 1267 struct blob_attr *ret; 1268 1269 blob_buf_init(&b, 0); 1270 arr = blobmsg_open_array(&b, "m"); 1271 tbl = blobmsg_open_table(&b, NULL); 1272 blobmsg_add_u32(&b, "containerID", container_id); 1273 blobmsg_add_u32(&b, "hostID", 0); 1274 blobmsg_add_u32(&b, "size", 1); 1275 blobmsg_close_table(&b, tbl); 1276 blobmsg_close_array(&b, arr); 1277 ret = blob_memdup(blobmsg_data(b.head)); 1278 blob_buf_free(&b); 1279 1280 return ret; 1281 } 1282 1283 int fs_mount_enable_idmap(const char *target, uint32_t uid, uint32_t gid) 1284 { 1285 struct mount *m = avl_find_element(&mounts, target, m, avl); 1286 1287 if (!m) 1288 return ENOENT; 1289 1290 free(m->uidmappings); 1291 free(m->gidmappings); 1292 m->uidmappings = single_idmap(uid); 1293 m->gidmappings = single_idmap(gid); 1294 if (!m->uidmappings || !m->gidmappings) 1295 return ENOMEM; 1296 1297 m->idmap = true; 1298 m->idmap_recursive = false; 1299 1300 return 0; 1301 } 1302 1303 static void build_noafile(void) { 1304 int fd; 1305 1306 fd = creat(UJAIL_NOAFILE, 0000); 1307 if (fd < 0) 1308 return; 1309 1310 close(fd); 1311 return; 1312 } 1313 1314 static int do_mount_fd(const char *root, int fd, const char *target, int error) 1315 { 1316 char new[PATH_MAX]; 1317 struct stat s; 1318 1319 snprintf(new, sizeof(new), "%s%s", root, target); 1320 1321 if (fstat(fd, &s)) { 1322 if (error) 1323 ERROR("fstat(fd:%d) failed: %m\n", fd); 1324 close(fd); 1325 return error; 1326 } 1327 1328 if (S_ISDIR(s.st_mode)) { 1329 mkdir_p(new, 0755); 1330 } else { 1331 mkdir_p(dirname(new), 0755); 1332 snprintf(new, sizeof(new), "%s%s", root, target); 1333 int cfd = open(new, O_CREAT|O_WRONLY|O_TRUNC|O_EXCL, 0644); 1334 if (cfd >= 0) 1335 close(cfd); 1336 if (error && cfd < 0 && errno != EEXIST) { 1337 ERROR("failed to create mount target %s: %m\n", new); 1338 close(fd); 1339 return errno; 1340 } 1341 } 1342 1343 if (sys_move_mount(fd, "", AT_FDCWD, new, MOVE_MOUNT_F_EMPTY_PATH)) { 1344 if (error) 1345 ERROR("move_mount() to %s failed: %m\n", new); 1346 close(fd); 1347 return error; 1348 } 1349 1350 close(fd); 1351 DEBUG("move_mount fd to %s\n", new); 1352 return 0; 1353 } 1354 1355 static int idmap_mount_target(const char *root, struct mount *m, char *target, size_t tlen) 1356 { 1357 struct stat s; 1358 const char *target_rel; 1359 struct open_how how = { 1360 .flags = O_CREAT | O_WRONLY | O_TRUNC | O_CLOEXEC, 1361 .mode = 0644, 1362 .resolve = RESOLVE_BENEATH | RESOLVE_NO_MAGICLINKS, 1363 }; 1364 int fd; 1365 1366 snprintf(target, tlen, "%s%s", root, m->target); 1367 1368 if (stat(m->source, &s)) { 1369 if (m->error) 1370 ERROR("stat(%s) failed: %m\n", m->source); 1371 return -1; 1372 } 1373 1374 if (S_ISDIR(s.st_mode)) { 1375 mkdir_p(target, 0755); 1376 return 0; 1377 } 1378 1379 target_rel = m->target; 1380 assert(target_rel); 1381 mkdir_p(dirname(strdupa(target)), 0755); 1382 snprintf(target, tlen, "%s%s", root, m->target); 1383 while (*target_rel == '/') 1384 ++target_rel; 1385 fd = (jailroot_dirfd >= 0) 1386 ? sys_openat2(jailroot_dirfd, target_rel, &how, sizeof(how)) 1387 : open(target, O_CREAT | O_WRONLY | O_TRUNC | O_CLOEXEC, 0644); 1388 if (fd >= 0) 1389 close(fd); 1390 1391 return 0; 1392 } 1393 1394 static int idmap_tree_fd(const char *source, int source_fd, int userns_fd, 1395 unsigned long mountflags, unsigned long propagation, 1396 bool recursive) 1397 { 1398 struct ujail_mount_attr attr = { 0 }; 1399 unsigned int open_flags = OPEN_TREE_CLONE | OPEN_TREE_CLOEXEC; 1400 unsigned int setattr_flags = AT_EMPTY_PATH; 1401 int treefd; 1402 1403 if (recursive) { 1404 open_flags |= AT_RECURSIVE; 1405 setattr_flags |= AT_RECURSIVE; 1406 } 1407 1408 if (source_fd >= 0) 1409 treefd = sys_open_tree(source_fd, "", open_flags | AT_EMPTY_PATH); 1410 else 1411 treefd = sys_open_tree(AT_FDCWD, source, open_flags); 1412 if (treefd < 0) { 1413 ERROR("open_tree(%s): %m\n", source); 1414 return -1; 1415 } 1416 1417 /* keep the (possibly locked) atime mode unless one is requested */ 1418 attr.attr_set = MOUNT_ATTR_IDMAP | mountflags_to_attr(mountflags); 1419 if (mountflags & (MS_NOATIME | MS_RELATIME | MS_STRICTATIME)) 1420 attr.attr_clr |= MOUNT_ATTR__ATIME; 1421 attr.propagation = propagation; 1422 attr.userns_fd = userns_fd; 1423 1424 if (sys_mount_setattr(treefd, "", setattr_flags, &attr, sizeof(attr)) < 0) { 1425 ERROR("mount_setattr(IDMAP, %s): %m\n", source); 1426 close(treefd); 1427 return -1; 1428 } 1429 1430 return treefd; 1431 } 1432 1433 static int do_idmap_mount(const char *root, struct mount *m) 1434 { 1435 char target[PATH_MAX]; 1436 int treefd, userns_fd, ret = m->error; 1437 1438 if (!m->source || m->source == (void *)(-1)) { 1439 ERROR("idmap mount %s requires a source\n", m->target); 1440 return m->error; 1441 } 1442 1443 userns_fd = build_userns_fd(m->uidmappings, m->gidmappings); 1444 if (userns_fd < 0) { 1445 ERROR("build_userns_fd: %s\n", strerror(-userns_fd)); 1446 return m->error; 1447 } 1448 1449 if (idmap_mount_target(root, m, target, sizeof(target))) 1450 goto out_close; 1451 1452 treefd = idmap_tree_fd(m->source, m->source_fd, userns_fd, m->mountflags, 0, 1453 m->idmap_recursive); 1454 if (treefd < 0) 1455 goto out_close; 1456 1457 if (sys_move_mount(treefd, "", AT_FDCWD, target, MOVE_MOUNT_F_EMPTY_PATH) < 0) { 1458 if (m->error) 1459 ERROR("move_mount(%s -> %s): %m\n", m->source, target); 1460 close(treefd); 1461 goto out_close; 1462 } 1463 1464 if (m->propflags && mount("none", target, "none", m->propflags, NULL)) { 1465 if (m->error) 1466 ERROR("mount(propagation %#lx, %s): %m\n", m->propflags, target); 1467 close(treefd); 1468 goto out_close; 1469 } 1470 1471 DEBUG("idmap mount %s %s\n", m->source, target); 1472 close(treefd); 1473 ret = 0; 1474 1475 out_close: 1476 close(userns_fd); 1477 return ret; 1478 } 1479 1480 static int do_move_idmap_mount(const char *root, struct mount *m) 1481 { 1482 char target[PATH_MAX]; 1483 int ret = m->error; 1484 1485 if (idmap_mount_target(root, m, target, sizeof(target))) 1486 goto out; 1487 1488 if (sys_move_mount(m->idmap_treefd, "", AT_FDCWD, target, MOVE_MOUNT_F_EMPTY_PATH) < 0) { 1489 ERROR("move_mount(%s -> %s): %m\n", m->source, target); 1490 goto out; 1491 } 1492 1493 if (m->propflags && mount("none", target, "none", m->propflags, NULL)) { 1494 ERROR("mount(propagation %#lx, %s): %m\n", m->propflags, target); 1495 goto out; 1496 } 1497 1498 DEBUG("idmap volume %s %s\n", m->source, target); 1499 ret = 0; 1500 1501 out: 1502 close(m->idmap_treefd); 1503 m->idmap_treefd = -1; 1504 return ret; 1505 } 1506 1507 int jail_idmap_build(const char *extroot, 1508 struct blob_attr *uidmap, struct blob_attr *gidmap, 1509 int *fds, int maxfds) 1510 { 1511 int userns_fd; 1512 int n = 0; 1513 int fd; 1514 1515 if (!extroot) 1516 return 0; 1517 1518 userns_fd = build_userns_fd(uidmap, gidmap); 1519 if (userns_fd < 0) { 1520 ERROR("build_userns_fd: %s\n", strerror(-userns_fd)); 1521 return -1; 1522 } 1523 1524 if (n >= maxfds) 1525 goto err; 1526 fd = idmap_tree_fd(extroot, -1, userns_fd, 0, MS_PRIVATE, false); 1527 if (fd < 0) 1528 goto err; 1529 fds[n++] = fd; 1530 1531 close(userns_fd); 1532 return n; 1533 1534 err: 1535 close(userns_fd); 1536 while (n > 0) 1537 close(fds[--n]); 1538 return -1; 1539 } 1540 1541 bool jail_dir_is_fresh(const char *path) 1542 { 1543 struct dirent *e; 1544 char lf[PATH_MAX]; 1545 bool fresh = true, seen_lf = false; 1546 DIR *d, *l; 1547 1548 d = opendir(path); 1549 if (!d) 1550 return false; 1551 1552 while ((e = readdir(d))) { 1553 if (!strcmp(e->d_name, ".") || !strcmp(e->d_name, "..")) 1554 continue; 1555 if (!seen_lf && !strcmp(e->d_name, "lost+found")) { 1556 seen_lf = true; 1557 continue; 1558 } 1559 fresh = false; 1560 break; 1561 } 1562 closedir(d); 1563 1564 if (!fresh || !seen_lf) 1565 return fresh; 1566 1567 snprintf(lf, sizeof(lf), "%s/lost+found", path); 1568 l = opendir(lf); 1569 if (!l) 1570 return fresh; 1571 while ((e = readdir(l))) { 1572 if (strcmp(e->d_name, ".") && strcmp(e->d_name, "..")) { 1573 fresh = false; 1574 break; 1575 } 1576 } 1577 closedir(l); 1578 1579 return fresh; 1580 } 1581 1582 void jail_chown_fresh_volumes(uid_t uid, gid_t gid) 1583 { 1584 struct mount *m; 1585 char lf[PATH_MAX]; 1586 1587 avl_for_each_element(&mounts, m, avl) { 1588 if (!m->volume || !m->source || m->source == (void *)(-1)) 1589 continue; 1590 if (!jail_dir_is_fresh(m->source)) 1591 continue; 1592 if (chown(m->source, uid, gid)) 1593 ERROR("chown(fresh volume %s -> %u:%u): %m\n", m->source, uid, gid); 1594 snprintf(lf, sizeof(lf), "%s/lost+found", m->source); 1595 if (chown(lf, uid, gid) && errno != ENOENT) 1596 ERROR("chown(%s -> %u:%u): %m\n", lf, uid, gid); 1597 } 1598 } 1599 1600 int jail_idmap_assign(bool have_extroot, bool have_overlay, const int *fds, int nfds, 1601 int *extroot_fd, int *overlay_fd) 1602 { 1603 struct mount *m; 1604 int i = 0; 1605 1606 if (have_extroot && i < nfds) 1607 *extroot_fd = fds[i++]; 1608 1609 if (have_overlay && i < nfds) 1610 *overlay_fd = fds[i++]; 1611 1612 avl_for_each_element(&mounts, m, avl) { 1613 if (!m->volume) 1614 continue; 1615 if (i >= nfds) 1616 break; 1617 m->idmap_treefd = fds[i++]; 1618 } 1619 1620 return i; 1621 } 1622 1623 void mount_stage_dev(const char *jail_dev) 1624 { 1625 struct mount *m; 1626 struct stat s; 1627 char path[PATH_MAX]; 1628 bool is_dir; 1629 int fd; 1630 1631 avl_for_each_element(&mounts, m, avl) { 1632 if (strncmp(m->target, "/dev/", 5)) 1633 continue; 1634 if (m->source == (void *)(-1)) 1635 continue; 1636 1637 snprintf(path, sizeof(path), "%s%s", jail_dev, m->target + 4); 1638 1639 is_dir = !(m->mountflags & MS_BIND) || 1640 (m->source && !stat(m->source, &s) && S_ISDIR(s.st_mode)); 1641 if (is_dir) { 1642 mkdir_p(path, 0755); 1643 } else { 1644 mkdir_p(dirname(strdupa(path)), 0755); 1645 fd = open(path, O_CREAT | O_WRONLY | O_TRUNC | O_CLOEXEC, 0644); 1646 if (fd >= 0) 1647 close(fd); 1648 } 1649 } 1650 } 1651 1652 static bool mount_target_covers(const struct mount *outer, const struct mount *inner) 1653 { 1654 size_t len = strlen(outer->target); 1655 1656 if (!strcmp(outer->target, "/")) 1657 return strcmp(inner->target, "/") != 0; 1658 1659 return !strncmp(inner->target, outer->target, len) && inner->target[len] == '/'; 1660 } 1661 1662 static int mount_one(const char *jailroot, const char *jail_dev, struct mount *m) 1663 { 1664 char devtarget[PATH_MAX]; 1665 struct mount *outer; 1666 int ret; 1667 1668 if (m->mounted) 1669 return 0; 1670 1671 m->mounted = true; 1672 1673 /* whatever this one sits on has to be established first */ 1674 list_for_each_entry(outer, &mounts_order, list) 1675 if (!outer->mounted && mount_target_covers(outer, m) && 1676 mount_one(jailroot, jail_dev, outer)) 1677 return -1; 1678 1679 if (jail_dev && m->filesystemtype && !strcmp(m->filesystemtype, "tmpfs") && 1680 !strcmp(m->target, "/dev")) { 1681 snprintf(devtarget, sizeof(devtarget), "%s%s", jailroot, m->target); 1682 mkdir_p(devtarget, 0755); 1683 if (mount(jail_dev, devtarget, NULL, MS_BIND | MS_REC, NULL)) { 1684 ERROR("mount(MS_BIND, %s -> %s): %m\n", jail_dev, devtarget); 1685 return -1; 1686 } 1687 1688 return 0; 1689 } 1690 1691 if (m->idmap_treefd >= 0) 1692 return do_move_idmap_mount(jailroot, m) ? -1 : 0; 1693 1694 if (m->idmap) 1695 return do_idmap_mount(jailroot, m) ? -1 : 0; 1696 1697 if (m->source_fd >= 0) { 1698 ret = do_mount_fd(jailroot, m->source_fd, m->target, m->error); 1699 m->source_fd = -1; 1700 return ret ? -1 : 0; 1701 } 1702 1703 if (do_mount(jailroot, m->source, m->target, m->filesystemtype, m->mountflags, 1704 m->propflags, m->optstr, m->error, m->inner, m->source_fd)) 1705 return -1; 1706 1707 return 0; 1708 } 1709 1710 int mount_all(const char *jailroot, const char *jail_dev) { 1711 struct library *l; 1712 struct mount *m; 1713 int ret = 0; 1714 1715 build_noafile(); 1716 1717 jailroot_dirfd = open(jailroot, O_PATH | O_DIRECTORY | O_CLOEXEC); 1718 if (jailroot_dirfd < 0) 1719 ERROR("mount_all: open(%s, O_PATH|O_DIRECTORY): %m\n", jailroot); 1720 1721 avl_for_each_element(&libraries, l, avl) 1722 add_mount_bind(l->path, 1, -1); 1723 1724 /* the spec has the runtime establish the mounts in the order they are listed */ 1725 list_for_each_entry(m, &mounts_order, list) { 1726 if (mount_one(jailroot, jail_dev, m)) { 1727 ret = -1; 1728 break; 1729 } 1730 } 1731 1732 if (jailroot_dirfd >= 0) { 1733 close(jailroot_dirfd); 1734 jailroot_dirfd = -1; 1735 } 1736 1737 return ret; 1738 } 1739 1740 void mount_free(void) { 1741 struct mount *m, *tmp; 1742 1743 avl_remove_all_elements(&mounts, m, avl, tmp) { 1744 list_del(&m->list); 1745 if (m->source != (void*)(-1)) 1746 free((void*)m->source); 1747 if (m->source_fd >= 0) 1748 close(m->source_fd); 1749 free((void*)m->target); 1750 free((void*)m->filesystemtype); 1751 free((void*)m->optstr); 1752 free(m->uidmappings); 1753 free(m->gidmappings); 1754 free(m); 1755 } 1756 } 1757 1758 void mount_list_init(void) { 1759 avl_init(&mounts, avl_strcmp, false, NULL); 1760 INIT_LIST_HEAD(&mounts_order); 1761 } 1762 1763 static int add_script_interp(const char *path, const char *map, int size) 1764 { 1765 int start = 2; 1766 while (start < size && map[start] != '/') { 1767 start++; 1768 } 1769 if (start >= size) { 1770 ERROR("bad script interp (%s)\n", path); 1771 return -1; 1772 } 1773 int stop = start + 1; 1774 while (stop < size && map[stop] > 0x20 && map[stop] <= 0x7e) { 1775 stop++; 1776 } 1777 if (stop >= size || (stop-start) > PATH_MAX) { 1778 ERROR("bad script interp (%s)\n", path); 1779 return -1; 1780 } 1781 char buf[PATH_MAX]; 1782 strncpy(buf, map+start, stop-start); 1783 return add_path_and_deps(buf, 1, -1, 0); 1784 } 1785 1786 int add_2paths_and_deps(const char *path, const char *path2, int readonly, int error, int lib) 1787 { 1788 assert(path != NULL); 1789 assert(path2 != NULL); 1790 1791 if (lib == 0 && path[0] != '/') { 1792 ERROR("%s is not an absolute path\n", path); 1793 return error; 1794 } 1795 1796 char *map = NULL; 1797 char *fullpath = NULL; 1798 int fd, ret = -1; 1799 struct mount *bm = NULL; 1800 if (path[0] == '/') { 1801 if (avl_find(&mounts, path2)) 1802 return 0; 1803 fd = open(path, O_RDONLY|O_CLOEXEC); 1804 if (fd < 0) 1805 return error; 1806 _add_mount_bind(path, path2, readonly, error); 1807 bm = avl_find_element(&mounts, path2, bm, avl); 1808 if (bm && bm->source_fd < 0) 1809 bm->source_fd = sys_open_tree(AT_FDCWD, path, 1810 OPEN_TREE_CLONE | OPEN_TREE_CLOEXEC); 1811 } else { 1812 if (avl_find(&libraries, path)) 1813 return 0; 1814 fd = lib_open(&fullpath, path); 1815 if (fd < 0) 1816 return error; 1817 if (fullpath) 1818 alloc_library(fullpath, path); 1819 } 1820 1821 struct stat s; 1822 if (fstat(fd, &s) == -1) { 1823 ERROR("fstat(%s) failed: %m\n", path); 1824 ret = error; 1825 goto out; 1826 } 1827 1828 if (!S_ISREG(s.st_mode)) { 1829 ret = 0; 1830 goto out; 1831 } 1832 1833 /* too small to be an ELF or a script -> "normal" file */ 1834 if (s.st_size < 4) { 1835 ret = 0; 1836 goto out; 1837 } 1838 1839 map = mmap(NULL, s.st_size, PROT_READ, MAP_PRIVATE, fd, 0); 1840 if (map == MAP_FAILED) { 1841 ERROR("failed to mmap %s: %m\n", path); 1842 ret = -1; 1843 goto out; 1844 } 1845 1846 if (map[0] == '#' && map[1] == '!') { 1847 ret = add_script_interp(path, map, s.st_size); 1848 goto out; 1849 } 1850 1851 if (map[0] == ELFMAG0 && map[1] == ELFMAG1 && map[2] == ELFMAG2 && map[3] == ELFMAG3) { 1852 /* Pass the resolved fullpath, not the bare soname, so 1853 * elf_load_deps() can expand a $ORIGIN-relative DT_RPATH/ 1854 * DT_RUNPATH in this object against its real directory. */ 1855 ret = elf_load_deps(fullpath ? fullpath : path, map, s.st_size); 1856 goto out; 1857 } 1858 1859 ret = 0; 1860 1861 out: 1862 if (fd >= 0) 1863 close(fd); 1864 if (map) 1865 munmap(map, s.st_size); 1866 free(fullpath); 1867 1868 return ret; 1869 } 1870 1871 int add_2paths_nodeps(const char *path, const char *path2, int readonly, int error) 1872 { 1873 struct mount *bm = NULL; 1874 1875 if (path[0] != '/') { 1876 ERROR("%s is not an absolute path\n", path); 1877 return error; 1878 } 1879 1880 if (avl_find(&mounts, path2)) 1881 return 0; 1882 1883 if (_add_mount_bind(path, path2, readonly, error)) 1884 return error; 1885 1886 bm = avl_find_element(&mounts, path2, bm, avl); 1887 if (bm && bm->source_fd < 0) 1888 bm->source_fd = sys_open_tree(AT_FDCWD, path, 1889 OPEN_TREE_CLONE | OPEN_TREE_CLOEXEC); 1890 1891 return 0; 1892 } 1893
This page was automatically generated by LXR 0.3.1. • OpenWrt