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