ujail: rework fs jail part
[project/procd.git] / jail / fs.c
diff --git a/jail/fs.c b/jail/fs.c
new file mode 100644 (file)
index 0000000..714fa87
--- /dev/null
+++ b/jail/fs.c
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2015 John Crispin <blogic@openwrt.org>
+ * Copyright (C) 2015 Etienne Champetier <champetier.etienne@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1
+ * as published by the Free Software Foundation
+ *
+ * This program 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 General Public License for more details.
+ */
+
+#define _GNU_SOURCE
+
+#include <assert.h>
+#include <elf.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/limits.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <unistd.h>
+
+#include <libubox/avl.h>
+#include <libubox/avl-cmp.h>
+
+#include "elf.h"
+#include "fs.h"
+#include "jail.h"
+#include "log.h"
+
+struct mount {
+        struct avl_node avl;
+        const char *path;
+        int readonly;
+        int error;
+};
+
+struct avl_tree mounts;
+
+int add_mount(const char *path, int readonly, int error)
+{
+       assert(path != NULL);
+
+       if (avl_find(&mounts, path))
+               return 1;
+
+       struct mount *m;
+       m = calloc(1, sizeof(struct mount));
+       assert(m != NULL);
+       m->avl.key = m->path = strdup(path);
+       m->readonly = readonly;
+       m->error = error;
+
+       avl_insert(&mounts, &m->avl);
+       DEBUG("adding mount %s ro(%d) err(%d)\n", m->path, m->readonly, m->error != 0);
+       return 0;
+}
+
+int mount_all(const char *jailroot) {
+       struct library *l;
+       struct mount *m;
+
+       avl_for_each_element(&libraries, l, avl)
+               add_mount(l->path, 1, -1);
+
+       avl_for_each_element(&mounts, m, avl)
+               if (mount_bind(jailroot, m->path, m->readonly, m->error))
+                       return -1;
+
+       return 0;
+}
+
+void mount_list_init(void) {
+       avl_init(&mounts, avl_strcmp, false, NULL);
+}
+
+int add_path_and_deps(const char *path, int readonly, int error, int lib)
+{
+       assert(path != NULL);
+
+       if (lib == 0 && path[0] != '/') {
+               ERROR("%s is not an absolute path\n", path);
+               return error;
+       }
+
+       char *map = NULL;
+       int fd, ret = -1;
+       if (path[0] == '/') {
+               if (avl_find(&mounts, path))
+                       return 0;
+               fd = open(path, O_RDONLY);
+               if (fd == -1)
+                       return error;
+               add_mount(path, readonly, error);
+       } else {
+               if (avl_find(&libraries, path))
+                       return 0;
+               char *fullpath;
+               fd = lib_open(&fullpath, path);
+               if (fd == -1)
+                       return error;
+               if (fullpath) {
+                       alloc_library(fullpath, path);
+                       free(fullpath);
+               }
+       }
+
+       struct stat s;
+       if (fstat(fd, &s) == -1) {
+               ERROR("fstat(%s) failed: %s\n", path, strerror(errno));
+               ret = error;
+               goto out;
+       }
+
+       if (!S_ISREG(s.st_mode)) {
+               ret = 0;
+               goto out;
+       }
+
+       /* too small to be an ELF or a script -> "normal" file */
+       if (s.st_size < 4) {
+               ret = 0;
+               goto out;
+       }
+
+       map = mmap(NULL, s.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+       if (map == MAP_FAILED) {
+               ERROR("failed to mmap %s\n", path);
+               ret = -1;
+               goto out;
+       }
+
+       if (map[0] == ELFMAG0 && map[1] == ELFMAG1 && map[2] == ELFMAG2 && map[3] == ELFMAG3) {
+               ret = elf_load_deps(path, map);
+               goto out;
+       }
+
+       ret = 0;
+
+out:
+       if (fd >= 0)
+               close(fd);
+       if (map)
+               munmap(map, s.st_size);
+
+       return ret;
+}