]> git.proxmox.com Git - mirror_lxc.git/commitdiff
lxc-clone: support unprivileged use
authorSerge Hallyn <serge.hallyn@ubuntu.com>
Thu, 23 Jan 2014 00:18:04 +0000 (18:18 -0600)
committerStéphane Graber <stgraber@ubuntu.com>
Thu, 23 Jan 2014 20:09:17 +0000 (15:09 -0500)
This also fixes unprivileged use of lxc-snapshot and lxc-rename.

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

index 5cd73407003400c12691d599fdc32818e1e9f7c4..2c3388f59917af4c719188123a1bb6562b52323c 100644 (file)
@@ -2003,20 +2003,70 @@ struct bdev *bdev_init(const char *src, const char *dst, const char *data)
        return bdev;
 }
 
+struct rsync_data {
+       struct bdev *orig;
+       struct bdev *new;
+};
+
+static int rsync_rootfs(struct rsync_data *data)
+{
+       struct bdev *orig = data->orig,
+                   *new = data->new;
+
+       if (unshare(CLONE_NEWNS) < 0) {
+               SYSERROR("unshare CLONE_NEWNS");
+               return -1;
+       }
+
+       // If not a snapshot, copy the fs.
+       if (orig->ops->mount(orig) < 0) {
+               ERROR("failed mounting %s onto %s\n", orig->src, orig->dest);
+               return -1;
+       }
+       if (new->ops->mount(new) < 0) {
+               ERROR("failed mounting %s onto %s\n", new->src, new->dest);
+               return -1;
+       }
+       if (setgid(0) < 0) {
+               ERROR("Failed to setgid to 0");
+               return -1;
+       }
+       if (setuid(0) < 0) {
+               ERROR("Failed to setuid to 0");
+               return -1;
+       }
+       if (do_rsync(orig->dest, new->dest) < 0) {
+               ERROR("rsyncing %s to %s\n", orig->src, new->src);
+               return -1;
+       }
+
+       return 0;
+}
+
+static int rsync_rootfs_wrapper(void *data)
+{
+       struct rsync_data *arg = data;
+       return rsync_rootfs(arg);
+}
 /*
  * If we're not snaphotting, then bdev_copy becomes a simple case of mount
  * the original, mount the new, and rsync the contents.
  */
