diff options
author | Alexander Larsson <alexl@redhat.com> | 2016-04-29 12:35:12 +0200 |
---|---|---|
committer | Alexander Larsson <alexl@redhat.com> | 2016-04-29 15:52:47 +0200 |
commit | f55bb0edc97a9d72b89e3a0165178946aa395b33 (patch) | |
tree | 08eb9e743d9dc5e88df61d086095464e18328e3b /common | |
parent | 4c3bf179e2e4a2a298cd1db1d045adaf3f564532 (diff) | |
download | xdg-app-f55bb0edc97a9d72b89e3a0165178946aa395b33.tar.gz |
Remove xdg-app-helper
Diffstat (limited to 'common')
-rw-r--r-- | common/Makefile.am.inc | 18 | ||||
-rw-r--r-- | common/xdg-app-helper.c | 2711 |
2 files changed, 0 insertions, 2729 deletions
diff --git a/common/Makefile.am.inc b/common/Makefile.am.inc index de36640..0c88972 100644 --- a/common/Makefile.am.inc +++ b/common/Makefile.am.inc @@ -54,21 +54,3 @@ libxdgapp_common_la_CFLAGS = \ -I$(srcdir)/dbus-proxy \ $(NULL) libxdgapp_common_la_LIBADD = libglnx.la $(BASE_LIBS) $(OSTREE_LIBS) $(SOUP_LIBS) $(XAUTH_LIBS) $(LIBSECCOMP_LIBS) - -bin_PROGRAMS += \ - xdg-app-helper \ - $(NULL) - -xdg_app_helper_SOURCES = common/xdg-app-helper.c -xdg_app_helper_LDADD = $(LIBSECCOMP_LIBS) -xdg_app_helper_CFLAGS = $(LIBSECCOMP_CFLAGS) - -install-exec-hook: -if PRIV_MODE_SETUID - $(SUDO_BIN) chown root $(DESTDIR)$(bindir)/xdg-app-helper - $(SUDO_BIN) chmod u+s $(DESTDIR)$(bindir)/xdg-app-helper -else -if PRIV_MODE_FILECAPS - $(SUDO_BIN) setcap cap_sys_admin+ep $(DESTDIR)$(bindir)/xdg-app-helper -endif -endif diff --git a/common/xdg-app-helper.c b/common/xdg-app-helper.c deleted file mode 100644 index 0ca53b0..0000000 --- a/common/xdg-app-helper.c +++ /dev/null @@ -1,2711 +0,0 @@ -/* xdg-app-helper - * Copyright (C) 2014 Alexander Larsson - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. If not, see <http://www.gnu.org/licenses/>. - * - */ - -#include "config.h" - -#include <assert.h> -#include <arpa/inet.h> -#include <dirent.h> -#include <errno.h> -#include <fcntl.h> -#include <getopt.h> -#include <linux/loop.h> -#include <linux/netlink.h> -#include <linux/rtnetlink.h> -#include <net/if.h> -#include <netinet/in.h> -#include <sched.h> -#include <signal.h> -#include <poll.h> -#include <stdarg.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/mount.h> -#include <sys/socket.h> -#include <sys/stat.h> -#include <sys/syscall.h> -#include <sys/types.h> -#include <sys/wait.h> -#include <sys/eventfd.h> -#include <sys/signalfd.h> -#include <sys/capability.h> -#include <sys/prctl.h> -#include <sys/utsname.h> -#include <unistd.h> -#include <pwd.h> -#include <grp.h> - -#ifdef ENABLE_SECCOMP -#include <seccomp.h> -#endif - -#if 0 -#define __debug__(x) printf x -#else -#define __debug__(x) -#endif - -#define N_ELEMENTS(arr) (sizeof (arr) / sizeof ((arr)[0])) - -#define TRUE 1 -#define FALSE 0 -typedef int bool; - -#define READ_END 0 -#define WRITE_END 1 - -#define DEFAULT_SHELL "/bin/sh" - -/* Globals to avoid having to use getuid(), since the uid/gid changes during runtime */ -static uid_t uid; -static gid_t gid; -static bool is_privileged; - -static void -die_with_error (const char *format, ...) -{ - va_list args; - int errsv; - - errsv = errno; - - va_start (args, format); - vfprintf (stderr, format, args); - va_end (args); - - fprintf (stderr, ": %s\n", strerror (errsv)); - - exit (1); -} - -static void -die (const char *format, ...) -{ - va_list args; - - va_start (args, format); - vfprintf (stderr, format, args); - va_end (args); - - fprintf (stderr, "\n"); - - exit (1); -} - -static void -die_oom (void) -{ - die ("Out of memory"); -} - -static void * -xmalloc (size_t size) -{ - void *res = malloc (size); - if (res == NULL) - die_oom (); - return res; -} - -static void * -xrealloc (void *ptr, size_t size) -{ - void *res = realloc (ptr, size); - if (size != 0 && res == NULL) - die_oom (); - return res; -} - -static char * -xstrdup (const char *str) -{ - char *res; - - assert (str != NULL); - - res = strdup (str); - if (res == NULL) - die_oom (); - - return res; -} - -static void -xsetenv (const char *name, const char *value, int overwrite) -{ - if (setenv (name, value, overwrite)) - die ("setenv failed"); -} - -static void -xunsetenv (const char *name) -{ - if (unsetenv(name)) - die ("unsetenv failed"); -} - -static char * -strconcat (const char *s1, - const char *s2) -{ - size_t len = 0; - char *res; - - if (s1) - len += strlen (s1); - if (s2) - len += strlen (s2); - - res = xmalloc (len + 1); - *res = 0; - if (s1) - strcat (res, s1); - if (s2) - strcat (res, s2); - - return res; -} - -static char * -strconcat3 (const char *s1, - const char *s2, - const char *s3) -{ - size_t len = 0; - char *res; - - if (s1) - len += strlen (s1); - if (s2) - len += strlen (s2); - if (s3) - len += strlen (s3); - - res = xmalloc (len + 1); - *res = 0; - if (s1) - strcat (res, s1); - if (s2) - strcat (res, s2); - if (s3) - strcat (res, s3); - - return res; -} - -static char* -strdup_printf (const char *format, - ...) -{ - char *buffer = NULL; - va_list args; - - va_start (args, format); - vasprintf (&buffer, format, args); - va_end (args); - - if (buffer == NULL) - die_oom (); - - return buffer; -} - -static const char * -get_relative_path (const char *path) -{ - while (*path == '/') - path++; - return path; -} - -#ifndef HAVE_FDWALK -static int -fdwalk (int (*cb)(void *data, int fd), void *data) -{ - int open_max; - int fd; - int res = 0; - DIR *d; - - if ((d = opendir ("/proc/self/fd"))) - { - struct dirent *de; - - while ((de = readdir (d))) - { - long l; - char *e = NULL; - - if (de->d_name[0] == '.') - continue; - - errno = 0; - l = strtol (de->d_name, &e, 10); - if (errno != 0 || !e || *e) - continue; - - fd = (int) l; - - if ((long) fd != l) - continue; - - if (fd == dirfd (d)) - continue; - - if ((res = cb (data, fd)) != 0) - break; - } - - closedir (d); - return res; - } - - open_max = sysconf (_SC_OPEN_MAX); - - for (fd = 0; fd < open_max; fd++) - if ((res = cb (data, fd)) != 0) - break; - - return res; -} -#endif - - -static inline int raw_clone(unsigned long flags, void *child_stack) { -#if defined(__s390__) || defined(__CRIS__) - /* On s390 and cris the order of the first and second arguments - * of the raw clone() system call is reversed. */ - return (int) syscall(__NR_clone, child_stack, flags); -#else - return (int) syscall(__NR_clone, flags, child_stack); -#endif -} - -static void -setup_seccomp (bool devel, const char *arch) -{ -#ifdef ENABLE_SECCOMP - scmp_filter_ctx seccomp; - /**** BEGIN NOTE ON CODE SHARING - * - * There are today a number of different Linux container - * implementations. That will likely continue for long into the - * future. But we can still try to share code, and it's important - * to do so because it affects what library and application writers - * can do, and we should support code portability between different - * container tools. - * - * This syscall blacklist is copied from xdg-app, which was in turn - * clearly influenced by the Sandstorm.io blacklist. - * - * If you make any changes here, I suggest sending the changes along - * to other sandbox maintainers. Using the libseccomp list is also - * an appropriate venue: - * https://groups.google.com/forum/#!topic/libseccomp - * - * A non-exhaustive list of links to container tooling that might - * want to share this blacklist: - * - * https://github.com/sandstorm-io/sandstorm - * in src/sandstorm/supervisor.c++ - * http://cgit.freedesktop.org/xdg-app/xdg-app/ - * in lib/xdg-app-helper.c - * https://git.gnome.org/browse/linux-user-chroot - * in src/setup-seccomp.c - * - **** END NOTE ON CODE SHARING - */ - struct { - int scall; - struct scmp_arg_cmp *arg; - } syscall_blacklist[] = { - /* Block dmesg */ - {SCMP_SYS(syslog)}, - /* Useless old syscall */ - {SCMP_SYS(uselib)}, - /* Don't allow you to switch to bsd emulation or whatnot */ - {SCMP_SYS(personality)}, - /* Don't allow disabling accounting */ - {SCMP_SYS(acct)}, - /* 16-bit code is unnecessary in the sandbox, and modify_ldt is a - historic source of interesting information leaks. */ - {SCMP_SYS(modify_ldt)}, - /* Don't allow reading current quota use */ - {SCMP_SYS(quotactl)}, - - /* Scary VM/NUMA ops */ - {SCMP_SYS(move_pages)}, - {SCMP_SYS(mbind)}, - {SCMP_SYS(get_mempolicy)}, - {SCMP_SYS(set_mempolicy)}, - {SCMP_SYS(migrate_pages)}, - - /* Don't allow subnamespace setups: */ - {SCMP_SYS(unshare)}, - {SCMP_SYS(mount)}, - {SCMP_SYS(pivot_root)}, - {SCMP_SYS(clone), &SCMP_A0(SCMP_CMP_MASKED_EQ, CLONE_NEWUSER, CLONE_NEWUSER)}, - }; - - struct { - int scall; - struct scmp_arg_cmp *arg; - } syscall_nondevel_blacklist[] = { - /* Profiling operations; we expect these to be done by tools from outside - * the sandbox. In particular perf has been the source of many CVEs. - */ - {SCMP_SYS(perf_event_open)}, - {SCMP_SYS(ptrace)} - }; - /* Blacklist all but unix, inet, inet6 and netlink */ - int socket_family_blacklist[] = { - AF_AX25, - AF_IPX, - AF_APPLETALK, - AF_NETROM, - AF_BRIDGE, - AF_ATMPVC, - AF_X25, - AF_ROSE, - AF_DECnet, - AF_NETBEUI, - AF_SECURITY, - AF_KEY, - AF_NETLINK + 1, /* Last gets CMP_GE, so order is important */ - }; - int i, r; - - seccomp = seccomp_init(SCMP_ACT_ALLOW); - if (!seccomp) - return die_oom (); - - if (arch != NULL) - { - uint32_t arch_id = 0; - - if (strcmp (arch, "i386") == 0) - arch_id = SCMP_ARCH_X86; - else if (strcmp (arch, "x86_64") == 0) - arch_id = SCMP_ARCH_X86_64; - - /* We only really need to handle arches on multiarch systems. - * If only one arch is supported the default is fine */ - if (arch_id != 0) - { - /* This *adds* the target arch, instead of replacing the - native one. This is not ideal, because we'd like to only - allow the target arch, but we can't really disallow the - native arch at this point, because then xdg-app-helper - couldn't continue runnning. */ - r = seccomp_arch_add (seccomp, arch_id); - if (r < 0 && r != -EEXIST) - { - errno = -r; - die_with_error ("Failed to add architecture to seccomp filter"); - } - } - } - - /* TODO: Should we filter the kernel keyring syscalls in some way? - * We do want them to be used by desktop apps, but they could also perhaps - * leak system stuff or secrets from other apps. - */ - - for (i = 0; i < N_ELEMENTS (syscall_blacklist); i++) - { - int scall = syscall_blacklist[i].scall; - if (syscall_blacklist[i].arg) - r = seccomp_rule_add (seccomp, SCMP_ACT_ERRNO(EPERM), scall, 1, *syscall_blacklist[i].arg); - else - r = seccomp_rule_add (seccomp, SCMP_ACT_ERRNO(EPERM), scall, 0); - if (r < 0 && r == -EFAULT /* unknown syscall */) - { - errno = -r; - die_with_error ("Failed to block syscall %d", scall); - } - } - - if (!devel) - { - for (i = 0; i < N_ELEMENTS (syscall_nondevel_blacklist); i++) - { - int scall = syscall_nondevel_blacklist[i].scall; - if (syscall_nondevel_blacklist[i].arg) - r = seccomp_rule_add (seccomp, SCMP_ACT_ERRNO(EPERM), scall, 1, *syscall_nondevel_blacklist[i].arg); - else - r = seccomp_rule_add (seccomp, SCMP_ACT_ERRNO(EPERM), scall, 0); - if (r < 0 && r == -EFAULT /* unknown syscall */) - { - errno = -r; - die_with_error ("Failed to block syscall %d", scall); - } - } - } - - /* Socket filtering doesn't work on e.g. i386, so ignore failures here - * However, we need to user seccomp_rule_add_exact to avoid libseccomp doing - * something else: https://github.com/seccomp/libseccomp/issues/8 */ - for (i = 0; i < N_ELEMENTS (socket_family_blacklist); i++) - { - int family = socket_family_blacklist[i]; - if (i == N_ELEMENTS (socket_family_blacklist) - 1) - r = seccomp_rule_add_exact (seccomp, SCMP_ACT_ERRNO(EAFNOSUPPORT), SCMP_SYS(socket), 1, SCMP_A0(SCMP_CMP_GE, family)); - else - r = seccomp_rule_add_exact (seccomp, SCMP_ACT_ERRNO(EAFNOSUPPORT), SCMP_SYS(socket), 1, SCMP_A0(SCMP_CMP_EQ, family)); - } - - r = seccomp_load (seccomp); - if (r < 0) - { - errno = -r; - die_with_error ("Failed to install seccomp audit filter: "); - } - - seccomp_release (seccomp); -#endif -} - -static void -usage (char **argv) -{ - fprintf (stderr, "usage: %s [OPTIONS...] RUNTIMEPATH COMMAND [ARGS...]\n\n", argv[0]); - - fprintf (stderr, - " -a Specify path for application (mounted at /app)\n" - " -b DEST[=SOURCE] Bind extra source path read-only into DEST\n" - " -B DEST[=SOURCE] Bind extra source path into DEST\n" - " -M DEST[=SOURCE] Bind extra source path into DEST and remove original\n" - " -c Enable developer mode (allows strace and perf)\n" - " -d SOCKETPATH Use SOCKETPATH as dbus session bus\n" - " -D SOCKETPATH Use SOCKETPATH as dbus system bus\n" - " -e Make /app/exports writable\n" - " -E Make /etc a pure symlink to /usr/etc\n" - " -F Mount the host filesystems\n" - " -f Mount the host filesystems read-only\n" - " -g Allow use of direct rendering graphics\n" - " -H Mount the users home directory\n" - " -h Mount the users home directory read-only\n" - " -i Share IPC namespace with session\n" - " -I APPID Set app id (used to find app data)\n" - " -l Lock .ref files in all mounts\n" - " -m PATH Set path to xdg-app-session-helper output\n" - " -n Share network namespace with session\n" - " -p SOCKETPATH Use SOCKETPATH as pulseaudio connection\n" - " -P PATH Chdir into PATH before running\n" - " -r Bind mount /etc/resolv.conf\n" - " -s Share Shm namespace with session\n" - " -S FD Pass fd into app to detect when it dies\n" - " -v PATH Mount PATH as /var\n" - " -w Make /app writable\n" - " -W Make /usr writable\n" - " -x SOCKETPATH Use SOCKETPATH as X display\n" - " -y SOCKETPATH Use SOCKETPATH as Wayland display\n" - ); - exit (1); -} - -static int -pivot_root (const char * new_root, const char * put_old) -{ -#ifdef __NR_pivot_root - return syscall(__NR_pivot_root, new_root, put_old); -#else - errno = ENOSYS; - return -1; -#endif -} - -typedef enum { - FILE_TYPE_REGULAR, - FILE_TYPE_DIR, - FILE_TYPE_SYMLINK, - FILE_TYPE_SYSTEM_SYMLINK, - FILE_TYPE_BIND, - FILE_TYPE_BIND_RO, - FILE_TYPE_MOUNT, - FILE_TYPE_REMOUNT, - FILE_TYPE_DEVICE, - FILE_TYPE_SHM, - FILE_TYPE_ETC_PASSWD, - FILE_TYPE_ETC_GROUP, -} file_type_t; - -typedef enum { - FILE_FLAGS_NONE = 0, - FILE_FLAGS_NON_FATAL = 1 << 0, - FILE_FLAGS_IF_LAST_FAILED = 1 << 1, - FILE_FLAGS_DEVICES = 1 << 2, -} file_flags_t; - -typedef struct { - file_type_t type; - const char *name; - mode_t mode; - const char *data; - file_flags_t flags; - int *option; -} create_table_t; - -typedef struct { - const char *what; - const char *where; - const char *type; - const char *options; - unsigned long flags; -} mount_table_t; - -static bool create_etc_symlink = FALSE; -static bool create_etc_dir = TRUE; -static bool create_monitor_links = FALSE; -static bool bind_resolv_conf = FALSE; -static bool allow_dri = FALSE; - -static const create_table_t create[] = { - { FILE_TYPE_DIR, ".oldroot", 0755 }, - { FILE_TYPE_DIR, "usr", 0755 }, - { FILE_TYPE_DIR, "tmp", 01777 }, - { FILE_TYPE_DIR, "app", 0755}, - { FILE_TYPE_DIR, "run", 0755}, - { FILE_TYPE_DIR, "run/host", 0755}, - { FILE_TYPE_DIR, "run/host/monitor", 0700, NULL}, - { FILE_TYPE_DIR, "run/dbus", 0755}, - { FILE_TYPE_DIR, "run/media", 0755}, - { FILE_TYPE_DIR, "run/user", 0755}, - { FILE_TYPE_DIR, "run/user/%1$d", 0700, NULL}, - { FILE_TYPE_DIR, "run/user/%1$d/pulse", 0700, NULL}, - { FILE_TYPE_DIR, "run/user/%1$d/dconf", 0700, NULL}, - { FILE_TYPE_REGULAR, "run/user/%1$d/pulse/native", 0700, NULL}, - { FILE_TYPE_DIR, "var", 0755}, - { FILE_TYPE_SYMLINK, "var/tmp", 0755, "/tmp"}, - { FILE_TYPE_SYMLINK, "var/run", 0755, "/run"}, - { FILE_TYPE_SYSTEM_SYMLINK, "lib32", 0755, "usr/lib32"}, - { FILE_TYPE_SYSTEM_SYMLINK, "lib64", 0755, "usr/lib64"}, - { FILE_TYPE_SYSTEM_SYMLINK, "lib", 0755, "usr/lib"}, - { FILE_TYPE_SYSTEM_SYMLINK, "bin", 0755, "usr/bin" }, - { FILE_TYPE_SYSTEM_SYMLINK, "sbin", 0755, "usr/sbin"}, - { FILE_TYPE_SYMLINK, "etc", 0755, "usr/etc", 0, &create_etc_symlink}, - { FILE_TYPE_DIR, "etc", 0755, NULL, 0, &create_etc_dir}, - { FILE_TYPE_ETC_PASSWD, "etc/passwd", 0755, NULL, 0, &create_etc_dir}, - { FILE_TYPE_ETC_GROUP, "etc/group", 0755, NULL, 0, &create_etc_dir}, - { FILE_TYPE_REGULAR, "etc/resolv.conf", 0755, NULL, 0, &bind_resolv_conf}, - { FILE_TYPE_SYMLINK, "etc/resolv.conf", 0755, "/run/host/monitor/resolv.conf", 0, &create_monitor_links}, - { FILE_TYPE_REGULAR, "etc/machine-id", 0755, NULL, 0, &create_etc_dir}, - { FILE_TYPE_DIR, "tmp/.X11-unix", 0755 }, - { FILE_TYPE_REGULAR, "tmp/.X11-unix/X99", 0755 }, - { FILE_TYPE_DIR, "proc", 0755}, - { FILE_TYPE_MOUNT, "proc"}, - { FILE_TYPE_BIND_RO, "proc/sys", 0755, "proc/sys"}, - { FILE_TYPE_BIND_RO, "proc/sysrq-trigger", 0755, "proc/sysrq-trigger"}, - { FILE_TYPE_BIND_RO, "proc/irq", 0755, "proc/irq"}, - { FILE_TYPE_BIND_RO, "proc/bus", 0755, "proc/bus"}, - { FILE_TYPE_DIR, "sys", 0755}, - { FILE_TYPE_DIR, "sys/block", 0755}, - { FILE_TYPE_BIND, "sys/block", 0755, "/sys/block"}, - { FILE_TYPE_DIR, "sys/bus", 0755}, - { FILE_TYPE_BIND, "sys/bus", 0755, "/sys/bus"}, - { FILE_TYPE_DIR, "sys/class", 0755}, - { FILE_TYPE_BIND, "sys/class", 0755, "/sys/class"}, - { FILE_TYPE_DIR, "sys/dev", 0755}, - { FILE_TYPE_BIND, "sys/dev", 0755, "/sys/dev"}, - { FILE_TYPE_DIR, "sys/devices", 0755}, - { FILE_TYPE_BIND, "sys/devices", 0755, "/sys/devices"}, - { FILE_TYPE_DIR, "dev", 0755}, - { FILE_TYPE_DIR, "dev/pts", 0755}, - { FILE_TYPE_MOUNT, "dev/pts"}, - { FILE_TYPE_SYMLINK, "dev/ptmx", 0666, "pts/ptmx"}, - { FILE_TYPE_DIR, "dev/shm", 0755}, - { FILE_TYPE_SHM, "dev/shm"}, - { FILE_TYPE_DEVICE, "dev/null", 0666}, - { FILE_TYPE_DEVICE, "dev/zero", 0666}, - { FILE_TYPE_DEVICE, "dev/full", 0666}, - { FILE_TYPE_DEVICE, "dev/random", 0666}, - { FILE_TYPE_DEVICE, "dev/urandom", 0666}, - { FILE_TYPE_DEVICE, "dev/tty", 0666}, - { FILE_TYPE_DIR, "dev/dri", 0755}, - { FILE_TYPE_BIND_RO, "dev/dri", 0755, "/dev/dri", FILE_FLAGS_NON_FATAL|FILE_FLAGS_DEVICES, &allow_dri}, - { FILE_TYPE_DEVICE, "dev/nvidiactl", 0666, NULL, FILE_FLAGS_NON_FATAL, &allow_dri}, - { FILE_TYPE_DEVICE, "dev/nvidia0", 0666, NULL, FILE_FLAGS_NON_FATAL, &allow_dri}, -}; - -/* warning: Don't create any actual files here, as we could potentially - write over bind mounts to the system */ -static const create_table_t create_post[] = { - { FILE_TYPE_BIND_RO, "etc/machine-id", 0444, "/etc/machine-id", FILE_FLAGS_NON_FATAL}, - { FILE_TYPE_BIND_RO, "etc/machine-id", 0444, "/var/lib/dbus/machine-id", FILE_FLAGS_NON_FATAL | FILE_FLAGS_IF_LAST_FAILED}, - { FILE_TYPE_BIND_RO, "etc/resolv.conf", 0444, "/etc/resolv.conf", 0, &bind_resolv_conf}, -}; - -static const mount_table_t mount_table[] = { - { "proc", "proc", "proc", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV }, - { "devpts", "dev/pts", "devpts","newinstance,ptmxmode=0666,mode=620", MS_NOSUID|MS_NOEXEC }, - { "tmpfs", "dev/shm", "tmpfs", "mode=1777", MS_NOSUID|MS_NODEV|MS_STRICTATIME }, -}; - -const char *dont_mount_in_root[] = { - ".", "..", "lib", "lib32", "lib64", "bin", "sbin", "usr", "boot", "root", - "tmp", "etc", "app", "run", "proc", "sys", "dev", "var", ".oldroot" -}; - -typedef enum { - BIND_READONLY = (1<<0), - BIND_PRIVATE = (1<<1), - BIND_DEVICES = (1<<2), - BIND_RECURSIVE = (1<<3), -} bind_option_t; - -typedef struct { - char *src; - char *dest; - bool readonly; - bool move; -} ExtraFile; - -ExtraFile *extra_files = NULL; -int n_extra_files = 0; - -static void -add_extra_file (char *src, char *dest, bool readonly, bool move) -{ - int i = n_extra_files; - n_extra_files++; - extra_files = xrealloc (extra_files, n_extra_files * sizeof (ExtraFile)); - extra_files[i].src = src; - extra_files[i].dest = dest; - extra_files[i].readonly = readonly; - extra_files[i].move = move; -} - -static int n_lock_dirs = 0; -static const char **lock_dirs = NULL; - -static void -lock_dir (const char *dir) -{ - char *file = strconcat3 ("/", dir, "/.ref"); - struct flock lock = {0}; - int fd; - - fd = open (file, O_RDONLY | O_CLOEXEC); - free (file); - if (fd != -1) - { - lock.l_type = F_RDLCK; - lock.l_whence = SEEK_SET; - lock.l_start = 0; - lock.l_len = 0; - - if (fcntl(fd, F_SETLK, &lock) < 0) - { - printf ("lock failed\n"); - close (fd); - } - } -} - -static void -add_lock_dir (const char *dir) -{ - int i = n_lock_dirs; - - n_lock_dirs++; - lock_dirs = xrealloc (lock_dirs, n_lock_dirs * sizeof (char *)); - lock_dirs[i] = dir; -} - -/* We need to lock the dirs in pid1 because otherwise the - locks are not held by the right process and will not live - for the full duration of the sandbox. */ -static void -lock_all_dirs (void) -{ - int i; - for (i = 0; i < n_lock_dirs; i++) - lock_dir (lock_dirs[i]); -} - -static char * -load_file (const char *path) -{ - int fd; - char *data; - ssize_t data_read; - ssize_t data_len; - ssize_t res; - - fd = open (path, O_CLOEXEC | O_RDONLY); - if (fd == -1) - return NULL; - - data_read = 0; - data_len = 4080; - data = xmalloc (data_len); - - do - { - if (data_len == data_read + 1) - { - data_len *= 2; - data = xrealloc (data, data_len); - } - - do - res = read (fd, data + data_read, data_len - data_read - 1); - while (res < 0 && errno == EINTR); - - if (res < 0) - { - int errsv = errno; - free (data); - errno = errsv; - return NULL; - } - - data_read += res; - } - while (res > 0); - - data[data_read] = 0; - - close (fd); - - return data; -} - -static char * -skip_line (char *line) -{ - while (*line != 0 && *line != '\n') - line++; - - if (*line == '\n') - line++; - - return line; -} - -static char * -skip_token (char *line, bool eat_whitespace) -{ - while (*line != ' ' && *line != '\n') - line++; - - if (eat_whitespace && *line == ' ') - line++; - - return line; -} - -static bool -str_has_prefix (const char *str, - const char *prefix) -{ - return strncmp (str, prefix, strlen (prefix)) == 0; -} - -static char * -unescape_string (const char *escaped, ssize_t len) -{ - char *unescaped, *res; - const char *end; - - if (len < 0) - len = strlen (escaped); - end = escaped + len; - - unescaped = res = xmalloc (len + 1); - while (escaped < end) - { - if (*escaped == '\\') - { - *unescaped++ = - ((escaped[1] - '0') << 6) | - ((escaped[2] - '0') << 3) | - ((escaped[3] - '0') << 0); - escaped += 4; - } - else - *unescaped++ = *escaped++; - } - *unescaped = 0; - return res; -} - -static char * -get_mountinfo (const char *mountpoint) -{ - char *line_mountpoint, *line_mountpoint_end; - char *mountinfo; - char *free_me = NULL; - char *line, *line_start; - char *res = NULL; - int i; - - if (mountpoint[0] != '/') - { - char *cwd = getcwd(NULL, 0); - if (cwd == NULL) - die_oom (); - - mountpoint = free_me = strconcat3 (cwd, "/", mountpoint); - free (cwd); - } - - mountinfo = load_file ("/proc/self/mountinfo"); - if (mountinfo == NULL) - return NULL; - - line = mountinfo; - - while (*line != 0) - { - char *unescaped; - - line_start = line; - for (i = 0; i < 4; i++) - line = skip_token (line, TRUE); - line_mountpoint = line; - line = skip_token (line, FALSE); - line_mountpoint_end = line; - line = skip_line (line); - - unescaped = unescape_string (line_mountpoint, line_mountpoint_end - line_mountpoint); - if (strcmp (mountpoint, unescaped) == 0) - { - free (unescaped); - res = line_start; - line[-1] = 0; - break; - } - free (unescaped); - } - - if (free_me) - free (free_me); - - if (res) - res = xstrdup (res); - - free (mountinfo); - - return res; -} - -static unsigned long -get_mountflags (const char *mountpoint) -{ - char *line, *token, *end_token; - int i; - unsigned long flags = 0; - static const struct { int flag; char *name; } flags_data[] = { - { 0, "rw" }, - { MS_RDONLY, "ro" }, - { MS_NOSUID, "nosuid" }, - { MS_NODEV, "nodev" }, - { MS_NOEXEC, "noexec" }, - { MS_NOATIME, "noatime" }, - { MS_NODIRATIME, "nodiratime" }, - { MS_RELATIME, "relatime" }, - { 0, NULL } - }; - - line = get_mountinfo (mountpoint); - if (line == NULL) - return 0; - - token = line; - for (i = 0; i < 5; i++) - token = skip_token (token, TRUE); - - end_token = skip_token (token, FALSE); - *end_token = 0; - - do { - end_token = strchr (token, ','); - if (end_token != NULL) - *end_token = 0; - - for (i = 0; flags_data[i].name != NULL; i++) - { - if (strcmp (token, flags_data[i].name) == 0) - flags |= flags_data[i].flag; - } - - if (end_token) - token = end_token + 1; - else - token = NULL; - } while (token != NULL); - - free (line); - - return flags; -} - - -static char ** -get_submounts (const char *parent_mount) -{ - char *mountpoint, *mountpoint_end; - char **submounts; - int i, n_submounts, submounts_size; - char *mountinfo; - char *line; - - mountinfo = load_file ("/proc/self/mountinfo"); - if (mountinfo == NULL) - return NULL; - - submounts_size = 8; - n_submounts = 0; - submounts = xmalloc (sizeof (char *) * submounts_size); - - line = mountinfo; - - while (*line != 0) - { - char *unescaped; - for (i = 0; i < 4; i++) - line = skip_token (line, TRUE); - mountpoint = line; - line = skip_token (line, FALSE); - mountpoint_end = line; - line = skip_line (line); - *mountpoint_end = 0; - - unescaped = unescape_string (mountpoint, -1); - - if (*unescaped == '/' && - str_has_prefix (unescaped + 1, parent_mount) && - *(unescaped + 1 + strlen (parent_mount)) == '/') - { - if (n_submounts + 1 >= submounts_size) - { - submounts_size *= 2; - submounts = xrealloc (submounts, sizeof (char *) * submounts_size); - } - submounts[n_submounts++] = xstrdup (unescaped + 1); - } - free (unescaped); - } - - submounts[n_submounts] = NULL; - - free (mountinfo); - - return submounts; -} - -static int -bind_mount (const char *src, const char *dest, bind_option_t options) -{ - bool readonly = (options & BIND_READONLY) != 0; - bool private = (options & BIND_PRIVATE) != 0; - bool devices = (options & BIND_DEVICES) != 0; - bool recursive = (options & BIND_RECURSIVE) != 0; - unsigned long current_flags; - char **submounts; - int i; - - if (mount (src, dest, NULL, MS_MGC_VAL|MS_BIND|(recursive?MS_REC:0), NULL) != 0) - return 1; - - if (private) - { - if (mount ("none", dest, - NULL, MS_REC|MS_PRIVATE, NULL) != 0) - return 2; - } - - current_flags = get_mountflags (dest); - - if (mount ("none", dest, - NULL, MS_MGC_VAL|MS_BIND|MS_REMOUNT|current_flags|(devices?0:MS_NODEV)|MS_NOSUID|(readonly?MS_RDONLY:0), NULL) != 0) - return 3; - - /* We need to work around the fact that a bind mount does not apply the flags, so we need to manually - * apply the flags to all submounts in the recursive case. - * Note: This does not apply the flags to mounts which are later propagated into this namespace. - */ - if (recursive) - { - submounts = get_submounts (dest); - if (submounts == NULL) - return 4; - - for (i = 0; submounts[i] != NULL; i++) - { - current_flags = get_mountflags (submounts[i]); - if (mount ("none", submounts[i], - NULL, MS_MGC_VAL|MS_BIND|MS_REMOUNT|current_flags|(devices?0:MS_NODEV)|MS_NOSUID|(readonly?MS_RDONLY:0), NULL) != 0) - return 5; - free (submounts[i]); - } - - free (submounts); - } - - return 0; -} - -static bool -stat_is_dir (const char *pathname) -{ - struct stat buf; - - if (stat (pathname, &buf) != 0) - return FALSE; - - return S_ISDIR (buf.st_mode); -} - -static int -mkdir_with_parents (const char *pathname, - int mode, - bool create_last) -{ - char *fn, *p; - struct stat buf; - - if (pathname == NULL || *pathname == '\0') - { - errno = EINVAL; - return 1; - } - - fn = xstrdup (pathname); - - p = fn; - while (*p == '/') - p++; - - do - { - while (*p && *p != '/') - p++; - - if (!*p) - p = NULL; - else - *p = '\0'; - - if (!create_last && p == NULL) - break; - - if (stat (fn, &buf) != 0) - { - if (mkdir (fn, mode) == -1 && errno != EEXIST) - { - int errsave = errno; - free (fn); - errno = errsave; - return -1; - } - } - else if (!S_ISDIR (buf.st_mode)) - { - free (fn); - errno = ENOTDIR; - return -1; - } - - if (p) - { - *p++ = '/'; - while (*p && *p == '/') - p++; - } - } - while (p); - - free (fn); - - return 0; -} - -static bool -write_to_file (int fd, const char *content, ssize_t len) -{ - ssize_t res; - - while (len > 0) - { - res = write (fd, content, len); - if (res < 0 && errno == EINTR) - continue; - if (res <= 0) - return FALSE; - len -= res; - content += res; - } - - return TRUE; -} - -#define BUFSIZE 8192 -static bool -copy_file_data (int sfd, - int dfd) -{ - char buffer[BUFSIZE]; - ssize_t bytes_read; - - while (TRUE) - { - bytes_read = read (sfd, buffer, BUFSIZE); - if (bytes_read == -1) - { - if (errno == EINTR) - continue; - - return FALSE; - } - - if (bytes_read == 0) - break; - - if (!write_to_file (dfd, buffer, bytes_read)) - return FALSE; - } - - return TRUE; -} - -static bool -copy_file (const char *src_path, const char *dst_path, mode_t mode) -{ - int sfd, dfd; - bool res; - int errsv; - - sfd = open (src_path, O_CLOEXEC | O_RDONLY); - if (sfd == -1) - return FALSE; - - dfd = creat (dst_path, mode); - if (dfd == -1) - { - close (sfd); - return FALSE; - } - - res = copy_file_data (sfd, dfd); - - errsv = errno; - close (sfd); - close (dfd); - errno = errsv; - - return res; -} - -static bool -write_file (const char *path, const char *content) -{ - int fd; - bool res; - int errsv; - - fd = open (path, O_RDWR | O_CLOEXEC, 0); - if (fd == -1) - return FALSE; - - res = TRUE; - if (content) - res = write_to_file (fd, content, strlen (content)); - - errsv = errno; - close (fd); - errno = errsv; - - return res; -} - -static bool -create_file (const char *path, mode_t mode, const char *content) -{ - int fd; - bool res; - int errsv; - - fd = creat (path, mode); - if (fd == -1) - return FALSE; - - res = TRUE; - if (content) - res = write_to_file (fd, content, strlen (content)); - - errsv = errno; - close (fd); - errno = errsv; - - return res; -} - -static void -create_files (const create_table_t *create, int n_create, int ignore_shm, const char *usr_path) -{ - bool last_failed = FALSE; - int i; - int system_mode = FALSE; - - if (strcmp (usr_path, "/usr") == 0) - system_mode = TRUE; - - for (i = 0; i < n_create; i++) - { - char *name; - char *data = NULL; - mode_t mode = create[i].mode; - file_flags_t flags = create[i].flags; - int *option = create[i].option; - unsigned long current_mount_flags; - char *in_root; - int k; - bool found; - int res; - - if ((flags & FILE_FLAGS_IF_LAST_FAILED) && - !last_failed) - continue; - - if (option && !*option) - continue; - - name = strdup_printf (create[i].name, uid); - if (create[i].data) - data = strdup_printf (create[i].data, uid); - - last_failed = FALSE; - - switch (create[i].type) - { - case FILE_TYPE_DIR: - if (mkdir (name, mode) != 0) - die_with_error ("creating dir %s", name); - break; - - case FILE_TYPE_ETC_PASSWD: - { - char *content = NULL; - struct passwd *p = getpwuid (uid); - if (p) - { - content = strdup_printf ("%s:x:%d:%d:%s:%s:%s\n" - "nfsnobody:x:65534:65534:Unmapped user:/:/sbin/nologin\n", - p->pw_name, - uid, gid, - p->pw_gecos, - p->pw_dir, - DEFAULT_SHELL); - - } - - if (!create_file (name, mode, content)) - die_with_error ("creating file %s", name); - - if (content) - free (content); - } - break; - - case FILE_TYPE_ETC_GROUP: - { - char *content = NULL; - struct group *g = getgrgid (gid); - struct passwd *p = getpwuid (uid); - if (p && g) - { - content = strdup_printf ("%s:x:%d:%s\n" - "nfsnobody:x:65534:\n", - g->gr_name, - gid, p->pw_name); - } - - if (!create_file (name, mode, content)) - die_with_error ("creating file %s", name); - - if (content) - free (content); - } - break; - - case FILE_TYPE_REGULAR: - if (!create_file (name, mode, NULL)) - die_with_error ("creating file %s", name); - break; - - case FILE_TYPE_SYSTEM_SYMLINK: - if (system_mode) - { - struct stat buf; - in_root = strconcat ("/", name); - if (stat (in_root, &buf) == 0) - { - if (mkdir (name, mode) != 0) - die_with_error ("creating dir %s", name); - - if (bind_mount (in_root, name, BIND_PRIVATE | BIND_READONLY)) - die_with_error ("mount %s", name); - } - - free (in_root); - - break; - } - - /* Only create symlink if target exists */ - if (data != NULL && str_has_prefix (data, "usr/")) - { - struct stat buf; - char *in_usr = strconcat3 (usr_path, "/", data + strlen("usr/")); - int res; - - res = lstat (in_usr, &buf); - free (in_usr); - - if (res != 0) - data = NULL; - } - else - data = NULL; - - if (data == NULL) - break; - - /* else Fall through */ - - case FILE_TYPE_SYMLINK: - if (symlink (data, name) != 0) - die_with_error ("creating symlink %s", name); - break; - - case FILE_TYPE_BIND: - case FILE_TYPE_BIND_RO: - if ((res = bind_mount (data, name, - 0 | - ((create[i].type == FILE_TYPE_BIND_RO) ? BIND_READONLY : 0) | - ((flags & FILE_FLAGS_DEVICES) ? BIND_DEVICES : 0) - ))) - { - if (res > 1 || (flags & FILE_FLAGS_NON_FATAL) == 0) - die_with_error ("mounting bindmount %s", name); - last_failed = TRUE; - } - - break; - - case FILE_TYPE_SHM: - if (ignore_shm) - break; - - /* NOTE: Fall through, treat as mount */ - case FILE_TYPE_MOUNT: - found = FALSE; - for (k = 0; k < N_ELEMENTS(mount_table); k++) - { - if (strcmp (mount_table[k].where, name) == 0) - { - if (mount(mount_table[k].what, - mount_table[k].where, - mount_table[k].type, - mount_table[k].flags, - mount_table[k].options) < 0) - die_with_error ("Mounting %s", name); - found = TRUE; - } - } - - if (!found) - die ("Unable to find mount %s\n", name); - - break; - - case FILE_TYPE_REMOUNT: - current_mount_flags = get_mountflags (name); - if (mount ("none", name, - NULL, MS_MGC_VAL|MS_REMOUNT|current_mount_flags|mode, NULL) != 0) - die_with_error ("Unable to remount %s\n", name); - - break; - - case FILE_TYPE_DEVICE: - if (!create_file (name, mode, NULL)) - die_with_error ("creating file %s", name); - - in_root = strconcat ("/", name); - if ((res = bind_mount (in_root, name, - BIND_DEVICES))) - { - if (res > 1 || (flags & FILE_FLAGS_NON_FATAL) == 0) - die_with_error ("binding device %s", name); - } - free (in_root); - - break; - - default: - die ("Unknown create type %d\n", create[i].type); - } - - free (name); - free (data); - } -} - -static void -link_extra_etc_dirs () -{ - DIR *dir; - struct dirent *dirent; - - dir = opendir("usr/etc"); - if (dir != NULL) - { - while ((dirent = readdir(dir))) - { - char *dst_path; - char *src_path; - char *target; - struct stat st; - - src_path = strconcat ("etc/", dirent->d_name); - if (lstat (src_path, &st) == 0) - { - free (src_path); - continue; - } - - dst_path = strconcat ("usr/etc/", dirent->d_name); - if (lstat (dst_path, &st) != 0) - { - free (src_path); - free (dst_path); - continue; - } - - /* For symlinks we copy the actual symlink value, to correctly handle - things like /etc/localtime symlinks */ - if (S_ISLNK (st.st_mode)) - { - ssize_t r; - - target = xmalloc (st.st_size + 1); - r = readlink (dst_path, target, st.st_size); - if (r == -1) - die_with_error ("readlink %s", dst_path); - target[r] = 0; - } - else - target = strconcat ("/usr/etc/", dirent->d_name); - - if (symlink (target, src_path) != 0) - die_with_error ("symlink %s", src_path); - - free (dst_path); - free (src_path); - free (target); - } - } -} - -static void -mount_extra_root_dirs (int readonly) -{ - DIR *dir; - struct dirent *dirent; - int i; - - /* Bind mount most dirs in / into the new root */ - dir = opendir("/"); - if (dir != NULL) - { - while ((dirent = readdir(dir))) - { - bool dont_mount = FALSE; - char *path; - struct stat st; - - for (i = 0; i < N_ELEMENTS(dont_mount_in_root); i++) - { - if (strcmp (dirent->d_name, dont_mount_in_root[i]) == 0) - { - dont_mount = TRUE; - break; - } - } - - if (dont_mount) - continue; - - path = strconcat ("/", dirent->d_name); - - if (lstat (path, &st) != 0) - { - free (path); - continue; - } - - if (S_ISDIR(st.st_mode)) - { - if (mkdir (dirent->d_name, 0755) != 0) - die_with_error (dirent->d_name); - - if (bind_mount (path, dirent->d_name, BIND_RECURSIVE | (readonly ? BIND_READONLY : 0))) - die_with_error ("mount root subdir %s", dirent->d_name); - } - else if (S_ISLNK(st.st_mode)) - { - ssize_t r; - char *target; - - target = xmalloc (st.st_size + 1); - r = readlink (path, target, st.st_size); - if (r == -1) - die_with_error ("readlink %s", path); - target[r] = 0; - - if (symlink (target, dirent->d_name) != 0) - die_with_error ("symlink %s %s", target, dirent->d_name); - } - - free (path); - } - } -} - -static void -create_homedir (int mount_real_home, int mount_home_ro, const char *app_id) -{ - const char *home; - const char *relative_home; - const char *writable_home; - char *app_id_dir; - const char *relative_app_id_dir; - struct stat st; - - home = getenv("HOME"); - if (home == NULL) - return; - - relative_home = get_relative_path (home); - - if (mkdir_with_parents (relative_home, 0755, TRUE)) - die_with_error ("unable to create %s", relative_home); - - if (mount_real_home) - { - writable_home = home; - if (bind_mount (writable_home, relative_home, BIND_RECURSIVE | (mount_home_ro ? BIND_READONLY : 0))) - die_with_error ("unable to mount %s", home); - } - - if (app_id != NULL && - (!mount_real_home || mount_home_ro)) - { - app_id_dir = strconcat3 (home, "/.var/app/", app_id); - if (stat (app_id_dir, &st) == 0 && S_ISDIR (st.st_mode)) - { - relative_app_id_dir = get_relative_path (app_id_dir); - if (mkdir_with_parents (relative_app_id_dir, 0755, TRUE)) - die_with_error ("unable to create %s", relative_app_id_dir); - - if (bind_mount (app_id_dir, relative_app_id_dir, 0)) - die_with_error ("unable to mount %s", home); - } - free (app_id_dir); - } -} - -static void * -add_rta (struct nlmsghdr *header, int type, size_t size) -{ - struct rtattr *rta; - size_t rta_size = RTA_LENGTH(size); - - rta = (struct rtattr*)((char *)header + NLMSG_ALIGN(header->nlmsg_len)); - rta->rta_type = type; - rta->rta_len = rta_size; - - header->nlmsg_len = NLMSG_ALIGN(header->nlmsg_len) + rta_size; - - return RTA_DATA(rta); -} - -static int -rtnl_send_request (int rtnl_fd, struct nlmsghdr *header) -{ - struct sockaddr_nl dst_addr = { AF_NETLINK, 0 }; - ssize_t sent; - - sent = sendto (rtnl_fd, (void *)header, header->nlmsg_len, 0, - (struct sockaddr *)&dst_addr, sizeof (dst_addr)); - if (sent < 0) - return 1; - - return 0; -} - -static int -rtnl_read_reply (int rtnl_fd, int seq_nr) -{ - char buffer[1024]; - ssize_t received; - struct nlmsghdr *rheader; - - while (1) - { - received = recv (rtnl_fd, buffer, sizeof(buffer), 0); - if (received < 0) - return 1; - - rheader = (struct nlmsghdr *)buffer; - while (received >= NLMSG_HDRLEN) - { - if (rheader->nlmsg_seq != seq_nr) - return 1; - if (rheader->nlmsg_pid != getpid ()) - return 1; - if (rheader->nlmsg_type == NLMSG_ERROR) - { - uint32_t *err = NLMSG_DATA(rheader); - if (*err == 0) - return 0; - - return 1; - } - if (rheader->nlmsg_type == NLMSG_DONE) - return 0; - - rheader = NLMSG_NEXT(rheader, received); - } - } -} - -static int -rtnl_do_request (int rtnl_fd, struct nlmsghdr *header) -{ - if (!rtnl_send_request (rtnl_fd, header)) - return 1; - - if (!rtnl_read_reply (rtnl_fd, header->nlmsg_seq)) - return 1; - - return 0; -} - -static struct nlmsghdr * -rtnl_setup_request (char *buffer, int type, int flags, size_t size) -{ - struct nlmsghdr *header; - size_t len = NLMSG_LENGTH (size); - static uint32_t counter = 0; - - memset (buffer, 0, len); - - header = (struct nlmsghdr *)buffer; - header->nlmsg_len = len; - header->nlmsg_type = type; - header->nlmsg_flags = flags | NLM_F_REQUEST; - header->nlmsg_seq = counter++; - header->nlmsg_pid = getpid (); - - return (struct nlmsghdr *)header; -} - -static int -loopback_setup (void) -{ - int r, if_loopback; - int rtnl_fd = -1; - char buffer[1024]; - struct sockaddr_nl src_addr = { AF_NETLINK, 0 }; - struct nlmsghdr *header; - struct ifaddrmsg *addmsg; - struct ifinfomsg *infomsg; - struct in_addr *ip_addr; - int res = 1; - - src_addr.nl_pid = getpid (); - - if_loopback = (int) if_nametoindex ("lo"); - if (if_loopback <= 0) - goto error; - - rtnl_fd = socket (PF_NETLINK, SOCK_RAW|SOCK_CLOEXEC, NETLINK_ROUTE); - if (rtnl_fd < 0) - goto error; - - r = bind (rtnl_fd, (struct sockaddr *)&src_addr, sizeof (src_addr)); - if (r < 0) - goto error; - - header = rtnl_setup_request (buffer, RTM_NEWADDR, - NLM_F_CREATE|NLM_F_EXCL|NLM_F_ACK, - sizeof (struct ifaddrmsg)); - addmsg = NLMSG_DATA(header); - - addmsg->ifa_family = AF_INET; - addmsg->ifa_prefixlen = 8; - addmsg->ifa_flags = IFA_F_PERMANENT; - addmsg->ifa_scope = RT_SCOPE_HOST; - addmsg->ifa_index = if_loopback; - - ip_addr = add_rta (header, IFA_LOCAL, sizeof (*ip_addr)); - ip_addr->s_addr = htonl(INADDR_LOOPBACK); - - ip_addr = add_rta (header, IFA_ADDRESS, sizeof (*ip_addr)); - ip_addr->s_addr = htonl(INADDR_LOOPBACK); - - assert (header->nlmsg_len < sizeof (buffer)); - - if (rtnl_do_request (rtnl_fd, header)) - goto error; - - header = rtnl_setup_request (buffer, RTM_NEWLINK, - NLM_F_ACK, - sizeof (struct ifinfomsg)); - infomsg = NLMSG_DATA(header); - - infomsg->ifi_family = AF_UNSPEC; - infomsg->ifi_type = 0; - infomsg->ifi_index = if_loopback; - infomsg->ifi_flags = IFF_UP; - infomsg->ifi_change = IFF_UP; - - assert (header->nlmsg_len < sizeof (buffer)); - - if (rtnl_do_request (rtnl_fd, header)) - goto error; - - res = 0; - - error: - if (rtnl_fd != -1) - close (rtnl_fd); - - return res; -} - -static void -block_sigchild (void) -{ - sigset_t mask; - - sigemptyset (&mask); - sigaddset (&mask, SIGCHLD); - - if (sigprocmask (SIG_BLOCK, &mask, NULL) == -1) - die_with_error ("sigprocmask"); -} - -static void -unblock_sigchild (void) -{ - sigset_t mask; - - sigemptyset (&mask); - sigaddset (&mask, SIGCHLD); - - if (sigprocmask (SIG_UNBLOCK, &mask, NULL) == -1) - die_with_error ("sigprocmask"); -} - -static int -close_extra_fds (void *data, int fd) -{ - int *extra_fds = (int *)data; - int i; - - for (i = 0; extra_fds[i] != -1; i++) - if (fd == extra_fds[i]) - return 0; - - if (fd <= 2) - return 0; - - close (fd); - return 0; -} - -/* This stays around for as long as the initial process in the app does - * and when that exits it exits, propagating the exit status. We do this - * by having pid1 in the sandbox detect this exit and tell the monitor - * the exit status via a eventfd. We also track the exit of the sandbox - * pid1 via a signalfd for SIGCHLD, and exit with an error in this case. - * This is to catch e.g. problems during setup. */ -static void -monitor_child (int event_fd) -{ - int res; - uint64_t val; - ssize_t s; - int signal_fd; - sigset_t mask; - struct pollfd fds[2]; - struct signalfd_siginfo fdsi; - int dont_close[] = { event_fd, -1 }; - - /* Close all extra fds in the monitoring process. - Any passed in fds have been passed on to the child anyway. */ - fdwalk (close_extra_fds, dont_close); - - sigemptyset (&mask); - sigaddset (&mask, SIGCHLD); - - signal_fd = signalfd (-1, &mask, SFD_CLOEXEC | SFD_NONBLOCK); - if (signal_fd == -1) - die_with_error ("signalfd"); - - fds[0].fd = event_fd; - fds[0].events = POLLIN; - fds[1].fd = signal_fd; - fds[1].events = POLLIN; - - while (1) - { - fds[0].revents = fds[1].revents = 0; - res = poll (fds, 2, -1); - if (res == -1 && errno != EINTR) - die_with_error ("poll"); - - s = read (event_fd, &val, 8); - if (s == -1 && errno != EINTR && errno != EAGAIN) - die_with_error ("read eventfd"); - else if (s == 8) - exit ((int)val - 1); - - s = read (signal_fd, &fdsi, sizeof (struct signalfd_siginfo)); - if (s == -1 && errno != EINTR && errno != EAGAIN) - die_with_error ("read signalfd"); - else if (s == sizeof(struct signalfd_siginfo)) - { - if (fdsi.ssi_signo != SIGCHLD) - die ("Read unexpected signal\n"); - exit (1); - } - } -} - -/* This is pid1 in the app sandbox. It is needed because we're using - * pid namespaces, and someone has to reap zombies in it. We also detect - * when the initial process (pid 2) dies and report its exit status to - * the monitor so that it can return it to the original spawner. - * - * When there are no other processes in the sandbox the wait will return - * ECHILD, and we then exit pid1 to clean up the sandbox. */ -static int -do_init (int event_fd, pid_t initial_pid) -{ - int initial_exit_status = 1; - - /* Grab a read on all .ref files to make it possible to detect that - it is in use. This lock will automatically go away when this - process dies */ - lock_all_dirs (); - - while (1) - { - pid_t child; - int status; - - child = wait (&status); - if (child == initial_pid) - { - uint64_t val; - - if (WIFEXITED (status)) - initial_exit_status = WEXITSTATUS(status); - - val = initial_exit_status + 1; - write (event_fd, &val, 8); - } - - if (child == -1 && errno != EINTR) - { - if (errno != ECHILD) - die_with_error ("init wait()"); - break; - } - } - - return initial_exit_status; -} - -/* low 32bit caps needed */ -#define REQUIRED_CAPS_0 (CAP_TO_MASK(CAP_SYS_ADMIN)) -/* high 32bit caps needed */ -#define REQUIRED_CAPS_1 0 - -static void -acquire_caps (void) -{ - struct __user_cap_header_struct hdr; - struct __user_cap_data_struct data[2]; - - memset (&hdr, 0, sizeof(hdr)); - hdr.version = _LINUX_CAPABILITY_VERSION_3; - - if (capget (&hdr, data) < 0) - die_with_error ("capget failed"); - - if (((data[0].effective & REQUIRED_CAPS_0) == REQUIRED_CAPS_0) && - ((data[0].permitted & REQUIRED_CAPS_0) == REQUIRED_CAPS_0) && - ((data[1].effective & REQUIRED_CAPS_1) == REQUIRED_CAPS_1) && - ((data[1].permitted & REQUIRED_CAPS_1) == REQUIRED_CAPS_1)) - is_privileged = TRUE; - - if (getuid () != geteuid ()) - { - /* Tell kernel not clear capabilities when dropping root */ - if (prctl (PR_SET_KEEPCAPS, 1, 0, 0, 0) < 0) - die_with_error ("prctl(PR_SET_KEEPCAPS) failed"); - - /* Drop root uid, but retain the required permitted caps */ - if (setuid (getuid ()) < 0) - die_with_error ("unable to drop privs"); - } - - if (is_privileged) - { - memset (&hdr, 0, sizeof(hdr)); - hdr.version = _LINUX_CAPABILITY_VERSION_3; - - /* Drop all non-require capabilities */ - data[0].effective = REQUIRED_CAPS_0; - data[0].permitted = REQUIRED_CAPS_0; - data[0].inheritable = 0; - data[1].effective = REQUIRED_CAPS_1; - data[1].permitted = REQUIRED_CAPS_1; - data[1].inheritable = 0; - if (capset (&hdr, data) < 0) - die_with_error ("capset failed"); - } - /* Else, we try unprivileged user namespaces */ -} - -static void -drop_caps (void) -{ - struct __user_cap_header_struct hdr; - struct __user_cap_data_struct data[2]; - - if (!is_privileged) - return; - - memset (&hdr, 0, sizeof(hdr)); - hdr.version = _LINUX_CAPABILITY_VERSION_3; - data[0].effective = 0; - data[0].permitted = 0; - data[0].inheritable = 0; - data[1].effective = 0; - data[1].permitted = 0; - data[1].inheritable = 0; - - if (capset (&hdr, data) < 0) - die_with_error ("capset failed"); - - if (prctl (PR_SET_DUMPABLE, 1, 0, 0, 0) < 0) - die_with_error ("prctl(PR_SET_DUMPABLE) failed"); -} - -static char *arg_space; -size_t arg_space_size; - -static void -clean_argv (int argc, - char **argv) -{ - int i; - char *newargv; - - arg_space = argv[0]; - arg_space_size = argv[argc-1] - argv[0] + strlen (argv[argc-1]) + 1; - newargv = xmalloc (arg_space_size); - memcpy (newargv, arg_space, arg_space_size); - for (i = 0; i < argc; i++) - argv[i] = newargv + (argv[i] - arg_space); -} - -static void -set_procname (const char *name) -{ - strncpy (arg_space, name, arg_space_size); -} - -int -main (int argc, - char **argv) -{ - mode_t old_umask; - char *newroot; - char *runtime_path = NULL; - char *app_path = NULL; - char *chdir_path = NULL; - char *monitor_path = NULL; - char *app_id = NULL; - char *var_path = NULL; - char *pulseaudio_socket = NULL; - char *x11_socket = NULL; - char *wayland_socket = NULL; - char *system_dbus_socket = NULL; - char *session_dbus_socket = NULL; - char *opt_arch = NULL; - char *xdg_runtime_dir; - char **args; - char *tmp; - int n_args; - bool devel = FALSE; - bool share_shm = FALSE; - bool network = FALSE; - bool ipc = FALSE; - bool mount_host_fs = FALSE; - bool mount_host_fs_ro = FALSE; - bool mount_home = FALSE; - bool mount_home_ro = FALSE; - bool lock_files = FALSE; - bool writable = FALSE; - bool writable_app = FALSE; - bool writable_exports = FALSE; - int clone_flags; - char *old_cwd = NULL; - int c, i; - pid_t pid; - int event_fd; - int sync_fd = -1; - char *endp; - char *uid_map, *gid_map; - uid_t ns_uid; - gid_t ns_gid; - struct stat st_buf; - - /* Get the (optional) capabilities we need, drop root */ - acquire_caps (); - - /* Never gain any more privs during exec */ - if (prctl (PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) < 0) - die_with_error ("prctl(PR_SET_NO_NEW_CAPS) failed"); - - clean_argv (argc, argv); - - while ((c = getopt (argc, argv, "+inWwceEsfFHhrA:a:m:M:b:B:p:x:ly:d:D:v:I:gS:P:")) >= 0) - { - switch (c) - { - case 'A': - opt_arch = optarg; - break; - - case 'a': - app_path = optarg; - break; - - case 'c': - devel = TRUE; - break; - - case 'M': - /* Same, but remove source */ - goto extra_file; - - case 'B': - /* Same, but non-readonly */ - goto extra_file; - - case 'b': - extra_file: - /* Format: DEST[=SOURCE] */ - tmp = strchr (optarg, '='); - if (tmp == NULL) - { - /* no SOURCE, use DEST */ - tmp = optarg; - } - else - { - if (tmp[1] == 0) - usage (argv); - *tmp = 0; - tmp = tmp + 1; - } - - if (optarg[0] != '/') - die ("Extra directories must be absolute paths"); - - while (*optarg == '/') - optarg++; - - if (*optarg == 0) - die ("Extra directories must not be root"); - - add_extra_file (tmp, optarg, c == 'b', c == 'M'); - break; - - case 'd': - session_dbus_socket = optarg; - break; - - case 'D': - system_dbus_socket = optarg; - break; - - case 'e': - writable_exports = TRUE; - break; - - case 'E': - create_etc_symlink = TRUE; - create_etc_dir = FALSE; - break; - - case 'F': - mount_host_fs = TRUE; - break; - - case 'f': - mount_host_fs = TRUE; - mount_host_fs_ro = TRUE; - break; - - case 'g': - allow_dri = TRUE; - break; - - case 'H': - mount_home = TRUE; - break; - - case 'h': - mount_home = TRUE; - mount_home_ro = TRUE; - break; - - case 'i': - ipc = TRUE; - break; - - case 'I': - app_id = optarg; - break; - - case 'l': - lock_files = TRUE; - break; - - case 'm': - monitor_path = optarg; - break; - - case 'n': - network = TRUE; - break; - - case 'p': - pulseaudio_socket = optarg; - break; - - case 'P': - chdir_path = optarg; - break; - - case 'r': - bind_resolv_conf = TRUE; - break; - - case 's': - share_shm = TRUE; - break; - - case 'S': - sync_fd = strtol (optarg, &endp, 10); - if (endp == optarg || *endp != 0) - die ("Invalid fd argument"); - break; - - case 'v': - var_path = optarg; - break; - - case 'w': - writable_app = TRUE; - break; - - case 'W': - writable = TRUE; - break; - - case 'x': - x11_socket = optarg; - break; - - case 'y': - wayland_socket = optarg; - break; - - default: /* '?' */ - usage (argv); - } - } - - args = &argv[optind]; - n_args = argc - optind; - - if (monitor_path != NULL && create_etc_dir) - { - create_monitor_links = TRUE; - bind_resolv_conf = FALSE; - } - - if (n_args < 2) - usage (argv); - - runtime_path = args[0]; - args++; - n_args--; - - /* The initial code is run with high permissions - (at least CAP_SYS_ADMIN), so take lots of care. */ - - __debug__(("Creating xdg-app-root dir\n")); - - uid = getuid (); - gid = getgid (); - - newroot = strdup_printf ("/run/user/%d/.xdg-app-root", uid); - if (mkdir (newroot, 0755) && errno != EEXIST) - { - free (newroot); - newroot = "/tmp/.xdg-app-root"; - if (mkdir (newroot, 0755) && errno != EEXIST) - die_with_error ("Creating xdg-app-root failed"); - } - - __debug__(("creating new namespace\n")); - - event_fd = eventfd (0, EFD_CLOEXEC | EFD_NONBLOCK); - - block_sigchild (); /* Block before we clone to avoid races */ - - clone_flags = SIGCHLD | CLONE_NEWNS | CLONE_NEWPID; - if (!is_privileged) - clone_flags |= CLONE_NEWUSER; - if (!network) - clone_flags |= CLONE_NEWNET; - if (!ipc) - clone_flags |= CLONE_NEWIPC; - - pid = raw_clone (clone_flags, NULL); - if (pid == -1) - { - if (!is_privileged) - { - if (errno == EINVAL) - die ("Creating new namespace failed, likely because the kernel does not support user namespaces. Give the xdg-app-helper setuid root or cap_sys_admin+ep rights, or switch to a kernel with user namespace support."); - else if (errno == EPERM) - die ("No permissions to creating new namespace, likely because the kernel does not allow non-privileged user namespaces. On e.g. debian this can be enabled with 'sysctl kernel.unprivileged_userns_clone=1'."); - } - - die_with_error ("Creating new namespace failed"); - } - - if (pid != 0) - { - /* We don't need any caps in the launcher, drop them immediately. */ - drop_caps (); - if (app_id) - set_procname (strdup_printf ("xdg-app-helper %s launcher", app_id)); - monitor_child (event_fd); - exit (0); /* Should not be reached, but better safe... */ - } - - ns_uid = uid; - ns_gid = gid; - if (!is_privileged) - { - /* This is a bit hacky, but we need to first map the real uid/gid to - 0, otherwise we can't mount the devpts filesystem because root is - not mapped. Later we will create another child user namespace and - map back to the real uid */ - ns_uid = 0; - ns_gid = 0; - - uid_map = strdup_printf ("%d %d 1\n", ns_uid, uid); - if (!write_file ("/proc/self/uid_map", uid_map)) - die_with_error ("setting up uid map"); - free (uid_map); - - if (!write_file("/proc/self/setgroups", "deny\n")) - die_with_error ("error writing to setgroups"); - - gid_map = strdup_printf ("%d %d 1\n", ns_gid, gid); - if (!write_file ("/proc/self/gid_map", gid_map)) - die_with_error ("setting up gid map"); - free (gid_map); - } - - old_umask = umask (0); - - /* Mark everything as slave, so that we still - * receive mounts from the real root, but don't - * propagate mounts to the real root. */ - if (mount (NULL, "/", NULL, MS_SLAVE|MS_REC, NULL) < 0) - die_with_error ("Failed to make / slave"); - - /* Create a tmpfs which we will use as / in the namespace */ - if (mount ("", newroot, "tmpfs", MS_NODEV|MS_NOSUID, NULL) != 0) - die_with_error ("Failed to mount tmpfs"); - - old_cwd = get_current_dir_name (); - - if (chdir (newroot) != 0) - die_with_error ("chdir"); - - create_files (create, N_ELEMENTS (create), share_shm, runtime_path); - - /* If stdout is a tty, that means the sandbox can write to the - outside-sandbox tty. In that case we also creata a /dev/console - that points to this tty device. This should not cause any more - access than we already have, and it makes ttyname() work in the - sandbox. */ - if (isatty (1)) - { - char *host_tty_dev = ttyname (1); - if (host_tty_dev != NULL && *host_tty_dev != 0) - { - if (!create_file ("dev/console", 0666, NULL)) - die_with_error ("creating /dev/console"); - - if (bind_mount (host_tty_dev, "dev/console", BIND_DEVICES)) - die_with_error ("mount /dev/console"); - } - } - - if (share_shm) - { - if (bind_mount ("/dev/shm", "dev/shm", BIND_DEVICES)) - die_with_error ("mount /dev/shm"); - } - - if (bind_mount (runtime_path, "usr", BIND_PRIVATE | (writable?0:BIND_READONLY))) - die_with_error ("mount usr"); - - if (lock_files) - add_lock_dir ("usr"); - - if (app_path != NULL) - { - if (bind_mount (app_path, "app", BIND_PRIVATE | (writable_app?0:BIND_READONLY))) - die_with_error ("mount app"); - - if (lock_files) - add_lock_dir ("app"); - - if (!writable_app && writable_exports) - { - char *exports = strconcat (app_path, "/exports"); - - if (bind_mount (exports, "app/exports", BIND_PRIVATE)) - die_with_error ("mount app/exports"); - - free (exports); - } - } - - if (var_path != NULL) - { - if (bind_mount (var_path, "var", BIND_PRIVATE)) - die_with_error ("mount var"); - } - - create_files (create_post, N_ELEMENTS (create_post), share_shm, runtime_path); - - if (create_etc_dir) - link_extra_etc_dirs (); - - if (monitor_path) - { - if (bind_mount (monitor_path, "run/host/monitor", BIND_READONLY)) - die ("can't bind monitor dir"); - } - - /* Bind mount in X socket - * This is a bit iffy, as Xlib typically uses abstract unix domain sockets - * to connect to X, but that is not namespaced. We instead set DISPLAY=99 - * and point /tmp/.X11-unix/X99 to the right X socket. Any Xserver listening - * to global abstract unix domain sockets are still accessible to the app - * though... - */ - if (x11_socket) - { - struct stat st; - - if (stat (x11_socket, &st) == 0 && S_ISSOCK (st.st_mode)) - { - char *xauth_path = strdup_printf ("/run/user/%d/Xauthority", uid); - if (bind_mount (x11_socket, "tmp/.X11-unix/X99", 0)) - die ("can't bind X11 socket"); - - xsetenv ("DISPLAY", ":99.0", 1); - xsetenv ("XAUTHORITY", xauth_path, 1); - free (xauth_path); - } - else - { - xunsetenv ("DISPLAY"); - xunsetenv ("XAUTHORITY"); - } - } - else - { - xunsetenv ("DISPLAY"); - xunsetenv ("XAUTHORITY"); - } - - /* Bind mount in the Wayland socket */ - if (wayland_socket != 0) - { - char *wayland_path_relative = strdup_printf ("run/user/%d/wayland-0", uid); - if (!create_file (wayland_path_relative, 0666, NULL) || - bind_mount (wayland_socket, wayland_path_relative, 0)) - die ("can't bind Wayland socket %s -> %s: %s", wayland_socket, wayland_path_relative, strerror (errno)); - free (wayland_path_relative); - } - - if (pulseaudio_socket != NULL) - { - char *pulse_path_relative = strdup_printf ("run/user/%d/pulse/native", uid); - char *pulse_server = strdup_printf ("unix:/run/user/%d/pulse/native", uid); - char *config_path_relative = strdup_printf ("run/user/%d/pulse/config", uid); - char *config_path_absolute = strdup_printf ("/run/user/%d/pulse/config", uid); - char *client_config = strdup_printf ("enable-shm=%s\n", share_shm ? "yes" : "no"); - - if (create_file (config_path_relative, 0666, client_config) && - bind_mount (pulseaudio_socket, pulse_path_relative, BIND_READONLY) == 0) - { - xsetenv ("PULSE_SERVER", pulse_server, 1); - xsetenv ("PULSE_CLIENTCONFIG", config_path_absolute, 1); - } - else - { - xunsetenv ("PULSE_SERVER"); - } - - free (pulse_path_relative); - free (pulse_server); - free (config_path_relative); - free (config_path_absolute); - free (client_config); - } - - if (system_dbus_socket != NULL) - { - if (create_file ("run/dbus/system_bus_socket", 0666, NULL) && - bind_mount (system_dbus_socket, "run/dbus/system_bus_socket", 0) == 0) - xsetenv ("DBUS_SYSTEM_BUS_ADDRESS", "unix:path=/var/run/dbus/system_bus_socket", 1); - else - xunsetenv ("DBUS_SYSTEM_BUS_ADDRESS"); - } - - if (session_dbus_socket != NULL) - { - char *session_dbus_socket_path_relative = strdup_printf ("run/user/%d/bus", uid); - char *session_dbus_address = strdup_printf ("unix:path=/run/user/%d/bus", uid); - - if (create_file (session_dbus_socket_path_relative, 0666, NULL) && - bind_mount (session_dbus_socket, session_dbus_socket_path_relative, 0) == 0) - xsetenv ("DBUS_SESSION_BUS_ADDRESS", session_dbus_address, 1); - else - xunsetenv ("DBUS_SESSION_BUS_ADDRESS"); - - free (session_dbus_socket_path_relative); - free (session_dbus_address); - } - - if (mount_host_fs) - { - mount_extra_root_dirs (mount_host_fs_ro); - bind_mount ("/run/media", "run/media", BIND_RECURSIVE | (mount_host_fs_ro ? BIND_READONLY : 0)); - } - - if (!mount_host_fs || mount_host_fs_ro) - create_homedir (mount_home, mount_home_ro, app_id); - - if (mount_host_fs || mount_home) - { - char *dconf_run_path = strdup_printf ("/run/user/%d/dconf", uid); - - /* If the user has homedir access, also allow dconf run dir access */ - bind_mount (dconf_run_path, get_relative_path (dconf_run_path), 0); - free (dconf_run_path); - } - - for (i = 0; i < n_extra_files; i++) - { - bool is_dir; - - is_dir = stat_is_dir (extra_files[i].src); - - if (mkdir_with_parents (extra_files[i].dest, 0755, - is_dir && !extra_files[i].move)) - die_with_error ("create extra dir %s", extra_files[i].dest); - - if (extra_files[i].move) - { - if (!copy_file (extra_files[i].src, extra_files[i].dest, 0700)) - die_with_error ("copy extra file %s", extra_files[i].dest); - if (unlink (extra_files[i].src) != 0) - die_with_error ("unlink moved extra file %s", extra_files[i].src); - } - else - { - if (!is_dir) - create_file (extra_files[i].dest, 0700, NULL); - - if (bind_mount (extra_files[i].src, extra_files[i].dest, BIND_PRIVATE | (extra_files[i].readonly ? BIND_READONLY : 0))) - die_with_error ("mount extra dir %s", extra_files[i].src); - - if (lock_files && is_dir) - add_lock_dir (extra_files[i].dest); - } - } - - if (app_path != NULL && - lstat ("run/build", &st_buf) != 0 && - errno == ENOENT) - symlink ("/app/lib/debug/source", "run/build"); - - if (app_path != NULL && - lstat ("run/build-runtime", &st_buf) != 0 && - errno == ENOENT) - symlink ("/usr/lib/debug/source", "run/build-runtime"); - - if (!network) - loopback_setup (); - - if (pivot_root (newroot, ".oldroot")) - die_with_error ("pivot_root"); - - chdir ("/"); - - /* The old root better be rprivate or we will send unmount events to the parent namespace */ - if (mount (".oldroot", ".oldroot", NULL, MS_REC|MS_PRIVATE, NULL) != 0) - die_with_error ("Failed to make old root rprivate"); - - if (umount2 (".oldroot", MNT_DETACH)) - die_with_error ("unmount oldroot"); - - umask (old_umask); - - /* Now we have everything we need CAP_SYS_ADMIN for, so drop it */ - drop_caps (); - - if (chdir_path) - { - if (chdir (chdir_path)) - die_with_error ("Can't chdir to %s", chdir_path); - xsetenv ("PWD", chdir_path, 1); - } - else if (chdir (old_cwd) == 0) - { - xsetenv ("PWD", old_cwd, 1); - } - else - { - /* If the old cwd is not mapped, go to home */ - const char *home = getenv("HOME"); - if (home == NULL) - home = "/"; - - chdir (home); - xsetenv ("PWD", home, 1); - } - free (old_cwd); - - /* We can't pass regular LD_LIBRARY_PATH, as it would affect the - setuid helper aspect, so we use _LD_LIBRARY_PATH */ - if (getenv("_LD_LIBRARY_PATH")) - { - xsetenv ("LD_LIBRARY_PATH", getenv("_LD_LIBRARY_PATH"), 1); - xunsetenv ("_LD_LIBRARY_PATH"); - } - else - xunsetenv ("LD_LIBRARY_PATH"); - - xdg_runtime_dir = strdup_printf ("/run/user/%d", uid); - xsetenv ("XDG_RUNTIME_DIR", xdg_runtime_dir, 1); - free (xdg_runtime_dir); - if (monitor_path) - xsetenv ("TZ", ":/run/host/monitor/localtime", 0); - - __debug__(("forking for child\n")); - - pid = fork (); - if (pid == -1) - die_with_error("Can't fork for child"); - - if (pid == 0) - { - __debug__(("launch executable %s\n", args[0])); - - if (ns_uid != uid || ns_gid != gid) - { - /* Now that devpts is mounted we can create a new userspace - and map our uid 1:1 */ - - if (unshare (CLONE_NEWUSER)) - die_with_error ("unshare user ns"); - - uid_map = strdup_printf ("%d 0 1\n", uid); - if (!write_file ("/proc/self/uid_map", uid_map)) - die_with_error ("setting up uid map"); - free (uid_map); - - gid_map = strdup_printf ("%d 0 1\n", gid); - if (!write_file ("/proc/self/gid_map", gid_map)) - die_with_error ("setting up gid map"); - free (gid_map); - } - - __debug__(("setting up seccomp in child\n")); - setup_seccomp (devel, opt_arch); - - if (sync_fd != -1) - close (sync_fd); - - unblock_sigchild (); - - if (execvp (args[0], args) == -1) - die_with_error ("execvp %s", args[0]); - return 0; - } - - __debug__(("setting up seccomp in monitor\n")); - setup_seccomp (devel, opt_arch); - - /* Close all extra fds in pid 1. - Any passed in fds have been passed on to the child anyway. */ - { - int dont_close[] = { event_fd, sync_fd, -1 }; - fdwalk (close_extra_fds, dont_close); - } - - if (app_id) - set_procname (strdup_printf ("xdg-app-helper %s monitor", app_id)); - return do_init (event_fd, pid); -} |