[lxc-devel] [PATCH 1/1] lxc-clone: support unprivileged use
Serge Hallyn
serge.hallyn at ubuntu.com
Thu Jan 23 00:18:04 UTC 2014
This also fixes unprivileged use of lxc-snapshot and lxc-rename.
Signed-off-by: Serge Hallyn <serge.hallyn at ubuntu.com>
---
src/lxc/bdev.c | 100 +++++++++++++++++++++++++++++----------
src/lxc/bdev.h | 4 +-
src/lxc/lxccontainer.c | 126 +++++++++++++++++++++++++++----------------------
src/lxc/utils.h | 3 ++
4 files changed, 150 insertions(+), 83 deletions(-)
diff --git a/src/lxc/bdev.c b/src/lxc/bdev.c
index 5cd7340..2c3388f 100644
--- a/src/lxc/bdev.c
+++ b/src/lxc/bdev.c
@@ -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,
diff --git a/src/lxc/bdev.h b/src/lxc/bdev.h
index c45f086..cc7b59e 100644
--- a/src/lxc/bdev.h
+++ b/src/lxc/bdev.h
@@ -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,
diff --git a/src/lxc/lxccontainer.c b/src/lxc/lxccontainer.c
index 3723997..5a3802a 100644
--- a/src/lxc/lxccontainer.c
+++ b/src/lxc/lxccontainer.c
@@ -66,10 +66,6 @@
lxc_log_define(lxc_container, lxc);
-inline static bool am_unpriv() {
- return geteuid() != 0;
-}
-
static bool file_exists(const char *f)
{
struct stat statbuf;
@@ -2348,8 +2344,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");
@@ -2375,36 +2370,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);
@@ -2431,26 +2439,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);
}
/*
@@ -2489,16 +2503,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;
@@ -2541,6 +2552,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);
@@ -2581,12 +2599,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);
@@ -2608,11 +2645,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");
@@ -2685,11 +2717,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)
@@ -2828,11 +2855,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) {
@@ -2911,11 +2933,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");
@@ -2965,11 +2982,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;
diff --git a/src/lxc/utils.h b/src/lxc/utils.h
index ab2bd84..dd81c62 100644
--- a/src/lxc/utils.h
+++ b/src/lxc/utils.h
@@ -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
--
1.8.5.3
More information about the lxc-devel
mailing list