ujail: rework fs jail part
authorEtienne CHAMPETIER <champetier.etienne@gmail.com>
Fri, 27 Nov 2015 16:27:14 +0000 (16:27 +0000)
committerJohn Crispin <blogic@openwrt.org>
Fri, 27 Nov 2015 16:50:24 +0000 (17:50 +0100)
Change functions to work with full paths (do less split and concat of path)
  Store "soname" as key and the fullpath as path in "libraries"
  Remove "extras" list and replace it with "mounts" avl_tree
  ("mounts" also store fullpath)

Add add_path_and_deps() function to handle file/lib openning and mmaping
  Check if file is an elf (magic number) before passing it to elf_load_deps()
  elf_load_deps() now only handle elf parsing part
  next commit adds script (#!) handling

Use add_path_and_deps() with -r and -w args to automatically add dependencies

Signed-off-by: Etienne CHAMPETIER <champetier.etienne@gmail.com>
CMakeLists.txt
jail/elf.c
jail/elf.h
jail/fs.c [new file with mode: 0644]
jail/fs.h [new file with mode: 0644]
jail/jail.c
jail/jail.h [new file with mode: 0644]

index d749c253cfa57b5cc2e563c5f9a48f6580b6d53d..2718125939ec26ddb30e6a71a8d540f735dc091a 100644 (file)
@@ -87,7 +87,7 @@ ADD_DEPENDENCIES(preload-seccomp syscall-names-h)
 endif()
 
 IF(JAIL_SUPPORT)
-ADD_EXECUTABLE(ujail jail/jail.c jail/elf.c jail/capabilities.c)
+ADD_EXECUTABLE(ujail jail/jail.c jail/elf.c jail/fs.c jail/capabilities.c)
 TARGET_LINK_LIBRARIES(ujail ubox blobmsg_json)
 INSTALL(TARGETS ujail
        RUNTIME DESTINATION ${CMAKE_INSTALL_SBINDIR}
index 46c19df728ef5654b0c6ada58087d1a69ebeeed1..136789e1300dce1b924c13edd91931082d767bd0 100644 (file)
  */
 
 #define _GNU_SOURCE
-#include <sys/mman.h>
 
-#include <stdlib.h>
-#include <unistd.h>
 #include <string.h>
 #include <sys/stat.h>
 #include <fcntl.h>
-#include <libgen.h>
 #include <glob.h>
 #include <elf.h>
 #include <linux/limits.h>
@@ -27,6 +23,7 @@
 #include <libubox/utils.h>
 
 #include "elf.h"
+#include "fs.h"
 #include "log.h"
 
 struct avl_tree libraries;
@@ -52,7 +49,11 @@ static void alloc_library_path(const char *path)
        DEBUG("adding ld.so path %s\n", path);
 }
 
-static void alloc_library(const char *path, const char *name)
+/*
+ * path = full path
+ * name = soname/avl key
+ */
+void alloc_library(const char *path, const char *name)
 {
        struct library *l;
        char *_name, *_path;
@@ -67,47 +68,38 @@ static void alloc_library(const char *path, const char *name)
        l->path = strcpy(_path, path);
 
        avl_insert(&libraries, &l->avl);
-       DEBUG("adding library %s/%s\n", path, name);
+       DEBUG("adding library %s (%s)\n", path, name);
 }
 
-static int elf_open(char **dir, const char *file)
+int lib_open(char **fullpath, const char *file)
 {
        struct library_path *p;
        char path[PATH_MAX];
        int fd = -1;
 
-       *dir = NULL;
+       *fullpath = NULL;
 
        list_for_each_entry(p, &library_paths, list) {
-               if (strlen(p->path))
-                       snprintf(path, sizeof(path), "%s/%s", p->path, file);
-               else
-                       strncpy(path, file, sizeof(path));
+               snprintf(path, sizeof(path), "%s/%s", p->path, file);
                fd = open(path, O_RDONLY);
                if (fd >= 0) {
-                       *dir = p->path;
+                       *fullpath = strdup(path);
                        break;
                }
        }
 
-       if (fd == -1)
-               fd = open(file, O_RDONLY);
-
        return fd;
 }
 
 const char* find_lib(const char *file)
 {
        struct library *l;
-       static char path[PATH_MAX];
 
        l = avl_find_element(&libraries, file, l, avl);
        if (!l)
                return NULL;
 
-       snprintf(path, sizeof(path), "%s/%s", l->path, file);
-
-       return path;
+       return l->path;
 }
 
 static int elf64_find_section(const char *map, unsigned int type, unsigned int *offset, unsigned int *size, unsigned int *vaddr)
@@ -197,7 +189,7 @@ static int elf32_scan_dynamic(const char *map, int dyn_offset, int dyn_size, int
                if (curr->d_tag != DT_NEEDED)
                        continue;
 
-               if (elf_load_deps(&strtab[curr->d_un.d_val]))
+               if (add_path_and_deps(&strtab[curr->d_un.d_val], 1, -1, 1))
                        return -1;
        }
 
@@ -231,79 +223,37 @@ static int elf64_scan_dynamic(const char *map, int dyn_offset, int dyn_size, int
                if (curr->d_tag != DT_NEEDED)
                        continue;
 
-               if (elf_load_deps(&strtab[curr->d_un.d_val]))
+               if (add_path_and_deps(&strtab[curr->d_un.d_val], 1, -1, 1))
                        return -1;
        }
 
        return 0;
 }
 
