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