fs.c 14 KB

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