jail: always call cgroups_free()
[project/procd.git] / jail / jail.c
index 5f1ca6fa2d6e1c63ce8ffb9da0984531ceed2c54..529ac6b9ff53abe0fde209bd1a2151a34b396d98 100644 (file)
@@ -132,6 +132,7 @@ static struct {
        int pw_uid;
        int pw_gid;
        int gr_gid;
+       int root_map_uid;
        gid_t *additional_gids;
        size_t num_additional_gids;
        mode_t umask;
@@ -248,6 +249,7 @@ static void free_opts(bool parent) {
 
        free_library_search();
        mount_free();
+       cgroups_free();
 
        /* we need to keep argv, envp and seccomp filter in child */
        if (parent) { /* parent-only */
@@ -258,9 +260,6 @@ static void free_opts(bool parent) {
 
                free_oci_envp(opts.jail_argv);
                free_oci_envp(opts.envp);
-       } else { /* child-only */
-               if (opts.ocibundle)
-                       cgroups_free();
        }
 
        free_rlimits();
@@ -389,7 +388,7 @@ static int create_dev_console(const char *jail_root)
        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))
+       if (mount(console_fname, dev_console_path, "bind", MS_BIND, NULL))
                goto no_console;
 
        /* use PTY slave for stdio */
@@ -641,13 +640,13 @@ static int build_jail_fs(void)
        }
 
        /* oldroot can't be MS_SHARED else pivot_root() fails */