-int elf_load_deps(const char *library)
+int elf_load_deps(const char *path, const char *map)
 {
        unsigned int dyn_offset, dyn_size;
        unsigned int load_offset, load_vaddr;
-       struct stat s;
-       char *map = NULL, *dir = NULL;
-       int clazz, fd, ret = -1;
-
-       if (avl_find(&libraries, library))
-               return 0;
-
-       fd = elf_open(&dir, library);
-
-       if (fd < 0) {
-               ERROR("failed to open %s\n", library);
-               return -1;
-       }
-
-       if (fstat(fd, &s) == -1) {
-               ERROR("failed to stat %s\n", library);
-               ret = -1;
-               goto err_out;
-       }
-
-       map = mmap(NULL, s.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
-       if (map == MAP_FAILED) {
-               ERROR("failed to mmap %s\n", library);
-               ret = -1;
-               goto err_out;
-       }
 
        if (elf_find_section(map, PT_LOAD, &load_offset, NULL, &load_vaddr)) {
-               ERROR("failed to load the .load section from %s\n", library);
-               ret = -1;
-               goto err_out;
+               ERROR("failed to load the .load section from %s\n", path);
+               return -1;
        }
 
        if (elf_find_section(map, PT_DYNAMIC, &dyn_offset, &dyn_size, NULL)) {
-               ERROR("failed to load the .dynamic section from %s\n", library);
-               ret = -1;
-               goto err_out;
+               ERROR("failed to load the .dynamic section from %s\n", path);
+               return -1;
        }
 
-       if (dir) {
-               alloc_library(dir, library);
-       } else {
-               char *elf1 = strdup(library);
-               char *elf2 = strdup(library);
-
-               alloc_library(dirname(elf1), basename(elf2));
-               free(elf1);
-               free(elf2);
-       }
-       clazz = map[EI_CLASS];
+       int clazz = map[EI_CLASS];
 
        if (clazz == ELFCLASS32)
-               ret = elf32_scan_dynamic(map, dyn_offset, dyn_size, load_vaddr - load_offset);
+               return elf32_scan_dynamic(map, dyn_offset, dyn_size, load_vaddr - load_offset);
        else if (clazz == ELFCLASS64)
-               ret = elf64_scan_dynamic(map, dyn_offset, dyn_size, load_vaddr - load_offset);
-
-err_out:
-       if (map)
-               munmap(map, s.st_size);
-       close(fd);
+               return elf64_scan_dynamic(map, dyn_offset, dyn_size, load_vaddr - load_offset);
 
-       return ret;
+       ERROR("unknown elf format %d\n", clazz);
+       return -1;
 }
 
 static void load_ldso_conf(const char *conf)
index 6244e76c1059d4d306e44ec4b9fa70eceec054e3..78fedcd3323010ab90cea934a0bed530a0185d87 100644 (file)
@@ -29,8 +29,10 @@ struct library_path {
 
 extern struct avl_tree libraries;
 
-int elf_load_deps(const char *library);
+void alloc_library(const char *path, const char *name);
+int elf_load_deps(const char *path, const char *map);
 const char* find_lib(const char *file);
 void init_library_search(void);
+int lib_open(char **fullpath, const char *file);
 
 #endif
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;
+}
diff --git a/jail/fs.h b/jail/fs.h
new file mode 100644 (file)
index 0000000..343335f
--- /dev/null
+++ b/jail/fs.h
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+#ifndef _JAIL_FS_H_
+#define _JAIL_FS_H_
+
+int add_mount(const char *path, int readonly, int error);
+int add_path_and_deps(const char *path, int readonly, int error, int lib);
+int mount_all(const char *jailroot);
+void mount_list_init(void);
+
+#endif
index f62d121b9f1680c593a996a67b3f129f5b80cf7c..4297f717c0bd894885585857156fc1c3fdfa7031 100644 (file)
@@ -18,7 +18,6 @@
 
 #include <stdlib.h>
 #include <unistd.h>
-#include <values.h>
 #include <errno.h>
 #include <string.h>
 #include <sys/stat.h>
 #include <sched.h>
 #include <linux/limits.h>
 
-#include "elf.h"
 #include "capabilities.h"
+#include "elf.h"
+#include "fs.h"
+#include "jail.h"
 #include "log.h"
 
-#include <libubox/list.h>
 #include <libubox/uloop.h>
 
 #define STACK_SIZE     (1024 * 1024)
@@ -49,16 +49,6 @@ static struct {
        int sysfs;
 } opts;
 
-struct extra {
-       struct list_head list;
-
-       const char *path;
-       const char *name;
-       int readonly;
-};
-
-static LIST_HEAD(extras);
-
 extern int pivot_root(const char *new_root, const char *put_old);
 
 int debug = 0;
@@ -90,28 +80,23 @@ static int mkdir_p(char *dir, mode_t mask)
        return ret;
 }
 
