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 <stdlib.h> 25 #include <stdio.h> 26 #include <string.h> 27 #include <sys/stat.h> 28 #include <sys/mman.h> 29 #include <unistd.h> 30 #include <libgen.h> 31 32 #include <libubox/avl.h> 33 #include <libubox/avl-cmp.h> 34 #include <libubox/blobmsg.h> 35 #include <libubox/list.h> 36 #include <libubox/utils.h> 37 38 #include "elf.h" 39 #include "fs.h" 40 #include "jail.h" 41 #include "log.h" 42 43 #define UJAIL_NOAFILE "/tmp/.ujailnoafile" 44 45 /* 46 * mnt_already_visible() requires a new mount's atime class to match 47 * the host's existing instance or the kernel refuses it with EPERM 48 * ("Mount too revealing"). Detect the mountpoint's actual atime class 49 * instead of hardcoding one, so the new mount can never conflict. 50 */ 51 unsigned long detect_atime_flag(const char *mountpoint) 52 { 53 FILE *f; 54 char *line = NULL; 55 size_t linelen = 0; 56 unsigned long ret = MS_RELATIME; /* kernel default if nothing else is known */ 57 size_t mplen = strlen(mountpoint); 58 59 f = fopen("/proc/self/mountinfo", "r"); 60 if (!f) 61 return ret; 62 63 while (getline(&line, &linelen, f) != -1) { 64 /* mountinfo(5): field 5 is the mountpoint, field 6 its options */ 65 char *saveptr = NULL; 66 char *field; 67 int idx = 0; 68 char *mp_field = NULL, *opts_field = NULL; 69 70 for (field = strtok_r(line, " \t\n", &saveptr); field; 71 field = strtok_r(NULL, " \t\n", &saveptr), idx++) { 72 if (idx == 4) 73 mp_field = field; 74 else if (idx == 5) { 75 opts_field = field; 76 break; 77 } 78 } 79 80 if (!mp_field || !opts_field) 81 continue; 82 83 if (strlen(mp_field) != mplen || strcmp(mp_field, mountpoint)) 84 continue; 85 86 /* last matching entry wins: it's the topmost/currently-effective one */ 87 if (strstr(opts_field, "noatime")) 88 ret = MS_NOATIME; 89 else if (strstr(opts_field, "relatime")) 90 ret = MS_RELATIME; 91 else 92 /* strictatime is the absence of noatime/relatime, not a token */ 93 ret = MS_STRICTATIME; 94 } 95 96 free(line); 97 fclose(f); 98 99 return ret; 100 } 101 102 struct mount { 103 struct avl_node avl; 104 const char *source; 105 const char *target; 106 const char *filesystemtype; 107 unsigned long mountflags; 108 unsigned long propflags; 109 const char *optstr; 110 int error; 111 bool inner; 112 int source_fd; 113 }; 114 115 /* open_tree()/move_mount()/mount_setattr() have no glibc wrappers yet; 116 * struct ujail_mount_attr is declared in fs.h */ 117 int sys_open_tree(int dfd, const char *path, unsigned flags) 118 { 119 return syscall(SYS_open_tree, dfd, path, flags); 120 } 121 122 static int sys_move_mount(int from_dfd, const char *from_path, int to_dfd, const char *to_path, unsigned flags) 123 { 124 return syscall(SYS_move_mount, from_dfd, from_path, to_dfd, to_path, flags); 125 } 126 127 int sys_mount_setattr(int dfd, const char *path, unsigned flags, struct ujail_mount_attr *attr, size_t size) 128 { 129 return syscall(SYS_mount_setattr, dfd, path, flags, attr, size); 130 } 131 132 struct avl_tree mounts; 133 134 /* same masking as do_mount()'s is_mask branch, applied immediately 135 * against an absolute path instead of queued through jail_root. 136 * 137 * UJAIL_NOAFILE lives under the pre-pivot root, gone by the time this 138 * runs; JAIL_NOAFILE, a locked bind of procd's read-only noafile 139 * queued for deferred-userns jails, is used instead. */ 140 int mask_path_now(const char *path) 141 { 142 struct stat s; 143 144 if (stat(path, &s)) 145 return 0; /* doesn't exist, nothing to mask */ 146 147 if (S_ISDIR(s.st_mode)) { 148 if (mount("none", path, "tmpfs", MS_RDONLY | MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_RELATIME, "size=0,mode=000")) 149 return -1; 150 } else { 151 if (mount(JAIL_NOAFILE, path, "bind", MS_BIND, NULL)) 152 return -1; 153 if (mount(JAIL_NOAFILE, path, "bind", MS_REMOUNT | MS_BIND | MS_RDONLY | MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_RELATIME, NULL)) 154 return -1; 155 } 156 157 DEBUG("masked path %s\n", path); 158 return 0; 159 } 160 161 static void mountinfo_unescape(char *s) 162 { 163 char *r = s, *w = s; 164 165 while (*r) { 166 if (r[0] == '\\' && r[1] >= '' && r[1] <= '7' && 167 r[2] >= '' && r[2] <= '7' && r[3] >= '' && r[3] <= '7') { 168 *w++ = (char)(((r[1] - '') << 6) | ((r[2] - '') << 3) | (r[3] - '')); 169 r += 4; 170 } else { 171 *w++ = *r++; 172 } 173 } 174 *w = '\0'; 175 } 176 177 static unsigned long mountinfo_current_flags(const char *path) 178 { 179 unsigned long flags = MS_RELATIME; 180 bool found = false; 181 FILE *f; 182 char *line = NULL; 183 size_t linecap = 0; 184 185 f = fopen("/proc/self/mountinfo", "re"); 186 if (!f) 187 return flags; 188 189 while (getline(&line, &linecap, f) >= 0) { 190 char *mp, *opts, *save = NULL, *optsave = NULL; 191 char *tok; 192 unsigned long this_flags; 193 194 strtok_r(line, " ", &save); 195 strtok_r(NULL, " ", &save); 196 strtok_r(NULL, " ", &save); 197 strtok_r(NULL, " ", &save); 198 mp = strtok_r(NULL, " ", &save); 199 opts = strtok_r(NULL, " ", &save); 200 if (!mp || !opts) 201 continue; 202 mountinfo_unescape(mp); 203 if (strcmp(mp, path)) 204 continue; 205 206 this_flags = 0; 207 for (tok = strtok_r(opts, ",", &optsave); tok; 208 tok = strtok_r(NULL, ",", &optsave)) { 209 if (!strcmp(tok, "ro")) 210 this_flags |= MS_RDONLY; 211 else if (!strcmp(tok, "nosuid")) 212 this_flags |= MS_NOSUID; 213 else if (!strcmp(tok, "nodev")) 214 this_flags |= MS_NODEV; 215 else if (!strcmp(tok, "noexec")) 216 this_flags |= MS_NOEXEC; 217 else if (!strcmp(tok, "noatime")) 218 this_flags |= MS_NOATIME; 219 else if (!strcmp(tok, "relatime")) 220 this_flags |= MS_RELATIME; 221 else if (!strcmp(tok, "nodiratime")) 222 this_flags |= MS_NODIRATIME; 223 } 224 225 /* last match wins: it's the topmost/effective entry */ 226 flags = this_flags; 227 found = true; 228 } 229 free(line); 230 fclose(f); 231 232 if (!found) 233 return MS_RELATIME; 234 235 return flags; 236 } 237 238 static int do_mount(const char *root, const char *orig_source, const char *target, const char *filesystemtype, 239 unsigned long orig_mountflags, unsigned long propflags, const char *optstr, int error, bool inner) 240 { 241 struct stat s; 242 char new[PATH_MAX]; 243 char *source = (char *)orig_source; 244 int fd, ret = 0; 245 bool is_bind = (orig_mountflags & MS_BIND); 246 bool is_mask = (source == (void *)(-1)); 247 unsigned long mountflags = orig_mountflags; 248 249 assert(!(inner && is_mask)); 250 assert(!(inner && !orig_source)); 251 252 if (source && is_bind && stat(source, &s)) { 253 if (error) 254 ERROR("stat(%s) failed: %m\n", source); 255 return error; 256 } 257 258 if (inner) 259 if (asprintf(&source, "%s%s", root, orig_source) < 0) 260 return ENOMEM; 261 262 snprintf(new, sizeof(new), "%s%s", root, target?target:source); 263 264 if (is_mask) { 265 if (stat(new, &s)) 266 return 0; /* doesn't exists, nothing to mask */ 267 268 if (S_ISDIR(s.st_mode)) {/* use empty 0-sized tmpfs for directories */ 269 if (mount("none", new, "tmpfs", MS_RDONLY | MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_RELATIME, "size=0,mode=000")) 270 return error; 271 } else { 272 /* mount-bind 0-sized file having mode 000 */ 273 if (mount(UJAIL_NOAFILE, new, "bind", MS_BIND, NULL)) 274 return error; 275 276 if (mount(UJAIL_NOAFILE, new, "bind", MS_REMOUNT | MS_BIND | MS_RDONLY | MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_RELATIME, NULL)) 277 return error; 278 } 279 280 DEBUG("masked path %s\n", new); 281 return 0; 282 } 283 284 285 if (!is_bind || (source && S_ISDIR(s.st_mode))) { 286 mkdir_p(new, 0755); 287 } else if (is_bind && source) { 288 mkdir_p(dirname(new), 0755); 289 snprintf(new, sizeof(new), "%s%s", root, target?target:source); 290 fd = open(new, O_CREAT|O_WRONLY|O_TRUNC|O_EXCL, 0644); 291 if (fd >= 0) 292 close(fd); 293 294 if (error && fd < 0 && errno != EEXIST) { 295 ERROR("failed to create mount target %s: %m\n", new); 296 297 ret = errno; 298 goto free_source_out; 299 } 300 } 301 302 if (is_bind) { 303 if (mount(source?:new, new, filesystemtype?:"bind", MS_BIND | (mountflags & MS_REC), optstr)) { 304 if (error) 305 ERROR("failed to mount -B %s %s: %m\n", source, new); 306 307 ret = error; 308 goto free_source_out; 309 } 310 mountflags |= MS_REMOUNT; 311 } 312 313 const char *hack_fstype = ((!filesystemtype || strcmp(filesystemtype, "cgroup"))?filesystemtype:"cgroup2"); 314 if (mount(source?:(is_bind?new:NULL), new, hack_fstype?:"none", mountflags, optstr)) { 315 int mount_errno = errno; 316 317 if ((mountflags & MS_REMOUNT) && mount_errno == EPERM) { 318 /* Not a heuristic: re-read the kernel-enforced flags from 319 * mountinfo and only proceed once the security-relevant 320 * ones (ro/nosuid/nodev/noexec) are confirmed in effect. */ 321 unsigned long retry_flags = mountflags | mountinfo_current_flags(new); 322 323 if (retry_flags != mountflags && 324 !mount(source?:(is_bind?new:NULL), new, hack_fstype?:"none", retry_flags, optstr)) 325 goto mount_ok; 326 327 unsigned long lockable_flags = MS_RDONLY | MS_NOSUID | MS_NODEV | MS_NOEXEC; 328 unsigned long wanted = orig_mountflags & lockable_flags; 329 unsigned long got = mountinfo_current_flags(new) & lockable_flags; 330 331 if ((wanted & ~got) == 0) { 332 WARNING("remount(%s) to apply flags failed: %s (tolerated - " 333 "the flags actually in effect already satisfy the " 334 "request; expected under CLONE_NEWUSER for host-owned " 335 "bind sources)\n", new, strerror(mount_errno)); 336 goto mount_ok; 337 } 338 339 if (error) { 340 errno = mount_errno; 341 ERROR("failed to enforce mount restrictions on %s %s: %m " 342 "(missing flags: %s%s%s%s)\n", source, new, 343 (wanted & ~got & MS_RDONLY) ? "ro " : "", 344 (wanted & ~got & MS_NOSUID) ? "nosuid " : "", 345 (wanted & ~got & MS_NODEV) ? "nodev " : "", 346 (wanted & ~got & MS_NOEXEC) ? "noexec " : ""); 347 ret = error; 348 goto free_source_out; 349 } 350 351 WARNING("remount(%s) to apply mount restrictions failed and could not " 352 "be verified in effect; continuing best-effort since " 353 "this mount was not marked as critical\n", new); 354 goto mount_ok; 355 } 356 357 errno = mount_errno; 358 if (error) 359 ERROR("failed to mount %s %s: %m\n", source, new); 360 361 ret = error; 362 goto free_source_out; 363 } 364 365 mount_ok: 366 DEBUG("mount %s%s %s (%s)\n", (mountflags & MS_BIND)?"-B ":"", source, new, 367 (mountflags & MS_RDONLY)?"ro":"rw"); 368 369 if (propflags && mount("none", new, "none", propflags, NULL)) { 370 if (error) 371 ERROR("failed to mount --make-... %s \n", new); 372 373 ret = error; 374 } 375 376 free_source_out: 377 if (inner) 378 free(source); 379 380 return ret; 381 } 382 383 static int _add_mount(const char *source, const char *target, const char *filesystemtype, 384 unsigned long mountflags, unsigned long propflags, const char *optstr, 385 int error, bool inner) 386 { 387 assert(target != NULL); 388 389 if (avl_find(&mounts, target)) 390 return 1; 391 392 struct mount *m; 393 m = calloc(1, sizeof(struct mount)); 394 if (!m) 395 return ENOMEM; 396 397 m->avl.key = m->target = strdup(target); 398 if (source) { 399 if (source != (void*)(-1)) 400 m->source = strdup(source); 401 else 402 m->source = (void*)(-1); 403 } 404 if (filesystemtype) 405 m->filesystemtype = strdup(filesystemtype); 406 407 if (optstr) 408 m->optstr = strdup(optstr); 409 410 m->mountflags = mountflags; 411 m->propflags = propflags; 412 m->error = error; 413 m->inner = inner; 414 m->source_fd = -1; 415 416 avl_insert(&mounts, &m->avl); 417 DEBUG("adding mount %s %s bind(%d) ro(%d) err(%d)\n", (m->source == (void*)(-1))?"mask":m->source, m->target, 418 !!(m->mountflags & MS_BIND), !!(m->mountflags & MS_RDONLY), m->error != 0); 419 420 return 0; 421 } 422 423 int add_mount(const char *source, const char *target, const char *filesystemtype, 424 unsigned long mountflags, unsigned long propflags, const char *optstr, int error) 425 { 426 return _add_mount(source, target, filesystemtype, mountflags, propflags, optstr, error, false); 427 } 428 429 int add_mount_inner(const char *source, const char *target, const char *filesystemtype, 430 unsigned long mountflags, unsigned long propflags, const char *optstr, int error) 431 { 432 return _add_mount(source, target, filesystemtype, mountflags, propflags, optstr, error, true); 433 } 434 435 static int _add_mount_bind(const char *path, const char *path2, int readonly, int error) 436 { 437 unsigned long mountflags = MS_BIND; 438 439 if (readonly) 440 mountflags |= MS_RDONLY; 441 442 return add_mount(path, path2, NULL, mountflags, 0, NULL, error); 443 } 444 445 int add_mount_bind(const char *path, int readonly, int error) 446 { 447 return _add_mount_bind(path, path, readonly, error); 448 } 449 450 int add_mount_fd(int fd, const char *target, int error) 451 { 452 struct mount *m; 453 454 if (avl_find(&mounts, target)) 455 return 1; 456 457 m = calloc(1, sizeof(struct mount)); 458 if (!m) 459 return ENOMEM; 460 461 m->avl.key = m->target = strdup(target); 462 m->mountflags = MS_BIND; 463 m->error = error; 464 m->source_fd = fd; 465 466 avl_insert(&mounts, &m->avl); 467 DEBUG("adding mount fd:%d %s bind(1) ro(?) err(%d)\n", fd, target, error != 0); 468 469 return 0; 470 } 471 472 enum { 473 OCI_MOUNT_SOURCE, 474 OCI_MOUNT_DESTINATION, 475 OCI_MOUNT_TYPE, 476 OCI_MOUNT_OPTIONS, 477 __OCI_MOUNT_MAX, 478 }; 479 480 static const struct blobmsg_policy oci_mount_policy[] = { 481 [OCI_MOUNT_SOURCE] = { "source", BLOBMSG_TYPE_STRING }, 482 [OCI_MOUNT_DESTINATION] = { "destination", BLOBMSG_TYPE_STRING }, 483 [OCI_MOUNT_TYPE] = { "type", BLOBMSG_TYPE_STRING }, 484 [OCI_MOUNT_OPTIONS] = { "options", BLOBMSG_TYPE_ARRAY }, 485 }; 486 487 struct mount_opt { 488 struct list_head list; 489 char *optstr; 490 }; 491 492 #ifndef MS_LAZYTIME 493 #define MS_LAZYTIME (1 << 25) 494 #endif 495 496 static int parseOCImountopts(struct blob_attr *msg, unsigned long *mount_flags, unsigned long *propagation_flags, char **mount_data, int *error) 497 { 498 struct blob_attr *cur; 499 int rem; 500 unsigned long mf = 0; 501 unsigned long pf = 0; 502 char *tmp; 503 struct list_head fsopts = LIST_HEAD_INIT(fsopts); 504 size_t len = 0; 505 struct mount_opt *opt, *tmpopt; 506 507 blobmsg_for_each_attr(cur, msg, rem) { 508 tmp = blobmsg_get_string(cur); 509 if (!strcmp("ro", tmp)) 510 mf |= MS_RDONLY; 511 else if (!strcmp("rw", tmp)) 512 mf &= ~MS_RDONLY; 513 else if (!strcmp("bind", tmp)) 514 mf = MS_BIND; 515 else if (!strcmp("rbind", tmp)) 516 mf |= MS_BIND | MS_REC; 517 else if (!strcmp("sync", tmp)) 518 mf |= MS_SYNCHRONOUS; 519 else if (!strcmp("async", tmp)) 520 mf &= ~MS_SYNCHRONOUS; 521 else if (!strcmp("atime", tmp)) 522 mf &= ~MS_NOATIME; 523 else if (!strcmp("noatime", tmp)) 524 mf |= MS_NOATIME; 525 else if (!strcmp("defaults", tmp)) 526 mf = 0; /* rw, suid, dev, exec, auto, nouser, and async */ 527 else if (!strcmp("dev", tmp)) 528 mf &= ~MS_NODEV; 529 else if (!strcmp("nodev", tmp)) 530 mf |= MS_NODEV; 531 else if (!strcmp("iversion", tmp)) 532 mf |= MS_I_VERSION; 533 else if (!strcmp("noiversion", tmp)) 534 mf &= ~MS_I_VERSION; 535 else if (!strcmp("diratime", tmp)) 536 mf &= ~MS_NODIRATIME; 537 else if (!strcmp("nodiratime", tmp)) 538 mf |= MS_NODIRATIME; 539 else if (!strcmp("dirsync", tmp)) 540 mf |= MS_DIRSYNC; 541 else if (!strcmp("exec", tmp)) 542 mf &= ~MS_NOEXEC; 543 else if (!strcmp("noexec", tmp)) 544 mf |= MS_NOEXEC; 545 else if (!strcmp("mand", tmp)) 546 mf |= MS_MANDLOCK; 547 else if (!strcmp("nomand", tmp)) 548 mf &= ~MS_MANDLOCK; 549 else if (!strcmp("relatime", tmp)) 550 mf |= MS_RELATIME; 551 else if (!strcmp("norelatime", tmp)) 552 mf &= ~MS_RELATIME; 553 else if (!strcmp("strictatime", tmp)) 554 mf |= MS_STRICTATIME; 555 else if (!strcmp("nostrictatime", tmp)) 556 mf &= ~MS_STRICTATIME; 557 else if (!strcmp("lazytime", tmp)) 558 mf |= MS_LAZYTIME; 559 else if (!strcmp("nolazytime", tmp)) 560 mf &= ~MS_LAZYTIME; 561 else if (!strcmp("suid", tmp)) 562 mf &= ~MS_NOSUID; 563 else if (!strcmp("nosuid", tmp)) 564 mf |= MS_NOSUID; 565 else if (!strcmp("remount", tmp)) 566 mf |= MS_REMOUNT; 567 /* propagation flags */ 568 else if (!strcmp("private", tmp)) 569 pf |= MS_PRIVATE; 570 else if (!strcmp("rprivate", tmp)) 571 pf |= MS_PRIVATE | MS_REC; 572 else if (!strcmp("slave", tmp)) 573 pf |= MS_SLAVE; 574 else if (!strcmp("rslave", tmp)) 575 pf |= MS_SLAVE | MS_REC; 576 else if (!strcmp("shared", tmp)) 577 pf |= MS_SHARED; 578 else if (!strcmp("rshared", tmp)) 579 pf |= MS_SHARED | MS_REC; 580 else if (!strcmp("unbindable", tmp)) 581 pf |= MS_UNBINDABLE; 582 else if (!strcmp("runbindable", tmp)) 583 pf |= MS_UNBINDABLE | MS_REC; 584 /* special case: 'nofail' */ 585 else if(!strcmp("nofail", tmp)) 586 *error = 0; 587 else if (!strcmp("auto", tmp) || 588 !strcmp("noauto", tmp) || 589 !strcmp("user", tmp) || 590 !strcmp("group", tmp) || 591 !strcmp("_netdev", tmp)) 592 DEBUG("ignoring built-in mount option %s\n", tmp); 593 else { 594 /* filesystem-specific free-form option */ 595 opt = calloc(1, sizeof(*opt)); 596 opt->optstr = tmp; 597 list_add_tail(&opt->list, &fsopts); 598 } 599 }; 600 601 *mount_flags = mf; 602 *propagation_flags = pf; 603 604 list_for_each_entry(opt, &fsopts, list) { 605 if (len) 606 ++len; 607 608 len += strlen(opt->optstr); 609 }; 610 611 if (len) { 612 *mount_data = calloc(len + 1, sizeof(char)); 613 if (!(*mount_data)) 614 return ENOMEM; 615 616 len = 0; 617 list_for_each_entry(opt, &fsopts, list) { 618 if (len) 619 strcat(*mount_data, ","); 620 621 strcat(*mount_data, opt->optstr); 622 ++len; 623 } 624 625 list_for_each_entry_safe(opt, tmpopt, &fsopts, list) { 626 list_del(&opt->list); 627 free(opt); 628 } 629 } 630 631 DEBUG("mount flags(%08lx) propagation(%08lx) fsopts(\"%s\")\n", mf, pf, *mount_data?:""); 632 633 return 0; 634 } 635 636 static bool is_proc_or_sys_path(const char *path) 637 { 638 if (!strcmp(path, "/proc") || !strcmp(path, "/sys")) 639 return true; 640 641 if (!strncmp(path, "/proc/", 6)) 642 return true; 643 644 if (!strncmp(path, "/sys/", 5)) 645 return true; 646 647 return false; 648 } 649 650 int parseOCImount(struct blob_attr *msg) 651 { 652 struct blob_attr *tb[__OCI_MOUNT_MAX]; 653 unsigned long mount_flags = 0; 654 unsigned long propagation_flags = 0; 655 char *mount_data = NULL; 656 int ret, err = -1; 657 658 blobmsg_parse(oci_mount_policy, __OCI_MOUNT_MAX, tb, blobmsg_data(msg), blobmsg_len(msg)); 659 660 if (!tb[OCI_MOUNT_DESTINATION]) 661 return EINVAL; 662 663 if (tb[OCI_MOUNT_OPTIONS]) { 664 ret = parseOCImountopts(tb[OCI_MOUNT_OPTIONS], &mount_flags, &propagation_flags, &mount_data, &err); 665 if (ret) 666 return ret; 667 } 668 669 if (is_proc_or_sys_path(blobmsg_get_string(tb[OCI_MOUNT_DESTINATION])) && 670 ((mount_flags & MS_BIND) || 671 (tb[OCI_MOUNT_TYPE] && !strcmp(blobmsg_get_string(tb[OCI_MOUNT_TYPE]), "bind"))) && 672 !(mount_flags & MS_RDONLY)) { 673 ERROR("OCI mount config requests a writable bind mount onto %s; " 674 "refusing to allow write access to /proc or /sys\n", 675 blobmsg_get_string(tb[OCI_MOUNT_DESTINATION])); 676 if (mount_data) 677 free(mount_data); 678 return EPERM; 679 } 680 681 ret = add_mount(tb[OCI_MOUNT_SOURCE] ? blobmsg_get_string(tb[OCI_MOUNT_SOURCE]) : NULL, 682 blobmsg_get_string(tb[OCI_MOUNT_DESTINATION]), 683 tb[OCI_MOUNT_TYPE] ? blobmsg_get_string(tb[OCI_MOUNT_TYPE]) : NULL, 684 mount_flags, propagation_flags, mount_data, err); 685 686 if (mount_data) 687 free(mount_data); 688 689 return ret; 690 } 691 692 static void build_noafile(void) { 693 int fd; 694 695 fd = creat(UJAIL_NOAFILE, 0000); 696 if (fd < 0) 697 return; 698 699 close(fd); 700 return; 701 } 702 703 static int do_mount_fd(const char *root, int fd, const char *target, int error) 704 { 705 char new[PATH_MAX]; 706 struct stat s; 707 708 snprintf(new, sizeof(new), "%s%s", root, target); 709 710 if (fstat(fd, &s)) { 711 if (error) 712 ERROR("fstat(fd:%d) failed: %m\n", fd); 713 close(fd); 714 return error; 715 } 716 717 if (S_ISDIR(s.st_mode)) { 718 mkdir_p(new, 0755); 719 } else { 720 mkdir_p(dirname(new), 0755); 721 snprintf(new, sizeof(new), "%s%s", root, target); 722 int cfd = open(new, O_CREAT|O_WRONLY|O_TRUNC|O_EXCL, 0644); 723 if (cfd >= 0) 724 close(cfd); 725 if (error && cfd < 0 && errno != EEXIST) { 726 ERROR("failed to create mount target %s: %m\n", new); 727 close(fd); 728 return errno; 729 } 730 } 731 732 if (sys_move_mount(fd, "", AT_FDCWD, new, MOVE_MOUNT_F_EMPTY_PATH)) { 733 if (error) 734 ERROR("move_mount() to %s failed: %m\n", new); 735 close(fd); 736 return error; 737 } 738 739 close(fd); 740 DEBUG("move_mount fd to %s\n", new); 741 return 0; 742 } 743 744 int mount_all(const char *jailroot) { 745 struct library *l; 746 struct mount *m; 747 748 build_noafile(); 749 750 avl_for_each_element(&libraries, l, avl) 751 add_mount_bind(l->path, 1, -1); 752 753 avl_for_each_element(&mounts, m, avl) { 754 if (m->source_fd >= 0) { 755 if (do_mount_fd(jailroot, m->source_fd, m->target, m->error)) 756 return -1; 757 continue; 758 } 759 if (do_mount(jailroot, m->source, m->target, m->filesystemtype, m->mountflags, 760 m->propflags, m->optstr, m->error, m->inner)) 761 return -1; 762 } 763 764 return 0; 765 } 766 767 void mount_free(void) { 768 struct mount *m, *tmp; 769 770 avl_remove_all_elements(&mounts, m, avl, tmp) { 771 if (m->source != (void*)(-1)) 772 free((void*)m->source); 773 free((void*)m->target); 774 free((void*)m->filesystemtype); 775 free((void*)m->optstr); 776 free(m); 777 } 778 } 779 780 void mount_list_init(void) { 781 avl_init(&mounts, avl_strcmp, false, NULL); 782 } 783 784 static int add_script_interp(const char *path, const char *map, int size) 785 { 786 int start = 2; 787 while (start < size && map[start] != '/') { 788 start++; 789 } 790 if (start >= size) { 791 ERROR("bad script interp (%s)\n", path); 792 return -1; 793 } 794 int stop = start + 1; 795 while (stop < size && map[stop] > 0x20 && map[stop] <= 0x7e) { 796 stop++; 797 } 798 if (stop >= size || (stop-start) > PATH_MAX) { 799 ERROR("bad script interp (%s)\n", path); 800 return -1; 801 } 802 char buf[PATH_MAX]; 803 strncpy(buf, map+start, stop-start); 804 return add_path_and_deps(buf, 1, -1, 0); 805 } 806 807 int add_2paths_and_deps(const char *path, const char *path2, int readonly, int error, int lib) 808 { 809 assert(path != NULL); 810 assert(path2 != NULL); 811 812 if (lib == 0 && path[0] != '/') { 813 ERROR("%s is not an absolute path\n", path); 814 return error; 815 } 816 817 char *map = NULL; 818 char *fullpath = NULL; 819 int fd, ret = -1; 820 if (path[0] == '/') { 821 if (avl_find(&mounts, path2)) 822 return 0; 823 fd = open(path, O_RDONLY|O_CLOEXEC); 824 if (fd < 0) 825 return error; 826 _add_mount_bind(path, path2, readonly, error); 827 } else { 828 if (avl_find(&libraries, path)) 829 return 0; 830 fd = lib_open(&fullpath, path); 831 if (fd < 0) 832 return error; 833 if (fullpath) 834 alloc_library(fullpath, path); 835 } 836 837 struct stat s; 838 if (fstat(fd, &s) == -1) { 839 ERROR("fstat(%s) failed: %m\n", path); 840 ret = error; 841 goto out; 842 } 843 844 if (!S_ISREG(s.st_mode)) { 845 ret = 0; 846 goto out; 847 } 848 849 /* too small to be an ELF or a script -> "normal" file */ 850 if (s.st_size < 4) { 851 ret = 0; 852 goto out; 853 } 854 855 map = mmap(NULL, s.st_size, PROT_READ, MAP_PRIVATE, fd, 0); 856 if (map == MAP_FAILED) { 857 ERROR("failed to mmap %s: %m\n", path); 858 ret = -1; 859 goto out; 860 } 861 862 if (map[0] == '#' && map[1] == '!') { 863 ret = add_script_interp(path, map, s.st_size); 864 goto out; 865 } 866 867 if (map[0] == ELFMAG0 && map[1] == ELFMAG1 && map[2] == ELFMAG2 && map[3] == ELFMAG3) { 868 /* Pass the resolved fullpath, not the bare soname, so 869 * elf_load_deps() can expand a $ORIGIN-relative DT_RPATH/ 870 * DT_RUNPATH in this object against its real directory. */ 871 ret = elf_load_deps(fullpath ? fullpath : path, map, s.st_size); 872 goto out; 873 } 874 875 ret = 0; 876 877 out: 878 if (fd >= 0) 879 close(fd); 880 if (map) 881 munmap(map, s.st_size); 882 free(fullpath); 883 884 return ret; 885 } 886
This page was automatically generated by LXR 0.3.1. • OpenWrt