[lxc-devel] [PATCH (resubmit without trailing whitespace)] Replace use of chroot with pivot_root

Michael Holzt lxc at my.fqdn.org
Fri Jan 8 03:42:52 UTC 2010


Replace use of chroot with pivot_root to prevent users from breaking out
of the chroot of a container. Adds a new config keyword 'lxc.pivotdir'
which can optionally specify a temporary mountpoint for the pivot_root
syscall to move the old rootfs to. If not specified, a temporary
directory will be created in the new rootfs and used. After pivot_root
all mounts of the old rootfs and mount of the old rootfs itself will
be umounted before executing the container.

Signed-off-by: Michael Holzt <lxc at my.fqdn.org>
---
 src/lxc/conf.c    |  194 ++++++++++++++++++++++++++++++++++++++++++++++++++---
 src/lxc/conf.h    |    2 +
 src/lxc/confile.c |   18 +++++
 3 files changed, 203 insertions(+), 11 deletions(-)

diff --git a/src/lxc/conf.c b/src/lxc/conf.c
index da7c945..77cb637 100644
--- a/src/lxc/conf.c
+++ b/src/lxc/conf.c
@@ -65,6 +65,8 @@ lxc_log_define(lxc_conf, lxc);
 #define MS_REC 16384
 #endif
 
+extern int pivot_root(const char * new_root, const char * put_old);
+
 typedef int (*instanciate_cb)(struct lxc_netdev *);
 
 struct mount_opt {
@@ -336,7 +338,183 @@ static int setup_tty(const char *rootfs, const struct lxc_tty_info *tty_info)
 	return 0;
 }
 
-static int setup_rootfs(const char *rootfs)
+static int setup_rootfs_pivot_root_cb(void *buffer, void *data)
+{
+	struct lxc_list	*mountlist, *listentry, *iterator;
+	char *pivotdir, *mountpoint, *mountentry;
+	int found;
+	void **cbparm;
+
+	mountentry = buffer;
+	cbparm = (void **)data;
+
+	mountlist = cbparm[0];
+	pivotdir  = cbparm[1];
+
+	/* parse entry, first field is mountname, ignore */
+	mountpoint = strtok(mountentry, " ");
+	if (!mountpoint)
+		return -1;
+
+	/* second field is mountpoint */
+	mountpoint = strtok(NULL, " ");
+	if (!mountpoint)
+		return -1;
+
+	/* only consider mountpoints below old root fs */
+	if (strncmp(mountpoint, pivotdir, strlen(pivotdir)))
+		return 0;
+
+	/* filter duplicate mountpoints */
+	found = 0;
+	lxc_list_for_each(iterator, mountlist) {
+		if (!strcmp(iterator->elem, mountpoint)) {
+			found = 1;
+			break;
+		}
+	}
+	if (found)
+		return 0;
+
+	/* add entry to list */
+	listentry = malloc(sizeof(*listentry));
+	if (!listentry) {
+		SYSERROR("malloc for mountpoint listentry failed");
+		return -1;
+	}
+
+	listentry->elem = strdup(mountpoint);
+	if (!listentry->elem) {
+		SYSERROR("strdup failed");
+		return -1;
+	}
+	lxc_list_add_tail(mountlist, listentry);
+
+	return 0;
+}
+
+
+static int setup_rootfs_pivot_root(const char *rootfs, const char *pivotdir)
+{
+	char path[MAXPATHLEN], buffer[MAXPATHLEN];
+	void *cbparm[2];
+	struct lxc_list mountlist, *iterator;
+	int ok, still_mounted, last_still_mounted;
+	int pivotdir_is_temp = 0;
+
+	/* change into new root fs */
+	if (chdir(rootfs)) {
+		SYSERROR("can't chroot to new rootfs '%s'", rootfs);
+		return -1;
+	}
+
+	/* create temporary mountpoint if none specified */
+	if (!pivotdir) {
+
+		snprintf(path, sizeof(path), "./lxc-oldrootfs-XXXXXX" );
+		if (!mkdtemp(path)) {
+			SYSERROR("can't make temporary mountpoint");
+			return -1;
+		}
+
+		pivotdir = strdup(&path[1]); /* get rid of leading dot */
+		if (!pivotdir) {
+			SYSERROR("strdup failed");
+			return -1;
+		}
+
+		pivotdir_is_temp = 1;
+	}
+	else {
+		snprintf(path, sizeof(path), ".%s", pivotdir);
+	}
+
+	DEBUG("temporary mountpoint for old rootfs is '%s'", path);
+
+	/* pivot_root into our new root fs */
+
+	if (pivot_root(".", path)) {
+		SYSERROR("pivot_root syscall failed");
+		return -1;
+	}
+
+	if (chdir("/")) {
+		SYSERROR("can't chroot to / after pivot_root");
+		return -1;
+	}
+
+	DEBUG("pivot_root syscall to '%s' successful", pivotdir);
+
+	/* read and parse /proc/mounts in old root fs */
+	lxc_list_init(&mountlist);
+
+	snprintf(path, sizeof(path), "%s/", pivotdir);
+	cbparm[0] = &mountlist;
+	cbparm[1] = strdup(path);
+
+	if (!cbparm[1]) {
+		SYSERROR("strdup failed");
+		return -1;
+	}
+
+	snprintf(path, sizeof(path), "/%s/proc/mounts", pivotdir);
+	ok = lxc_file_for_each_line(path,
+			       setup_rootfs_pivot_root_cb,
+			       buffer, sizeof(buffer), &cbparm);
+	if (ok < 0) {
+		SYSERROR("failed to read or parse mount list '%s'", path);
+		return -1;
+	}
+
+	/* umount filesystems until none left or list no longer shrinks */
+	still_mounted = 0;
+	do {
+		last_still_mounted = still_mounted;
+		still_mounted = 0;
+
+		lxc_list_for_each(iterator, &mountlist) {
+
+			if (!umount(iterator->elem)) {
+				DEBUG("umounted '%s'", (char *)iterator->elem);
+				lxc_list_del(iterator);
+				continue;
+			}
+
+			if (errno != EBUSY) {
+				SYSERROR("failed to umount '%s'", (char *)iterator->elem);
+				return -1;
+			}
+
+			still_mounted++;
+		}
+	} while (still_mounted > 0 && still_mounted != last_still_mounted);
+
+	if (still_mounted) {
+		ERROR("could not umount %d mounts", still_mounted);
+		return -1;
+	}
+
+	/* umount old root fs */
+	if (umount(pivotdir)) {
+		SYSERROR("could not unmount old rootfs");
+		return -1;
+	}
+	DEBUG("umounted '%s'", pivotdir);
+
+	/* remove temporary mount point */
+	if (pivotdir_is_temp) {
+		if (rmdir(pivotdir)) {
+			SYSERROR("can't remove temporary mountpoint");
+			return -1;
+		}
+
+	}
+
+	INFO("pivoted to '%s'", rootfs);
+	return 0;
+}
+
+static int setup_rootfs(const char *rootfs, const char *pivotdir)
 {
 	char *tmpname;
 	int ret = -1;
@@ -360,18 +538,11 @@ static int setup_rootfs(const char *rootfs)
 		goto out;
 	}
 
