• 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 #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