]> git.ipfire.org Git - thirdparty/lxc.git/commitdiff
cgmanager: chown cgroups to the container root
authorSerge Hallyn <serge.hallyn@ubuntu.com>
Fri, 24 Jan 2014 05:56:15 +0000 (23:56 -0600)
committerStéphane Graber <stgraber@ubuntu.com>
Fri, 24 Jan 2014 22:27:44 +0000 (17:27 -0500)
After this patch, starting an unprivileged container using
cgmanager gets the cgroup chown to the container root, so
that it can install the cgmanager (proxy) and make cgroup
requests.

(Still desirable and not in this patch is the automatic setup of
/sys/fs/cgroup/manager/sock, which you can currently do with
two lxc.mount.entries)

Signed-off-by: Serge Hallyn <serge.hallyn@ubuntu.com>
Acked-by: Stéphane Graber <stgraber@ubuntu.com>
src/lxc/cgmanager.c
src/lxc/cgroup.c
src/lxc/cgroup.h
src/lxc/start.c

index e43e1f7c777595ecf524f99f6e8aec2a26326948..29c0e99052ada914ea39e4b01030cb2f55600a0b 100644 (file)
@@ -75,6 +75,44 @@ static void cgmanager_disconnected(DBusConnection *connection)
        }
 }
 
+static int send_creds(int sock, int rpid, int ruid, int rgid)
+{
+       struct msghdr msg = { 0 };
+       struct iovec iov;
+       struct cmsghdr *cmsg;
+       struct ucred cred = {
+               .pid = rpid,
+               .uid = ruid,
+               .gid = rgid,
+       };
+       char cmsgbuf[CMSG_SPACE(sizeof(cred))];
+       char buf[1];
+       buf[0] = 'p';
+
+       msg.msg_control = cmsgbuf;
+       msg.msg_controllen = sizeof(cmsgbuf);
+
+       cmsg = CMSG_FIRSTHDR(&msg);
+       cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred));
+       cmsg->cmsg_level = SOL_SOCKET;
+       cmsg->cmsg_type = SCM_CREDENTIALS;
+       memcpy(CMSG_DATA(cmsg), &cred, sizeof(cred));
+
+       msg.msg_name = NULL;
+       msg.msg_namelen = 0;
+
+       iov.iov_base = buf;
+       iov.iov_len = sizeof(buf);
+       msg.msg_iov = &iov;
+       msg.msg_iovlen = 1;
+
+       if (sendmsg(sock, &msg, 0) < 0) {
+               perror("sendmsg");
+               return -1;
+       }
+       return 0;
+}
+
 #define CGMANAGER_DBUS_SOCK "unix:path=/sys/fs/cgroup/cgmanager/sock"
 bool lxc_init_cgmanager(void)
 {
@@ -120,10 +158,121 @@ static bool lxc_cgmanager_create(const char *controller, const char *cgroup_path
                return false;
        }
 
-       // TODO - try to chown the cgroup to the container root
        return true;
 }
 
+struct chown_data {
+       const char *controller;
+       const char *cgroup_path;
+};
+
+static int do_chown_cgroup(const char *controller, const char *cgroup_path)
+{
+       int sv[2] = {-1, -1}, optval = 1;
+       char buf[1];
+
+       if (setgid(0) < 0)
+               WARN("Failed to setgid to 0");
+       if (setuid(0) < 0)
+               WARN("Failed to setuid to 0");
+
+       if (socketpair(AF_UNIX, SOCK_DGRAM, 0, sv) < 0) {
+               SYSERROR("Error creating socketpair");
+               return -1;
+       }
+       if (setsockopt(sv[1], SOL_SOCKET, SO_PASSCRED, &optval, sizeof(optval)) == -1) {
+               SYSERROR("setsockopt failed");
+               return -1;
+       }
+       if (setsockopt(sv[0], SOL_SOCKET, SO_PASSCRED, &optval, sizeof(optval)) == -1) {
+               SYSERROR("setsockopt failed");
+               return -1;
+       }
+       if ( cgmanager_chown_scm_sync(NULL, cgroup_manager, controller,
+                                      cgroup_path, sv[1]) != 0) {
+               ERROR("call to cgmanager_chown_scm_sync failed");
+               return -1;
+       }
+       /* now send credentials */
+
+       fd_set rfds;
+       FD_ZERO(&rfds);
+       FD_SET(sv[0], &rfds);
+       if (select(sv[0]+1, &rfds, NULL, NULL, NULL) < 0) {
+               ERROR("Error getting go-ahead from server: %s", strerror(errno));
+               return -1;
+       }
+       if (read(sv[0], &buf, 1) != 1) {
+               ERROR("Error getting reply from server over socketpair");
+               return -1;
+       }
+       if (send_creds(sv[0], getpid(), getuid(), getgid())) {
+               ERROR("Error sending pid over SCM_CREDENTIAL");
+               return -1;
+       }
+       FD_ZERO(&rfds);
+       FD_SET(sv[0], &rfds);
+       if (select(sv[0]+1, &rfds, NULL, NULL, NULL) < 0) {
+               ERROR("Error getting go-ahead from server: %s", strerror(errno));
+               return -1;
+       }
+       if (read(sv[0], &buf, 1) != 1) {
+               ERROR("Error getting reply from server over socketpair");
+               return -1;
+       }
+       if (send_creds(sv[0], getpid(), 0, 0)) {
+               ERROR("Error sending pid over SCM_CREDENTIAL");
+               return -1;
+       }
+       FD_ZERO(&rfds);
+       FD_SET(sv[0], &rfds);
+       if (select(sv[0]+1, &rfds, NULL, NULL, NULL) < 0) {
+               ERROR("Error getting go-ahead from server: %s", strerror(errno));
+               return -1;
+       }
+       int ret = read(sv[0], buf, 1);
+       close(sv[0]);
+       close(sv[1]);
+       if (ret == 1 && *buf == '1')
+               return 0;
+       return -1;
+}
+
+static int chown_cgroup_wrapper(void *data)
+{
+       struct chown_data *arg = data;
+       return do_chown_cgroup(arg->controller, arg->cgroup_path);
+}
+
+static bool chown_cgroup(const char *controller, const char *cgroup_path,
+                       struct lxc_conf *conf)
+{
+       pid_t pid;
+       struct chown_data data;
+       data.controller = controller;
+       data.cgroup_path = cgroup_path;
+
+       if (lxc_list_empty(&conf->id_map)) {
+               if (do_chown_cgroup(controller, cgroup_path) < 0)
+                       return false;
+               return true;
+       }
+
+       if ((pid = fork()) < 0) {
+               SYSERROR("fork");
+               return false;
+       }
+       if (pid > 0) {
+               if (wait_for_pid(pid)) {
+                       ERROR("Error chowning cgroup");
+                       return false;
+               }
+               return true;
+       }
+       if (userns_exec_1(conf, chown_cgroup_wrapper, &data) < 0)
+               exit(1);
+       exit(0);
+}
 
 struct cgm_data {
        int nr_subsystems;
@@ -432,6 +581,19 @@ static bool cgm_setup_limits(struct lxc_handler *handler, bool with_devices)
        return setup_limits(handler, with_devices);
 }
 
