From patchwork Wed Nov 25 17:54:48 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Etienne Champetier X-Patchwork-Id: 548694 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from arrakis.dune.hu (arrakis.dune.hu [78.24.191.176]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id D93241402BB for ; Thu, 26 Nov 2015 05:06:36 +1100 (AEDT) Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b=b4M6eZvP; dkim-atps=neutral Received: from arrakis.dune.hu (localhost [127.0.0.1]) by arrakis.dune.hu (Postfix) with ESMTP id D42DC28C16E; Wed, 25 Nov 2015 18:57:46 +0100 (CET) X-Spam-Checker-Version: SpamAssassin 3.3.2 (2011-06-06) on arrakis.dune.hu X-Spam-Level: X-Spam-Status: No, score=-1.5 required=5.0 tests=BAYES_00,FREEMAIL_FROM, T_DKIM_INVALID autolearn=unavailable version=3.3.2 Received: from arrakis.dune.hu (localhost [127.0.0.1]) by arrakis.dune.hu (Postfix) with ESMTP id 2DA8628BF7B for ; Wed, 25 Nov 2015 18:55:41 +0100 (CET) X-policyd-weight: using cached result; rate: -8.5 Received: from mail-wm0-f50.google.com (mail-wm0-f50.google.com [74.125.82.50]) by arrakis.dune.hu (Postfix) with ESMTPS for ; Wed, 25 Nov 2015 18:55:12 +0100 (CET) Received: by wmvv187 with SMTP id v187so268695381wmv.1 for ; Wed, 25 Nov 2015 09:55:12 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=/k5y3yyk0BRQeTIzlbIQ26/45BoKxwzlPQocuSsNbXM=; b=b4M6eZvPtI8THOkxcVdZKxesaFriRl8VeIER7NDUJtzReaQUXL8WZuqN0eJGoZNSb9 G6JsfflYOKLmhpLAdHb4oPpmW+uUvm3BYdF++QB1ifVNtF+Cei9UdVBIh4VPlVGMa6z3 OjzMBtY7gcuU0OBHoz1yGUHowcoW7EiWlPvtCVJ62UpGyHhSq+btgiqhe57y4oU8q6ri bp3GuVbINFi/YLiHwYosQAy9VkQPnO1+xE305cD/ICvq8gLb+UuUscQm/tv60WQVPbUK PUM24pnLIx1lJWHmWuT+R4dwQHT/cy9KU69cGy5xHIsguyvdke1LnuCXy8vbvCWYB8Ck 10+g== X-Received: by 10.194.57.178 with SMTP id j18mr52408095wjq.113.1448474112575; Wed, 25 Nov 2015 09:55:12 -0800 (PST) Received: from ubuntu1404.lxcnattst (ns623510.ovh.net. [5.135.134.9]) by smtp.gmail.com with ESMTPSA id cv3sm24180221wjc.20.2015.11.25.09.55.11 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Wed, 25 Nov 2015 09:55:12 -0800 (PST) From: Etienne CHAMPETIER To: OpenWrt Development List Date: Wed, 25 Nov 2015 17:54:48 +0000 Message-Id: <1448474090-30280-16-git-send-email-champetier.etienne@gmail.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1448474090-30280-1-git-send-email-champetier.etienne@gmail.com> References: <1448474090-30280-1-git-send-email-champetier.etienne@gmail.com> Subject: [OpenWrt-Devel] [PATCH procd v2 15/17] ujail: rework fs jail part X-BeenThere: openwrt-devel@lists.openwrt.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: OpenWrt Development List List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: openwrt-devel-bounces@lists.openwrt.org Sender: "openwrt-devel" 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 | 153 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ jail/fs.h | 18 +++++++ jail/jail.c | 91 ++++++++++------------------------ jail/jail.h | 15 ++++++ 7 files changed, 240 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 bb9c143..e34a383 100644 --- a/jail/elf.h +++ b/jail/elf.h @@ -28,6 +28,8 @@ 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); diff --git a/jail/fs.c b/jail/fs.c new file mode 100644 index 0000000..aeab730 --- /dev/null +++ b/jail/fs.c @@ -0,0 +1,153 @@ +/* + * 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..c2e10bb --- /dev/null +++ b/jail/fs.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. + */ +#pragma once + +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); 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..cbffbb9 --- /dev/null +++ b/jail/jail.h @@ -0,0 +1,15 @@ +/* + * 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. + */ +#pragma once + +int mount_bind(const char *root, const char *path, int readonly, int error);