[OpenWrt-Devel] [PATCH 2/2] [procd, RFC] ujail: reworks & cleanups
Etienne CHAMPETIER
champetier.etienne at gmail.com
Thu Aug 20 18:39:51 EDT 2015
This is an RFC patch for ujail
-use EXIT_SUCCESS/EXIT_FAILURE (not -1)
-parse every options in main, put them in opts struct
-add CLONE_NEWIPC to the clone() call (it's already compiled in openwrt kernel)
-return the exit status of the jailed process, or the num of the signal that killed it
-add missing options to usage()
-add a warning in usage() about ujail security
-debug option can now take an int as parameter (~debug level),
with -d2 you now activate "LD_DEBUG=all" for exemple
-do not depend on libpreload-seccomp.so if -S is not present
-there is now only one ujail process instead of two
jail creation is now as follow:
1) create jail root dir (mkdir)
2) create new namespace (clone)
(in the parent wait for the child with uloop)
3) build the jail root fs (mount bind all the libs/bins ...),
pivot_root and mount special fs (procfs, sysfs) (build_jail_fs())
4) build envp (LD_PRELOAD the seccomp helper or ...)
5) coming soon: drop capabilities()
6) execve the jailed bin
7) remove jail root dir (once child is dead)
there is no need to umount anything because we are already in the namespace
Todo:
-add capabilities() support
-allow signals from the parent to the child
Feature request:
-when we add a file or dir, detect if it's an exec and add it's dependencies
Signed-off-by: Etienne CHAMPETIER <champetier.etienne at gmail.com>
---
jail/jail.c | 390 ++++++++++++++++++++++++------------------------------------
1 file changed, 155 insertions(+), 235 deletions(-)
diff --git a/jail/jail.c b/jail/jail.c
index 2bba292..dd46c86 100644
--- a/jail/jail.c
+++ b/jail/jail.c
@@ -43,7 +43,17 @@
#include <libubox/uloop.h>
#define STACK_SIZE (1024 * 1024)
-#define OPT_ARGS "P:S:n:r:w:psuldo"
+#define OPT_ARGS "P:S:n:r:w:d:psulo"
+
+static struct {
+ char *path;
+ char *name;
+ char **jail_argv;
+ char *seccomp;
+ int procfs;
+ int ronly;
+ int sysfs;
+} opts;
struct extra {
struct list_head list;
@@ -125,7 +135,7 @@ static int mount_bind(const char *root, const char *path, const char *name, int
return -1;
}
- if (readonly && mount(old, new, NULL, MS_BIND | MS_REMOUNT | MS_RDONLY, NULL)) {
+ if (readonly && mount(NULL, new, NULL, MS_BIND | MS_REMOUNT | MS_RDONLY, NULL)) {
ERROR("failed to remount ro %s: %s\n", new, strerror(errno));
return -1;
}
@@ -135,80 +145,75 @@ static int mount_bind(const char *root, const char *path, const char *name, int
return 0;
}
-static int build_jail(const char *path)
+static int build_jail_fs()
{
struct library *l;
struct extra *m;
- int ret = 0;
- mkdir(path, 0755);
-
- if (mount("tmpfs", path, "tmpfs", MS_NOATIME, "mode=0755")) {
+ if (mount("tmpfs", opts.path, "tmpfs", MS_NOATIME, "mode=0755")) {
ERROR("tmpfs mount failed %s\n", strerror(errno));
return -1;
}
- avl_for_each_element(&libraries, l, avl)
- if (mount_bind(path, l->path, l->name, 1, -1))
- return -1;
-
- list_for_each_entry(m, &extras, list)
- if (mount_bind(path, m->path, m->name, m->readonly, 0))
- return -1;
-
- return ret;
-}
+ if (chdir(opts.path)) {
+ ERROR("failed to chdir() in the jail root\n");
+ return -1;
+ }
-static void _umount(const char *root, const char *path)
-{
- char *buf = NULL;
+ avl_init(&libraries, avl_strcmp, false, NULL);
+ alloc_library_path("/lib64");
+ alloc_library_path("/lib");
+ alloc_library_path("/usr/lib");
+ load_ldso_conf("/etc/ld.so.conf");
- if (asprintf(&buf, "%s%s", root, path) < 0) {
- ERROR("failed to alloc umount buffer: %s\n", strerror(errno));
- } else {
- DEBUG("umount %s\n", buf);
- umount(buf);
- free(buf);
+ if (elf_load_deps(*opts.jail_argv)) {
+ ERROR("failed to load dependencies\n");
+ return -1;
}
-}
-static int stop_jail(const char *root)
-{
- struct library *l;
- struct extra *m;
+ if (opts.seccomp && elf_load_deps("libpreload-seccomp.so")) {
+ ERROR("failed to load libpreload-seccomp.so\n");
+ return -1;
+ }
- avl_for_each_element(&libraries, l, avl) {
- char path[256];
- char *p = l->path;
+ avl_for_each_element(&libraries, l, avl)
+ if (mount_bind(opts.path, l->path, l->name, 1, -1))
+ return -1;
- if (strstr(p, "local"))
- p = "/lib";
+ list_for_each_entry(m, &extras, list)
+ if (mount_bind(opts.path, m->path, m->name, m->readonly, 0))
+ return -1;
- snprintf(path, sizeof(path), "%s%s/%s", root, p, l->name);
- DEBUG("umount %s\n", path);
- umount(path);
+ char *mpoint;
+ if (asprintf(&mpoint, "%s/old", opts.path) < 0) {
+ ERROR("failed to alloc pivot path: %s\n", strerror(errno));
+ return -1;
}
-
- list_for_each_entry(m, &extras, list) {
- char path[256];
-
- snprintf(path, sizeof(path), "%s%s/%s", root, m->path, m->name);
- DEBUG("umount %s\n", path);
- umount(path);
+ mkdir_p(mpoint, 0755);
+ if (pivot_root(opts.path, mpoint) == -1) {
+ ERROR("pivot_root failed:%s\n", strerror(errno));
+ free(mpoint);
+ return -1;
}
-
- _umount(root, "/proc");
- _umount(root, "/sys");
-
- DEBUG("umount %s\n", root);
- umount(root);
- rmdir(root);
+ free(mpoint);
+ umount2("/old", MNT_DETACH);
+ rmdir("/old");
+ if (opts.procfs) {
+ mkdir("/proc", 0755);
+ mount("proc", "/proc", "proc", MS_NOATIME, 0);
+ }
+ if (opts.sysfs) {
+ mkdir("/sys", 0755);
+ mount("sysfs", "/sys", "sysfs", MS_NOATIME, 0);
+ }
+ if (opts.ronly)
+ mount(NULL, "/", NULL, MS_RDONLY | MS_REMOUNT, 0);
return 0;
}
#define MAX_ENVP 8
-static char** build_envp(const char *seccomp, int debug)
+static char** build_envp(const char *seccomp)
{
static char *envp[MAX_ENVP];
static char preload_var[64];
@@ -227,177 +232,77 @@ static char** build_envp(const char *seccomp, int debug)
snprintf(preload_var, sizeof(preload_var), "LD_PRELOAD=%s", preload_lib);
envp[count++] = preload_var;
}
- if (debug)
+ if (debug > 1)
envp[count++] = debug_var;
return envp;
}
-static int spawn(const char *path, char **argv, const char *seccomp)
-{
- pid_t pid = fork();
-
- if (pid < 0) {
- ERROR("failed to spawn %s: %s\n", *argv, strerror(errno));
- return -1;
- } else if (!pid) {
- char **envp = build_envp(seccomp, 0);
-
- INFO("spawning %s\n", *argv);
- execve(*argv, argv, envp);
- ERROR("failed to spawn child %s: %s\n", *argv, strerror(errno));
- exit(-1);
- }
-
- return pid;
-}
-
-static int usage(void)
+static void usage(void)
{
- fprintf(stderr, "jail <options> -D <binary> <params ...>\n");
+ fprintf(stderr, "ujail <options> -- <binary> <params ...>\n");
fprintf(stderr, " -P <path>\tpath where the jail will be staged\n");
fprintf(stderr, " -S <file>\tseccomp filter\n");
fprintf(stderr, " -n <name>\tthe name of the jail\n");
fprintf(stderr, " -r <file>\treadonly files that should be staged\n");
fprintf(stderr, " -w <file>\twriteable files that should be staged\n");
- fprintf(stderr, " -p\t\tjail has /proc\t\n");
- fprintf(stderr, " -s\t\tjail has /sys\t\n");
- fprintf(stderr, " -l\t\tjail has /dev/log\t\n");
- fprintf(stderr, " -u\t\tjail has a ubus socket\t\n");
-
- return -1;
-}
-
-static int child_running = 1;
-
-static void child_process_handler(struct uloop_process *c, int ret)
-{
- INFO("child (%d) exited: %d\n", c->pid, ret);
- uloop_end();
- child_running = 0;
+ fprintf(stderr, " -d <num>\tshow debug log (increase num to increase verbosity)\n");
+ fprintf(stderr, " -p\t\tjail has /proc\n");
+ fprintf(stderr, " -s\t\tjail has /sys\n");
+ fprintf(stderr, " -l\t\tjail has /dev/log\n");
+ fprintf(stderr, " -u\t\tjail has a ubus socket\n");
+ fprintf(stderr, " -o\t\tremont jail root (/) read only\n");
+ fprintf(stderr, "\nWarning: by default root inside the jail is the same\n\
+and he has the same powers as root outside the jail,\n\
+thus he can escape the jail and/or break stuff.\n\
+Please use an appropriate seccomp filter (-S) to restrict his powers\n");
}
-struct uloop_process child_process = {
- .cb = child_process_handler,
-};
-
-static int spawn_child(void *arg)
+static int spawn_jail(void *arg)
{
- char *path = get_current_dir_name();
- int procfs = 0, sysfs = 0;
- char *seccomp = NULL;
- char **argv = arg;
- int argc = 0, ch;
- char *mpoint;
- int ronly = 0;
-
- while (argv[argc])
- argc++;
-
- optind = 0;
- while ((ch = getopt(argc, argv, OPT_ARGS)) != -1) {
- switch (ch) {
- case 'd':
- debug = 1;
- break;
- case 'S':
- seccomp = optarg;
- break;
- case 'p':
- procfs = 1;
- break;
- case 'o':
- ronly = 1;
- break;
- case 's':
- sysfs = 1;
- break;
- case 'n':
- if (sethostname(optarg, strlen(optarg)))
- ERROR("failed to sethostname: %s\n", strerror(errno));
- break;
- }
+ if (opts.name && sethostname(opts.name, strlen(opts.name))) {
+ ERROR("failed to sethostname: %s\n", strerror(errno));
}
- if (asprintf(&mpoint, "%s/old", path) < 0) {
- ERROR("failed to alloc pivot path: %s\n", strerror(errno));
- return -1;
- }
- mkdir_p(mpoint, 0755);
- if (pivot_root(path, mpoint) == -1) {
- ERROR("pivot_root failed:%s\n", strerror(errno));
- return -1;
- }
- free(mpoint);
- umount2("/old", MNT_DETACH);
- rmdir("/old");
- if (procfs) {
- mkdir("/proc", 0755);
- mount("proc", "/proc", "proc", MS_NOATIME, 0);
+ if (build_jail_fs()) {
+ ERROR("failed to build jail fs");
+ exit(EXIT_FAILURE);
}
- if (sysfs) {
- mkdir("/sys", 0755);
- mount("sysfs", "/sys", "sysfs", MS_NOATIME, 0);
- }
- if (ronly)
- mount(NULL, "/", NULL, MS_RDONLY | MS_REMOUNT, 0);
- uloop_init();
+ char **envp = build_envp(opts.seccomp);
+ if (!envp)
+ exit(EXIT_FAILURE);
- child_process.pid = spawn(path, &argv[optind], seccomp);
- uloop_process_add(&child_process);
- uloop_run();
- uloop_done();
- if (child_running) {
- kill(child_process.pid, SIGTERM);
- waitpid(child_process.pid, NULL, 0);
- }
+ //TODO: drop capabilities() here
+ //prctl(PR_CAPBSET_DROP, ..., 0, 0, 0);
- return 0;
+ INFO("exec-ing %s\n", *opts.jail_argv);
+ execve(*opts.jail_argv, opts.jail_argv, envp);
+ //we get there only if execve fails
+ ERROR("failed to execve %s: %s\n", *opts.jail_argv, strerror(errno));
+ exit(EXIT_FAILURE);
}
-static int namespace_running = 1;
+static int jail_running = 1;
+static int jail_return_code = 0;
-static void namespace_process_handler(struct uloop_process *c, int ret)
+static void jail_process_handler(struct uloop_process *c, int ret)
{
- INFO("namespace (%d) exited: %d\n", c->pid, ret);
+ if (WIFEXITED(ret)) {
+ jail_return_code = WEXITSTATUS(ret);
+ INFO("jail (%d) exited with exit: %d\n", c->pid, jail_return_code);
+ } else {
+ jail_return_code = WTERMSIG(ret);
+ INFO("jail (%d) exited with signal: %d\n", c->pid, jail_return_code);
+ }
+ jail_running = 0;
uloop_end();
- namespace_running = 0;
}
-struct uloop_process namespace_process = {
- .cb = namespace_process_handler,
+static struct uloop_process jail_process = {
+ .cb = jail_process_handler,
};
-static void spawn_namespace(const char *path, int argc, char **argv)
-{
- char *dir = get_current_dir_name();
-
- uloop_init();
- if (chdir(path)) {
- ERROR("failed to chdir() into the jail\n");
- return;
- }
- namespace_process.pid = clone(spawn_child,
- child_stack + STACK_SIZE,
- CLONE_NEWUTS | CLONE_NEWPID | CLONE_NEWNS | SIGCHLD, argv);
-
- if (namespace_process.pid != -1) {
- if (chdir(dir))
- ERROR("failed to chdir() out of the jail\n");
- free(dir);
- uloop_process_add(&namespace_process);
- uloop_run();
- uloop_done();
- if (namespace_running) {
- kill(namespace_process.pid, SIGTERM);
- waitpid(namespace_process.pid, NULL, 0);
- }
- } else {
- ERROR("failed to spawn namespace: %s\n", strerror(errno));
- }
-}
-
static void add_extra(char *name, int readonly)
{
struct extra *f;
@@ -419,16 +324,14 @@ static void add_extra(char *name, int readonly)
int main(int argc, char **argv)
{
uid_t uid = getuid();
- const char *name = NULL;
- char *path = NULL;
- struct stat s;
- int ch, ret;
char log[] = "/dev/log";
char ubus[] = "/var/run/ubus.sock";
+ int ret = EXIT_SUCCESS;
+ int ch;
if (uid) {
ERROR("not root, aborting: %s\n", strerror(errno));
- return -1;
+ return EXIT_FAILURE;
}
umask(022);
@@ -436,15 +339,26 @@ int main(int argc, char **argv)
while ((ch = getopt(argc, argv, OPT_ARGS)) != -1) {
switch (ch) {
case 'd':
- debug = 1;
+ debug = atoi(optarg);
+ break;
+ case 'p':
+ opts.procfs = 1;
+ break;
+ case 'o':
+ opts.ronly = 1;
+ break;
+ case 's':
+ opts.sysfs = 1;
+ break;
+ case 'S':
+ opts.seccomp = optarg;
break;
case 'P':
- path = optarg;
+ opts.path = optarg;
break;
case 'n':
- name = optarg;
+ opts.name = optarg;
break;
- case 'S':
case 'r':
add_extra(optarg, 1);
break;
@@ -460,46 +374,52 @@ int main(int argc, char **argv)
}
}
- if (argc - optind < 1)
- return usage();
+ //no <binary> param found
+ if (argc - optind < 1) {
+ usage();
+ return EXIT_FAILURE;
+ }
- if (!path && asprintf(&path, "/tmp/%s", basename(argv[optind])) == -1) {
- ERROR("failed to set root path\n: %s", strerror(errno));
- return -1;
+ opts.jail_argv = &argv[optind];
+
+ if (!opts.path && asprintf(&opts.path, "/tmp/%s", basename(*opts.jail_argv)) == -1) {
+ ERROR("failed to asprintf root path: %s\n", strerror(errno));
+ return EXIT_FAILURE;
}
- if (!stat(path, &s)) {
- ERROR("%s already exists: %s\n", path, strerror(errno));
- return -1;
+ if (mkdir(opts.path, 0755)) {
+ ERROR("unable to create root path: %s (%s)\n", opts.path, strerror(errno));
+ return EXIT_FAILURE;
}
- if (name)
- prctl(PR_SET_NAME, name, NULL, NULL, NULL);
+ if (opts.name)
+ prctl(PR_SET_NAME, opts.name, NULL, NULL, NULL);
- avl_init(&libraries, avl_strcmp, false, NULL);
- alloc_library_path("/lib64");
- alloc_library_path("/lib");
- alloc_library_path("/usr/lib");
- load_ldso_conf("/etc/ld.so.conf");
+ uloop_init();
+ jail_process.pid = clone(spawn_jail,
+ child_stack + STACK_SIZE,
+ CLONE_NEWUTS | CLONE_NEWPID | CLONE_NEWNS | CLONE_NEWIPC | SIGCHLD, argv);
- if (elf_load_deps(argv[optind])) {
- ERROR("failed to load dependencies\n");
- return -1;
+ if (jail_process.pid != -1) {
+ uloop_process_add(&jail_process);
+ uloop_run();
+ uloop_done();
+ if (jail_running) {
+ kill(jail_process.pid, SIGTERM);
+ waitpid(jail_process.pid, NULL, 0);
+ }
+ } else {
+ ERROR("failed to spawn namespace: %s\n", strerror(errno));
+ ret = EXIT_FAILURE;
}
- if (elf_load_deps("libpreload-seccomp.so")) {
- ERROR("failed to load libpreload-seccomp.so\n");
- return -1;
+ if (rmdir(opts.path)) {
+ ERROR("Unable to remove root path: %s (%s)\n", opts.path, strerror(errno));
+ ret = EXIT_FAILURE;
}
- ret = build_jail(path);
-
- if (!ret)
- spawn_namespace(path, argc, argv);
- else
- ERROR("failed to build jail\n");
-
- stop_jail(path);
+ if (ret)
+ return ret;
- return ret;
+ return jail_return_code;
}
--
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