-struct bdev *bdev_copy(const char *src, const char *oldname, const char *cname,
-                       const char *oldpath, const char *lxcpath, const char *bdevtype,
+struct bdev *bdev_copy(struct lxc_container *c0, const char *cname,
+                       const char *lxcpath, const char *bdevtype,
                        int flags, const char *bdevdata, uint64_t newsize,
                        int *needs_rdep)
 {
        struct bdev *orig, *new;
        pid_t pid;
+       int ret;
        bool snap = flags & LXC_CLONE_SNAPSHOT;
        bool maybe_snap = flags & LXC_CLONE_MAYBE_SNAPSHOT;
        bool keepbdevtype = flags & LXC_CLONE_KEEPBDEVTYPE;
+       const char *src = c0->lxc_conf->rootfs.path;
+       const char *oldname = c0->name;
+       const char *oldpath = c0->config_path;
+       struct rsync_data data;
 
        /* if the container name doesn't show up in the rootfs path, then
         * we don't know how to come up with a new name
@@ -2049,6 +2099,21 @@ struct bdev *bdev_copy(const char *src, const char *oldname, const char *cname,
                }
        }
 
+       /* check for privilege */
+       if (am_unpriv()) {
+               if (bdevtype && strcmp(bdevtype, "dir") != 0) {
+                       ERROR("Unprivileged users can only make dir copy-clones");
+                       bdev_put(orig);
+                       return NULL;
+               }
+               if (strcmp(orig->type, "dir") != 0) {
+                       ERROR("Unprivileged users can only make dir copy-clones");
+                       bdev_put(orig);
+                       return NULL;
+               }
+       }
+
+
        /*
         * special case for snapshot - if caller requested maybe_snapshot and
         * keepbdevtype and backing store is directory, then proceed with a copy
@@ -2081,6 +2146,8 @@ struct bdev *bdev_copy(const char *src, const char *oldname, const char *cname,
                bdev_put(new);
                return NULL;
        }
+       if (snap)
+               return new;
 
        pid = fork();
        if (pid < 0) {
@@ -2100,29 +2167,14 @@ struct bdev *bdev_copy(const char *src, const char *oldname, const char *cname,
                return new;
        }
 
-       if (unshare(CLONE_NEWNS) < 0) {
-               SYSERROR("unshare CLONE_NEWNS");
-               exit(1);
-       }
-       if (snap)
-               exit(0);
-
-       // If not a snapshot, copy the fs.
-       if (orig->ops->mount(orig) < 0) {
-               ERROR("failed mounting %s onto %s\n", src, orig->dest);
-               exit(1);
-       }
-       if (new->ops->mount(new) < 0) {
-               ERROR("failed mounting %s onto %s\n", new->src, new->dest);
-               exit(1);
-       }
-       if (do_rsync(orig->dest, new->dest) < 0) {
-               ERROR("rsyncing %s to %s\n", orig->src, new->src);
-               exit(1);
-       }
-       // don't bother umounting, ns exit will do that
+       data.orig = orig;
+       data.new = new;
+       if (am_unpriv())
+               ret = userns_exec_1(c0->lxc_conf, rsync_rootfs_wrapper, &data);
+       else
+               ret = rsync_rootfs(&data);
 
-       exit(0);
+       exit(ret == 0 ? 0 : 1);
 }
 
 static struct bdev * do_bdev_create(const char *dest, const char *type,
index c45f08675f05f9b6fefd038162652e6216400d39..cc7b59e847ba1b91ebbd1f37bea2eb5b16f63a51 100644 (file)
@@ -99,8 +99,8 @@ char *overlayfs_getlower(char *p);
  */
 struct bdev *bdev_init(const char *src, const char *dst, const char *data);
 
-struct bdev *bdev_copy(const char *src, const char *oldname, const char *cname,
-                       const char *oldpath, const char *lxcpath, const char *bdevtype,
+struct bdev *bdev_copy(struct lxc_container *c0, const char *cname,
+                       const char *lxcpath, const char *bdevtype,
                        int flags, const char *bdevdata, uint64_t newsize,
                        int *needs_rdep);
 struct bdev *bdev_create(const char *dest, const char *type,
index 28de4550bc2d17f029e86a95db80f352e60aff29..1520cd3c76550642264babee5bb808ea1cbd2811 100644 (file)
 
 lxc_log_define(lxc_container, lxc);
 
-inline static bool am_unpriv() {
-       return geteuid() != 0;
-}
-
 static bool file_exists(const char *f)
 {
        struct stat statbuf;
@@ -2381,8 +2377,7 @@ static int copy_storage(struct lxc_container *c0, struct lxc_container *c,
        struct bdev *bdev;
        int need_rdep;
 
-       bdev = bdev_copy(c0->lxc_conf->rootfs.path, c0->name, c->name,
-                       c0->config_path, c->config_path, newtype, flags,
+       bdev = bdev_copy(c0, c->name, c->config_path, newtype, flags,
                        bdevdata, newsize, &need_rdep);
        if (!bdev) {
                ERROR("Error copying storage");
@@ -2408,36 +2403,49 @@ static int copy_storage(struct lxc_container *c0, struct lxc_container *c,
        return 0;
 }
 
-static int clone_update_rootfs(struct lxc_container *c0,
-                              struct lxc_container *c, int flags,
-                              char **hookargs)
+struct clone_update_data {
+       struct lxc_container *c0;
+       struct lxc_container *c1;
+       int flags;
+       char **hookargs;
+};
+
+static int clone_update_rootfs(struct clone_update_data *data)
 {
+       struct lxc_container *c0 = data->c0;
+       struct lxc_container *c = data->c1;
+       int flags = data->flags;
+       char **hookargs = data->hookargs;
        int ret = -1;
        char path[MAXPATHLEN];
        struct bdev *bdev;
        FILE *fout;
-       pid_t pid;
        struct lxc_conf *conf = c->lxc_conf;
 
        /* update hostname in rootfs */
        /* we're going to mount, so run in a clean namespace to simplify cleanup */
 
-       pid = fork();
-       if (pid < 0)
+       if (setgid(0) < 0) {
+               ERROR("Failed to setgid to 0");
                return -1;
-       if (pid > 0)
-               return wait_for_pid(pid);
+       }
+       if (setuid(0) < 0) {
+               ERROR("Failed to setuid to 0");
+               return -1;
+       }
 
+       if (unshare(CLONE_NEWNS) < 0)
+               return -1;
        bdev = bdev_init(c->lxc_conf->rootfs.path, c->lxc_conf->rootfs.mount, NULL);
        if (!bdev)
-               exit(1);
+               return -1;
        if (strcmp(bdev->type, "dir") != 0) {
                if (unshare(CLONE_NEWNS) < 0) {
                        ERROR("error unsharing mounts");
-                       exit(1);
+                       return -1;
                }
                if (bdev->ops->mount(bdev) < 0)
-                       exit(1);
+                       return -1;
        } else { // TODO come up with a better way
                if (bdev->dest)
                        free(bdev->dest);
@@ -2464,26 +2472,32 @@ static int clone_update_rootfs(struct lxc_container *c0,
 
                if (run_lxc_hooks(c->name, "clone", conf, c->get_config_path(c), hookargs)) {
                        ERROR("Error executing clone hook for %s", c->name);
-                       exit(1);
+                       return -1;
                }
        }
 
        if (!(flags & LXC_CLONE_KEEPNAME)) {
                ret = snprintf(path, MAXPATHLEN, "%s/etc/hostname", bdev->dest);
                if (ret < 0 || ret >= MAXPATHLEN)
-                       exit(1);
+                       return -1;
                if (!file_exists(path))
-                       exit(0);
+                       return 0;
                if (!(fout = fopen(path, "w"))) {
                        SYSERROR("unable to open %s: ignoring\n", path);
-                       exit(0);
+                       return 0;
                }
                if (fprintf(fout, "%s", c->name) < 0)
-                       exit(1);
+                       return -1;
                if (fclose(fout) < 0)
-                       exit(1);
+                       return -1;
        }
-       exit(0);
+       return 0;
+}
+
+static int clone_update_rootfs_wrapper(void *data)
+{
+       struct clone_update_data *arg = (struct clone_update_data *) data;
+       return clone_update_rootfs(arg);
 }
 
 /*
@@ -2522,16 +2536,13 @@ static struct lxc_container *lxcapi_clone(struct lxc_container *c, const char *n
        char newpath[MAXPATHLEN];
        int ret, storage_copied = 0;
        const char *n, *l;
+       struct clone_update_data data;
        FILE *fout;
+       pid_t pid;
 
        if (!c || !c->is_defined(c))
                return NULL;
 
-       if (am_unpriv()) {
-               ERROR(NOT_SUPPORTED_ERROR, __FUNCTION__);
-               return NULL;
-       }
-
        if (container_mem_lock(c))
                return NULL;
 
@@ -2574,6 +2585,13 @@ static struct lxc_container *lxcapi_clone(struct lxc_container *c, const char *n
                goto out;
        }
 
+       if (am_unpriv()) {
+               if (chown_mapped_root(newpath, c->lxc_conf) < 0) {
+                       ERROR("Error chowning %s to container root\n", newpath);
+                       goto out;
+               }
+       }
+
        c2 = lxc_container_new(n, l);
        if (!c2) {
                ERROR("clone: failed to create new container (%s %s)", n, l);
@@ -2614,12 +2632,31 @@ static struct lxc_container *lxcapi_clone(struct lxc_container *c, const char *n
        if (!c2->save_config(c2, NULL))
                goto out;
 
-       if (clone_update_rootfs(c, c2, flags, hookargs) < 0)
+       if ((pid = fork()) < 0) {
+               SYSERROR("fork");
                goto out;
+       }
+       if (pid > 0) {
+               ret = wait_for_pid(pid);
+               if (ret)
+                       goto out;
+               container_mem_unlock(c);
+               return c2;
+       }
+       data.c0 = c;
+       data.c1 = c2;
+       data.flags = flags;
+       data.hookargs = hookargs;
+       if (am_unpriv())
+               ret = userns_exec_1(c->lxc_conf, clone_update_rootfs_wrapper,
+                               &data);
+       else
+               ret = clone_update_rootfs(&data);
+       if (ret < 0)
+               exit(1);
 
-       // TODO: update c's lxc.snapshot = count
        container_mem_unlock(c);
-       return c2;
+       exit(0);
 
 out:
        container_mem_unlock(c);
@@ -2641,11 +2678,6 @@ static bool lxcapi_rename(struct lxc_container *c, const char *newname)
        if (!c || !c->name || !c->config_path)
                return false;
 
-       if (am_unpriv()) {
-               ERROR(NOT_SUPPORTED_ERROR, __FUNCTION__);
-               return false;
-       }
-
        bdev = bdev_init(c->lxc_conf->rootfs.path, c->lxc_conf->rootfs.mount, NULL);
        if (!bdev) {
                ERROR("Failed to find original backing store type");
@@ -2718,11 +2750,6 @@ static int lxcapi_snapshot(struct lxc_container *c, const char *commentfile)
        struct lxc_container *c2;
        char snappath[MAXPATHLEN], newname[20];
 
-       if (am_unpriv()) {
-               ERROR(NOT_SUPPORTED_ERROR, __FUNCTION__);
-               return -1;
-       }
-
        // /var/lib/lxc -> /var/lib/lxcsnaps \0
        ret = snprintf(snappath, MAXPATHLEN, "%ssnaps/%s", c->config_path, c->name);
        if (ret < 0 || ret >= MAXPATHLEN)
@@ -2861,11 +2888,6 @@ static int lxcapi_snapshot_list(struct lxc_container *c, struct lxc_snapshot **r
        if (!c || !lxcapi_is_defined(c))
                return -1;
 
-       if (am_unpriv()) {
-               ERROR(NOT_SUPPORTED_ERROR, __FUNCTION__);
-               return -1;
-       }
-
        // snappath is ${lxcpath}snaps/${lxcname}/
        dirlen = snprintf(snappath, MAXPATHLEN, "%ssnaps/%s", c->config_path, c->name);
        if (dirlen < 0 || dirlen >= MAXPATHLEN) {
@@ -2944,11 +2966,6 @@ static bool lxcapi_snapshot_restore(struct lxc_container *c, const char *snapnam
        if (!c || !c->name || !c->config_path)
                return false;
 
-       if (am_unpriv()) {
-               ERROR(NOT_SUPPORTED_ERROR, __FUNCTION__);
-               return false;
-       }
-
        bdev = bdev_init(c->lxc_conf->rootfs.path, c->lxc_conf->rootfs.mount, NULL);
        if (!bdev) {
                ERROR("Failed to find original backing store type");
@@ -2998,11 +3015,6 @@ static bool lxcapi_snapshot_destroy(struct lxc_container *c, const char *snapnam
        if (!c || !c->name || !c->config_path)
                return false;
 
-       if (am_unpriv()) {
-               ERROR(NOT_SUPPORTED_ERROR, __FUNCTION__);
-               return false;
-       }
-
        ret = snprintf(clonelxcpath, MAXPATHLEN, "%ssnaps/%s", c->config_path, c->name);
        if (ret < 0 || ret >= MAXPATHLEN)
                goto err;
index ab2bd8426e61de953a59b4ef1b807fb0ccab4d06..dd81c62643f5eb316d72f68e6088b05d8950c764 100644 (file)
@@ -260,4 +260,7 @@ extern void **lxc_append_null_to_array(void **array, size_t count);
 //initialize rand with urandom
 extern int randseed(bool);
 
+inline static bool am_unpriv(void) {
+       return geteuid() != 0;
+}
 #endif