• source navigation  • diff markup  • identifier search  • freetext search  • 

Sources/procd/jail/fs.c

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

This page was automatically generated by LXR 0.3.1.  •  OpenWrt