+static bool cgm_chown(struct lxc_handler *handler)
+{
+       struct cgm_data *d = handler->cgroup_info->data;
+       int i;
+
+       for (i = 0; i < d->nr_subsystems; i++) {
+               if (!chown_cgroup(d->subsystems[i], d->cgroup_path, handler->conf))
+                       WARN("Failed to chown %s:%s to container root",
+                               d->subsystems[i], d->cgroup_path);
+       }
+       return true;
+}
+
 static struct cgroup_ops cgmanager_ops = {
        .destroy = cgm_destroy,
        .init = cgm_init,
@@ -443,6 +605,7 @@ static struct cgroup_ops cgmanager_ops = {
        .set = cgm_set,
        .unfreeze_fromhandler = cgm_unfreeze_fromhandler,
        .setup_limits = cgm_setup_limits,
-       .name = "cgmanager"
+       .name = "cgmanager",
+       .chown = cgm_chown,
 };
 #endif
index 4482b326be6ae02970a9580c3d3acd33482638f1..ce1096d62a696ce9343cec22d3401cba64423e01 100644 (file)
@@ -2199,6 +2199,7 @@ static struct cgroup_ops cgfs_ops = {
        .unfreeze_fromhandler = cgfs_unfreeze_fromhandler,
        .setup_limits = cgroupfs_setup_limits,
        .name = "cgroupfs",
+       .chown = NULL,
 };
 static void init_cg_ops(void)
 {
@@ -2297,3 +2298,10 @@ bool cgroup_setup_limits(struct lxc_handler *handler, bool with_devices)
 {
        return active_cg_ops->setup_limits(handler, with_devices);
 }
+
+bool cgroup_chown(struct lxc_handler *handler)
+{
+       if (active_cg_ops->chown)
+               return active_cg_ops->chown(handler);
+       return true;
+}
index af5a5c8330f949dff226955dacce50c4ee7f15dc..fbb3b8702155240cea2702ab32a4bc4396225217 100644 (file)
@@ -180,6 +180,7 @@ struct cgroup_ops {
        int (*get)(const char *filename, char *value, size_t len, const char *name, const char *lxcpath);
        int (*unfreeze_fromhandler)(struct lxc_handler *handler);
        bool (*setup_limits)(struct lxc_handler *handler, bool with_devices);
+       bool (*chown)(struct lxc_handler *handler);
        const char *name;
 };
 
@@ -209,6 +210,7 @@ extern void cgroup_destroy(struct lxc_handler *handler);
 extern bool cgroup_init(struct lxc_handler *handler);
 extern bool cgroup_create(struct lxc_handler *handler);
 extern bool cgroup_setup_limits(struct lxc_handler *handler, bool with_devices);
+extern bool cgroup_chown(struct lxc_handler *handler);
 extern bool cgroup_enter(struct lxc_handler *handler);
 extern void cgroup_cleanup(struct lxc_handler *handler);
 extern bool cgroup_create_legacy(struct lxc_handler *handler);
index 2fcd8457cc7b03cebc4aa8002eff04ce90310c4a..6de7a831762124ac7c4eb3b19f05277411f117f1 100644 (file)
@@ -804,6 +804,9 @@ static int lxc_spawn(struct lxc_handler *handler)
        if (!cgroup_enter(handler))
                goto out_delete_net;
 
+       if (!cgroup_chown(handler))
+               goto out_delete_net;
+
        if (failed_before_rename)
                goto out_delete_net;