-static int mount_bind(const char *root, const char *path, const char *name, int readonly, int error)
+int mount_bind(const char *root, const char *path, int readonly, int error)
 {
        struct stat s;
-       char old[PATH_MAX];
        char new[PATH_MAX];
        int fd;
 
-       snprintf(old, sizeof(old), "%s/%s", path, name);
-       snprintf(new, sizeof(new), "%s%s", root, path);
-
-       mkdir_p(new, 0755);
-
-       snprintf(new, sizeof(new), "%s%s/%s", root, path, name);
-
-       if (stat(old, &s)) {
-               ERROR("%s does not exist\n", old);
+       if (stat(path, &s)) {
+               ERROR("stat(%s) failed: %s\n", path, strerror(errno));
                return error;
        }
 
+       snprintf(new, sizeof(new), "%s%s", root, path);
        if (S_ISDIR(s.st_mode)) {
                mkdir_p(new, 0755);
        } else {
+               mkdir_p(dirname(new), 0755);
+               snprintf(new, sizeof(new), "%s%s", root, path);
                fd = creat(new, 0644);
                if (fd == -1) {
                        ERROR("failed to create %s: %s\n", new, strerror(errno));
@@ -120,8 +105,8 @@ static int mount_bind(const char *root, const char *path, const char *name, int
                close(fd);
        }
 
-       if (mount(old, new, NULL, MS_BIND, NULL)) {
-               ERROR("failed to mount -B %s %s: %s\n", old, new, strerror(errno));
+       if (mount(path, new, NULL, MS_BIND, NULL)) {
+               ERROR("failed to mount -B %s %s: %s\n", path, new, strerror(errno));
                return -1;
        }
 
@@ -130,16 +115,13 @@ static int mount_bind(const char *root, const char *path, const char *name, int
                return -1;
        }
 
-       DEBUG("mount -B %s %s\n", old, new);
+       DEBUG("mount -B %s %s\n", path, new);
 
        return 0;
 }
 
 static int build_jail_fs(void)
 {
-       struct library *l;
-       struct extra *m;
-
        if (mount("tmpfs", opts.path, "tmpfs", MS_NOATIME, "mode=0755")) {
                ERROR("tmpfs mount failed %s\n", strerror(errno));
                return -1;
@@ -150,25 +132,20 @@ static int build_jail_fs(void)
                return -1;
        }
 
-       init_library_search();
-
-       if (elf_load_deps(*opts.jail_argv)) {
+       if (add_path_and_deps(*opts.jail_argv, 1, -1, 0)) {
                ERROR("failed to load dependencies\n");
                return -1;
        }
 
-       if (opts.seccomp && elf_load_deps("libpreload-seccomp.so")) {
+       if (opts.seccomp && add_path_and_deps("libpreload-seccomp.so", 1, -1, 1)) {
                ERROR("failed to load libpreload-seccomp.so\n");
                return -1;
        }
 
-       avl_for_each_element(&libraries, l, avl)
-               if (mount_bind(opts.path, l->path, l->name, 1, -1))
-                       return -1;
-
-       list_for_each_entry(m, &extras, list)
-               if (mount_bind(opts.path, m->path, m->name, m->readonly, 0))
-                       return -1;
+       if (mount_all(opts.path)) {
+               ERROR("mount_all() failed\n");
+               return -1;
+       }
 
        char *mpoint;
        if (asprintf(&mpoint, "%s/old", opts.path) < 0) {
@@ -299,24 +276,6 @@ static struct uloop_process jail_process = {
        .cb = jail_process_handler,
 };
 
-static void add_extra(char *name, int readonly)
-{
-       struct extra *f;
-
-       if (*name != '/') {
-               ERROR("%s is not an absolute path\n", name);
-               return;
-       }
-
-       f = calloc(1, sizeof(struct extra));
-
-       f->name = basename(name);
-       f->path = dirname(strdup(name));
-       f->readonly = readonly;
-
-       list_add_tail(&f->list, &extras);
-}
-
 int main(int argc, char **argv)
 {
        uid_t uid = getuid();
@@ -331,6 +290,8 @@ int main(int argc, char **argv)
        }
 
        umask(022);
+       mount_list_init();
+       init_library_search();
 
        while ((ch = getopt(argc, argv, OPT_ARGS)) != -1) {
                switch (ch) {
@@ -351,11 +312,11 @@ int main(int argc, char **argv)
                        break;
                case 'S':
                        opts.seccomp = optarg;
-                       add_extra(optarg, 1);
+                       add_mount(optarg, 1, -1);
                        break;
                case 'C':
                        opts.capabilities = optarg;
-                       add_extra(optarg, 1);
+                       add_mount(optarg, 1, -1);
                        break;
                case 'P':
                        opts.namespace = 1;
@@ -366,19 +327,19 @@ int main(int argc, char **argv)
                        break;
                case 'r':
                        opts.namespace = 1;
-                       add_extra(optarg, 1);
+                       add_path_and_deps(optarg, 1, 0, 0);
                        break;
                case 'w':
                        opts.namespace = 1;
-                       add_extra(optarg, 0);
+                       add_path_and_deps(optarg, 0, 0, 0);
                        break;
                case 'u':
                        opts.namespace = 1;
-                       add_extra(ubus, 0);
+                       add_mount(ubus, 0, -1);
                        break;
                case 'l':
                        opts.namespace = 1;
-                       add_extra(log, 0);
+                       add_mount(log, 0, -1);
                        break;
                }
        }
diff --git a/jail/jail.h b/jail/jail.h
new file mode 100644 (file)
index 0000000..5739d3d
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * 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.
+ */
+#ifndef _JAIL_JAIL_H_
+#define _JAIL_JAIL_H_
+
+int mount_bind(const char *root, const char *path, int readonly, int error);
+
+#endif