From 15ff8b1d09187c11b1331f811cc75b0dd2076827 Mon Sep 17 00:00:00 2001 From: Etienne CHAMPETIER Date: Fri, 27 Nov 2015 16:27:14 +0000 Subject: [PATCH] ujail: rework fs jail part 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 --- CMakeLists.txt | 2 +- jail/elf.c | 98 +++++++++---------------------------- jail/elf.h | 4 +- jail/fs.c | 152 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ jail/fs.h | 21 ++++++++ jail/jail.c | 91 ++++++++++------------------------ jail/jail.h | 18 +++++++ 7 files changed, 245 insertions(+), 141 deletions(-) create mode 100644 jail/fs.c create mode 100644 jail/fs.h create mode 100644 jail/jail.h diff --git a/CMakeLists.txt b/CMakeLists.txt index d749c25..2718125 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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} diff --git a/jail/elf.c b/jail/elf.c index 46c19df..136789e 100644 --- a/jail/elf.c +++ b/jail/elf.c @@ -12,14 +12,10 @@ */ #define _GNU_SOURCE -#include -#include -#include #include #include #include -#include #include #include #include @@ -27,6 +23,7 @@ #include #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) diff --git a/jail/elf.h b/jail/elf.h index 6244e76..78fedcd 100644 --- a/jail/elf.h +++ b/jail/elf.h @@ -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 index 0000000..714fa87 --- /dev/null +++ b/jail/fs.c @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2015 John Crispin + * Copyright (C) 2015 Etienne Champetier + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#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 index 0000000..343335f --- /dev/null +++ b/jail/fs.h @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2015 Etienne Champetier + * + * 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 diff --git a/jail/jail.c b/jail/jail.c index f62d121..4297f71 100644 --- a/jail/jail.c +++ b/jail/jail.c @@ -18,7 +18,6 @@ #include #include -#include #include #include #include @@ -27,11 +26,12 @@ #include #include -#include "elf.h" #include "capabilities.h" +#include "elf.h" +#include "fs.h" +#include "jail.h" #include "log.h" -#include #include #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 index 0000000..5739d3d --- /dev/null +++ b/jail/jail.h @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2015 Etienne Champetier + * + * 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 -- 2.11.0