jail: set user and group inside jail
authorDaniel Golle <daniel@makrotopia.org>
Sun, 29 Dec 2019 14:23:34 +0000 (16:23 +0200)
committerDaniel Golle <daniel@makrotopia.org>
Mon, 30 Dec 2019 17:52:25 +0000 (19:52 +0200)
This allows jailed services to run as users other than root, simply
because some services refuse to be run as UID 0.
Previously, setting the the process UID and GID before launching the
jail wrapper prevented the jail from starting.
Rather than setting them in procd/service.c, pass user and group
parameters to ujail and set them inside ujail just before executing the
service.

Signed-off-by: Daniel Golle <daniel@makrotopia.org>
jail/jail.c
service/instance.c

index 54e78419b539ae6b618bc3680c01fdd9582ea318..47da1c1229a9481a97ba79be90a92301d38e0499 100644 (file)
 #include <sys/mount.h>
 #include <sys/prctl.h>
 #include <sys/wait.h>
+#include <sys/types.h>
 
 #include <stdlib.h>
 #include <unistd.h>
 #include <errno.h>
+#include <pwd.h>
+#include <grp.h>
 #include <string.h>
 #include <sys/stat.h>
 #include <fcntl.h>
@@ -36,7 +39,7 @@
 #include <libubox/uloop.h>
 
 #define STACK_SIZE     (1024 * 1024)
-#define OPT_ARGS       "S:C:n:h:r:w:d:psuloc"
+#define OPT_ARGS       "S:C:n:h:r:w:d:psulocU:G:"
 
 static struct {
        char *name;
@@ -44,6 +47,8 @@ static struct {
        char **jail_argv;
        char *seccomp;
        char *capabilities;
+       char *user;
+       char *group;
        int no_new_privs;
        int namespace;
        int procfs;
@@ -225,6 +230,8 @@ static void usage(void)
        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, "  -U <name>\tuser to run jailed process\n");
+       fprintf(stderr, "  -G <name>\tgroup to run jailed process\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\
@@ -237,6 +244,9 @@ and will only drop capabilities/apply seccomp filter.\n\n");
 
 static int exec_jail(void *_notused)
 {
+       struct passwd *p = NULL;
+       struct group *g = NULL;
+
        if (opts.capabilities && drop_capabilities(opts.capabilities))
                exit(EXIT_FAILURE);
 
@@ -256,6 +266,39 @@ static int exec_jail(void *_notused)
                exit(EXIT_FAILURE);
        }
 
+       if (opts.user) {
+               p = getpwnam(opts.user);
+               if (!p) {
+                       ERROR("failed to get uid/gid for user %s: %d (%s)\n",
+                             opts.user, errno, strerror(errno));
+                       exit(EXIT_FAILURE);
+               }
+       }
+
+       if (opts.group) {
+               g = getgrnam(opts.group);
+               if (!g) {
+                       ERROR("failed to get gid for group %s: %m\n", opts.group);
+                       exit(EXIT_FAILURE);
+               }
+       }
+
+       if (p && p->pw_gid && initgroups(opts.user, p->pw_gid)) {
+               ERROR("failed to initgroups() for user %s: %m\n", opts.user);
+               exit(EXIT_FAILURE);
+       }
+
+       if (g && g->gr_gid && setgid(g->gr_gid)) {
+               ERROR("failed to set group id %d: %m\n", g?g->gr_gid:p->pw_gid);
+               exit(EXIT_FAILURE);
+       }
+
+       if (p && p->pw_uid && setuid(p->pw_uid)) {
+               ERROR("failed to set user id %d: %m\n", p->pw_uid);
+               exit(EXIT_FAILURE);
+       }
+
+
        char **envp = build_envp(opts.seccomp);
        if (!envp)
                exit(EXIT_FAILURE);
@@ -371,6 +414,12 @@ int main(int argc, char **argv)
                        opts.namespace = 1;
                        add_mount(log, 0, -1);
                        break;
+               case 'U':
+                       opts.user = optarg;
+                       break;
+               case 'G':
+                       opts.group = optarg;
+                       break;
                }
        }
 
@@ -425,6 +474,14 @@ int main(int argc, char **argv)
                add_mount("/dev/urandom", 0, -1);
                add_mount("/dev/zero", 0, -1);
 
+               if (opts.user || opts.group) {
+                       add_mount("/etc/passwd", 0, -1);
+                       add_mount("/etc/group", 0, -1);
+               }
+
+               if (opts.namespace & NAMESPACE_IPC)
+                       flags |= CLONE_NEWIPC;
+
                int flags = CLONE_NEWPID | CLONE_NEWNS | CLONE_NEWIPC | SIGCHLD;
                if (opts.hostname)
                        flags |= CLONE_NEWUTS;
index 3098ff3bb50c478811e480ba774c16e0802dd6b3..abd1f34649120f7de40640652ac98e1d429d1788 100644 (file)
@@ -222,6 +222,16 @@ jail_run(struct service_instance *in, char **argv)
                argv[argc++] = in->seccomp;
        }
 
+       if (in->user) {
+               argv[argc++] = "-U";
+               argv[argc++] = in->user;
+       }
+
+       if (in->group) {
+               argv[argc++] = "-G";
+               argv[argc++] = in->group;
+       }
+
        if (in->no_new_privs)
                argv[argc++] = "-c";
 
@@ -370,15 +380,15 @@ instance_run(struct service_instance *in, int _stdout, int _stderr)
                closefd(_stderr);
        }
 
-       if (in->user && in->pw_gid && initgroups(in->user, in->pw_gid)) {
+       if (!in->has_jail && in->user && in->pw_gid && initgroups(in->user, in->pw_gid)) {
                ERROR("failed to initgroups() for user %s: %m\n", in->user);
                exit(127);
        }
-       if (in->gr_gid && setgid(in->gr_gid)) {
+       if (!in->has_jail && in->gr_gid && setgid(in->gr_gid)) {
                ERROR("failed to set group id %d: %m\n", in->gr_gid);
                exit(127);
        }
-       if (in->uid && setuid(in->uid)) {
+       if (!in->has_jail && in->uid && setuid(in->uid)) {
                ERROR("failed to set user id %d: %m\n", in->uid);
                exit(127);
        }
@@ -833,6 +843,12 @@ instance_jail_parse(struct service_instance *in, struct blob_attr *attr)
        if (in->seccomp)
                jail->argc += 2;
 
+       if (in->user)
+               jail->argc += 2;
+
+       if (in->group)
+               jail->argc += 2;
+
        if (in->no_new_privs)
                jail->argc++;