-	if (chroot(tmpname)) {
-		SYSERROR("failed to set chroot %s", tmpname);
+	if (setup_rootfs_pivot_root(tmpname, pivotdir)) {
+		ERROR("failed to pivot_root to '%s'", rootfs);
 		goto out;
 	}
 
-	if (chdir(getenv("HOME")) && chdir("/")) {
-		SYSERROR("failed to change to home directory");
-		goto out;
-	}
-
-	INFO("chrooted to '%s'", rootfs);
-
 	ret = 0;
 out:
 	rmdir(tmpname);
@@ -818,6 +989,7 @@ out:
 int lxc_conf_init(struct lxc_conf *conf)
 {
 	conf->rootfs = NULL;
+	conf->pivotdir = NULL;
 	conf->fstab = NULL;
 	conf->utsname = NULL;
 	conf->tty = 0;
@@ -1118,7 +1290,7 @@ int lxc_setup(const char *name, struct lxc_conf *lxc_conf)
 		return -1;
 	}
 
-	if (setup_rootfs(lxc_conf->rootfs)) {
+	if (setup_rootfs(lxc_conf->rootfs, lxc_conf->pivotdir)) {
 		ERROR("failed to set rootfs for '%s'", name);
 		return -1;
 	}
diff --git a/src/lxc/conf.h b/src/lxc/conf.h
index 8548eeb..e9a9c51 100644
--- a/src/lxc/conf.h
+++ b/src/lxc/conf.h
@@ -157,6 +157,7 @@ struct lxc_tty_info {
  */
 struct lxc_conf {
 	char *rootfs;
+	char *pivotdir;
 	char *fstab;
 	int tty;
 	int pts;
@@ -191,6 +192,7 @@ extern int conf_has(const char *name, const char *info);
 
 #define conf_has_fstab(__name)   conf_has(__name, "fstab")
 #define conf_has_rootfs(__name)  conf_has(__name, "rootfs")
+#define conf_has_pivotdir(__name) conf_has(__name, "pivotdir")
 #define conf_has_utsname(__name) conf_has(__name, "utsname")
 #define conf_has_network(__name) conf_has(__name, "network")
 #define conf_has_console(__name) conf_has(__name, "console")
diff --git a/src/lxc/confile.c b/src/lxc/confile.c
index a5b6263..6e15d09 100644
--- a/src/lxc/confile.c
+++ b/src/lxc/confile.c
@@ -45,6 +45,7 @@ static int config_tty(const char *, char *, struct lxc_conf *);
 static int config_cgroup(const char *, char *, struct lxc_conf *);
 static int config_mount(const char *, char *, struct lxc_conf *);
 static int config_rootfs(const char *, char *, struct lxc_conf *);
+static int config_pivotdir(const char *, char *, struct lxc_conf *);
 static int config_utsname(const char *, char *, struct lxc_conf *);
 static int config_network_type(const char *, char *, struct lxc_conf *);
 static int config_network_flags(const char *, char *, struct lxc_conf *);
@@ -72,6 +73,7 @@ static struct config config[] = {
 	{ "lxc.cgroup",               config_cgroup               },
 	{ "lxc.mount",                config_mount                },
 	{ "lxc.rootfs",               config_rootfs               },
+	{ "lxc.pivotdir",             config_pivotdir             },
 	{ "lxc.utsname",              config_utsname              },
 	{ "lxc.network.type",         config_network_type         },
 	{ "lxc.network.flags",        config_network_flags        },
@@ -580,6 +582,22 @@ static int config_rootfs(const char *key, char *value, struct lxc_conf *lxc_conf
 	return 0;
 }
 
+static int config_pivotdir(const char *key, char *value, struct lxc_conf *lxc_conf)
+{
+	if (strlen(value) >= MAXPATHLEN) {
+		ERROR("%s path is too long", value);
+		return -1;
+	}
+
+	lxc_conf->pivotdir = strdup(value);
+	if (!lxc_conf->pivotdir) {
+		SYSERROR("failed to duplicate string %s", value);
+		return -1;
+	}
+
+	return 0;
+}
+
 static int config_utsname(const char *key, char *value, struct lxc_conf *lxc_conf)
 {
 	struct utsname *utsname;
-- 
1.6.5





More information about the lxc-devel mailing list