jail: handle containers seperately
[oweals/procd.git] / jail / jail.c
index 87b671befb4b9a4a2b869647516b33f15a528aff..45906904451bee621108f7e5df3fb12930fe2538 100644 (file)
@@ -40,7 +40,7 @@
 #include <libubus.h>
 
 #define STACK_SIZE     (1024 * 1024)
-#define OPT_ARGS       "S:C:n:h:r:w:d:psulocU:G:NR:fFO:T:"
+#define OPT_ARGS       "S:C:n:h:r:w:d:psulocU:G:NR:fFO:T:Ey"
 
 static struct {
        char *name;
@@ -58,9 +58,11 @@ static struct {
        int procfs;
        int ronly;
        int sysfs;
+       int console;
        int pw_uid;
        int pw_gid;
        int gr_gid;
+       int require_jail;
 } opts;
 
 
@@ -70,6 +72,8 @@ int debug = 0;
 
 static char child_stack[STACK_SIZE];
 
+int console_fd;
+
 static int mkdir_p(char *dir, mode_t mask)
 {
        char *l = strrchr(dir, '/');
@@ -183,10 +187,79 @@ out:
        return ret;
 }
 
+static void pass_console(int console_fd)
+{
+       struct ubus_context *ctx = ubus_connect(NULL);
+       static struct blob_buf req;
+       uint32_t id;
+
+       if (!ctx)
+               return;
+
+       blob_buf_init(&req, 0);
+       blobmsg_add_string(&req, "name", opts.name);
+
+       if (ubus_lookup_id(ctx, "container", &id) ||
+           ubus_invoke_fd(ctx, id, "console_set", req.head, NULL, NULL, 3000, console_fd))
+               INFO("ubus request failed\n");
+       else
+               close(console_fd);
+
+       blob_buf_free(&req);
+       ubus_free(ctx);
+}
+
+static int create_dev_console(const char *jail_root)
+{
+       char *console_fname;
+       char dev_console_path[PATH_MAX];
+       int slave_console_fd;
+
+       /* Open UNIX/98 virtual console */
+       console_fd = posix_openpt(O_RDWR | O_NOCTTY);
+       if (console_fd == -1)
+               return -1;
+
+       console_fname = ptsname(console_fd);
+       DEBUG("got console fd %d and PTS client name %s\n", console_fd, console_fname);
+       if (!console_fname)
+               goto no_console;
+
+       grantpt(console_fd);
+       unlockpt(console_fd);
+
+       /* pass PTY master to procd */
+       pass_console(console_fd);
+
+       /* mount-bind PTY slave to /dev/console in jail */
+       snprintf(dev_console_path, sizeof(dev_console_path), "%s/dev/console", jail_root);
+       close(creat(dev_console_path, 0620));
+
+       if (mount(console_fname, dev_console_path, NULL, MS_BIND, NULL))
+               goto no_console;
+
+       /* use PTY slave for stdio */
+       slave_console_fd = open(console_fname, O_RDWR); /* | O_NOCTTY */
+       dup2(slave_console_fd, 0);
+       dup2(slave_console_fd, 1);
+       dup2(slave_console_fd, 2);
+       close(slave_console_fd);
+
+       INFO("using guest console %s\n", console_fname);
+
+       return 0;
+
+no_console:
+       close(console_fd);
+       return 1;
+}
+
 static int build_jail_fs(void)
 {
        char jail_root[] = "/tmp/ujail-XXXXXX";
        char tmpovdir[] = "/tmp/ujail-overlay-XXXXXX";
+       char tmpdevdir[] = "/tmp/ujail-XXXXXX/dev";
+       char tmpdevptsdir[] = "/tmp/ujail-XXXXXX/dev/pts";
        char *overlaydir = NULL;
 
        if (mkdtemp(jail_root) == NULL) {
@@ -240,6 +313,19 @@ static int build_jail_fs(void)
                return -1;
        }
 
+       snprintf(tmpdevdir, sizeof(tmpdevdir), "%s/dev", jail_root);
+       mkdir_p(tmpdevdir, 0755);
+       if (mount(NULL, tmpdevdir, "tmpfs", MS_NOATIME | MS_NOEXEC | MS_NOSUID, "size=1M"))
+               return -1;
+
+       snprintf(tmpdevptsdir, sizeof(tmpdevptsdir), "%s/dev/pts", jail_root);
+       mkdir_p(tmpdevptsdir, 0755);
+       if (mount(NULL, tmpdevptsdir, "devpts", MS_NOATIME | MS_NOEXEC | MS_NOSUID, NULL))
+               return -1;
+
+       if (opts.console)
+               create_dev_console(jail_root);
+
        if (mount_all(jail_root)) {
                ERROR("mount_all() failed\n");
                return -1;
@@ -460,6 +546,8 @@ static void usage(void)
        fprintf(stderr, "  -R <dir>\texternal jail rootfs (system container)\n");
        fprintf(stderr, "  -O <dir>\tdirectory for r/w overlayfs\n");
        fprintf(stderr, "  -T <size>\tuse tmpfs r/w overlayfs with <size>\n");
+       fprintf(stderr, "  -E\t\tfail if jail cannot be setup\n");
+       fprintf(stderr, "  -y\t\tprovide jail console\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\
@@ -478,7 +566,6 @@ static int exec_jail(void *pipes_ptr)
        close(pipes[0]);
        close(pipes[3]);
 
-
        buf[0] = 'i';
        if (write(pipes[1], buf, 1) < 1) {
                ERROR("can't write to parent\n");
@@ -709,6 +796,12 @@ int main(int argc, char **argv)
                case 'T':
                        opts.tmpoverlaysize = optarg;
                        break;
+               case 'E':
+                       opts.require_jail = 1;
+                       break;
+               case 'y':
+                       opts.console = 1;
+                       break;
                }
        }
 
@@ -748,7 +841,9 @@ int main(int argc, char **argv)
 
        if (opts.namespace && opts.seccomp && add_path_and_deps("libpreload-seccomp.so", 1, -1, 1)) {
                ERROR("failed to load libpreload-seccomp.so\n");
-               return -1;
+               opts.seccomp = 0;
+               if (opts.require_jail)
+                       return -1;
        }
 
        if (opts.name)
@@ -762,7 +857,7 @@ int main(int argc, char **argv)
 
                if (!sigismember(&sigmask, i))
                        continue;
-               if ((i == SIGCHLD) || (i == SIGPIPE))
+               if ((i == SIGCHLD) || (i == SIGPIPE) || (i == SIGSEGV))
                        continue;
 
                s.sa_handler = jail_handle_signal;
@@ -775,15 +870,20 @@ int main(int argc, char **argv)
                        add_mount("/dev/null", 0, -1);
                        add_mount("/dev/random", 0, -1);
                        add_mount("/dev/urandom", 0, -1);
-                       add_mount("/dev/tty", 0, -1);
                        add_mount("/dev/zero", 0, -1);
-                       add_mount("/dev/console", 0, -1);
+                       add_mount("/dev/ptmx", 0, -1);
+                       add_mount("/dev/tty", 0, -1);
 
                        if (!opts.extroot && (opts.user || opts.group)) {
                                add_mount("/etc/passwd", 0, -1);
                                add_mount("/etc/group", 0, -1);
                        }
 
+#if defined(__GLIBC__)
+                       if (!opts.extroot)
+                               add_mount("/etc/nsswitch.conf", 0, -1);
+#endif
+
                        if (!(opts.namespace & CLONE_NEWNET)) {
                                add_mount("/etc/resolv.conf", 0, -1);
                        }
@@ -823,6 +923,10 @@ int main(int argc, char **argv)
                }
 
                if (opts.namespace & CLONE_NEWNET) {
+                       if (!opts.name) {
+                               ERROR("netns needs a named jail\n");
+                               return -1;
+                       }
                        netns_fd = netns_open_pid(jail_process.pid);
                        netns_updown(jail_process.pid, true);
                }