• 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 <linux/mount.h>
 25 #include <sched.h>
 26 #include <signal.h>
 27 #include <stdlib.h>
 28 #include <stdio.h>
 29 #include <string.h>
 30 #include <sys/socket.h>
 31 #include <sys/stat.h>
 32 #include <sys/mman.h>
 33 #include <sys/syscall.h>
 34 #include <sys/wait.h>
 35 #include <unistd.h>
 36 #include <libgen.h>
 37 #include <dirent.h>
 38 
 39 #include <libubox/avl.h>
 40 #include <libubox/avl-cmp.h>
 41 #include <libubox/blobmsg.h>
 42 #include <libubox/list.h>
 43 #include <libubox/utils.h>
 44 
 45 #include "elf.h"
 46 #include "fs.h"
 47 #include "jail.h"
 48 #include "log.h"
 49 
 50 #define UJAIL_NOAFILE "/tmp/.ujailnoafile"
 51 
 52 /*
 53  * mnt_already_visible() requires a new mount's atime class to match
 54  * the host's existing instance or the kernel refuses it with EPERM
 55  * ("Mount too revealing"). Detect the mountpoint's actual atime class
 56  * instead of hardcoding one, so the new mount can never conflict.
 57  */
 58 unsigned long detect_atime_flag(const char *mountpoint)
 59 {
 60         FILE *f;
 61         char *line = NULL;
 62         size_t linelen = 0;
 63         unsigned long ret = MS_RELATIME; /* kernel default if nothing else is known */
 64         size_t mplen = strlen(mountpoint);
 65 
 66         f = fopen("/proc/self/mountinfo", "r");
 67         if (!f)
 68                 return ret;
 69 
 70         while (getline(&line, &linelen, f) != -1) {
 71                 /* mountinfo(5): field 5 is the mountpoint, field 6 its options */
 72                 char *saveptr = NULL;
 73                 char *field;
 74                 int idx = 0;
 75                 char *mp_field = NULL, *opts_field = NULL;
 76 
 77                 for (field = strtok_r(line, " \t\n", &saveptr); field;
 78                      field = strtok_r(NULL, " \t\n", &saveptr), idx++) {
 79                         if (idx == 4)
 80                                 mp_field = field;
 81                         else if (idx == 5) {
 82                                 opts_field = field;
 83                                 break;
 84                         }
 85                 }
 86 
 87                 if (!mp_field || !opts_field)
 88                         continue;
 89 
 90                 if (strlen(mp_field) != mplen || strcmp(mp_field, mountpoint))
 91                         continue;
 92 
 93                 /* last matching entry wins: it's the topmost/currently-effective one */
 94                 if (strstr(opts_field, "noatime"))
 95                         ret = MS_NOATIME;
 96                 else if (strstr(opts_field, "relatime"))
 97                         ret = MS_RELATIME;
 98                 else
 99                         /* strictatime is the absence of noatime/relatime, not a token */
100                         ret = MS_STRICTATIME;
101         }
102 
103         free(line);
104         fclose(f);
105 
106         return ret;
107 }
108 
109 #ifndef MOUNT_ATTR_IDMAP
110 #define MOUNT_ATTR_IDMAP 0x00100000
111 #endif
112 
113 #ifndef MOUNT_ATTR__ATIME
114 #define MOUNT_ATTR__ATIME       0x00000070
115 #endif
116 #ifndef MOUNT_ATTR_RELATIME
117 #define MOUNT_ATTR_RELATIME     0x00000000
118 #endif
119 #ifndef MOUNT_ATTR_NOATIME
120 #define MOUNT_ATTR_NOATIME      0x00000010
121 #endif
122 #ifndef MOUNT_ATTR_STRICTATIME
123 #define MOUNT_ATTR_STRICTATIME  0x00000020
124 #endif
125 #ifndef MOUNT_ATTR_NODIRATIME
126 #define MOUNT_ATTR_NODIRATIME   0x00000080
127 #endif
128 
129 /* MS_* -> MOUNT_ATTR_* for fsmount()/mount_setattr() */
130 static unsigned mountflags_to_attr(unsigned long mountflags)
131 {
132         unsigned attr = 0;
133 
134         if (mountflags & MS_RDONLY)
135                 attr |= MOUNT_ATTR_RDONLY;
136         if (mountflags & MS_NOSUID)
137                 attr |= MOUNT_ATTR_NOSUID;
138         if (mountflags & MS_NODEV)
139                 attr |= MOUNT_ATTR_NODEV;
140         if (mountflags & MS_NOEXEC)
141                 attr |= MOUNT_ATTR_NOEXEC;
142         if (mountflags & MS_NODIRATIME)
143                 attr |= MOUNT_ATTR_NODIRATIME;
144         if (mountflags & MS_NOATIME)
145                 attr |= MOUNT_ATTR_NOATIME;
146         else if (mountflags & MS_STRICTATIME)
147                 attr |= MOUNT_ATTR_STRICTATIME;
148         else
149                 attr |= MOUNT_ATTR_RELATIME;
150 
151         return attr;
152 }
153 
154 int sys_openat2(int dfd, const char *path, struct open_how *how, size_t size)
155 {
156         return syscall(SYS_openat2, dfd, path, how, size);
157 }
158 
159 static int jailroot_dirfd = -1;
160 
161 static unsigned int idmap_host_offset;
162 
163 void jail_set_idmap_offset(unsigned int offset)
164 {
165         idmap_host_offset = offset;
166 }
167 
168 static int write_mappings_file(pid_t pid, const char *which, struct blob_attr *mappings)
169 {
170         enum {
171                 OCI_LINUX_UIDGIDMAP_CONTAINERID,
172                 OCI_LINUX_UIDGIDMAP_HOSTID,
173                 OCI_LINUX_UIDGIDMAP_SIZE,
174                 __OCI_LINUX_UIDGIDMAP_MAX,
175         };
176         static const struct blobmsg_policy policy[] = {
177                 [OCI_LINUX_UIDGIDMAP_CONTAINERID] = { "containerID", BLOBMSG_TYPE_INT32 },
178                 [OCI_LINUX_UIDGIDMAP_HOSTID] = { "hostID", BLOBMSG_TYPE_INT32 },
179                 [OCI_LINUX_UIDGIDMAP_SIZE] = { "size", BLOBMSG_TYPE_INT32 },
180         };
181         struct blob_attr *tb[__OCI_LINUX_UIDGIDMAP_MAX];
182         struct blob_attr *cur;
183         char path[64];
184         char *buf = NULL;
185         size_t buflen = 0;
186         FILE *mem;
187         ssize_t w;
188         int rem, fd, ret = 0, saved_err;
189 
190         mem = open_memstream(&buf, &buflen);
191         if (!mem)
192                 return errno;
193 
194         blobmsg_for_each_attr(cur, mappings, rem) {
195                 blobmsg_parse(policy, __OCI_LINUX_UIDGIDMAP_MAX, tb,
196                               blobmsg_data(cur), blobmsg_len(cur));
197                 if (!tb[OCI_LINUX_UIDGIDMAP_CONTAINERID] ||
198                     !tb[OCI_LINUX_UIDGIDMAP_HOSTID] ||
199                     !tb[OCI_LINUX_UIDGIDMAP_SIZE]) {
200                         fclose(mem);
201                         free(buf);
202                         return EINVAL;
203                 }
204                 fprintf(mem, "%u %u %u\n",
205                         blobmsg_get_u32(tb[OCI_LINUX_UIDGIDMAP_CONTAINERID]),
206                         blobmsg_get_u32(tb[OCI_LINUX_UIDGIDMAP_HOSTID]) + idmap_host_offset,
207                         blobmsg_get_u32(tb[OCI_LINUX_UIDGIDMAP_SIZE]));
208         }
209         fclose(mem);
210 
211         if (!buflen) {
212                 free(buf);
213                 return 0;
214         }
215 
216         snprintf(path, sizeof(path), "/proc/%d/%s", pid, which);
217         fd = open(path, O_WRONLY | O_CLOEXEC);
218         if (fd < 0) {
219                 ret = errno;
220                 free(buf);
221                 return ret;
222         }
223 
224         w = write(fd, buf, buflen);
225         if (w < 0)
226                 ret = errno;
227         else if ((size_t)w != buflen)
228                 ret = EIO;
229 
230         saved_err = ret;
231         close(fd);
232         free(buf);
233         return saved_err;
234 }
235 
236 int build_userns_fd(struct blob_attr *uidmappings, struct blob_attr *gidmappings)
237 {
238         int sync[2];
239         pid_t pid;
240         char path[64];
241         char buf;
242         int fd = -1;
243         int ret, saved_err = 0;
244 
245         if (socketpair(AF_UNIX, SOCK_STREAM, 0, sync) < 0)
246                 return -errno;
247 
248         pid = fork();
249         if (pid < 0) {
250                 ret = -errno;
251                 close(sync[0]);
252                 close(sync[1]);
253                 return ret;
254         }
255 
256         if (pid == 0) {
257                 close(sync[0]);
258                 if (unshare(CLONE_NEWUSER) < 0)
259                         _exit(EXIT_FAILURE);
260                 if (send(sync[1], "R", 1, MSG_NOSIGNAL) != 1)
261                         _exit(EXIT_FAILURE);
262                 if (read(sync[1], &buf, 1) < 0) {
263                 }
264                 _exit(EXIT_SUCCESS);
265         }
266 
267         close(sync[1]);
268         if (read(sync[0], &buf, 1) != 1 || buf != 'R') {
269                 saved_err = EIO;
270                 goto out;
271         }
272 
273         if (uidmappings && (ret = write_mappings_file(pid, "uid_map", uidmappings))) {
274                 saved_err = ret;
275                 goto out;
276         }
277         if (gidmappings) {
278                 int gfd;
279                 snprintf(path, sizeof(path), "/proc/%d/setgroups", pid);
280                 gfd = open(path, O_WRONLY | O_CLOEXEC);
281                 if (gfd >= 0) {
282                         (void)!write(gfd, "deny", 4);
283                         close(gfd);
284                 }
285                 if ((ret = write_mappings_file(pid, "gid_map", gidmappings))) {
286                         saved_err = ret;
287                         goto out;
288                 }
289         }
290 
291         snprintf(path, sizeof(path), "/proc/%d/ns/user", pid);
292         fd = open(path, O_RDONLY | O_CLOEXEC);
293         if (fd < 0)
294                 saved_err = errno;
295 
296 out:
297         (void)send(sync[0], "X", 1, MSG_NOSIGNAL);
298         close(sync[0]);
299         waitpid(pid, NULL, 0);
300         if (fd < 0) {
301                 errno = saved_err ? saved_err : EIO;
302                 return -errno;
303         }
304         return fd;
305 }
306 
307 struct mount {
308         struct avl_node avl;
309         struct list_head list;
310         const char *source;
311         const char *target;
312         const char *filesystemtype;
313         unsigned long mountflags;
314         unsigned long propflags;
315         const char *optstr;
316         int error;
317         bool inner;
318         int source_fd;
319         bool idmap;
320         bool idmap_recursive;
321         bool volume;
322         bool mounted;
323         int idmap_treefd;
324         struct blob_attr *uidmappings;
325         struct blob_attr *gidmappings;
326 };
327 
328 /* open_tree()/move_mount()/mount_setattr() have no glibc wrappers yet;
329  * struct ujail_mount_attr is declared in fs.h */
330 int sys_open_tree(int dfd, const char *path, unsigned flags)
331 {
332         return syscall(SYS_open_tree, dfd, path, flags);
333 }
334 
335 int sys_move_mount(int from_dfd, const char *from_path, int to_dfd, const char *to_path, unsigned flags)
336 {
337         return syscall(SYS_move_mount, from_dfd, from_path, to_dfd, to_path, flags);
338 }
339 
340 int sys_fsopen(const char *fsname, unsigned flags)
341 {
342         return syscall(SYS_fsopen, fsname, flags);
343 }
344 
345 int sys_fsconfig(int fd, unsigned cmd, const char *key, const void *value, int aux)
346 {
347         return syscall(SYS_fsconfig, fd, cmd, key, value, aux);
348 }
349 
350 int sys_fsmount(int fd, unsigned flags, unsigned attr_flags)
351 {
352         return syscall(SYS_fsmount, fd, flags, attr_flags);
353 }
354 
355 int sys_mount_setattr(int dfd, const char *path, unsigned flags, struct ujail_mount_attr *attr, size_t size)
356 {
357         return syscall(SYS_mount_setattr, dfd, path, flags, attr, size);
358 }
359 
360 struct avl_tree mounts;
361 static LIST_HEAD(mounts_order);
362 
363 /* same masking as do_mount()'s is_mask branch, applied immediately
364  * against an absolute path instead of queued through jail_root.
365  *
366  * UJAIL_NOAFILE lives under the pre-pivot root, gone by the time this
367  * runs; JAIL_NOAFILE, a locked bind of procd's read-only noafile
368  * queued for deferred-userns jails, is used instead. */
369 int mask_path_now(const char *path)
370 {
371         struct stat s;
372 
373         if (stat(path, &s))
374                 return 0; /* doesn't exist, nothing to mask */
375 
376         if (S_ISDIR(s.st_mode)) {
377                 if (mount("none", path, "tmpfs", MS_RDONLY | MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_RELATIME, "size=0,mode=000"))
378                         return -1;
379         } else {
380                 if (mount(JAIL_NOAFILE, path, "bind", MS_BIND, NULL))
381                         return -1;
382                 if (remount_readonly(path, MS_NOSUID | MS_NOEXEC | MS_NODEV))
383                         return -1;
384         }
385 
386         DEBUG("masked path %s\n", path);
387         return 0;
388 }
389 
390 static void mountinfo_unescape(char *s)
391 {
392         char *r = s, *w = s;
393 
394         while (*r) {
395                 if (r[0] == '\\' && r[1] >= '' && r[1] <= '7' &&
396                     r[2] >= '' && r[2] <= '7' && r[3] >= '' && r[3] <= '7') {
397                         *w++ = (char)(((r[1] - '') << 6) | ((r[2] - '') << 3) | (r[3] - ''));
398                         r += 4;
399                 } else {
400                         *w++ = *r++;
401                 }
402         }
403         *w = '\0';
404 }
405 
406 /*
407  * The flags a locked mount will not let a remount clear. The atime class is
408  * deliberately absent: path_mount() preserves it when the remount names none.
409  */
410 static unsigned long mountinfo_current_flags(const char *path)
411 {
412         unsigned long flags = 0;
413         bool found = false;
414         FILE *f;
415         char *line = NULL;
416         size_t linecap = 0;
417 
418         f = fopen("/proc/self/mountinfo", "re");
419         if (!f)
420                 return flags;
421 
422         while (getline(&line, &linecap, f) >= 0) {
423                 char *mp, *opts, *save = NULL, *optsave = NULL;
424                 char *tok;
425                 unsigned long this_flags;
426 
427                 strtok_r(line, " ", &save);
428                 strtok_r(NULL, " ", &save);
429                 strtok_r(NULL, " ", &save);
430                 strtok_r(NULL, " ", &save);
431                 mp = strtok_r(NULL, " ", &save);
432                 opts = strtok_r(NULL, " ", &save);
433                 if (!mp || !opts)
434                         continue;
435                 mountinfo_unescape(mp);
436                 if (strcmp(mp, path))
437                         continue;
438 
439                 this_flags = 0;
440                 for (tok = strtok_r(opts, ",", &optsave); tok;
441                      tok = strtok_r(NULL, ",", &optsave)) {
442                         if (!strcmp(tok, "ro"))
443                                 this_flags |= MS_RDONLY;
444                         else if (!strcmp(tok, "nosuid"))
445                                 this_flags |= MS_NOSUID;
446                         else if (!strcmp(tok, "nodev"))
447                                 this_flags |= MS_NODEV;
448                         else if (!strcmp(tok, "noexec"))
449                                 this_flags |= MS_NOEXEC;
450                 }
451 
452                 /* last match wins: it's the topmost/effective entry */
453                 flags = this_flags;
454                 found = true;
455         }
456         free(line);
457         fclose(f);
458 
459         if (!found)
460                 return 0;
461 
462         return flags;
463 }
464 
465 /*
466  * Self-bind @path and remount it read-only, preserving the flags already in
467  * effect. Mounts copied in by unshare(CLONE_NEWNS) under a userns that does
468  * not own them are MNT_LOCK_{NOSUID,NODEV,NOEXEC,ATIME}; a remount clearing
469  * any of those fails with EPERM.
470  */
471 int remount_readonly(const char *path, unsigned long flags)
472 {
473         flags |= MS_REMOUNT | MS_BIND | MS_RDONLY | mountinfo_current_flags(path);
474 
475         return mount(NULL, path, NULL, flags, NULL);
476 }
477 
478 int bind_remount_readonly(const char *path, unsigned long flags)
479 {
480         if (mount(path, path, "bind", MS_BIND | (flags & MS_REC), NULL))
481                 return -1;
482 
483         return remount_readonly(path, flags);
484 }
485 
486 static bool fs_userns;
487 
488 void jail_fs_set_userns(bool enabled)
489 {
490         fs_userns = enabled;
491 }
492 
493 static bool mount_opts_has(const char *opts, const char *needle);
494 static bool mount_opts_gid_unmapped(const char *opts);
495 static void mount_opts_drop(const char *opts, const char *key, char *buf, size_t len);
496 
497 static int do_mount(const char *root, const char *orig_source, const char *target, const char *filesystemtype,
498                     unsigned long orig_mountflags, unsigned long propflags, const char *optstr, int error, bool inner,
499                     int source_fd)
500 {
501         struct stat s;
502         char new[PATH_MAX];
503         char tmpfs_data[512];
504         char devpts_data[512];
505         const char *mount_data;
506         char *source = (char *)orig_source;
507         int fd, err, ret = 0;
508         bool is_bind = (orig_mountflags & MS_BIND);
509         bool is_mask = (source == (void *)(-1));
510         bool use_fd = false;
511         unsigned long mountflags = orig_mountflags;
512 
513         assert(!(inner && is_mask));
514         assert(!(inner && !orig_source));
515 
516         if (source && is_bind && stat(source, &s)) {
517                 if (source_fd >= 0 && !fstatat(source_fd, "", &s, AT_EMPTY_PATH)) {
518                         use_fd = true;
519                 } else {
520                         if (error)
521                                 ERROR("stat(%s) failed: %m\n", source);
522                         return error;
523                 }
524         }
525 
526         if (inner)
527                 if (asprintf(&source, "%s%s", root, orig_source) < 0)
528                         return ENOMEM;
529 
530         snprintf(new, sizeof(new), "%s%s", root, target?target:source);
531 
532         if (is_mask) {
533                 if (stat(new, &s))
534                         return 0; /* doesn't exists, nothing to mask */
535 
536                 if (S_ISDIR(s.st_mode)) {/* use empty 0-sized tmpfs for directories */
537                         err = mount("none", new, "tmpfs", MS_RDONLY | MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_RELATIME, "size=0,mode=000");
538                 } else {
539                         /* mount-bind 0-sized file having mode 000 */
540                         err = mount(UJAIL_NOAFILE, new, "bind", MS_BIND, NULL);
541                         if (!err)
542                                 err = remount_readonly(new, MS_NOSUID | MS_NOEXEC | MS_NODEV);
543                 }
544 
545                 if (err) {
546                         if (error)
547                                 ERROR("failed to mask %s: %m\n", new);
548                         else
549                                 WARNING("could not mask optional path %s: %m\n",
550                                         new);
551                         return error;
552                 }
553 
554                 DEBUG("masked path %s\n", new);
555                 return 0;
556         }
557 
558 
559         if (!is_bind || (source && S_ISDIR(s.st_mode))) {
560                 mkdir_p(new, 0755);
561         } else if (is_bind && source) {
562                 const char *target_rel = target ? target : source;
563                 struct open_how how = {
564                         .flags = O_CREAT | O_WRONLY | O_TRUNC | O_EXCL | O_CLOEXEC,
565                         .mode = 0644,
566                         .resolve = RESOLVE_BENEATH | RESOLVE_NO_MAGICLINKS,
567                 };
568 
569                 assert(target_rel);
570                 mkdir_p(dirname(new), 0755);
571                 snprintf(new, sizeof(new), "%s%s", root, target?target:source);
572                 while (*target_rel == '/')
573                         ++target_rel;
574                 fd = (jailroot_dirfd >= 0)
575                      ? sys_openat2(jailroot_dirfd, target_rel, &how, sizeof(how))
576                      : open(new, O_CREAT|O_WRONLY|O_TRUNC|O_EXCL|O_CLOEXEC, 0644);
577                 if (fd >= 0)
578                         close(fd);
579 
580                 if (error && fd < 0 && errno != EEXIST) {
581                         ERROR("failed to create mount target %s: %m\n", new);
582 
583                         ret = errno;
584                         goto free_source_out;
585                 }
586         }
587 
588         if (is_bind) {
589                 if (use_fd) {
590                         if (sys_move_mount(source_fd, "", AT_FDCWD, new, MOVE_MOUNT_F_EMPTY_PATH) < 0) {
591                                 if (error)
592                                         ERROR("move_mount(%s -> %s): %m\n", source, new);
593                                 ret = error;
594                                 goto free_source_out;
595                         }
596                 } else if (mount(source?:new, new, filesystemtype?:"bind", MS_BIND | (mountflags & MS_REC), optstr)) {
597                         if (error)
598                                 ERROR("failed to mount -B %s %s: %m\n", source, new);
599 
600                         ret = error;
601                         goto free_source_out;
602                 }
603                 mountflags |= MS_REMOUNT;
604         }
605 
606         mount_data = optstr;
607         if (filesystemtype && !strcmp(filesystemtype, "devpts") && fs_userns &&
608             mount_opts_gid_unmapped(optstr)) {
609                 mount_opts_drop(optstr, "gid=", devpts_data, sizeof(devpts_data));
610                 mount_data = devpts_data;
611         }
612 
613         if (filesystemtype && !strcmp(filesystemtype, "tmpfs") && !fs_userns &&
614             !mount_opts_has(optstr ?: "", "swap") && !mount_opts_has(optstr ?: "", "noswap")) {
615                 if (optstr && *optstr)
616                         snprintf(tmpfs_data, sizeof(tmpfs_data), "%s,noswap", optstr);
617                 else
618                         snprintf(tmpfs_data, sizeof(tmpfs_data), "noswap");
619                 mount_data = tmpfs_data;
620         }
621 
622         const char *hack_fstype = ((!filesystemtype || strcmp(filesystemtype, "cgroup"))?filesystemtype:"cgroup2");
623         if (mount(source?:(is_bind?new:NULL), new, hack_fstype?:"none", mountflags, mount_data)) {
624                 int mount_errno = errno;
625 
626                 if ((mountflags & MS_REMOUNT) && mount_errno == EPERM) {
627                         /* Not a heuristic: re-read the kernel-enforced flags from
628                          * mountinfo and only proceed once the security-relevant
629                          * ones (ro/nosuid/nodev/noexec) are confirmed in effect. */
630                         unsigned long retry_flags = mountflags | mountinfo_current_flags(new);
631 
632                         if (retry_flags != mountflags &&
633                             !mount(source?:(is_bind?new:NULL), new, hack_fstype?:"none", retry_flags, mount_data))
634                                 goto mount_ok;
635 
636                         unsigned long lockable_flags = MS_RDONLY | MS_NOSUID | MS_NODEV | MS_NOEXEC;
637                         unsigned long wanted = orig_mountflags & lockable_flags;
638                         unsigned long got = mountinfo_current_flags(new) & lockable_flags;
639 
640                         if ((wanted & ~got) == 0) {
641                                 WARNING("remount(%s) to apply flags failed: %s (tolerated - "
642                                 "the flags actually in effect already satisfy the "
643                                 "request; expected under CLONE_NEWUSER for host-owned "
644                                 "bind sources)\n", new, strerror(mount_errno));
645                                 goto mount_ok;
646                         }
647 
648                         if (error) {
649                                 errno = mount_errno;
650                                 ERROR("failed to enforce mount restrictions on %s %s: %m "
651                                       "(missing flags: %s%s%s%s)\n", source, new,
652                                       (wanted & ~got & MS_RDONLY) ? "ro " : "",
653                                       (wanted & ~got & MS_NOSUID) ? "nosuid " : "",
654                                       (wanted & ~got & MS_NODEV) ? "nodev " : "",
655                                       (wanted & ~got & MS_NOEXEC) ? "noexec " : "");
656                                 ret = error;
657                                 goto free_source_out;
658                         }
659 
660                         WARNING("remount(%s) to apply mount restrictions failed and could not "
661                                 "be verified in effect; continuing best-effort since "
662                                 "this mount was not marked as critical\n", new);
663                         goto mount_ok;
664                 }
665 
666                 errno = mount_errno;
667                 if (error)
668                         ERROR("failed to mount %s %s: %m\n", source, new);
669 
670                 ret = error;
671                 goto free_source_out;
672         }
673 
674 mount_ok:
675         DEBUG("mount %s%s %s (%s)\n", (mountflags & MS_BIND)?"-B ":"", source, new,
676               (mountflags & MS_RDONLY)?"ro":"rw");
677 
678         if (propflags && mount("none", new, "none", propflags, NULL)) {
679                 if (error)
680                         ERROR("failed to mount --make-... %s \n", new);
681 
682                 ret = error;
683         }
684 
685 free_source_out:
686         if (inner)
687                 free(source);
688 
689         return ret;
690 }
691 
692 static bool nullable_str_eq(const char *a, const char *b)
693 {
694         if (a == b)
695                 return true;
696         if (!a || !b)
697                 return false;
698         return !strcmp(a, b);
699 }
700 
701 static int _add_mount(const char *source, const char *target, const char *filesystemtype,
702                       unsigned long mountflags, unsigned long propflags, const char *optstr,
703                       int error, bool inner)
704 {
705         struct mount *m;
706 
707         assert(target != NULL);
708 
709         m = avl_find_element(&mounts, target, m, avl);
710         if (m) {
711                 bool source_match;
712                 if (m->source == (void *)(-1) || source == (void *)(-1))
713                         source_match = (m->source == source);
714                 else
715                         source_match = nullable_str_eq(m->source, source);
716 
717                 if (source_match &&
718                     nullable_str_eq(m->filesystemtype, filesystemtype) &&
719                     nullable_str_eq(m->optstr, optstr) &&
720                     m->mountflags == mountflags &&
721                     m->propflags == propflags &&
722                     m->error == error &&
723                     m->inner == inner)
724                         return 0;
725 
726                 return EEXIST;
727         }
728 
729         m = calloc(1, sizeof(struct mount));
730         if (!m)
731                 return ENOMEM;
732 
733         m->idmap_treefd = -1;
734         m->source_fd = -1;
735         m->avl.key = m->target = strdup(target);
736         if (source) {
737                 if (source != (void*)(-1))
738                         m->source = strdup(source);
739                 else
740                         m->source = (void*)(-1);
741         }
742         if (filesystemtype)
743                 m->filesystemtype = strdup(filesystemtype);
744 
745         if (optstr)
746                 m->optstr = strdup(optstr);
747 
748         m->mountflags = mountflags;
749         m->propflags = propflags;
750         m->error = error;
751         m->inner = inner;
752         m->source_fd = -1;
753 
754         avl_insert(&mounts, &m->avl);
755         list_add_tail(&m->list, &mounts_order);
756         DEBUG("adding mount %s %s bind(%d) ro(%d) err(%d)\n", (m->source == (void*)(-1))?"mask":m->source, m->target,
757                 !!(m->mountflags & MS_BIND), !!(m->mountflags & MS_RDONLY), m->error != 0);
758 
759         return 0;
760 }
761 
762 int add_mount(const char *source, const char *target, const char *filesystemtype,
763               unsigned long mountflags, unsigned long propflags, const char *optstr, int error)
764 {
765         return _add_mount(source, target, filesystemtype, mountflags, propflags, optstr, error, false);
766 }
767 
768 int add_mount_inner(const char *source, const char *target, const char *filesystemtype,
769               unsigned long mountflags, unsigned long propflags, const char *optstr, int error)
770 {
771         return _add_mount(source, target, filesystemtype, mountflags, propflags, optstr, error, true);
772 }
773 
774 static int _add_mount_bind(const char *path, const char *path2, int readonly, int error)
775 {
776         unsigned long mountflags = MS_BIND;
777 
778         if (readonly)
779                 mountflags |= MS_RDONLY;
780 
781         return add_mount(path, path2, NULL, mountflags, 0, NULL, error);
782 }
783 
784 int add_mount_bind(const char *path, int readonly, int error)
785 {
786         return _add_mount_bind(path, path, readonly, error);
787 }
788 
789 int add_mount_fd(int fd, const char *target, int error)
790 {
791         struct mount *m;
792 
793         if (avl_find(&mounts, target))
794                 return 1;
795 
796         m = calloc(1, sizeof(struct mount));
797         if (!m)
798                 return ENOMEM;
799 
800         m->avl.key = m->target = strdup(target);
801         m->mountflags = MS_BIND;
802         m->error = error;
803         m->source_fd = fd;
804 
805         avl_insert(&mounts, &m->avl);
806         list_add_tail(&m->list, &mounts_order);
807         DEBUG("adding mount fd:%d %s bind(1) ro(?) err(%d)\n", fd, target, error != 0);
808 
809         return 0;
810 }
811 
812 /*
813  * sysfs needs CAP_SYS_ADMIN in the userns owning the netns. fsmount() the
814  * queued sysfs entries here, before clone(), for do_mount_fd().
815  */
816 int premount_sysfs(void)
817 {
818         struct mount *m;
819         int fsfd, mfd;
820 
821         list_for_each_entry(m, &mounts_order, list) {
822                 if (!m->filesystemtype || strcmp(m->filesystemtype, "sysfs") ||
823                     m->source_fd >= 0)
824                         continue;
825 
826                 fsfd = sys_fsopen("sysfs", FSOPEN_CLOEXEC);
827                 if (fsfd < 0)
828                         return -1;
829                 if (sys_fsconfig(fsfd, FSCONFIG_CMD_CREATE, NULL, NULL, 0)) {
830                         close(fsfd);
831                         return -1;
832                 }
833                 mfd = sys_fsmount(fsfd, FSMOUNT_CLOEXEC, mountflags_to_attr(m->mountflags));
834                 close(fsfd);
835                 if (mfd < 0)
836                         return -1;
837 
838                 m->source_fd = mfd;
839                 DEBUG("pre-mounted sysfs for %s as fd:%d\n", m->target, mfd);
840         }
841 
842         return 0;
843 }
844 
845 int add_mount_volume(const char *source, const char *target, int error)
846 {
847         struct mount *m;
848         int ret;
849 
850         ret = add_mount(source, target, NULL,
851                         MS_BIND | MS_NOEXEC | MS_NOSUID | MS_NODEV, 0, NULL, error);
852         if (ret && ret != EEXIST)
853                 return ret;
854 
855         m = avl_find_element(&mounts, target, m, avl);
856         if (m)
857                 m->volume = true;
858 
859         return ret;
860 }
861 
862 char *resolve_mount_source(const char *source)
863 {
864         char *real;
865 
866         if (!source)
867                 return NULL;
868 
869         if (source[0] != '/')
870                 return strdup(source);
871 
872         real = realpath(source, NULL);
873         return real ? real : strdup(source);
874 }
875 
876 enum {
877         OCI_MOUNT_SOURCE,
878         OCI_MOUNT_DESTINATION,
879         OCI_MOUNT_TYPE,
880         OCI_MOUNT_OPTIONS,
881         OCI_MOUNT_UIDMAPPINGS,
882         OCI_MOUNT_GIDMAPPINGS,
883         __OCI_MOUNT_MAX,
884 };
885 
886 static const struct blobmsg_policy oci_mount_policy[] = {
887         [OCI_MOUNT_SOURCE] = { "source", BLOBMSG_TYPE_STRING },
888         [OCI_MOUNT_DESTINATION] = { "destination", BLOBMSG_TYPE_STRING },
889         [OCI_MOUNT_TYPE] = { "type", BLOBMSG_TYPE_STRING },
890         [OCI_MOUNT_OPTIONS] = { "options", BLOBMSG_TYPE_ARRAY },
891         [OCI_MOUNT_UIDMAPPINGS] = { "uidMappings", BLOBMSG_TYPE_ARRAY },
892         [OCI_MOUNT_GIDMAPPINGS] = { "gidMappings", BLOBMSG_TYPE_ARRAY },
893 };
894 
895 struct mount_opt {
896         struct list_head list;
897         char *optstr;
898 };
899 
900 #ifndef MS_LAZYTIME
901 #define MS_LAZYTIME (1 << 25)
902 #endif
903 
904 bool jail_propagation_flags_add(const char *opt, unsigned long *flags)
905 {
906         if (!strcmp("private", opt))
907                 *flags |= MS_PRIVATE;
908         else if (!strcmp("rprivate", opt))
909                 *flags |= MS_PRIVATE | MS_REC;
910         else if (!strcmp("slave", opt))
911                 *flags |= MS_SLAVE;
912         else if (!strcmp("rslave", opt))
913                 *flags |= MS_SLAVE | MS_REC;
914         else if (!strcmp("shared", opt))
915                 *flags |= MS_SHARED;
916         else if (!strcmp("rshared", opt))
917                 *flags |= MS_SHARED | MS_REC;
918         else if (!strcmp("unbindable", opt))
919                 *flags |= MS_UNBINDABLE;
920         else if (!strcmp("runbindable", opt))
921                 *flags |= MS_UNBINDABLE | MS_REC;
922         else
923                 return false;
924 
925         return true;
926 }
927 
928 static int parseOCImountopts(struct blob_attr *msg, unsigned long *mount_flags, unsigned long *propagation_flags, char **mount_data, int *error, bool *idmap, bool *idmap_recursive)
929 {
930         struct blob_attr *cur;
931         int rem;
932         unsigned long mf = 0;
933         unsigned long pf = 0;
934         char *tmp;
935         struct list_head fsopts = LIST_HEAD_INIT(fsopts);
936         size_t len = 0;
937         struct mount_opt *opt, *tmpopt;
938 
939         *idmap = false;
940         *idmap_recursive = false;
941 
942         blobmsg_for_each_attr(cur, msg, rem) {
943                 tmp = blobmsg_get_string(cur);
944                 if (!strcmp("idmap", tmp)) {
945                         *idmap = true;
946                         continue;
947                 } else if (!strcmp("ridmap", tmp)) {
948                         *idmap = true;
949                         *idmap_recursive = true;
950                         continue;
951                 } else if (jail_propagation_flags_add(tmp, &pf))
952                         continue;
953                 else if (!strcmp("ro", tmp))
954                         mf |= MS_RDONLY;
955                 else if (!strcmp("rw", tmp))
956                         mf &= ~MS_RDONLY;
957                 else if (!strcmp("bind", tmp))
958                         mf = MS_BIND;
959                 else if (!strcmp("rbind", tmp))
960                         mf |= MS_BIND | MS_REC;
961                 else if (!strcmp("sync", tmp))
962                         mf |= MS_SYNCHRONOUS;
963                 else if (!strcmp("async", tmp))
964                         mf &= ~MS_SYNCHRONOUS;
965                 else if (!strcmp("atime", tmp))
966                         mf &= ~MS_NOATIME;
967                 else if (!strcmp("noatime", tmp))
968                         mf |= MS_NOATIME;
969                 else if (!strcmp("defaults", tmp))
970                         mf = 0; /* rw, suid, dev, exec, auto, nouser, and async */
971                 else if (!strcmp("dev", tmp))
972                         mf &= ~MS_NODEV;
973                 else if (!strcmp("nodev", tmp))
974                         mf |= MS_NODEV;
975                 else if (!strcmp("iversion", tmp))
976                         mf |= MS_I_VERSION;
977                 else if (!strcmp("noiversion", tmp))
978                         mf &= ~MS_I_VERSION;
979                 else if (!strcmp("diratime", tmp))
980                         mf &= ~MS_NODIRATIME;
981                 else if (!strcmp("nodiratime", tmp))
982                         mf |= MS_NODIRATIME;
983                 else if (!strcmp("dirsync", tmp))
984                         mf |= MS_DIRSYNC;
985                 else if (!strcmp("exec", tmp))
986                         mf &= ~MS_NOEXEC;
987                 else if (!strcmp("noexec", tmp))
988                         mf |= MS_NOEXEC;
989                 else if (!strcmp("mand", tmp))
990                         mf |= MS_MANDLOCK;
991                 else if (!strcmp("nomand", tmp))
992                         mf &= ~MS_MANDLOCK;
993                 else if (!strcmp("relatime", tmp))
994                         mf |= MS_RELATIME;
995                 else if (!strcmp("norelatime", tmp))
996                         mf &= ~MS_RELATIME;
997                 else if (!strcmp("strictatime", tmp))
998                         mf |= MS_STRICTATIME;
999                 else if (!strcmp("nostrictatime", tmp))
1000                         mf &= ~MS_STRICTATIME;
1001                 else if (!strcmp("lazytime", tmp))
1002                         mf |= MS_LAZYTIME;
1003                 else if (!strcmp("nolazytime", tmp))
1004                         mf &= ~MS_LAZYTIME;
1005                 else if (!strcmp("suid", tmp))
1006                         mf &= ~MS_NOSUID;
1007                 else if (!strcmp("nosuid", tmp))
1008                         mf |= MS_NOSUID;
1009                 else if (!strcmp("remount", tmp))
1010                         mf |= MS_REMOUNT;
1011                 /* special case: 'nofail' */
1012                 else if(!strcmp("nofail", tmp))
1013                         *error = 0;
1014                 else if (!strcmp("auto", tmp) ||
1015                          !strcmp("noauto", tmp) ||
1016                          !strcmp("user", tmp) ||
1017                          !strcmp("group", tmp) ||
1018                          !strcmp("_netdev", tmp))
1019                         DEBUG("ignoring built-in mount option %s\n", tmp);
1020                 else {
1021                         /* filesystem-specific free-form option */
1022                         opt = calloc(1, sizeof(*opt));
1023                         opt->optstr = tmp;
1024                         list_add_tail(&opt->list, &fsopts);
1025                 }
1026         };
1027 
1028         *mount_flags = mf;
1029         *propagation_flags = pf;
1030 
1031         list_for_each_entry(opt, &fsopts, list) {
1032                 if (len)
1033                         ++len;
1034 
1035                 len += strlen(opt->optstr);
1036         };
1037 
1038         if (len) {
1039                 *mount_data = calloc(len + 1, sizeof(char));
1040                 if (!(*mount_data))
1041                         return ENOMEM;
1042 
1043                 len = 0;
1044                 list_for_each_entry(opt, &fsopts, list) {
1045                         if (len)
1046                                 strcat(*mount_data, ",");
1047 
1048                         strcat(*mount_data, opt->optstr);
1049                         ++len;
1050                 }
1051 
1052                 list_for_each_entry_safe(opt, tmpopt, &fsopts, list) {
1053                         list_del(&opt->list);
1054                         free(opt);
1055                 }
1056         }
1057 
1058         DEBUG("mount flags(%08lx) propagation(%08lx) fsopts(\"%s\")\n", mf, pf, *mount_data?:"");
1059 
1060         return 0;
1061 }
1062 
1063 static bool is_proc_or_sys_path(const char *path)
1064 {
1065         if (!strcmp(path, "/proc") || !strcmp(path, "/sys"))
1066                 return true;
1067 
1068         if (!strncmp(path, "/proc/", 6))
1069                 return true;
1070 
1071         if (!strncmp(path, "/sys/", 5))
1072                 return true;
1073 
1074         return false;
1075 }
1076 
1077 static bool id_is_mapped(const char *mapfile, unsigned long id)
1078 {
1079         unsigned long inside, outside, count;
1080         char line[128];
1081         bool mapped = false;
1082         FILE *f;
1083 
1084         f = fopen(mapfile, "r");
1085         if (!f)
1086                 return true;
1087 
1088         while (fgets(line, sizeof(line), f)) {
1089                 if (sscanf(line, "%lu %lu %lu", &inside, &outside, &count) != 3)
1090                         continue;
1091 
1092                 if (id >= inside && id - inside < count) {
1093                         mapped = true;
1094                         break;
1095                 }
1096         }
1097 
1098         fclose(f);
1099 
1100         return mapped;
1101 }
1102 
1103 static bool mount_opts_gid_unmapped(const char *opts)
1104 {
1105         const char *p;
1106 
1107         if (!opts)
1108                 return false;
1109 
1110         for (p = opts; p; p = strchr(p, ',')) {
1111                 if (*p == ',')
1112                         ++p;
1113 
1114                 if (!strncmp(p, "gid=", 4))
1115                         return !id_is_mapped("/proc/self/gid_map", strtoul(p + 4, NULL, 10));
1116         }
1117 
1118         return false;
1119 }
1120 
1121 static void mount_opts_drop(const char *opts, const char *key, char *buf, size_t len)
1122 {
1123         size_t klen = strlen(key);
1124         const char *p, *end;
1125         size_t used = 0;
1126 
1127         buf[0] = '\0';
1128 
1129         for (p = opts; p && *p; p = end) {
1130                 end = strchr(p, ',');
1131                 if (end)
1132                         ++end;
1133 
1134                 if (!strncmp(p, key, klen))
1135                         continue;
1136 
1137                 while (*p && used + 1 < len) {
1138                         buf[used++] = *p;
1139                         if (*p++ == ',')
1140                                 break;
1141                 }
1142                 buf[used] = '\0';
1143         }
1144 
1145         if (used && buf[used - 1] == ',')
1146                 buf[used - 1] = '\0';
1147 }
1148 
1149 static bool mount_opts_has(const char *opts, const char *needle)
1150 {
1151         size_t nlen = strlen(needle);
1152         const char *p = opts;
1153 
1154         while (p && *p) {
1155                 const char *end = strchr(p, ',');
1156                 size_t plen = end ? (size_t)(end - p) : strlen(p);
1157 
1158                 if (plen == nlen && !strncmp(p, needle, nlen))
1159                         return true;
1160                 if (!end)
1161                         break;
1162                 p = end + 1;
1163         }
1164         return false;
1165 }
1166 
1167 int parseOCImount(struct blob_attr *msg)
1168 {
1169         struct blob_attr *tb[__OCI_MOUNT_MAX];
1170         unsigned long mount_flags = 0;
1171         unsigned long propagation_flags = 0;
1172         char *mount_data = NULL;
1173         char *destination, *abs_destination = NULL;
1174         char *rsrc = NULL;
1175         bool idmap = false, idmap_recursive = false;
1176         int ret, err = -1;
1177 
1178         blobmsg_parse(oci_mount_policy, __OCI_MOUNT_MAX, tb, blobmsg_data(msg), blobmsg_len(msg));
1179 
1180         if (!tb[OCI_MOUNT_DESTINATION])
1181                 return EINVAL;
1182 
1183         if (tb[OCI_MOUNT_OPTIONS]) {
1184                 ret = parseOCImountopts(tb[OCI_MOUNT_OPTIONS], &mount_flags, &propagation_flags, &mount_data, &err, &idmap, &idmap_recursive);
1185                 if (ret)
1186                         return ret;
1187         }
1188 
1189         destination = blobmsg_get_string(tb[OCI_MOUNT_DESTINATION]);
1190         if (destination[0] != '/') {
1191                 if (asprintf(&abs_destination, "/%s", destination) < 0) {
1192                         free(mount_data);
1193                         return ENOMEM;
1194                 }
1195                 destination = abs_destination;
1196         }
1197 
1198         if (is_proc_or_sys_path(destination) &&
1199             ((mount_flags & MS_BIND) ||
1200              (tb[OCI_MOUNT_TYPE] && !strcmp(blobmsg_get_string(tb[OCI_MOUNT_TYPE]), "bind"))) &&
1201             !(mount_flags & MS_RDONLY)) {
1202                 ERROR("OCI mount config requests a writable bind mount onto %s; "
1203                       "refusing to allow write access to /proc or /sys\n",
1204                       destination);
1205                 free(abs_destination);
1206                 if (mount_data)
1207                         free(mount_data);
1208                 return EPERM;
1209         }
1210 
1211         if (tb[OCI_MOUNT_SOURCE])
1212                 rsrc = resolve_mount_source(blobmsg_get_string(tb[OCI_MOUNT_SOURCE]));
1213 
1214         ret = add_mount(rsrc,
1215                   destination,
1216                   tb[OCI_MOUNT_TYPE] ? blobmsg_get_string(tb[OCI_MOUNT_TYPE]) : NULL,
1217                   mount_flags, propagation_flags, mount_data, err);
1218 
1219         free(rsrc);
1220 
1221         if (!ret && (idmap || tb[OCI_MOUNT_UIDMAPPINGS] || tb[OCI_MOUNT_GIDMAPPINGS])) {
1222                 struct mount *m = avl_find_element(&mounts, destination, m, avl);
1223                 if (m) {
1224                         m->idmap = idmap || tb[OCI_MOUNT_UIDMAPPINGS] || tb[OCI_MOUNT_GIDMAPPINGS];
1225                         m->idmap_recursive = idmap_recursive;
1226                         if (tb[OCI_MOUNT_UIDMAPPINGS]) {
1227                                 free(m->uidmappings);
1228                                 m->uidmappings = blob_memdup(tb[OCI_MOUNT_UIDMAPPINGS]);
1229                                 if (!m->uidmappings) {
1230                                         free(abs_destination);
1231                                         free(mount_data);
1232                                         return ENOMEM;
1233                                 }
1234                         }
1235                         if (tb[OCI_MOUNT_GIDMAPPINGS]) {
1236                                 free(m->gidmappings);
1237                                 m->gidmappings = blob_memdup(tb[OCI_MOUNT_GIDMAPPINGS]);
1238                                 if (!m->gidmappings) {
1239                                         free(abs_destination);
1240                                         free(mount_data);
1241                                         return ENOMEM;
1242                                 }
1243                         }
1244                 }
1245         }
1246 
1247         free(abs_destination);
1248         if (mount_data)
1249                 free(mount_data);
1250 
1251         return ret;
1252 }
1253 
1254 bool mount_is_defined(const char *target)
1255 {
1256         struct mount *m;
1257 
1258         m = avl_find_element(&mounts, target, m, avl);
1259 
1260         return m != NULL;
1261 }
1262 
1263 static struct blob_attr *single_idmap(uint32_t container_id)
1264 {
1265         struct blob_buf b = {};
1266         void *arr, *tbl;
1267         struct blob_attr *ret;
1268 
1269         blob_buf_init(&b, 0);
1270         arr = blobmsg_open_array(&b, "m");
1271         tbl = blobmsg_open_table(&b, NULL);
1272         blobmsg_add_u32(&b, "containerID", container_id);
1273         blobmsg_add_u32(&b, "hostID", 0);
1274         blobmsg_add_u32(&b, "size", 1);
1275         blobmsg_close_table(&b, tbl);
1276         blobmsg_close_array(&b, arr);
1277         ret = blob_memdup(blobmsg_data(b.head));
1278         blob_buf_free(&b);
1279 
1280         return ret;
1281 }
1282 
1283 int fs_mount_enable_idmap(const char *target, uint32_t uid, uint32_t gid)
1284 {
1285         struct mount *m = avl_find_element(&mounts, target, m, avl);
1286 
1287         if (!m)
1288                 return ENOENT;
1289 
1290         free(m->uidmappings);
1291         free(m->gidmappings);
1292         m->uidmappings = single_idmap(uid);
1293         m->gidmappings = single_idmap(gid);
1294         if (!m->uidmappings || !m->gidmappings)
1295                 return ENOMEM;
1296 
1297         m->idmap = true;
1298         m->idmap_recursive = false;
1299 
1300         return 0;
1301 }
1302 
1303 static void build_noafile(void) {
1304         int fd;
1305 
1306         fd = creat(UJAIL_NOAFILE, 0000);
1307         if (fd < 0)
1308                 return;
1309 
1310         close(fd);
1311         return;
1312 }
1313 
1314 static int do_mount_fd(const char *root, int fd, const char *target, int error)
1315 {
1316         char new[PATH_MAX];
1317         struct stat s;
1318 
1319         snprintf(new, sizeof(new), "%s%s", root, target);
1320 
1321         if (fstat(fd, &s)) {
1322                 if (error)
1323                         ERROR("fstat(fd:%d) failed: %m\n", fd);
1324                 close(fd);
1325                 return error;
1326         }
1327 
1328         if (S_ISDIR(s.st_mode)) {
1329                 mkdir_p(new, 0755);
1330         } else {
1331                 mkdir_p(dirname(new), 0755);
1332                 snprintf(new, sizeof(new), "%s%s", root, target);
1333                 int cfd = open(new, O_CREAT|O_WRONLY|O_TRUNC|O_EXCL, 0644);
1334                 if (cfd >= 0)
1335                         close(cfd);
1336                 if (error && cfd < 0 && errno != EEXIST) {
1337                         ERROR("failed to create mount target %s: %m\n", new);
1338                         close(fd);
1339                         return errno;
1340                 }
1341         }
1342 
1343         if (sys_move_mount(fd, "", AT_FDCWD, new, MOVE_MOUNT_F_EMPTY_PATH)) {
1344                 if (error)
1345                         ERROR("move_mount() to %s failed: %m\n", new);
1346                 close(fd);
1347                 return error;
1348         }
1349 
1350         close(fd);
1351         DEBUG("move_mount fd to %s\n", new);
1352         return 0;
1353 }
1354 
1355 static int idmap_mount_target(const char *root, struct mount *m, char *target, size_t tlen)
1356 {
1357         struct stat s;
1358         const char *target_rel;
1359         struct open_how how = {
1360                 .flags = O_CREAT | O_WRONLY | O_TRUNC | O_CLOEXEC,
1361                 .mode = 0644,
1362                 .resolve = RESOLVE_BENEATH | RESOLVE_NO_MAGICLINKS,
1363         };
1364         int fd;
1365 
1366         snprintf(target, tlen, "%s%s", root, m->target);
1367 
1368         if (stat(m->source, &s)) {
1369                 if (m->error)
1370                         ERROR("stat(%s) failed: %m\n", m->source);
1371                 return -1;
1372         }
1373 
1374         if (S_ISDIR(s.st_mode)) {
1375                 mkdir_p(target, 0755);
1376                 return 0;
1377         }
1378 
1379         target_rel = m->target;
1380         assert(target_rel);
1381         mkdir_p(dirname(strdupa(target)), 0755);
1382         snprintf(target, tlen, "%s%s", root, m->target);
1383         while (*target_rel == '/')
1384                 ++target_rel;
1385         fd = (jailroot_dirfd >= 0)
1386              ? sys_openat2(jailroot_dirfd, target_rel, &how, sizeof(how))
1387              : open(target, O_CREAT | O_WRONLY | O_TRUNC | O_CLOEXEC, 0644);
1388         if (fd >= 0)
1389                 close(fd);
1390 
1391         return 0;
1392 }
1393 
1394 static int idmap_tree_fd(const char *source, int source_fd, int userns_fd,
1395                          unsigned long mountflags, unsigned long propagation,
1396                          bool recursive)
1397 {
1398         struct ujail_mount_attr attr = { 0 };
1399         unsigned int open_flags = OPEN_TREE_CLONE | OPEN_TREE_CLOEXEC;
1400         unsigned int setattr_flags = AT_EMPTY_PATH;
1401         int treefd;
1402 
1403         if (recursive) {
1404                 open_flags |= AT_RECURSIVE;
1405                 setattr_flags |= AT_RECURSIVE;
1406         }
1407 
1408         if (source_fd >= 0)
1409                 treefd = sys_open_tree(source_fd, "", open_flags | AT_EMPTY_PATH);
1410         else
1411                 treefd = sys_open_tree(AT_FDCWD, source, open_flags);
1412         if (treefd < 0) {
1413                 ERROR("open_tree(%s): %m\n", source);
1414                 return -1;
1415         }
1416 
1417         /* keep the (possibly locked) atime mode unless one is requested */
1418         attr.attr_set = MOUNT_ATTR_IDMAP | mountflags_to_attr(mountflags);
1419         if (mountflags & (MS_NOATIME | MS_RELATIME | MS_STRICTATIME))
1420                 attr.attr_clr |= MOUNT_ATTR__ATIME;
1421         attr.propagation = propagation;
1422         attr.userns_fd = userns_fd;
1423 
1424         if (sys_mount_setattr(treefd, "", setattr_flags, &attr, sizeof(attr)) < 0) {
1425                 ERROR("mount_setattr(IDMAP, %s): %m\n", source);
1426                 close(treefd);
1427                 return -1;
1428         }
1429 
1430         return treefd;
1431 }
1432 
1433 static int do_idmap_mount(const char *root, struct mount *m)
1434 {
1435         char target[PATH_MAX];
1436         int treefd, userns_fd, ret = m->error;
1437 
1438         if (!m->source || m->source == (void *)(-1)) {
1439                 ERROR("idmap mount %s requires a source\n", m->target);
1440                 return m->error;
1441         }
1442 
1443         userns_fd = build_userns_fd(m->uidmappings, m->gidmappings);
1444         if (userns_fd < 0) {
1445                 ERROR("build_userns_fd: %s\n", strerror(-userns_fd));
1446                 return m->error;
1447         }
1448 
1449         if (idmap_mount_target(root, m, target, sizeof(target)))
1450                 goto out_close;
1451 
1452         treefd = idmap_tree_fd(m->source, m->source_fd, userns_fd, m->mountflags, 0,
1453                                m->idmap_recursive);
1454         if (treefd < 0)
1455                 goto out_close;
1456 
1457         if (sys_move_mount(treefd, "", AT_FDCWD, target, MOVE_MOUNT_F_EMPTY_PATH) < 0) {
1458                 if (m->error)
1459                         ERROR("move_mount(%s -> %s): %m\n", m->source, target);
1460                 close(treefd);
1461                 goto out_close;
1462         }
1463 
1464         if (m->propflags && mount("none", target, "none", m->propflags, NULL)) {
1465                 if (m->error)
1466                         ERROR("mount(propagation %#lx, %s): %m\n", m->propflags, target);
1467                 close(treefd);
1468                 goto out_close;
1469         }
1470 
1471         DEBUG("idmap mount %s %s\n", m->source, target);
1472         close(treefd);
1473         ret = 0;
1474 
1475 out_close:
1476         close(userns_fd);
1477         return ret;
1478 }
1479 
1480 static int do_move_idmap_mount(const char *root, struct mount *m)
1481 {
1482         char target[PATH_MAX];
1483         int ret = m->error;
1484 
1485         if (idmap_mount_target(root, m, target, sizeof(target)))
1486                 goto out;
1487 
1488         if (sys_move_mount(m->idmap_treefd, "", AT_FDCWD, target, MOVE_MOUNT_F_EMPTY_PATH) < 0) {
1489                 ERROR("move_mount(%s -> %s): %m\n", m->source, target);
1490                 goto out;
1491         }
1492 
1493         if (m->propflags && mount("none", target, "none", m->propflags, NULL)) {
1494                 ERROR("mount(propagation %#lx, %s): %m\n", m->propflags, target);
1495                 goto out;
1496         }
1497 
1498         DEBUG("idmap volume %s %s\n", m->source, target);
1499         ret = 0;
1500 
1501 out:
1502         close(m->idmap_treefd);
1503         m->idmap_treefd = -1;
1504         return ret;
1505 }
1506 
1507 int jail_idmap_build(const char *extroot,
1508                      struct blob_attr *uidmap, struct blob_attr *gidmap,
1509                      int *fds, int maxfds)
1510 {
1511         int userns_fd;
1512         int n = 0;
1513         int fd;
1514 
1515         if (!extroot)
1516                 return 0;
1517 
1518         userns_fd = build_userns_fd(uidmap, gidmap);
1519         if (userns_fd < 0) {
1520                 ERROR("build_userns_fd: %s\n", strerror(-userns_fd));
1521                 return -1;
1522         }
1523 
1524         if (n >= maxfds)
1525                 goto err;
1526         fd = idmap_tree_fd(extroot, -1, userns_fd, 0, MS_PRIVATE, false);
1527         if (fd < 0)
1528                 goto err;
1529         fds[n++] = fd;
1530 
1531         close(userns_fd);
1532         return n;
1533 
1534 err:
1535         close(userns_fd);
1536         while (n > 0)
1537                 close(fds[--n]);
1538         return -1;
1539 }
1540 
1541 bool jail_dir_is_fresh(const char *path)
1542 {
1543         struct dirent *e;
1544         char lf[PATH_MAX];
1545         bool fresh = true, seen_lf = false;
1546         DIR *d, *l;
1547 
1548         d = opendir(path);
1549         if (!d)
1550                 return false;
1551 
1552         while ((e = readdir(d))) {
1553                 if (!strcmp(e->d_name, ".") || !strcmp(e->d_name, ".."))
1554                         continue;
1555                 if (!seen_lf && !strcmp(e->d_name, "lost+found")) {
1556                         seen_lf = true;
1557                         continue;
1558                 }
1559                 fresh = false;
1560                 break;
1561         }
1562         closedir(d);
1563 
1564         if (!fresh || !seen_lf)
1565                 return fresh;
1566 
1567         snprintf(lf, sizeof(lf), "%s/lost+found", path);
1568         l = opendir(lf);
1569         if (!l)
1570                 return fresh;
1571         while ((e = readdir(l))) {
1572                 if (strcmp(e->d_name, ".") && strcmp(e->d_name, "..")) {
1573                         fresh = false;
1574                         break;
1575                 }
1576         }
1577         closedir(l);
1578 
1579         return fresh;
1580 }
1581 
1582 void jail_chown_fresh_volumes(uid_t uid, gid_t gid)
1583 {
1584         struct mount *m;
1585         char lf[PATH_MAX];
1586 
1587         avl_for_each_element(&mounts, m, avl) {
1588                 if (!m->volume || !m->source || m->source == (void *)(-1))
1589                         continue;
1590                 if (!jail_dir_is_fresh(m->source))
1591                         continue;
1592                 if (chown(m->source, uid, gid))
1593                         ERROR("chown(fresh volume %s -> %u:%u): %m\n", m->source, uid, gid);
1594                 snprintf(lf, sizeof(lf), "%s/lost+found", m->source);
1595                 if (chown(lf, uid, gid) && errno != ENOENT)
1596                         ERROR("chown(%s -> %u:%u): %m\n", lf, uid, gid);
1597         }
1598 }
1599 
1600 int jail_idmap_assign(bool have_extroot, bool have_overlay, const int *fds, int nfds,
1601                       int *extroot_fd, int *overlay_fd)
1602 {
1603         struct mount *m;
1604         int i = 0;
1605 
1606         if (have_extroot && i < nfds)
1607                 *extroot_fd = fds[i++];
1608 
1609         if (have_overlay && i < nfds)
1610                 *overlay_fd = fds[i++];
1611 
1612         avl_for_each_element(&mounts, m, avl) {
1613                 if (!m->volume)
1614                         continue;
1615                 if (i >= nfds)
1616                         break;
1617                 m->idmap_treefd = fds[i++];
1618         }
1619 
1620         return i;
1621 }
1622 
1623 void mount_stage_dev(const char *jail_dev)
1624 {
1625         struct mount *m;
1626         struct stat s;
1627         char path[PATH_MAX];
1628         bool is_dir;
1629         int fd;
1630 
1631         avl_for_each_element(&mounts, m, avl) {
1632                 if (strncmp(m->target, "/dev/", 5))
1633                         continue;
1634                 if (m->source == (void *)(-1))
1635                         continue;
1636 
1637                 snprintf(path, sizeof(path), "%s%s", jail_dev, m->target + 4);
1638 
1639                 is_dir = !(m->mountflags & MS_BIND) ||
1640                          (m->source && !stat(m->source, &s) && S_ISDIR(s.st_mode));
1641                 if (is_dir) {
1642                         mkdir_p(path, 0755);
1643                 } else {
1644                         mkdir_p(dirname(strdupa(path)), 0755);
1645                         fd = open(path, O_CREAT | O_WRONLY | O_TRUNC | O_CLOEXEC, 0644);
1646                         if (fd >= 0)
1647                                 close(fd);
1648                 }
1649         }
1650 }
1651 
1652 static bool mount_target_covers(const struct mount *outer, const struct mount *inner)
1653 {
1654         size_t len = strlen(outer->target);
1655 
1656         if (!strcmp(outer->target, "/"))
1657                 return strcmp(inner->target, "/") != 0;
1658 
1659         return !strncmp(inner->target, outer->target, len) && inner->target[len] == '/';
1660 }
1661 
1662 static int mount_one(const char *jailroot, const char *jail_dev, struct mount *m)
1663 {
1664         char devtarget[PATH_MAX];
1665         struct mount *outer;
1666         int ret;
1667 
1668         if (m->mounted)
1669                 return 0;
1670 
1671         m->mounted = true;
1672 
1673         /* whatever this one sits on has to be established first */
1674         list_for_each_entry(outer, &mounts_order, list)
1675                 if (!outer->mounted && mount_target_covers(outer, m) &&
1676                     mount_one(jailroot, jail_dev, outer))
1677                         return -1;
1678 
1679         if (jail_dev && m->filesystemtype && !strcmp(m->filesystemtype, "tmpfs") &&
1680             !strcmp(m->target, "/dev")) {
1681                 snprintf(devtarget, sizeof(devtarget), "%s%s", jailroot, m->target);
1682                 mkdir_p(devtarget, 0755);
1683                 if (mount(jail_dev, devtarget, NULL, MS_BIND | MS_REC, NULL)) {
1684                         ERROR("mount(MS_BIND, %s -> %s): %m\n", jail_dev, devtarget);
1685                         return -1;
1686                 }
1687 
1688                 return 0;
1689         }
1690 
1691         if (m->idmap_treefd >= 0)
1692                 return do_move_idmap_mount(jailroot, m) ? -1 : 0;
1693 
1694         if (m->idmap)
1695                 return do_idmap_mount(jailroot, m) ? -1 : 0;
1696 
1697         if (m->source_fd >= 0) {
1698                 ret = do_mount_fd(jailroot, m->source_fd, m->target, m->error);
1699                 m->source_fd = -1;
1700                 return ret ? -1 : 0;
1701         }
1702 
1703         if (do_mount(jailroot, m->source, m->target, m->filesystemtype, m->mountflags,
1704                      m->propflags, m->optstr, m->error, m->inner, m->source_fd))
1705                 return -1;
1706 
1707         return 0;
1708 }
1709 
1710 int mount_all(const char *jailroot, const char *jail_dev) {
1711         struct library *l;
1712         struct mount *m;
1713         int ret = 0;
1714 
1715         build_noafile();
1716 
1717         jailroot_dirfd = open(jailroot, O_PATH | O_DIRECTORY | O_CLOEXEC);
1718         if (jailroot_dirfd < 0)
1719                 ERROR("mount_all: open(%s, O_PATH|O_DIRECTORY): %m\n", jailroot);
1720 
1721         avl_for_each_element(&libraries, l, avl)
1722                 add_mount_bind(l->path, 1, -1);
1723 
1724         /* the spec has the runtime establish the mounts in the order they are listed */
1725         list_for_each_entry(m, &mounts_order, list) {
1726                 if (mount_one(jailroot, jail_dev, m)) {
1727                         ret = -1;
1728                         break;
1729                 }
1730         }
1731 
1732         if (jailroot_dirfd >= 0) {
1733                 close(jailroot_dirfd);
1734                 jailroot_dirfd = -1;
1735         }
1736 
1737         return ret;
1738 }
1739 
1740 void mount_free(void) {
1741         struct mount *m, *tmp;
1742 
1743         avl_remove_all_elements(&mounts, m, avl, tmp) {
1744                 list_del(&m->list);
1745                 if (m->source != (void*)(-1))
1746                         free((void*)m->source);
1747                 if (m->source_fd >= 0)
1748                         close(m->source_fd);
1749                 free((void*)m->target);
1750                 free((void*)m->filesystemtype);
1751                 free((void*)m->optstr);
1752                 free(m->uidmappings);
1753                 free(m->gidmappings);
1754                 free(m);
1755         }
1756 }
1757 
1758 void mount_list_init(void) {
1759         avl_init(&mounts, avl_strcmp, false, NULL);
1760         INIT_LIST_HEAD(&mounts_order);
1761 }
1762 
1763 static int add_script_interp(const char *path, const char *map, int size)
1764 {
1765         int start = 2;
1766         while (start < size && map[start] != '/') {
1767                 start++;
1768         }
1769         if (start >= size) {
1770                 ERROR("bad script interp (%s)\n", path);
1771                 return -1;
1772         }
1773         int stop = start + 1;
1774         while (stop < size && map[stop] > 0x20 && map[stop] <= 0x7e) {
1775                 stop++;
1776         }
1777         if (stop >= size || (stop-start) > PATH_MAX) {
1778                 ERROR("bad script interp (%s)\n", path);
1779                 return -1;
1780         }
1781         char buf[PATH_MAX];
1782         strncpy(buf, map+start, stop-start);
1783         return add_path_and_deps(buf, 1, -1, 0);
1784 }
1785 
1786 int add_2paths_and_deps(const char *path, const char *path2, int readonly, int error, int lib)
1787 {
1788         assert(path != NULL);
1789         assert(path2 != NULL);
1790 
1791         if (lib == 0 && path[0] != '/') {
1792                 ERROR("%s is not an absolute path\n", path);
1793                 return error;
1794         }
1795 
1796         char *map = NULL;
1797         char *fullpath = NULL;
1798         int fd, ret = -1;
1799         struct mount *bm = NULL;
1800         if (path[0] == '/') {
1801                 if (avl_find(&mounts, path2))
1802                         return 0;
1803                 fd = open(path, O_RDONLY|O_CLOEXEC);
1804                 if (fd < 0)
1805                         return error;
1806                 _add_mount_bind(path, path2, readonly, error);
1807                 bm = avl_find_element(&mounts, path2, bm, avl);
1808                 if (bm && bm->source_fd < 0)
1809                         bm->source_fd = sys_open_tree(AT_FDCWD, path,
1810                                                       OPEN_TREE_CLONE | OPEN_TREE_CLOEXEC);
1811         } else {
1812                 if (avl_find(&libraries, path))
1813                         return 0;
1814                 fd = lib_open(&fullpath, path);
1815                 if (fd < 0)
1816                         return error;
1817                 if (fullpath)
1818                         alloc_library(fullpath, path);
1819         }
1820 
1821         struct stat s;
1822         if (fstat(fd, &s) == -1) {
1823                 ERROR("fstat(%s) failed: %m\n", path);
1824                 ret = error;
1825                 goto out;
1826         }
1827 
1828         if (!S_ISREG(s.st_mode)) {
1829                 ret = 0;
1830                 goto out;
1831         }
1832 
1833         /* too small to be an ELF or a script -> "normal" file */
1834         if (s.st_size < 4) {
1835                 ret = 0;
1836                 goto out;
1837         }
1838 
1839         map = mmap(NULL, s.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
1840         if (map == MAP_FAILED) {
1841                 ERROR("failed to mmap %s: %m\n", path);
1842                 ret = -1;
1843                 goto out;
1844         }
1845 
1846         if (map[0] == '#' && map[1] == '!') {
1847                 ret = add_script_interp(path, map, s.st_size);
1848                 goto out;
1849         }
1850 
1851         if (map[0] == ELFMAG0 && map[1] == ELFMAG1 && map[2] == ELFMAG2 && map[3] == ELFMAG3) {
1852                 /* Pass the resolved fullpath, not the bare soname, so
1853                  * elf_load_deps() can expand a $ORIGIN-relative DT_RPATH/
1854                  * DT_RUNPATH in this object against its real directory. */
1855                 ret = elf_load_deps(fullpath ? fullpath : path, map, s.st_size);
1856                 goto out;
1857         }
1858 
1859         ret = 0;
1860 
1861 out:
1862         if (fd >= 0)
1863                 close(fd);
1864         if (map)
1865                 munmap(map, s.st_size);
1866         free(fullpath);
1867 
1868         return ret;
1869 }
1870 
1871 int add_2paths_nodeps(const char *path, const char *path2, int readonly, int error)
1872 {
1873         struct mount *bm = NULL;
1874 
1875         if (path[0] != '/') {
1876                 ERROR("%s is not an absolute path\n", path);
1877                 return error;
1878         }
1879 
1880         if (avl_find(&mounts, path2))
1881                 return 0;
1882 
1883         if (_add_mount_bind(path, path2, readonly, error))
1884                 return error;
1885 
1886         bm = avl_find_element(&mounts, path2, bm, avl);
1887         if (bm && bm->source_fd < 0)
1888                 bm->source_fd = sys_open_tree(AT_FDCWD, path,
1889                                               OPEN_TREE_CLONE | OPEN_TREE_CLOEXEC);
1890 
1891         return 0;
1892 }
1893 

This page was automatically generated by LXR 0.3.1.  •  OpenWrt