• 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 <sys/syscall.h>
 22 #include <fcntl.h>
 23 #include <linux/limits.h>
 24 #include <stdlib.h>
 25 #include <stdio.h>
 26 #include <string.h>
 27 #include <sys/stat.h>
 28 #include <sys/mman.h>
 29 #include <unistd.h>
 30 #include <libgen.h>
 31 
 32 #include <libubox/avl.h>
 33 #include <libubox/avl-cmp.h>
 34 #include <libubox/blobmsg.h>
 35 #include <libubox/list.h>
 36 #include <libubox/utils.h>
 37 
 38 #include "elf.h"
 39 #include "fs.h"
 40 #include "jail.h"
 41 #include "log.h"
 42 
 43 #define UJAIL_NOAFILE "/tmp/.ujailnoafile"
 44 
 45 /*
 46  * mnt_already_visible() requires a new mount's atime class to match
 47  * the host's existing instance or the kernel refuses it with EPERM
 48  * ("Mount too revealing"). Detect the mountpoint's actual atime class
 49  * instead of hardcoding one, so the new mount can never conflict.
 50  */
 51 unsigned long detect_atime_flag(const char *mountpoint)
 52 {
 53         FILE *f;
 54         char *line = NULL;
 55         size_t linelen = 0;
 56         unsigned long ret = MS_RELATIME; /* kernel default if nothing else is known */
 57         size_t mplen = strlen(mountpoint);
 58 
 59         f = fopen("/proc/self/mountinfo", "r");
 60         if (!f)
 61                 return ret;
 62 
 63         while (getline(&line, &linelen, f) != -1) {
 64                 /* mountinfo(5): field 5 is the mountpoint, field 6 its options */
 65                 char *saveptr = NULL;
 66                 char *field;
 67                 int idx = 0;
 68                 char *mp_field = NULL, *opts_field = NULL;
 69 
 70                 for (field = strtok_r(line, " \t\n", &saveptr); field;
 71                      field = strtok_r(NULL, " \t\n", &saveptr), idx++) {
 72                         if (idx == 4)
 73                                 mp_field = field;
 74                         else if (idx == 5) {
 75                                 opts_field = field;
 76                                 break;
 77                         }
 78                 }
 79 
 80                 if (!mp_field || !opts_field)
 81                         continue;
 82 
 83                 if (strlen(mp_field) != mplen || strcmp(mp_field, mountpoint))
 84                         continue;
 85 
 86                 /* last matching entry wins: it's the topmost/currently-effective one */
 87                 if (strstr(opts_field, "noatime"))
 88                         ret = MS_NOATIME;
 89                 else if (strstr(opts_field, "relatime"))
 90                         ret = MS_RELATIME;
 91                 else
 92                         /* strictatime is the absence of noatime/relatime, not a token */
 93                         ret = MS_STRICTATIME;
 94         }
 95 
 96         free(line);
 97         fclose(f);
 98 
 99         return ret;
100 }
101 
102 struct mount {
103         struct avl_node avl;
104         const char *source;
105         const char *target;
106         const char *filesystemtype;
107         unsigned long mountflags;
108         unsigned long propflags;
109         const char *optstr;
110         int error;
111         bool inner;
112         int source_fd;
113 };
114 
115 /* open_tree()/move_mount()/mount_setattr() have no glibc wrappers yet;
116  * struct ujail_mount_attr is declared in fs.h */
117 int sys_open_tree(int dfd, const char *path, unsigned flags)
118 {
119         return syscall(SYS_open_tree, dfd, path, flags);
120 }
121 
122 static int sys_move_mount(int from_dfd, const char *from_path, int to_dfd, const char *to_path, unsigned flags)
123 {
124         return syscall(SYS_move_mount, from_dfd, from_path, to_dfd, to_path, flags);
125 }
126 
127 int sys_mount_setattr(int dfd, const char *path, unsigned flags, struct ujail_mount_attr *attr, size_t size)
128 {
129         return syscall(SYS_mount_setattr, dfd, path, flags, attr, size);
130 }
131 
132 struct avl_tree mounts;
133 
134 /* same masking as do_mount()'s is_mask branch, applied immediately
135  * against an absolute path instead of queued through jail_root.
136  *
137  * UJAIL_NOAFILE lives under the pre-pivot root, gone by the time this
138  * runs; JAIL_NOAFILE, a locked bind of procd's read-only noafile
139  * queued for deferred-userns jails, is used instead. */
140 int mask_path_now(const char *path)
141 {
142         struct stat s;
143 
144         if (stat(path, &s))
145                 return 0; /* doesn't exist, nothing to mask */
146 
147         if (S_ISDIR(s.st_mode)) {
148                 if (mount("none", path, "tmpfs", MS_RDONLY | MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_RELATIME, "size=0,mode=000"))
149                         return -1;
150         } else {
151                 if (mount(JAIL_NOAFILE, path, "bind", MS_BIND, NULL))
152                         return -1;
153                 if (mount(JAIL_NOAFILE, path, "bind", MS_REMOUNT | MS_BIND | MS_RDONLY | MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_RELATIME, NULL))
154                         return -1;
155         }
156 
157         DEBUG("masked path %s\n", path);
158         return 0;
159 }
160 
161 static void mountinfo_unescape(char *s)
162 {
163         char *r = s, *w = s;
164 
165         while (*r) {
166                 if (r[0] == '\\' && r[1] >= '' && r[1] <= '7' &&
167                     r[2] >= '' && r[2] <= '7' && r[3] >= '' && r[3] <= '7') {
168                         *w++ = (char)(((r[1] - '') << 6) | ((r[2] - '') << 3) | (r[3] - ''));
169                         r += 4;
170                 } else {
171                         *w++ = *r++;
172                 }
173         }
174         *w = '\0';
175 }
176 
177 static unsigned long mountinfo_current_flags(const char *path)
178 {
179         unsigned long flags = MS_RELATIME;
180         bool found = false;
181         FILE *f;
182         char *line = NULL;
183         size_t linecap = 0;
184 
185         f = fopen("/proc/self/mountinfo", "re");
186         if (!f)
187                 return flags;
188 
189         while (getline(&line, &linecap, f) >= 0) {
190                 char *mp, *opts, *save = NULL, *optsave = NULL;
191                 char *tok;
192                 unsigned long this_flags;
193 
194                 strtok_r(line, " ", &save);
195                 strtok_r(NULL, " ", &save);
196                 strtok_r(NULL, " ", &save);
197                 strtok_r(NULL, " ", &save);
198                 mp = strtok_r(NULL, " ", &save);
199                 opts = strtok_r(NULL, " ", &save);
200                 if (!mp || !opts)
201                         continue;
202                 mountinfo_unescape(mp);
203                 if (strcmp(mp, path))
204                         continue;
205 
206                 this_flags = 0;
207                 for (tok = strtok_r(opts, ",", &optsave); tok;
208                      tok = strtok_r(NULL, ",", &optsave)) {
209                         if (!strcmp(tok, "ro"))
210                                 this_flags |= MS_RDONLY;
211                         else if (!strcmp(tok, "nosuid"))
212                                 this_flags |= MS_NOSUID;
213                         else if (!strcmp(tok, "nodev"))
214                                 this_flags |= MS_NODEV;
215                         else if (!strcmp(tok, "noexec"))
216                                 this_flags |= MS_NOEXEC;
217                         else if (!strcmp(tok, "noatime"))
218                                 this_flags |= MS_NOATIME;
219                         else if (!strcmp(tok, "relatime"))
220                                 this_flags |= MS_RELATIME;
221                         else if (!strcmp(tok, "nodiratime"))
222                                 this_flags |= MS_NODIRATIME;
223                 }
224 
225                 /* last match wins: it's the topmost/effective entry */
226                 flags = this_flags;
227                 found = true;
228         }
229         free(line);
230         fclose(f);
231 
232         if (!found)
233                 return MS_RELATIME;
234 
235         return flags;
236 }
237 
238 static int do_mount(const char *root, const char *orig_source, const char *target, const char *filesystemtype,
239                     unsigned long orig_mountflags, unsigned long propflags, const char *optstr, int error, bool inner)
240 {
241         struct stat s;
242         char new[PATH_MAX];
243         char *source = (char *)orig_source;
244         int fd, ret = 0;
245         bool is_bind = (orig_mountflags & MS_BIND);
246         bool is_mask = (source == (void *)(-1));
247         unsigned long mountflags = orig_mountflags;
248 
249         assert(!(inner && is_mask));
250         assert(!(inner && !orig_source));
251 
252         if (source && is_bind && stat(source, &s)) {
253                 if (error)
254                         ERROR("stat(%s) failed: %m\n", source);
255                 return error;
256         }
257 
258         if (inner)
259                 if (asprintf(&source, "%s%s", root, orig_source) < 0)
260                         return ENOMEM;
261 
262         snprintf(new, sizeof(new), "%s%s", root, target?target:source);
263 
264         if (is_mask) {
265                 if (stat(new, &s))
266                         return 0; /* doesn't exists, nothing to mask */
267 
268                 if (S_ISDIR(s.st_mode)) {/* use empty 0-sized tmpfs for directories */
269                         if (mount("none", new, "tmpfs", MS_RDONLY | MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_RELATIME, "size=0,mode=000"))
270                                 return error;
271                 } else {
272                         /* mount-bind 0-sized file having mode 000 */
273                         if (mount(UJAIL_NOAFILE, new, "bind", MS_BIND, NULL))
274                                 return error;
275 
276                         if (mount(UJAIL_NOAFILE, new, "bind", MS_REMOUNT | MS_BIND | MS_RDONLY | MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_RELATIME, NULL))
277                                 return error;
278                 }
279 
280                 DEBUG("masked path %s\n", new);
281                 return 0;
282         }
283 
284 
285         if (!is_bind || (source && S_ISDIR(s.st_mode))) {
286                 mkdir_p(new, 0755);
287         } else if (is_bind && source) {
288                 mkdir_p(dirname(new), 0755);
289                 snprintf(new, sizeof(new), "%s%s", root, target?target:source);
290                 fd = open(new, O_CREAT|O_WRONLY|O_TRUNC|O_EXCL, 0644);
291                 if (fd >= 0)
292                         close(fd);
293 
294                 if (error && fd < 0 && errno != EEXIST) {
295                         ERROR("failed to create mount target %s: %m\n", new);
296 
297                         ret = errno;
298                         goto free_source_out;
299                 }
300         }
301 
302         if (is_bind) {
303                 if (mount(source?:new, new, filesystemtype?:"bind", MS_BIND | (mountflags & MS_REC), optstr)) {
304                         if (error)
305                                 ERROR("failed to mount -B %s %s: %m\n", source, new);
306 
307                         ret = error;
308                         goto free_source_out;
309                 }
310                 mountflags |= MS_REMOUNT;
311         }
312 
313         const char *hack_fstype = ((!filesystemtype || strcmp(filesystemtype, "cgroup"))?filesystemtype:"cgroup2");
314         if (mount(source?:(is_bind?new:NULL), new, hack_fstype?:"none", mountflags, optstr)) {
315                 int mount_errno = errno;
316 
317                 if ((mountflags & MS_REMOUNT) && mount_errno == EPERM) {
318                         /* Not a heuristic: re-read the kernel-enforced flags from
319                          * mountinfo and only proceed once the security-relevant
320                          * ones (ro/nosuid/nodev/noexec) are confirmed in effect. */
321                         unsigned long retry_flags = mountflags | mountinfo_current_flags(new);
322 
323                         if (retry_flags != mountflags &&
324                             !mount(source?:(is_bind?new:NULL), new, hack_fstype?:"none", retry_flags, optstr))
325                                 goto mount_ok;
326 
327                         unsigned long lockable_flags = MS_RDONLY | MS_NOSUID | MS_NODEV | MS_NOEXEC;
328                         unsigned long wanted = orig_mountflags & lockable_flags;
329                         unsigned long got = mountinfo_current_flags(new) & lockable_flags;
330 
331                         if ((wanted & ~got) == 0) {
332                                 WARNING("remount(%s) to apply flags failed: %s (tolerated - "
333                                 "the flags actually in effect already satisfy the "
334                                 "request; expected under CLONE_NEWUSER for host-owned "
335                                 "bind sources)\n", new, strerror(mount_errno));
336                                 goto mount_ok;
337                         }
338 
339                         if (error) {
340                                 errno = mount_errno;
341                                 ERROR("failed to enforce mount restrictions on %s %s: %m "
342                                       "(missing flags: %s%s%s%s)\n", source, new,
343                                       (wanted & ~got & MS_RDONLY) ? "ro " : "",
344                                       (wanted & ~got & MS_NOSUID) ? "nosuid " : "",
345                                       (wanted & ~got & MS_NODEV) ? "nodev " : "",
346                                       (wanted & ~got & MS_NOEXEC) ? "noexec " : "");
347                                 ret = error;
348                                 goto free_source_out;
349                         }
350 
351                         WARNING("remount(%s) to apply mount restrictions failed and could not "
352                                 "be verified in effect; continuing best-effort since "
353                                 "this mount was not marked as critical\n", new);
354                         goto mount_ok;
355                 }
356 
357                 errno = mount_errno;
358                 if (error)
359                         ERROR("failed to mount %s %s: %m\n", source, new);
360 
361                 ret = error;
362                 goto free_source_out;
363         }
364 
365 mount_ok:
366         DEBUG("mount %s%s %s (%s)\n", (mountflags & MS_BIND)?"-B ":"", source, new,
367               (mountflags & MS_RDONLY)?"ro":"rw");
368 
369         if (propflags && mount("none", new, "none", propflags, NULL)) {
370                 if (error)
371                         ERROR("failed to mount --make-... %s \n", new);
372 
373                 ret = error;
374         }
375 
376 free_source_out:
377         if (inner)
378                 free(source);
379 
380         return ret;
381 }
382 
383 static int _add_mount(const char *source, const char *target, const char *filesystemtype,
384                       unsigned long mountflags, unsigned long propflags, const char *optstr,
385                       int error, bool inner)
386 {
387         assert(target != NULL);
388 
389         if (avl_find(&mounts, target))
390                 return 1;
391 
392         struct mount *m;
393         m = calloc(1, sizeof(struct mount));
394         if (!m)
395                 return ENOMEM;
396 
397         m->avl.key = m->target = strdup(target);
398         if (source) {
399                 if (source != (void*)(-1))
400                         m->source = strdup(source);
401                 else
402                         m->source = (void*)(-1);
403         }
404         if (filesystemtype)
405                 m->filesystemtype = strdup(filesystemtype);
406 
407         if (optstr)
408                 m->optstr = strdup(optstr);
409 
410         m->mountflags = mountflags;
411         m->propflags = propflags;
412         m->error = error;
413         m->inner = inner;
414         m->source_fd = -1;
415 
416         avl_insert(&mounts, &m->avl);
417         DEBUG("adding mount %s %s bind(%d) ro(%d) err(%d)\n", (m->source == (void*)(-1))?"mask":m->source, m->target,
418                 !!(m->mountflags & MS_BIND), !!(m->mountflags & MS_RDONLY), m->error != 0);
419 
420         return 0;
421 }
422 
423 int add_mount(const char *source, const char *target, const char *filesystemtype,
424               unsigned long mountflags, unsigned long propflags, const char *optstr, int error)
425 {
426         return _add_mount(source, target, filesystemtype, mountflags, propflags, optstr, error, false);
427 }
428 
429 int add_mount_inner(const char *source, const char *target, const char *filesystemtype,
430               unsigned long mountflags, unsigned long propflags, const char *optstr, int error)
431 {
432         return _add_mount(source, target, filesystemtype, mountflags, propflags, optstr, error, true);
433 }
434 
435 static int _add_mount_bind(const char *path, const char *path2, int readonly, int error)
436 {
437         unsigned long mountflags = MS_BIND;
438 
439         if (readonly)
440                 mountflags |= MS_RDONLY;
441 
442         return add_mount(path, path2, NULL, mountflags, 0, NULL, error);
443 }
444 
445 int add_mount_bind(const char *path, int readonly, int error)
446 {
447         return _add_mount_bind(path, path, readonly, error);
448 }
449 
450 int add_mount_fd(int fd, const char *target, int error)
451 {
452         struct mount *m;
453 
454         if (avl_find(&mounts, target))
455                 return 1;
456 
457         m = calloc(1, sizeof(struct mount));
458         if (!m)
459                 return ENOMEM;
460 
461         m->avl.key = m->target = strdup(target);
462         m->mountflags = MS_BIND;
463         m->error = error;
464         m->source_fd = fd;
465 
466         avl_insert(&mounts, &m->avl);
467         DEBUG("adding mount fd:%d %s bind(1) ro(?) err(%d)\n", fd, target, error != 0);
468 
469         return 0;
470 }
471 
472 enum {
473         OCI_MOUNT_SOURCE,
474         OCI_MOUNT_DESTINATION,
475         OCI_MOUNT_TYPE,
476         OCI_MOUNT_OPTIONS,
477         __OCI_MOUNT_MAX,
478 };
479 
480 static const struct blobmsg_policy oci_mount_policy[] = {
481         [OCI_MOUNT_SOURCE] = { "source", BLOBMSG_TYPE_STRING },
482         [OCI_MOUNT_DESTINATION] = { "destination", BLOBMSG_TYPE_STRING },
483         [OCI_MOUNT_TYPE] = { "type", BLOBMSG_TYPE_STRING },
484         [OCI_MOUNT_OPTIONS] = { "options", BLOBMSG_TYPE_ARRAY },
485 };
486 
487 struct mount_opt {
488         struct list_head list;
489         char *optstr;
490 };
491 
492 #ifndef MS_LAZYTIME
493 #define MS_LAZYTIME (1 << 25)
494 #endif
495 
496 static int parseOCImountopts(struct blob_attr *msg, unsigned long *mount_flags, unsigned long *propagation_flags, char **mount_data, int *error)
497 {
498         struct blob_attr *cur;
499         int rem;
500         unsigned long mf = 0;
501         unsigned long pf = 0;
502         char *tmp;
503         struct list_head fsopts = LIST_HEAD_INIT(fsopts);
504         size_t len = 0;
505         struct mount_opt *opt, *tmpopt;
506 
507         blobmsg_for_each_attr(cur, msg, rem) {
508                 tmp = blobmsg_get_string(cur);
509                 if (!strcmp("ro", tmp))
510                         mf |= MS_RDONLY;
511                 else if (!strcmp("rw", tmp))
512                         mf &= ~MS_RDONLY;
513                 else if (!strcmp("bind", tmp))
514                         mf = MS_BIND;
515                 else if (!strcmp("rbind", tmp))
516                         mf |= MS_BIND | MS_REC;
517                 else if (!strcmp("sync", tmp))
518                         mf |= MS_SYNCHRONOUS;
519                 else if (!strcmp("async", tmp))
520                         mf &= ~MS_SYNCHRONOUS;
521                 else if (!strcmp("atime", tmp))
522                         mf &= ~MS_NOATIME;
523                 else if (!strcmp("noatime", tmp))
524                         mf |= MS_NOATIME;
525                 else if (!strcmp("defaults", tmp))
526                         mf = 0; /* rw, suid, dev, exec, auto, nouser, and async */
527                 else if (!strcmp("dev", tmp))
528                         mf &= ~MS_NODEV;
529                 else if (!strcmp("nodev", tmp))
530                         mf |= MS_NODEV;
531                 else if (!strcmp("iversion", tmp))
532                         mf |= MS_I_VERSION;
533                 else if (!strcmp("noiversion", tmp))
534                         mf &= ~MS_I_VERSION;
535                 else if (!strcmp("diratime", tmp))
536                         mf &= ~MS_NODIRATIME;
537                 else if (!strcmp("nodiratime", tmp))
538                         mf |= MS_NODIRATIME;
539                 else if (!strcmp("dirsync", tmp))
540                         mf |= MS_DIRSYNC;
541                 else if (!strcmp("exec", tmp))
542                         mf &= ~MS_NOEXEC;
543                 else if (!strcmp("noexec", tmp))
544                         mf |= MS_NOEXEC;
545                 else if (!strcmp("mand", tmp))
546                         mf |= MS_MANDLOCK;
547                 else if (!strcmp("nomand", tmp))
548                         mf &= ~MS_MANDLOCK;
549                 else if (!strcmp("relatime", tmp))
550                         mf |= MS_RELATIME;
551                 else if (!strcmp("norelatime", tmp))
552                         mf &= ~MS_RELATIME;
553                 else if (!strcmp("strictatime", tmp))
554                         mf |= MS_STRICTATIME;
555                 else if (!strcmp("nostrictatime", tmp))
556                         mf &= ~MS_STRICTATIME;
557                 else if (!strcmp("lazytime", tmp))
558                         mf |= MS_LAZYTIME;
559                 else if (!strcmp("nolazytime", tmp))
560                         mf &= ~MS_LAZYTIME;
561                 else if (!strcmp("suid", tmp))
562                         mf &= ~MS_NOSUID;
563                 else if (!strcmp("nosuid", tmp))
564                         mf |= MS_NOSUID;
565                 else if (!strcmp("remount", tmp))
566                         mf |= MS_REMOUNT;
567                 /* propagation flags */
568                 else if (!strcmp("private", tmp))
569                         pf |= MS_PRIVATE;
570                 else if (!strcmp("rprivate", tmp))
571                         pf |= MS_PRIVATE | MS_REC;
572                 else if (!strcmp("slave", tmp))
573                         pf |= MS_SLAVE;
574                 else if (!strcmp("rslave", tmp))
575                         pf |= MS_SLAVE | MS_REC;
576                 else if (!strcmp("shared", tmp))
577                         pf |= MS_SHARED;
578                 else if (!strcmp("rshared", tmp))
579                         pf |= MS_SHARED | MS_REC;
580                 else if (!strcmp("unbindable", tmp))
581                         pf |= MS_UNBINDABLE;
582                 else if (!strcmp("runbindable", tmp))
583                         pf |= MS_UNBINDABLE | MS_REC;
584                 /* special case: 'nofail' */
585                 else if(!strcmp("nofail", tmp))
586                         *error = 0;
587                 else if (!strcmp("auto", tmp) ||
588                          !strcmp("noauto", tmp) ||
589                          !strcmp("user", tmp) ||
590                          !strcmp("group", tmp) ||
591                          !strcmp("_netdev", tmp))
592                         DEBUG("ignoring built-in mount option %s\n", tmp);
593                 else {
594                         /* filesystem-specific free-form option */
595                         opt = calloc(1, sizeof(*opt));
596                         opt->optstr = tmp;
597                         list_add_tail(&opt->list, &fsopts);
598                 }
599         };
600 
601         *mount_flags = mf;
602         *propagation_flags = pf;
603 
604         list_for_each_entry(opt, &fsopts, list) {
605                 if (len)
606                         ++len;
607 
608                 len += strlen(opt->optstr);
609         };
610 
611         if (len) {
612                 *mount_data = calloc(len + 1, sizeof(char));
613                 if (!(*mount_data))
614                         return ENOMEM;
615 
616                 len = 0;
617                 list_for_each_entry(opt, &fsopts, list) {
618                         if (len)
619                                 strcat(*mount_data, ",");
620 
621                         strcat(*mount_data, opt->optstr);
622                         ++len;
623                 }
624 
625                 list_for_each_entry_safe(opt, tmpopt, &fsopts, list) {
626                         list_del(&opt->list);
627                         free(opt);
628                 }
629         }
630 
631         DEBUG("mount flags(%08lx) propagation(%08lx) fsopts(\"%s\")\n", mf, pf, *mount_data?:"");
632 
633         return 0;
634 }
635 
636 static bool is_proc_or_sys_path(const char *path)
637 {
638         if (!strcmp(path, "/proc") || !strcmp(path, "/sys"))
639                 return true;
640 
641         if (!strncmp(path, "/proc/", 6))
642                 return true;
643 
644         if (!strncmp(path, "/sys/", 5))
645                 return true;
646 
647         return false;
648 }
649 
650 int parseOCImount(struct blob_attr *msg)
651 {
652         struct blob_attr *tb[__OCI_MOUNT_MAX];
653         unsigned long mount_flags = 0;
654         unsigned long propagation_flags = 0;
655         char *mount_data = NULL;
656         int ret, err = -1;
657 
658         blobmsg_parse(oci_mount_policy, __OCI_MOUNT_MAX, tb, blobmsg_data(msg), blobmsg_len(msg));
659 
660         if (!tb[OCI_MOUNT_DESTINATION])
661                 return EINVAL;
662 
663         if (tb[OCI_MOUNT_OPTIONS]) {
664                 ret = parseOCImountopts(tb[OCI_MOUNT_OPTIONS], &mount_flags, &propagation_flags, &mount_data, &err);
665                 if (ret)
666                         return ret;
667         }
668 
669         if (is_proc_or_sys_path(blobmsg_get_string(tb[OCI_MOUNT_DESTINATION])) &&
670             ((mount_flags & MS_BIND) ||
671              (tb[OCI_MOUNT_TYPE] && !strcmp(blobmsg_get_string(tb[OCI_MOUNT_TYPE]), "bind"))) &&
672             !(mount_flags & MS_RDONLY)) {
673                 ERROR("OCI mount config requests a writable bind mount onto %s; "
674                       "refusing to allow write access to /proc or /sys\n",
675                       blobmsg_get_string(tb[OCI_MOUNT_DESTINATION]));
676                 if (mount_data)
677                         free(mount_data);
678                 return EPERM;
679         }
680 
681         ret = add_mount(tb[OCI_MOUNT_SOURCE] ? blobmsg_get_string(tb[OCI_MOUNT_SOURCE]) : NULL,
682                   blobmsg_get_string(tb[OCI_MOUNT_DESTINATION]),
683                   tb[OCI_MOUNT_TYPE] ? blobmsg_get_string(tb[OCI_MOUNT_TYPE]) : NULL,
684                   mount_flags, propagation_flags, mount_data, err);
685 
686         if (mount_data)
687                 free(mount_data);
688 
689         return ret;
690 }
691 
692 static void build_noafile(void) {
693         int fd;
694 
695         fd = creat(UJAIL_NOAFILE, 0000);
696         if (fd < 0)
697                 return;
698 
699         close(fd);
700         return;
701 }
702 
703 static int do_mount_fd(const char *root, int fd, const char *target, int error)
704 {
705         char new[PATH_MAX];
706         struct stat s;
707 
708         snprintf(new, sizeof(new), "%s%s", root, target);
709 
710         if (fstat(fd, &s)) {
711                 if (error)
712                         ERROR("fstat(fd:%d) failed: %m\n", fd);
713                 close(fd);
714                 return error;
715         }
716 
717         if (S_ISDIR(s.st_mode)) {
718                 mkdir_p(new, 0755);
719         } else {
720                 mkdir_p(dirname(new), 0755);
721                 snprintf(new, sizeof(new), "%s%s", root, target);
722                 int cfd = open(new, O_CREAT|O_WRONLY|O_TRUNC|O_EXCL, 0644);
723                 if (cfd >= 0)
724                         close(cfd);
725                 if (error && cfd < 0 && errno != EEXIST) {
726                         ERROR("failed to create mount target %s: %m\n", new);
727                         close(fd);
728                         return errno;
729                 }
730         }
731 
732         if (sys_move_mount(fd, "", AT_FDCWD, new, MOVE_MOUNT_F_EMPTY_PATH)) {
733                 if (error)
734                         ERROR("move_mount() to %s failed: %m\n", new);
735                 close(fd);
736                 return error;
737         }
738 
739         close(fd);
740         DEBUG("move_mount fd to %s\n", new);
741         return 0;
742 }
743 
744 int mount_all(const char *jailroot) {
745         struct library *l;
746         struct mount *m;
747 
748         build_noafile();
749 
750         avl_for_each_element(&libraries, l, avl)
751                 add_mount_bind(l->path, 1, -1);
752 
753         avl_for_each_element(&mounts, m, avl) {
754                 if (m->source_fd >= 0) {
755                         if (do_mount_fd(jailroot, m->source_fd, m->target, m->error))
756                                 return -1;
757                         continue;
758                 }
759                 if (do_mount(jailroot, m->source, m->target, m->filesystemtype, m->mountflags,
760                              m->propflags, m->optstr, m->error, m->inner))
761                         return -1;
762         }
763 
764         return 0;
765 }
766 
767 void mount_free(void) {
768         struct mount *m, *tmp;
769 
770         avl_remove_all_elements(&mounts, m, avl, tmp) {
771                 if (m->source != (void*)(-1))
772                         free((void*)m->source);
773                 free((void*)m->target);
774                 free((void*)m->filesystemtype);
775                 free((void*)m->optstr);
776                 free(m);
777         }
778 }
779 
780 void mount_list_init(void) {
781         avl_init(&mounts, avl_strcmp, false, NULL);
782 }
783 
784 static int add_script_interp(const char *path, const char *map, int size)
785 {
786         int start = 2;
787         while (start < size && map[start] != '/') {
788                 start++;
789         }
790         if (start >= size) {
791                 ERROR("bad script interp (%s)\n", path);
792                 return -1;
793         }
794         int stop = start + 1;
795         while (stop < size && map[stop] > 0x20 && map[stop] <= 0x7e) {
796                 stop++;
797         }
798         if (stop >= size || (stop-start) > PATH_MAX) {
799                 ERROR("bad script interp (%s)\n", path);
800                 return -1;
801         }
802         char buf[PATH_MAX];
803         strncpy(buf, map+start, stop-start);
804         return add_path_and_deps(buf, 1, -1, 0);
805 }
806 
807 int add_2paths_and_deps(const char *path, const char *path2, int readonly, int error, int lib)
808 {
809         assert(path != NULL);
810         assert(path2 != NULL);
811 
812         if (lib == 0 && path[0] != '/') {
813                 ERROR("%s is not an absolute path\n", path);
814                 return error;
815         }
816 
817         char *map = NULL;
818         char *fullpath = NULL;
819         int fd, ret = -1;
820         if (path[0] == '/') {
821                 if (avl_find(&mounts, path2))
822                         return 0;
823                 fd = open(path, O_RDONLY|O_CLOEXEC);
824                 if (fd < 0)
825                         return error;
826                 _add_mount_bind(path, path2, readonly, error);
827         } else {
828                 if (avl_find(&libraries, path))
829                         return 0;
830                 fd = lib_open(&fullpath, path);
831                 if (fd < 0)
832                         return error;
833                 if (fullpath)
834                         alloc_library(fullpath, path);
835         }
836 
837         struct stat s;
838         if (fstat(fd, &s) == -1) {
839                 ERROR("fstat(%s) failed: %m\n", path);
840                 ret = error;
841                 goto out;
842         }
843 
844         if (!S_ISREG(s.st_mode)) {
845                 ret = 0;
846                 goto out;
847         }
848 
849         /* too small to be an ELF or a script -> "normal" file */
850         if (s.st_size < 4) {
851                 ret = 0;
852                 goto out;
853         }
854 
855         map = mmap(NULL, s.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
856         if (map == MAP_FAILED) {
857                 ERROR("failed to mmap %s: %m\n", path);
858                 ret = -1;
859                 goto out;
860         }
861 
862         if (map[0] == '#' && map[1] == '!') {
863                 ret = add_script_interp(path, map, s.st_size);
864                 goto out;
865         }
866 
867         if (map[0] == ELFMAG0 && map[1] == ELFMAG1 && map[2] == ELFMAG2 && map[3] == ELFMAG3) {
868                 /* Pass the resolved fullpath, not the bare soname, so
869                  * elf_load_deps() can expand a $ORIGIN-relative DT_RPATH/
870                  * DT_RUNPATH in this object against its real directory. */
871                 ret = elf_load_deps(fullpath ? fullpath : path, map, s.st_size);
872                 goto out;
873         }
874 
875         ret = 0;
876 
877 out:
878         if (fd >= 0)
879                 close(fd);
880         if (map)
881                 munmap(map, s.st_size);
882         free(fullpath);
883 
884         return ret;
885 }
886 

This page was automatically generated by LXR 0.3.1.  •  OpenWrt