-       if (mount("none", "/", NULL, MS_REC|MS_PRIVATE, NULL)) {
+       if (mount("none", "/", "none", MS_REC|MS_PRIVATE, NULL)) {
                ERROR("private mount failed %m\n");
                return -1;
        }
 
        if (opts.extroot) {
-               if (mount(opts.extroot, jail_root, NULL, MS_BIND, NULL)) {
+               if (mount(opts.extroot, jail_root, "bind", MS_BIND, NULL)) {
                        ERROR("extroot mount failed %m\n");
                        return -1;
                }
@@ -761,7 +760,7 @@ static void enter_jail_fs(void)
                free_and_exit(-1);
        }
        if (opts.ronly)
-               mount(NULL, "/", NULL, MS_REMOUNT | MS_BIND | MS_RDONLY, 0);
+               mount(NULL, "/", "bind", MS_REMOUNT | MS_BIND | MS_RDONLY, 0);
 
        umask(old_umask);
        post_jail_fs();
@@ -965,7 +964,7 @@ static void usage(void)
        fprintf(stderr, "  -E\t\tfail if jail cannot be setup\n");
        fprintf(stderr, "  -y\t\tprovide jail console\n");
        fprintf(stderr, "  -J <dir>\tcreate container from OCI bundle\n");
-       fprintf(stderr, "  -j\t\tstart container immediately\n");
+       fprintf(stderr, "  -i\t\tstart container immediately\n");
        fprintf(stderr, "  -P <pidfile>\tcreate <pidfile>\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\
@@ -1096,6 +1095,8 @@ static int exec_jail(void *arg)
        char buf[1];
 
        exit_from_child = true;
+       prctl(PR_SET_SECUREBITS, 0);
+
        uloop_init();
        signals_init();
 
@@ -1126,6 +1127,8 @@ static int exec_jail(void *arg)
        if (opts.namespace & CLONE_NEWCGROUP)
                unshare(CLONE_NEWCGROUP);
 
+       setns_open(CLONE_NEWCGROUP);
+
        if ((opts.namespace & CLONE_NEWUSER) || (opts.setns.user != -1)) {
                if (setregid(0, 0) < 0) {
                        ERROR("setgid\n");
@@ -1214,9 +1217,10 @@ static void post_start_hook(void)
        if (opts.set_umask)
                umask(opts.umask);
 
-       /* restore securebits back to normal */
+       /* restore securebits back to normal (and lock them if not in userns) */
        if (opts.capset.apply) {
-               if (prctl(PR_SET_SECUREBITS, 0)) {
+               if (prctl(PR_SET_SECUREBITS, (opts.namespace & CLONE_NEWUSER)?0:
+                   SECBIT_KEEP_CAPS_LOCKED|SECBIT_NO_SETUID_FIXUP_LOCKED|SECBIT_NOROOT_LOCKED)) {
                        ERROR("prctl(PR_SET_SECUREBITS) failed: %m\n");
                        free_and_exit(EXIT_FAILURE);
                }
@@ -1818,8 +1822,14 @@ static int parseOCIlinuxns(struct blob_attr *msg)
        }
 
        return 0;
-};
+}
 
+static void get_jail_root_user(bool is_gidmap, uint32_t container_id, uint32_t host_id, uint32_t size)
+{
+       if (container_id == 0 && size >= 1)
+               if (!is_gidmap)
+                       opts.root_map_uid = host_id;
+}
 
 enum {
        OCI_LINUX_UIDGIDMAP_CONTAINERID,
@@ -1866,6 +1876,10 @@ static int parseOCIuidgidmappings(struct blob_attr *msg, bool is_gidmap)
        blobmsg_for_each_attr(cur, msg, rem) {
                blobmsg_parse(oci_linux_uidgidmap_policy, __OCI_LINUX_UIDGIDMAP_MAX, tb, blobmsg_data(cur), blobmsg_len(cur));
 
+               get_jail_root_user(is_gidmap, blobmsg_get_u32(tb[OCI_LINUX_UIDGIDMAP_CONTAINERID]),
+                        blobmsg_get_u32(tb[OCI_LINUX_UIDGIDMAP_HOSTID]),
+                        blobmsg_get_u32(tb[OCI_LINUX_UIDGIDMAP_SIZE]));
+
                /* write mapping line into pre-allocated string */
                len = snprintf(&map[pos], totallen + 1, "%d %d %d\n",
                         blobmsg_get_u32(tb[OCI_LINUX_UIDGIDMAP_CONTAINERID]),
@@ -2547,6 +2561,12 @@ int main(int argc, char **argv)
        opts.setns.time = -1;
 #endif
 
+       /*
+        * uid in parent user namespace representing root user in new
+        * user namespace, defaults to nobody unless specified in uidMappings
+        */
+       opts.root_map_uid = 65534;
+
        if (opts.capabilities && parseOCIcapabilities_from_file(&opts.capset, opts.capabilities)) {
                ERROR("failed to read capabilities from file %s\n", opts.capabilities);
                ret=-1;
@@ -2696,13 +2716,13 @@ static void post_main(struct uloop_timeout *t)
 #endif
 
                        if (!(opts.namespace & CLONE_NEWNET)) {
-                               add_mount_bind("/etc/resolv.conf", 1, -1);
+                               add_mount_bind("/etc/resolv.conf", 1, 0);
                        } else if (opts.setns.net == -1) {
                                char hostdir[PATH_MAX];
 
                                snprintf(hostdir, PATH_MAX, "/tmp/resolv.conf-%s.d", opts.name);
                                mkdir_p(hostdir, 0755);
-                               add_mount(hostdir, "/dev/resolv.conf.d", NULL, MS_BIND | MS_NOEXEC | MS_NOATIME | MS_NOSUID | MS_NODEV | MS_RDONLY, 0, NULL, -1);
+                               add_mount(hostdir, "/dev/resolv.conf.d", NULL, MS_BIND | MS_NOEXEC | MS_NOATIME | MS_NOSUID | MS_NODEV | MS_RDONLY, 0, NULL, 0);
                        }
 
                        /* default mounts */
@@ -2735,7 +2755,7 @@ static void post_main(struct uloop_timeout *t)
 
                        }
                        if (opts.sysfs || opts.ocibundle)
-                               add_mount("sysfs", "/sys", "sysfs", MS_NOATIME | MS_NODEV | MS_NOEXEC | MS_NOSUID | MS_RDONLY, 0, NULL, -1);
+                               add_mount("sysfs", "/sys", "sysfs", MS_RELATIME | MS_NODEV | MS_NOEXEC | MS_NOSUID | MS_RDONLY, 0, NULL, -1);
 
                        if (opts.ocibundle)
                                add_mount("shm", "/dev/shm", "tmpfs", MS_NOSUID | MS_NOEXEC | MS_NODEV, 0, "mode=1777", -1);
@@ -2753,9 +2773,19 @@ static void post_main(struct uloop_timeout *t)
                if (opts.setns.time != -1) {
                        timens_fd = ns_open_pid("time", getpid());
                        setns_open(CLONE_NEWTIME);
+               } else {
+                       timens_fd = -1;
                }
 #endif
 
+               if (opts.namespace & CLONE_NEWUSER) {
+                       if (prctl(PR_SET_SECUREBITS, SECBIT_NO_SETUID_FIXUP)) {
+                               ERROR("prctl(PR_SET_SECUREBITS) failed: %m\n");
+                               free_and_exit(EXIT_FAILURE);
+                       }
+                       seteuid(opts.root_map_uid);
+               }
+
                jail_process.pid = clone(exec_jail, child_stack + STACK_SIZE, SIGCHLD | (opts.namespace & (~CLONE_NEWCGROUP)), NULL);
        } else {
                jail_process.pid = fork();
@@ -2768,6 +2798,8 @@ static void post_main(struct uloop_timeout *t)
                uloop_process_add(&jail_process);
                jail_running = 1;
                seteuid(0);
+               prctl(PR_SET_SECUREBITS, 0);
+
                if (pidns_fd != -1) {
                        setns(pidns_fd, CLONE_NEWPID);
                        close(pidns_fd);