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 <fcntl.h> 22 #include <linux/limits.h> 23 #include <stdlib.h> 24 #include <stdio.h> 25 #include <string.h> 26 #include <sys/stat.h> 27 #include <sys/mman.h> 28 #include <unistd.h> 29 #include <libgen.h> 30 31 #include <libubox/avl.h> 32 #include <libubox/avl-cmp.h> 33 #include <libubox/blobmsg.h> 34 #include <libubox/list.h> 35 #include <libubox/utils.h> 36 37 #include "elf.h" 38 #include "fs.h" 39 #include "jail.h" 40 #include "log.h" 41 42 #define UJAIL_NOAFILE "/tmp/.ujailnoafile" 43 44 struct mount { 45 struct avl_node avl; 46 const char *source; 47 const char *target; 48 const char *filesystemtype; 49 unsigned long mountflags; 50 unsigned long propflags; 51 const char *optstr; 52 int error; 53 bool inner; 54 }; 55 56 struct avl_tree mounts; 57 58 static int do_mount(const char *root, const char *orig_source, const char *target, const char *filesystemtype, 59 unsigned long orig_mountflags, unsigned long propflags, const char *optstr, int error, bool inner) 60 { 61 struct stat s; 62 char new[PATH_MAX]; 63 char *source = (char *)orig_source; 64 int fd, ret = 0; 65 bool is_bind = (orig_mountflags & MS_BIND); 66 bool is_mask = (source == (void *)(-1)); 67 unsigned long mountflags = orig_mountflags; 68 69 assert(!(inner && is_mask)); 70 assert(!(inner && !orig_source)); 71 72 if (source && is_bind && stat(source, &s)) { 73 if (error) 74 ERROR("stat(%s) failed: %m\n", source); 75 return error; 76 } 77 78 if (inner) 79 if (asprintf(&source, "%s%s", root, orig_source) < 0) 80 return ENOMEM; 81 82 snprintf(new, sizeof(new), "%s%s", root, target?target:source); 83 84 if (is_mask) { 85 if (stat(new, &s)) 86 return 0; /* doesn't exists, nothing to mask */ 87 88 if (S_ISDIR(s.st_mode)) {/* use empty 0-sized tmpfs for directories */ 89 if (mount("none", new, "tmpfs", MS_RDONLY | MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_NOATIME, "size=0,mode=000")) 90 return error; 91 } else { 92 /* mount-bind 0-sized file having mode 000 */ 93 if (mount(UJAIL_NOAFILE, new, "bind", MS_BIND, NULL)) 94 return error; 95 96 if (mount(UJAIL_NOAFILE, new, "bind", MS_REMOUNT | MS_BIND | MS_RDONLY | MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_NOATIME, NULL)) 97 return error; 98 } 99 100 DEBUG("masked path %s\n", new); 101 return 0; 102 } 103 104 105 if (!is_bind || (source && S_ISDIR(s.st_mode))) { 106 mkdir_p(new, 0755); 107 } else if (is_bind && source) { 108 mkdir_p(dirname(new), 0755); 109 snprintf(new, sizeof(new), "%s%s", root, target?target:source); 110 fd = open(new, O_CREAT|O_WRONLY|O_TRUNC|O_EXCL, 0644); 111 if (fd >= 0) 112 close(fd); 113 114 if (error && fd < 0 && errno != EEXIST) { 115 ERROR("failed to create mount target %s: %m\n", new); 116 117 ret = errno; 118 goto free_source_out; 119 } 120 } 121 122 if (is_bind) { 123 if (mount(source?:new, new, filesystemtype?:"bind", MS_BIND | (mountflags & MS_REC), optstr)) { 124 if (error) 125 ERROR("failed to mount -B %s %s: %m\n", source, new); 126 127 ret = error; 128 goto free_source_out; 129 } 130 mountflags |= MS_REMOUNT; 131 } 132 133 const char *hack_fstype = ((!filesystemtype || strcmp(filesystemtype, "cgroup"))?filesystemtype:"cgroup2"); 134 if (mount(source?:(is_bind?new:NULL), new, hack_fstype?:"none", mountflags, optstr)) { 135 if (error) 136 ERROR("failed to mount %s %s: %m\n", source, new); 137 138 ret = error; 139 goto free_source_out; 140 } 141 142 DEBUG("mount %s%s %s (%s)\n", (mountflags & MS_BIND)?"-B ":"", source, new, 143 (mountflags & MS_RDONLY)?"ro":"rw"); 144 145 if (propflags && mount("none", new, "none", propflags, NULL)) { 146 if (error) 147 ERROR("failed to mount --make-... %s \n", new); 148 149 ret = error; 150 } 151 152 free_source_out: 153 if (inner) 154 free(source); 155 156 return ret; 157 } 158 159 static int _add_mount(const char *source, const char *target, const char *filesystemtype, 160 unsigned long mountflags, unsigned long propflags, const char *optstr, 161 int error, bool inner) 162 { 163 assert(target != NULL); 164 165 if (avl_find(&mounts, target)) 166 return 1; 167 168 struct mount *m; 169 m = calloc(1, sizeof(struct mount)); 170 if (!m) 171 return ENOMEM; 172 173 m->avl.key = m->target = strdup(target); 174 if (source) { 175 if (source != (void*)(-1)) 176 m->source = strdup(source); 177 else 178 m->source = (void*)(-1); 179 } 180 if (filesystemtype) 181 m->filesystemtype = strdup(filesystemtype); 182 183 if (optstr) 184 m->optstr = strdup(optstr); 185 186 m->mountflags = mountflags; 187 m->propflags = propflags; 188 m->error = error; 189 m->inner = inner; 190 191 avl_insert(&mounts, &m->avl); 192 DEBUG("adding mount %s %s bind(%d) ro(%d) err(%d)\n", (m->source == (void*)(-1))?"mask":m->source, m->target, 193 !!(m->mountflags & MS_BIND), !!(m->mountflags & MS_RDONLY), m->error != 0); 194 195 return 0; 196 } 197 198 int add_mount(const char *source, const char *target, const char *filesystemtype, 199 unsigned long mountflags, unsigned long propflags, const char *optstr, int error) 200 { 201 return _add_mount(source, target, filesystemtype, mountflags, propflags, optstr, error, false); 202 } 203 204 int add_mount_inner(const char *source, const char *target, const char *filesystemtype, 205 unsigned long mountflags, unsigned long propflags, const char *optstr, int error) 206 { 207 return _add_mount(source, target, filesystemtype, mountflags, propflags, optstr, error, true); 208 } 209 210 static int _add_mount_bind(const char *path, const char *path2, int readonly, int error) 211 { 212 unsigned long mountflags = MS_BIND; 213 214 if (readonly) 215 mountflags |= MS_RDONLY; 216 217 return add_mount(path, path2, NULL, mountflags, 0, NULL, error); 218 } 219 220 int add_mount_bind(const char *path, int readonly, int error) 221 { 222 return _add_mount_bind(path, path, readonly, error); 223 } 224 225 enum { 226 OCI_MOUNT_SOURCE, 227 OCI_MOUNT_DESTINATION, 228 OCI_MOUNT_TYPE, 229 OCI_MOUNT_OPTIONS, 230 __OCI_MOUNT_MAX, 231 }; 232 233 static const struct blobmsg_policy oci_mount_policy[] = { 234 [OCI_MOUNT_SOURCE] = { "source", BLOBMSG_TYPE_STRING }, 235 [OCI_MOUNT_DESTINATION] = { "destination", BLOBMSG_TYPE_STRING }, 236 [OCI_MOUNT_TYPE] = { "type", BLOBMSG_TYPE_STRING }, 237 [OCI_MOUNT_OPTIONS] = { "options", BLOBMSG_TYPE_ARRAY }, 238 }; 239 240 struct mount_opt { 241 struct list_head list; 242 char *optstr; 243 }; 244 245 #ifndef MS_LAZYTIME 246 #define MS_LAZYTIME (1 << 25) 247 #endif 248 249 static int parseOCImountopts(struct blob_attr *msg, unsigned long *mount_flags, unsigned long *propagation_flags, char **mount_data, int *error) 250 { 251 struct blob_attr *cur; 252 int rem; 253 unsigned long mf = 0; 254 unsigned long pf = 0; 255 char *tmp; 256 struct list_head fsopts = LIST_HEAD_INIT(fsopts); 257 size_t len = 0; 258 struct mount_opt *opt, *tmpopt; 259 260 blobmsg_for_each_attr(cur, msg, rem) { 261 tmp = blobmsg_get_string(cur); 262 if (!strcmp("ro", tmp)) 263 mf |= MS_RDONLY; 264 else if (!strcmp("rw", tmp)) 265 mf &= ~MS_RDONLY; 266 else if (!strcmp("bind", tmp)) 267 mf = MS_BIND; 268 else if (!strcmp("rbind", tmp)) 269 mf |= MS_BIND | MS_REC; 270 else if (!strcmp("sync", tmp)) 271 mf |= MS_SYNCHRONOUS; 272 else if (!strcmp("async", tmp)) 273 mf &= ~MS_SYNCHRONOUS; 274 else if (!strcmp("atime", tmp)) 275 mf &= ~MS_NOATIME; 276 else if (!strcmp("noatime", tmp)) 277 mf |= MS_NOATIME; 278 else if (!strcmp("defaults", tmp)) 279 mf = 0; /* rw, suid, dev, exec, auto, nouser, and async */ 280 else if (!strcmp("dev", tmp)) 281 mf &= ~MS_NODEV; 282 else if (!strcmp("nodev", tmp)) 283 mf |= MS_NODEV; 284 else if (!strcmp("iversion", tmp)) 285 mf |= MS_I_VERSION; 286 else if (!strcmp("noiversion", tmp)) 287 mf &= ~MS_I_VERSION; 288 else if (!strcmp("diratime", tmp)) 289 mf &= ~MS_NODIRATIME; 290 else if (!strcmp("nodiratime", tmp)) 291 mf |= MS_NODIRATIME; 292 else if (!strcmp("dirsync", tmp)) 293 mf |= MS_DIRSYNC; 294 else if (!strcmp("exec", tmp)) 295 mf &= ~MS_NOEXEC; 296 else if (!strcmp("noexec", tmp)) 297 mf |= MS_NOEXEC; 298 else if (!strcmp("mand", tmp)) 299 mf |= MS_MANDLOCK; 300 else if (!strcmp("nomand", tmp)) 301 mf &= ~MS_MANDLOCK; 302 else if (!strcmp("relatime", tmp)) 303 mf |= MS_RELATIME; 304 else if (!strcmp("norelatime", tmp)) 305 mf &= ~MS_RELATIME; 306 else if (!strcmp("strictatime", tmp)) 307 mf |= MS_STRICTATIME; 308 else if (!strcmp("nostrictatime", tmp)) 309 mf &= ~MS_STRICTATIME; 310 else if (!strcmp("lazytime", tmp)) 311 mf |= MS_LAZYTIME; 312 else if (!strcmp("nolazytime", tmp)) 313 mf &= ~MS_LAZYTIME; 314 else if (!strcmp("suid", tmp)) 315 mf &= ~MS_NOSUID; 316 else if (!strcmp("nosuid", tmp)) 317 mf |= MS_NOSUID; 318 else if (!strcmp("remount", tmp)) 319 mf |= MS_REMOUNT; 320 /* propagation flags */ 321 else if (!strcmp("private", tmp)) 322 pf |= MS_PRIVATE; 323 else if (!strcmp("rprivate", tmp)) 324 pf |= MS_PRIVATE | MS_REC; 325 else if (!strcmp("slave", tmp)) 326 pf |= MS_SLAVE; 327 else if (!strcmp("rslave", tmp)) 328 pf |= MS_SLAVE | MS_REC; 329 else if (!strcmp("shared", tmp)) 330 pf |= MS_SHARED; 331 else if (!strcmp("rshared", tmp)) 332 pf |= MS_SHARED | MS_REC; 333 else if (!strcmp("unbindable", tmp)) 334 pf |= MS_UNBINDABLE; 335 else if (!strcmp("runbindable", tmp)) 336 pf |= MS_UNBINDABLE | MS_REC; 337 /* special case: 'nofail' */ 338 else if(!strcmp("nofail", tmp)) 339 *error = 0; 340 else if (!strcmp("auto", tmp) || 341 !strcmp("noauto", tmp) || 342 !strcmp("user", tmp) || 343 !strcmp("group", tmp) || 344 !strcmp("_netdev", tmp)) 345 DEBUG("ignoring built-in mount option %s\n", tmp); 346 else { 347 /* filesystem-specific free-form option */ 348 opt = calloc(1, sizeof(*opt)); 349 opt->optstr = tmp; 350 list_add_tail(&opt->list, &fsopts); 351 } 352 }; 353 354 *mount_flags = mf; 355 *propagation_flags = pf; 356 357 list_for_each_entry(opt, &fsopts, list) { 358 if (len) 359 ++len; 360 361 len += strlen(opt->optstr); 362 }; 363 364 if (len) { 365 *mount_data = calloc(len + 1, sizeof(char)); 366 if (!(*mount_data)) 367 return ENOMEM; 368 369 len = 0; 370 list_for_each_entry(opt, &fsopts, list) { 371 if (len) 372 strcat(*mount_data, ","); 373 374 strcat(*mount_data, opt->optstr); 375 ++len; 376 } 377 378 list_for_each_entry_safe(opt, tmpopt, &fsopts, list) { 379 list_del(&opt->list); 380 free(opt); 381 } 382 } 383 384 DEBUG("mount flags(%08lx) propagation(%08lx) fsopts(\"%s\")\n", mf, pf, *mount_data?:""); 385 386 return 0; 387 } 388 389 int parseOCImount(struct blob_attr *msg) 390 { 391 struct blob_attr *tb[__OCI_MOUNT_MAX]; 392 unsigned long mount_flags = 0; 393 unsigned long propagation_flags = 0; 394 char *mount_data = NULL; 395 int ret, err = -1; 396 397 blobmsg_parse(oci_mount_policy, __OCI_MOUNT_MAX, tb, blobmsg_data(msg), blobmsg_len(msg)); 398 399 if (!tb[OCI_MOUNT_DESTINATION]) 400 return EINVAL; 401 402 if (tb[OCI_MOUNT_OPTIONS]) { 403 ret = parseOCImountopts(tb[OCI_MOUNT_OPTIONS], &mount_flags, &propagation_flags, &mount_data, &err); 404 if (ret) 405 return ret; 406 } 407 408 ret = add_mount(tb[OCI_MOUNT_SOURCE] ? blobmsg_get_string(tb[OCI_MOUNT_SOURCE]) : NULL, 409 blobmsg_get_string(tb[OCI_MOUNT_DESTINATION]), 410 tb[OCI_MOUNT_TYPE] ? blobmsg_get_string(tb[OCI_MOUNT_TYPE]) : NULL, 411 mount_flags, propagation_flags, mount_data, err); 412 413 if (mount_data) 414 free(mount_data); 415 416 return ret; 417 } 418 419 static void build_noafile(void) { 420 int fd; 421 422 fd = creat(UJAIL_NOAFILE, 0000); 423 if (fd < 0) 424 return; 425 426 close(fd); 427 return; 428 } 429 430 int mount_all(const char *jailroot) { 431 struct library *l; 432 struct mount *m; 433 434 build_noafile(); 435 436 avl_for_each_element(&libraries, l, avl) 437 add_mount_bind(l->path, 1, -1); 438 439 avl_for_each_element(&mounts, m, avl) 440 if (do_mount(jailroot, m->source, m->target, m->filesystemtype, m->mountflags, 441 m->propflags, m->optstr, m->error, m->inner)) 442 return -1; 443 444 return 0; 445 } 446 447 void mount_free(void) { 448 struct mount *m, *tmp; 449 450 avl_remove_all_elements(&mounts, m, avl, tmp) { 451 if (m->source != (void*)(-1)) 452 free((void*)m->source); 453 free((void*)m->target); 454 free((void*)m->filesystemtype); 455 free((void*)m->optstr); 456 free(m); 457 } 458 } 459 460 void mount_list_init(void) { 461 avl_init(&mounts, avl_strcmp, false, NULL); 462 } 463 464 static int add_script_interp(const char *path, const char *map, int size) 465 { 466 int start = 2; 467 while (start < size && map[start] != '/') { 468 start++; 469 } 470 if (start >= size) { 471 ERROR("bad script interp (%s)\n", path); 472 return -1; 473 } 474 int stop = start + 1; 475 while (stop < size && map[stop] > 0x20 && map[stop] <= 0x7e) { 476 stop++; 477 } 478 if (stop >= size || (stop-start) > PATH_MAX) { 479 ERROR("bad script interp (%s)\n", path); 480 return -1; 481 } 482 char buf[PATH_MAX]; 483 strncpy(buf, map+start, stop-start); 484 return add_path_and_deps(buf, 1, -1, 0); 485 } 486 487 int add_2paths_and_deps(const char *path, const char *path2, int readonly, int error, int lib) 488 { 489 assert(path != NULL); 490 assert(path2 != NULL); 491 492 if (lib == 0 && path[0] != '/') { 493 ERROR("%s is not an absolute path\n", path); 494 return error; 495 } 496 497 char *map = NULL; 498 int fd, ret = -1; 499 if (path[0] == '/') { 500 if (avl_find(&mounts, path2)) 501 return 0; 502 fd = open(path, O_RDONLY|O_CLOEXEC); 503 if (fd < 0) 504 return error; 505 _add_mount_bind(path, path2, readonly, error); 506 } else { 507 if (avl_find(&libraries, path)) 508 return 0; 509 char *fullpath; 510 fd = lib_open(&fullpath, path); 511 if (fd < 0) 512 return error; 513 if (fullpath) { 514 alloc_library(fullpath, path); 515 free(fullpath); 516 } 517 } 518 519 struct stat s; 520 if (fstat(fd, &s) == -1) { 521 ERROR("fstat(%s) failed: %m\n", path); 522 ret = error; 523 goto out; 524 } 525 526 if (!S_ISREG(s.st_mode)) { 527 ret = 0; 528 goto out; 529 } 530 531 /* too small to be an ELF or a script -> "normal" file */ 532 if (s.st_size < 4) { 533 ret = 0; 534 goto out; 535 } 536 537 map = mmap(NULL, s.st_size, PROT_READ, MAP_PRIVATE, fd, 0); 538 if (map == MAP_FAILED) { 539 ERROR("failed to mmap %s: %m\n", path); 540 ret = -1; 541 goto out; 542 } 543 544 if (map[0] == '#' && map[1] == '!') { 545 ret = add_script_interp(path, map, s.st_size); 546 goto out; 547 } 548 549 if (map[0] == ELFMAG0 && map[1] == ELFMAG1 && map[2] == ELFMAG2 && map[3] == ELFMAG3) { 550 ret = elf_load_deps(path, map); 551 goto out; 552 } 553 554 ret = 0; 555 556 out: 557 if (fd >= 0) 558 close(fd); 559 if (map) 560 munmap(map, s.st_size); 561 562 return ret; 563 } 564
This page was automatically generated by LXR 0.3.1. • OpenWrt