From cf6a986d822fc99e4fde3b82dd8f03aca58482bc Mon Sep 17 00:00:00 2001 From: aszlig Date: Tue, 3 Oct 2017 15:27:24 +0200 Subject: pkgs/sandbox: Prepare for querying runtime paths We're now using a makefile for building the sandbox and use pkg-config to pass in the flags we need for compiling against lib(nix)store. Right now the sandbox itself doesn't do anything different because we're not actually using the (incomplete) code for querying the store. Signed-off-by: aszlig --- pkgs/games/build-support/build-sandbox/default.nix | 25 +- pkgs/games/build-support/build-sandbox/sandbox.c | 589 --------------------- .../games/build-support/build-sandbox/src/Makefile | 17 + .../build-support/build-sandbox/src/get-closure.cc | 23 + .../build-support/build-sandbox/src/sandbox.c | 589 +++++++++++++++++++++ 5 files changed, 637 insertions(+), 606 deletions(-) delete mode 100644 pkgs/games/build-support/build-sandbox/sandbox.c create mode 100644 pkgs/games/build-support/build-sandbox/src/Makefile create mode 100644 pkgs/games/build-support/build-sandbox/src/get-closure.cc create mode 100644 pkgs/games/build-support/build-sandbox/src/sandbox.c (limited to 'pkgs') diff --git a/pkgs/games/build-support/build-sandbox/default.nix b/pkgs/games/build-support/build-sandbox/default.nix index 2b45f3e5..3bb41093 100644 --- a/pkgs/games/build-support/build-sandbox/default.nix +++ b/pkgs/games/build-support/build-sandbox/default.nix @@ -1,17 +1,17 @@ -{ stdenv, lib }: +{ stdenv, lib, pkgconfig, nix }: drv: { extraSandboxPaths ? [], ... }@attrs: stdenv.mkDerivation ({ name = "${drv.name}-sandboxed"; - src = drv; + src = ./src; - phases = [ "buildPhase" "installPhase" ]; + inherit drv; exportReferencesGraph = [ "sandbox-closure" drv ]; - buildPhase = '' + configurePhase = '' runtimeDeps="$(sed -ne ' p; n; n @@ -28,7 +28,7 @@ stdenv.mkDerivation ({ y/X/9/ x; n; p; x bcdown - ' sandbox-closure | sort -u)" + ' ../sandbox-closure | sort -u)" echo 'static bool setup_app_paths(void) {' > params.c @@ -45,17 +45,8 @@ stdenv.mkDerivation ({ cat params.c ''; - installPhase = '' - mkdir -p "$out/bin" - for bin in "$src"/bin/*; do - progname="$(basename "$bin")" - gcc -g -std=gnu11 -Wall \ - -DWRAPPED_PATH=\""$bin"\" \ - -DWRAPPED_PROGNAME=\""$progname"\" \ - -DPARAMS_FILE=\""$(pwd)/params.c"\" \ - -DFS_ROOT_DIR=\""$out"\" \ - -o "$out/bin/$progname" ${./sandbox.c} - done - ''; + nativeBuildInputs = [ pkgconfig ]; + buildInputs = [ nix ]; + makeFlags = [ "BINDIR=${drv}/bin" ]; } // removeAttrs attrs [ "extraSandboxPaths" ]) diff --git a/pkgs/games/build-support/build-sandbox/sandbox.c b/pkgs/games/build-support/build-sandbox/sandbox.c deleted file mode 100644 index 9df02296..00000000 --- a/pkgs/games/build-support/build-sandbox/sandbox.c +++ /dev/null @@ -1,589 +0,0 @@ -#define _GNU_SOURCE -#define _POSIX_C_SOURCE 200809L - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static bool write_proc(int proc_pid_fd, const char *fname, const char *buf, - size_t buflen, bool ignore_errors) -{ - int fd; - - if ((fd = openat(proc_pid_fd, fname, O_WRONLY)) == -1) { - fprintf(stderr, "open %s: %s\n", fname, strerror(errno)); - return false; - } - - if (write(fd, buf, buflen) == -1) { - if (!ignore_errors) - fprintf(stderr, "write %s: %s\n", fname, strerror(errno)); - close(fd); - return ignore_errors; - } - - close(fd); - return true; -} - -#define WRITE_IDMAP(file, value) \ - buflen = snprintf(buf, 100, "%1$lu %1$lu 1", (unsigned long)value); \ - if (buflen >= 100) { \ - fputs("Unable to write buffer for " file ".\n", stderr); \ - close(proc_pid_fd); \ - return false; \ - } else if (buflen < 0) { \ - perror("snprintf " file " buffer"); \ - close(proc_pid_fd); \ - return false; \ - } \ - if (!write_proc(proc_pid_fd, file, buf, buflen, false)) { \ - close(proc_pid_fd); \ - return false; \ - } - -static bool write_maps(pid_t parent_pid) -{ - int proc_pid_fd; - size_t buflen; - char buf[100]; - - buflen = snprintf(buf, 100, "/proc/%lu", (unsigned long)parent_pid); - if (buflen >= 100) { - fputs("Unable to write buffer for child pid proc path.\n", stderr); - return false; - } else if (buflen < 0) { - perror("snprintf child pid proc path"); - return false; - } - - if ((proc_pid_fd = open(buf, O_RDONLY | O_DIRECTORY)) == -1) { - fprintf(stderr, "open %s: %s\n", buf, strerror(errno)); - return false; - } - - WRITE_IDMAP("uid_map", geteuid()); - - // Kernels prior to Linux 3.19 which do not impose setgroups() - // restrictions won't have this file, so ignore failure. - write_proc(proc_pid_fd, "setgroups", "deny", 4, true); - - WRITE_IDMAP("gid_map", getegid()); - - return true; -} - -static bool makedirs(const char *path) -{ - char *tmp, *segment; - - if ((tmp = strdup(path)) == NULL) { - fprintf(stderr, "strdup of %s: %s\n", path, strerror(errno)); - return false; - } - - segment = dirname(tmp); - - if (!(segment[0] == '/' && segment[1] == '\0')) { - if (!makedirs(segment)) { - free(tmp); - return false; - } - } - - (void)mkdir(path, 0755); - free(tmp); - return true; -} - -static char *get_mount_target(const char *path) -{ - size_t pathlen = strlen(path), rootdir_len = strlen(FS_ROOT_DIR); - char *target; - - if ((target = malloc(rootdir_len + pathlen + 1)) == NULL) { - perror("malloc mount target"); - return NULL; - } - - memcpy(target, FS_ROOT_DIR, rootdir_len); - memcpy(target + rootdir_len, path, pathlen + 1); - return target; -} - -static bool bind_mount(const char *path, bool restricted, bool resolve) -{ - int mflags = MS_BIND | MS_REC; - char src[PATH_MAX], *target; - - if (restricted) - mflags |= MS_NOSUID | MS_NODEV | MS_NOATIME; - - if (resolve) { - if (realpath(path, src) == NULL) { - fprintf(stderr, "realpath of %s: %s\n", path, strerror(errno)); - return false; - } - } - - if ((target = get_mount_target(resolve ? src : path)) == NULL) - return false; - - if (!makedirs(target)) { - free(target); - return false; - } - - if (mount(resolve ? src : path, target, "", mflags, NULL) == -1) { - fprintf(stderr, "mount %s to %s: %s\n", - resolve ? src : path, target, strerror(errno)); - free(target); - return false; - } - - free(target); - return true; -} - -static bool bind_file(const char *path) -{ - char *target, *tmp; - - if ((target = get_mount_target(path)) == NULL) - return false; - - if ((tmp = strdup(target)) == NULL) { - perror("strdup bind file target path"); - free(target); - return false; - } - - if (!makedirs(dirname(tmp))) { - free(target); - free(tmp); - return false; - } - - free(tmp); - - if (creat(target, 0666) == -1) { - fprintf(stderr, "unable to create %s: %s\n", target, strerror(errno)); - free(target); - return false; - } - - if (mount(path, target, "", MS_BIND, NULL) == -1) { - fprintf(stderr, "mount file %s to %s: %s\n", - path, target, strerror(errno)); - free(target); - return false; - } - - return true; -} - -struct envar_offset { - int start; - int length; - int var_start; - int var_length; - struct envar_offset *next; -}; - -static struct envar_offset *alloc_offset(void) -{ - struct envar_offset *new_offset; - new_offset = malloc(sizeof(struct envar_offset)); - - if (new_offset == NULL) { - perror("malloc envar_offset"); - return NULL; - } - - new_offset->next = NULL; - return new_offset; -} - -static struct envar_offset *push_offset(struct envar_offset *current, - struct envar_offset **base) -{ - if (current == NULL) { - if ((current = alloc_offset()) != NULL) - *base = current; - return current; - } - - return current->next = alloc_offset(); -} - -static void free_offsets(struct envar_offset *base) -{ - struct envar_offset *next; - if (base == NULL) - return; - next = base->next; - free(base); - if (next != NULL) - free_offsets(next); -} - -static char *expand_xdg_fallback(const char *xdg_var) -{ - static char *home = NULL; - static size_t homelen; - char *result; - - if (home == NULL) { - if ((home = getenv("HOME")) == NULL) { - fputs("Unable find $HOME.\n", stderr); - return NULL; - } - homelen = strlen(home); - } - - if (strcmp(xdg_var, "XDG_DATA_HOME") == 0) { - result = malloc(homelen + 14); - if (result == NULL) { - perror("malloc XDG_DATA_HOME"); - return NULL; - } - memcpy(result, home, homelen); - memcpy(result + homelen, "/.local/share", 14); - return result; - } else if (strcmp(xdg_var, "XDG_CONFIG_HOME") == 0) { - result = malloc(homelen + 9); - if (result == NULL) { - perror("malloc XDG_CONFIG_HOME"); - return NULL; - } - memcpy(result, home, homelen); - memcpy(result + homelen, "/.config", 9); - return result; - } - - return NULL; -} - -static char *get_offset_var(struct envar_offset *offset, const char *haystack) -{ - char *tmp, *result; - - tmp = strndup(haystack + offset->var_start, offset->var_length); - - if (tmp == NULL) { - perror("strndup"); - return NULL; - } - - result = getenv(tmp); - if (result == NULL) { - if ((result = expand_xdg_fallback(tmp)) == NULL) { - fprintf(stderr, "Unable find variable %s in %s\n", tmp, haystack); - free(tmp); - return NULL; - } - free(tmp); - return result; - } - free(tmp); - return strdup(result); -} - -static char *replace_env_offset_free(const char *path, - struct envar_offset *offset) -{ - struct envar_offset *tmp_offset; - size_t buflen, pathlen, varlen, tmplen; - int inpos = 0, outpos = 0; - char *buf, *curvar; - - buflen = pathlen = strlen(path); - - if ((buf = malloc(buflen + 1)) == NULL) { - perror("malloc replace_env buffer"); - return NULL; - } - - while (offset != NULL) { - if ((curvar = get_offset_var(offset, path)) == NULL) { - free(buf); - free_offsets(offset); - return NULL; - } - - varlen = strlen(curvar); - tmplen = varlen + (buflen - offset->length); - - if (tmplen > buflen) { - if ((buf = realloc(buf, (buflen = tmplen) + 1)) == NULL) { - perror("realloc replace_env buffer"); - free(buf); - free(curvar); - free_offsets(offset); - return NULL; - } - } - - memcpy(buf + outpos, path + inpos, offset->start - inpos); - outpos += offset->start - inpos; - inpos = offset->start; - - memcpy(buf + outpos, curvar, varlen); - outpos += varlen; - inpos += offset->length; - - free(curvar); - - tmp_offset = offset; - offset = offset->next; - free(tmp_offset); - } - - memcpy(buf + outpos, path + inpos, pathlen - inpos); - *(buf + outpos + (pathlen - inpos)) = '\0'; - - return buf; -} - -static char *replace_env(const char *path) -{ - int i = 0, start = 0, var_start = 0; - size_t pathlen; - bool in_var = false, curly = false; - struct envar_offset *base = NULL, *offset = NULL; - - pathlen = strlen(path); - - while (i < pathlen) { - if (path[i] == '$' && !curly && !in_var) { - if (i + 1 >= pathlen) - break; - - start = i; - - if (path[i + 1] == '{') { - curly = true; - var_start = i + 2; - ++i; - } else { - in_var = true; - var_start = i + 1; - } - } else if (in_var) { - if (!(path[i] >= 'a' && path[i] <= 'z') && - !(path[i] >= 'A' && path[i] <= 'Z') && - !(path[i] >= '0' && path[i] <= '9') && - path[i] != '_' - ) { - in_var = false; - - if ((offset = push_offset(offset, &base)) == NULL) { - free_offsets(base); - return NULL; - } - - offset->start = start; - offset->length = i - start; - offset->var_start = var_start; - offset->var_length = i - var_start; - continue; - } - } else if (curly) { - if (path[i] == '}') { - curly = false; - - if ((offset = push_offset(offset, &base)) == NULL) { - free_offsets(base); - return NULL; - } - - offset->start = start; - offset->length = (i + 1) - offset->start; - offset->var_start = var_start; - offset->var_length = i - offset->var_start; - } - } - - ++i; - } - - if (in_var) { - if ((offset = push_offset(offset, &base)) == NULL) { - free_offsets(base); - return NULL; - } - - offset->start = start; - offset->length = i - start; - offset->var_start = var_start; - offset->var_length = i - var_start; - } - - return replace_env_offset_free(path, base); -} - -static bool extra_mount(const char *path) -{ - char *expanded; - if ((expanded = replace_env(path)) == NULL) - return false; - - if (!bind_mount(expanded, true, true)) { - free(expanded); - return false; - } - - free(expanded); - return true; -} - -static bool setup_xauthority(void) -{ - char *xauth, *home; - bool result; - size_t homelen; - - if ((xauth = getenv("XAUTHORITY")) != NULL) - return bind_file(xauth); - - if ((home = getenv("HOME")) == NULL) { - fputs("Unable find $HOME.\n", stderr); - return false; - } - - homelen = strlen(home); - - if ((xauth = malloc(homelen + 13)) == NULL) { - perror("malloc xauth file path"); - return false; - } - - memcpy(xauth, home, homelen); - memcpy(xauth + homelen, "/.Xauthority", 13); - - result = bind_file(xauth); - free(xauth); - return result; -} - -#include PARAMS_FILE - -static bool setup_chroot(void) -{ - int mflags; - - mflags = MS_NOEXEC | MS_NOSUID | MS_NODEV | MS_NOATIME; - - if (mount("none", FS_ROOT_DIR, "tmpfs", mflags, NULL) == -1) { - perror("mount rootfs"); - return false; - } - - if (!bind_mount("/etc", true, false)) - return false; - - if (!bind_mount("/dev", false, false)) - return false; - - if (!bind_mount("/proc", false, false)) - return false; - - if (!bind_mount("/sys", false, false)) - return false; - - if (!bind_mount("/run", false, false)) - return false; - - if (!bind_mount("/var/run", false, false)) - return false; - - if (!bind_mount("/tmp", true, false)) - return false; - - if (!setup_app_paths()) - return false; - - if (!setup_xauthority()) - return false; - - if (chroot(FS_ROOT_DIR) == -1) { - perror("chroot"); - return false; - } - - if (chdir("/") == -1) { - perror("chdir rootfs"); - return false; - } - - return true; -} - -int main(int argc, char **argv) -{ - int sync_pipe[2]; - char sync_status = '.'; - int child_status; - pid_t pid, parent_pid; - - if (pipe(sync_pipe) == -1) { - perror("pipe"); - return 1; - } - - parent_pid = getpid(); - - switch (pid = fork()) { - case -1: - perror("fork"); - return 1; - case 0: - close(sync_pipe[1]); - if (read(sync_pipe[0], &sync_status, 1) == -1) { - perror("read pipe from parent"); - _exit(1); - } else if (sync_status == 'X') - _exit(1); - close(sync_pipe[0]); - _exit(write_maps(parent_pid) ? 0 : 1); - default: - if (unshare(CLONE_NEWNS | CLONE_NEWUSER) == -1) { - perror("unshare"); - if (write(sync_pipe[1], "X", 1) == -1) - perror("signal child exit"); - waitpid(pid, NULL, 0); - return 1; - } - - close(sync_pipe[1]); - waitpid(pid, &child_status, 0); - if (WIFEXITED(child_status) && WEXITSTATUS(child_status) == 0) - break; - return 1; - } - - if (!setup_chroot()) - return 1; - - argv[0] = WRAPPED_PROGNAME; - if (execv(WRAPPED_PATH, argv) == -1) { - fprintf(stderr, "exec %s: %s\n", WRAPPED_PATH, strerror(errno)); - return 1; - } - - // Should never be reached. - return 1; -} diff --git a/pkgs/games/build-support/build-sandbox/src/Makefile b/pkgs/games/build-support/build-sandbox/src/Makefile new file mode 100644 index 00000000..57c732af --- /dev/null +++ b/pkgs/games/build-support/build-sandbox/src/Makefile @@ -0,0 +1,17 @@ +BINARIES = $(wildcard $(BINDIR)/*) +WRAPPERS = $(subst $(BINDIR),$(out)/bin,$(BINARIES)) + +CFLAGS = -Wall -std=gnu11 -DFS_ROOT_DIR=\"$(out)\" +CXXFLAGS = -Wall -std=c++14 `pkg-config --cflags nix-main` +LDFLAGS = `pkg-config --libs nix-main` + +all: get-closure.o + +$(out)/bin/%: CFLAGS += -DWRAPPED_PROGNAME=\"$(@F)\" +$(out)/bin/%: CFLAGS += -DWRAPPED_PATH=\"$(BINDIR)/$(@F)\" +$(out)/bin/%: get-closure.o + mkdir -p $(out)/bin + $(CC) -o $@ $(CFLAGS) $(LDFLAGS) $? sandbox.c + +.PHONY: install +install: $(WRAPPERS) diff --git a/pkgs/games/build-support/build-sandbox/src/get-closure.cc b/pkgs/games/build-support/build-sandbox/src/get-closure.cc new file mode 100644 index 00000000..11330a9c --- /dev/null +++ b/pkgs/games/build-support/build-sandbox/src/get-closure.cc @@ -0,0 +1,23 @@ +#include +#include +#include +#include + +using namespace nix; + +int get_closure(const char *path) +{ + Path query(path); + PathSet paths; + auto store = openStore(false); + + computeFSClosure( + *store, followLinksToStorePath(query), paths, false, true + ); + + for (auto i = paths.begin(); i != paths.end(); ++i) { + // TODO! + } + + return 1; +} diff --git a/pkgs/games/build-support/build-sandbox/src/sandbox.c b/pkgs/games/build-support/build-sandbox/src/sandbox.c new file mode 100644 index 00000000..dd0fe845 --- /dev/null +++ b/pkgs/games/build-support/build-sandbox/src/sandbox.c @@ -0,0 +1,589 @@ +#define _GNU_SOURCE +#define _POSIX_C_SOURCE 200809L + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static bool write_proc(int proc_pid_fd, const char *fname, const char *buf, + size_t buflen, bool ignore_errors) +{ + int fd; + + if ((fd = openat(proc_pid_fd, fname, O_WRONLY)) == -1) { + fprintf(stderr, "open %s: %s\n", fname, strerror(errno)); + return false; + } + + if (write(fd, buf, buflen) == -1) { + if (!ignore_errors) + fprintf(stderr, "write %s: %s\n", fname, strerror(errno)); + close(fd); + return ignore_errors; + } + + close(fd); + return true; +} + +#define WRITE_IDMAP(file, value) \ + buflen = snprintf(buf, 100, "%1$lu %1$lu 1", (unsigned long)value); \ + if (buflen >= 100) { \ + fputs("Unable to write buffer for " file ".\n", stderr); \ + close(proc_pid_fd); \ + return false; \ + } else if (buflen < 0) { \ + perror("snprintf " file " buffer"); \ + close(proc_pid_fd); \ + return false; \ + } \ + if (!write_proc(proc_pid_fd, file, buf, buflen, false)) { \ + close(proc_pid_fd); \ + return false; \ + } + +static bool write_maps(pid_t parent_pid) +{ + int proc_pid_fd; + size_t buflen; + char buf[100]; + + buflen = snprintf(buf, 100, "/proc/%lu", (unsigned long)parent_pid); + if (buflen >= 100) { + fputs("Unable to write buffer for child pid proc path.\n", stderr); + return false; + } else if (buflen < 0) { + perror("snprintf child pid proc path"); + return false; + } + + if ((proc_pid_fd = open(buf, O_RDONLY | O_DIRECTORY)) == -1) { + fprintf(stderr, "open %s: %s\n", buf, strerror(errno)); + return false; + } + + WRITE_IDMAP("uid_map", geteuid()); + + // Kernels prior to Linux 3.19 which do not impose setgroups() + // restrictions won't have this file, so ignore failure. + write_proc(proc_pid_fd, "setgroups", "deny", 4, true); + + WRITE_IDMAP("gid_map", getegid()); + + return true; +} + +static bool makedirs(const char *path) +{ + char *tmp, *segment; + + if ((tmp = strdup(path)) == NULL) { + fprintf(stderr, "strdup of %s: %s\n", path, strerror(errno)); + return false; + } + + segment = dirname(tmp); + + if (!(segment[0] == '/' && segment[1] == '\0')) { + if (!makedirs(segment)) { + free(tmp); + return false; + } + } + + (void)mkdir(path, 0755); + free(tmp); + return true; +} + +static char *get_mount_target(const char *path) +{ + size_t pathlen = strlen(path), rootdir_len = strlen(FS_ROOT_DIR); + char *target; + + if ((target = malloc(rootdir_len + pathlen + 1)) == NULL) { + perror("malloc mount target"); + return NULL; + } + + memcpy(target, FS_ROOT_DIR, rootdir_len); + memcpy(target + rootdir_len, path, pathlen + 1); + return target; +} + +static bool bind_mount(const char *path, bool restricted, bool resolve) +{ + int mflags = MS_BIND | MS_REC; + char src[PATH_MAX], *target; + + if (restricted) + mflags |= MS_NOSUID | MS_NODEV | MS_NOATIME; + + if (resolve) { + if (realpath(path, src) == NULL) { + fprintf(stderr, "realpath of %s: %s\n", path, strerror(errno)); + return false; + } + } + + if ((target = get_mount_target(resolve ? src : path)) == NULL) + return false; + + if (!makedirs(target)) { + free(target); + return false; + } + + if (mount(resolve ? src : path, target, "", mflags, NULL) == -1) { + fprintf(stderr, "mount %s to %s: %s\n", + resolve ? src : path, target, strerror(errno)); + free(target); + return false; + } + + free(target); + return true; +} + +static bool bind_file(const char *path) +{ + char *target, *tmp; + + if ((target = get_mount_target(path)) == NULL) + return false; + + if ((tmp = strdup(target)) == NULL) { + perror("strdup bind file target path"); + free(target); + return false; + } + + if (!makedirs(dirname(tmp))) { + free(target); + free(tmp); + return false; + } + + free(tmp); + + if (creat(target, 0666) == -1) { + fprintf(stderr, "unable to create %s: %s\n", target, strerror(errno)); + free(target); + return false; + } + + if (mount(path, target, "", MS_BIND, NULL) == -1) { + fprintf(stderr, "mount file %s to %s: %s\n", + path, target, strerror(errno)); + free(target); + return false; + } + + return true; +} + +struct envar_offset { + int start; + int length; + int var_start; + int var_length; + struct envar_offset *next; +}; + +static struct envar_offset *alloc_offset(void) +{ + struct envar_offset *new_offset; + new_offset = malloc(sizeof(struct envar_offset)); + + if (new_offset == NULL) { + perror("malloc envar_offset"); + return NULL; + } + + new_offset->next = NULL; + return new_offset; +} + +static struct envar_offset *push_offset(struct envar_offset *current, + struct envar_offset **base) +{ + if (current == NULL) { + if ((current = alloc_offset()) != NULL) + *base = current; + return current; + } + + return current->next = alloc_offset(); +} + +static void free_offsets(struct envar_offset *base) +{ + struct envar_offset *next; + if (base == NULL) + return; + next = base->next; + free(base); + if (next != NULL) + free_offsets(next); +} + +static char *expand_xdg_fallback(const char *xdg_var) +{ + static char *home = NULL; + static size_t homelen; + char *result; + + if (home == NULL) { + if ((home = getenv("HOME")) == NULL) { + fputs("Unable find $HOME.\n", stderr); + return NULL; + } + homelen = strlen(home); + } + + if (strcmp(xdg_var, "XDG_DATA_HOME") == 0) { + result = malloc(homelen + 14); + if (result == NULL) { + perror("malloc XDG_DATA_HOME"); + return NULL; + } + memcpy(result, home, homelen); + memcpy(result + homelen, "/.local/share", 14); + return result; + } else if (strcmp(xdg_var, "XDG_CONFIG_HOME") == 0) { + result = malloc(homelen + 9); + if (result == NULL) { + perror("malloc XDG_CONFIG_HOME"); + return NULL; + } + memcpy(result, home, homelen); + memcpy(result + homelen, "/.config", 9); + return result; + } + + return NULL; +} + +static char *get_offset_var(struct envar_offset *offset, const char *haystack) +{ + char *tmp, *result; + + tmp = strndup(haystack + offset->var_start, offset->var_length); + + if (tmp == NULL) { + perror("strndup"); + return NULL; + } + + result = getenv(tmp); + if (result == NULL) { + if ((result = expand_xdg_fallback(tmp)) == NULL) { + fprintf(stderr, "Unable find variable %s in %s\n", tmp, haystack); + free(tmp); + return NULL; + } + free(tmp); + return result; + } + free(tmp); + return strdup(result); +} + +static char *replace_env_offset_free(const char *path, + struct envar_offset *offset) +{ + struct envar_offset *tmp_offset; + size_t buflen, pathlen, varlen, tmplen; + int inpos = 0, outpos = 0; + char *buf, *curvar; + + buflen = pathlen = strlen(path); + + if ((buf = malloc(buflen + 1)) == NULL) { + perror("malloc replace_env buffer"); + return NULL; + } + + while (offset != NULL) { + if ((curvar = get_offset_var(offset, path)) == NULL) { + free(buf); + free_offsets(offset); + return NULL; + } + + varlen = strlen(curvar); + tmplen = varlen + (buflen - offset->length); + + if (tmplen > buflen) { + if ((buf = realloc(buf, (buflen = tmplen) + 1)) == NULL) { + perror("realloc replace_env buffer"); + free(buf); + free(curvar); + free_offsets(offset); + return NULL; + } + } + + memcpy(buf + outpos, path + inpos, offset->start - inpos); + outpos += offset->start - inpos; + inpos = offset->start; + + memcpy(buf + outpos, curvar, varlen); + outpos += varlen; + inpos += offset->length; + + free(curvar); + + tmp_offset = offset; + offset = offset->next; + free(tmp_offset); + } + + memcpy(buf + outpos, path + inpos, pathlen - inpos); + *(buf + outpos + (pathlen - inpos)) = '\0'; + + return buf; +} + +static char *replace_env(const char *path) +{ + int i = 0, start = 0, var_start = 0; + size_t pathlen; + bool in_var = false, curly = false; + struct envar_offset *base = NULL, *offset = NULL; + + pathlen = strlen(path); + + while (i < pathlen) { + if (path[i] == '$' && !curly && !in_var) { + if (i + 1 >= pathlen) + break; + + start = i; + + if (path[i + 1] == '{') { + curly = true; + var_start = i + 2; + ++i; + } else { + in_var = true; + var_start = i + 1; + } + } else if (in_var) { + if (!(path[i] >= 'a' && path[i] <= 'z') && + !(path[i] >= 'A' && path[i] <= 'Z') && + !(path[i] >= '0' && path[i] <= '9') && + path[i] != '_' + ) { + in_var = false; + + if ((offset = push_offset(offset, &base)) == NULL) { + free_offsets(base); + return NULL; + } + + offset->start = start; + offset->length = i - start; + offset->var_start = var_start; + offset->var_length = i - var_start; + continue; + } + } else if (curly) { + if (path[i] == '}') { + curly = false; + + if ((offset = push_offset(offset, &base)) == NULL) { + free_offsets(base); + return NULL; + } + + offset->start = start; + offset->length = (i + 1) - offset->start; + offset->var_start = var_start; + offset->var_length = i - offset->var_start; + } + } + + ++i; + } + + if (in_var) { + if ((offset = push_offset(offset, &base)) == NULL) { + free_offsets(base); + return NULL; + } + + offset->start = start; + offset->length = i - start; + offset->var_start = var_start; + offset->var_length = i - var_start; + } + + return replace_env_offset_free(path, base); +} + +static bool extra_mount(const char *path) +{ + char *expanded; + if ((expanded = replace_env(path)) == NULL) + return false; + + if (!bind_mount(expanded, true, true)) { + free(expanded); + return false; + } + + free(expanded); + return true; +} + +static bool setup_xauthority(void) +{ + char *xauth, *home; + bool result; + size_t homelen; + + if ((xauth = getenv("XAUTHORITY")) != NULL) + return bind_file(xauth); + + if ((home = getenv("HOME")) == NULL) { + fputs("Unable find $HOME.\n", stderr); + return false; + } + + homelen = strlen(home); + + if ((xauth = malloc(homelen + 13)) == NULL) { + perror("malloc xauth file path"); + return false; + } + + memcpy(xauth, home, homelen); + memcpy(xauth + homelen, "/.Xauthority", 13); + + result = bind_file(xauth); + free(xauth); + return result; +} + +#include "params.c" + +static bool setup_chroot(void) +{ + int mflags; + + mflags = MS_NOEXEC | MS_NOSUID | MS_NODEV | MS_NOATIME; + + if (mount("none", FS_ROOT_DIR, "tmpfs", mflags, NULL) == -1) { + perror("mount rootfs"); + return false; + } + + if (!bind_mount("/etc", true, false)) + return false; + + if (!bind_mount("/dev", false, false)) + return false; + + if (!bind_mount("/proc", false, false)) + return false; + + if (!bind_mount("/sys", false, false)) + return false; + + if (!bind_mount("/run", false, false)) + return false; + + if (!bind_mount("/var/run", false, false)) + return false; + + if (!bind_mount("/tmp", true, false)) + return false; + + if (!setup_app_paths()) + return false; + + if (!setup_xauthority()) + return false; + + if (chroot(FS_ROOT_DIR) == -1) { + perror("chroot"); + return false; + } + + if (chdir("/") == -1) { + perror("chdir rootfs"); + return false; + } + + return true; +} + +int main(int argc, char **argv) +{ + int sync_pipe[2]; + char sync_status = '.'; + int child_status; + pid_t pid, parent_pid; + + if (pipe(sync_pipe) == -1) { + perror("pipe"); + return 1; + } + + parent_pid = getpid(); + + switch (pid = fork()) { + case -1: + perror("fork"); + return 1; + case 0: + close(sync_pipe[1]); + if (read(sync_pipe[0], &sync_status, 1) == -1) { + perror("read pipe from parent"); + _exit(1); + } else if (sync_status == 'X') + _exit(1); + close(sync_pipe[0]); + _exit(write_maps(parent_pid) ? 0 : 1); + default: + if (unshare(CLONE_NEWNS | CLONE_NEWUSER) == -1) { + perror("unshare"); + if (write(sync_pipe[1], "X", 1) == -1) + perror("signal child exit"); + waitpid(pid, NULL, 0); + return 1; + } + + close(sync_pipe[1]); + waitpid(pid, &child_status, 0); + if (WIFEXITED(child_status) && WEXITSTATUS(child_status) == 0) + break; + return 1; + } + + if (!setup_chroot()) + return 1; + + argv[0] = WRAPPED_PROGNAME; + if (execv(WRAPPED_PATH, argv) == -1) { + fprintf(stderr, "exec %s: %s\n", WRAPPED_PATH, strerror(errno)); + return 1; + } + + // Should never be reached. + return 1; +} -- cgit 1.4.1