[OpenWrt-Devel] [PATCH procd v2 15/17] ujail: rework fs jail part

Etienne CHAMPETIER champetier.etienne at gmail.com
Wed Nov 25 12:54:48 EST 2015


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 at gmail.com>
---
 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 <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)
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 <blogic at openwrt.org>
+ * Copyright (C) 2015 Etienne Champetier <champetier.etienne at 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
index 0000000..c2e10bb
--- /dev/null
+++ b/jail/fs.h
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) 2015 Etienne Champetier <champetier.etienne at 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.
+ */
+#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 <stdlib.h>
 #include <unistd.h>
-#include <values.h>
 #include <errno.h>
 #include <string.h>
 #include <sys/stat.h>
@@ -27,11 +26,12 @@
 #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
index 0000000..cbffbb9
--- /dev/null
+++ b/jail/jail.h
@@ -0,0 +1,15 @@
+/*
+ * Copyright (C) 2015 Etienne Champetier <champetier.etienne at 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.
+ */
+#pragma once
+
+int mount_bind(const char *root, const char *path, int readonly, int error);
-- 
1.9.1
_______________________________________________
openwrt-devel mailing list
openwrt-devel at lists.openwrt.org
https://lists.openwrt.org/cgi-bin/mailman/listinfo/openwrt-devel



More information about the openwrt-devel mailing list