[lxc-devel] Patch/RFC: allow pivot_root, unmount old fs

Michael Holzt lxc at my.fqdn.org
Tue Jan 5 05:31:14 UTC 2010


Hello everyone,

i've written another (experimental) patch and would like comments on it.

lxc currently does a chroot into the target rootfs. chroot is insecure and
can easily be broken, as demonstrated here:

| root at synergy:~# touch /this_is_the_realrootfs_ouch
| # touch /container/webhost/this_is_the_container
| # lxc-start -n webhost /bin/sh
| # ls this*
| this_is_the_container
| # ./breakchroot
| # ls this*
| this_is_the_realrootfs_ouch

code to break chroot taken from
http://www.bpfh.net/simes/computing/chroot-break.html


Now this can be fixed. As our container has his own mount namespace, we can
easily pivot_root into the rootfs and then unmount all old mounts. The patch
attached add a new config keyword which contains the path to a temporary
mount for the old rootfs (inside the container). This stops the chroot break
method shown before. 

Example:

| root at synergy:~# grep pivotdir /var/lib/lxc/webhost/config
| lxc.pivotdir = /oldrootfs
| root at synergy:~# ls -lad /container/webhost/oldrootfs
| drwxr-xr-x 2 root root 4096 2010-01-02 03:59 /container/webhost/oldrootfs
| root at synergy:~# lxc-start -n webhost /bin/sh
| # mount -t proc proc /proc
| # cat /proc/mounts
| rootfs / rootfs rw 0 0
| /dev/root / ext3 rw,relatime,errors=remount-ro,data=writeback 0 0
| devpts /dev/console devpts rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=000 0 0
| proc /proc proc rw,relatime 0 0
| # ls this*   
| this_is_the_container
| # ./breakchroot
| # ls this*
| this_is_the_container

The patch is somewhat ugly for now, but i think this feature is very
usefull and this approach should be implemented.

Please try and comment!


Regards,
Michael


-- 
It's an insane world, but i'm proud to be a part of it. -- Bill Hicks
-------------- next part --------------
diff -Naur lxc-0.6.4/src/lxc/conf.c lxc-0.6.4-pivot/src/lxc/conf.c
--- lxc-0.6.4/src/lxc/conf.c	2009-11-24 09:47:26.000000000 +0100
+++ lxc-0.6.4-pivot/src/lxc/conf.c	2010-01-05 06:18:16.000000000 +0100
@@ -65,6 +65,8 @@
 #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 {
@@ -334,7 +336,108 @@
 	return 0;
 }
 
-static int setup_rootfs(const char *rootfs)
+static int setup_pivot_root(const char *rootfs, const char *pivotdir)
+{
+	char path[MAXPATHLEN];
+	FILE *mounts;
+	struct lxc_list mountlist, *listentry, *iterator;
+	char buf[1000], *mountpoint, *p;
+	int counter, oldcounter;
+	
+	chdir(rootfs);
+	
+	snprintf(path, sizeof(path), ".%s", pivotdir);
+	if (pivot_root(".", path)) {
+		SYSERROR("pivot_root syscall failed");
+		return -1;
+	}
+	
+	chdir("/");
+	
+	DEBUG("pivot_root syscall successfull");
+	
+	/* open /proc/mounts in old rootfs */
+	snprintf(path, sizeof(path), "/%s/proc/mounts", pivotdir);
+        mounts = fopen(path, "r");
+	if ( !mounts ) {
+		SYSERROR("Can't open '%s'", path);
+		return -1;
+	}
+
+	/* parse mounts */
+	snprintf(path, sizeof(path), "%s/", pivotdir);
+	
+	lxc_list_init(&mountlist);
+	while (!feof(mounts)) {
+		fgets(buf, sizeof(buf), mounts);
+
+		/* strtok does not work here. why? */
+		p = buf;
+		while ( *p++ != ' ' ) ;
+		mountpoint = p;
+		while ( *++p != ' ' );
+		*p = 0;
+
+		/* only mounts in old rootfs */
+		if (!strncmp(mountpoint, path, strlen(path))) {
+
+			/* first try to unmount right now*/
+			if (!umount(mountpoint)) {
+				DEBUG("unmounted %s", mountpoint);
+			}
+				
+			/* when failed, put on list */
+			else {
+				DEBUG("mounted: %s", mountpoint);
+				listentry = malloc(sizeof(*listentry));
+				if (!listentry) {
+					SYSERROR("malloc for mountpoint list");
+					return -1;
+				}
+				
+				listentry->elem = strdup(mountpoint);
+				lxc_list_add_tail(&mountlist, listentry);
+			}
+		}
+	}
+
+	fclose(mounts);
+
+	/* process list if not empty */
+	if (!lxc_list_empty(&mountlist)) {
+		counter = 0;
+
+		/* try unmount until all done or list no longer shrinks */
+		do {
+			oldcounter = 0;
+			counter = 0;
+			lxc_list_for_each(iterator, &mountlist) {
+				counter++;
+				if (!umount(iterator->elem)) {
+					DEBUG("unmounted %s", iterator->elem);
+					lxc_list_del(iterator);
+				}
+			}
+			
+		} while ( counter > 0 && counter != oldcounter );		
+		
+		if ( counter > 0 ) {
+			ERROR("could not unmount all old mountd");
+			return -1;
+		}
+	
+	}
+	
+	/* unmount old rootfs */
+	if (umount(pivotdir)) {
+		SYSERROR("could not unmount old rootfs");
+		return -1;
+	}
+
+	return 0;
+}	
+
+static int setup_rootfs(const char *rootfs, const char *pivotdir)
 {
 	char *tmpname;
 	int ret = -1;
@@ -358,18 +461,28 @@
 		goto out;
 	}
 
-	if (chroot(tmpname)) {
-		SYSERROR("failed to set chroot %s", tmpname);
-		goto out;
-	}
+        if (pivotdir) {
+        	if (setup_pivot_root(tmpname,pivotdir)) {
+        		ERROR("failed to pivot_root to '%s' (put_old: '%s')", rootfs, pivotdir);
+        		goto out;
+		}
 
-	if (chdir(getenv("HOME")) && chdir("/")) {
-		SYSERROR("failed to change to home directory");
-		goto out;
+		INFO("pivoted to '%s'", rootfs);
 	}
+	else {
+		if (chroot(tmpname)) {
+			SYSERROR("failed to set chroot %s", tmpname);
+			goto out;
+		}
 
-	INFO("chrooted to '%s'", rootfs);
+		if (chdir(getenv("HOME")) && chdir("/")) {
+			SYSERROR("failed to change to home directory");
+			goto out;
+		}
 
+		INFO("chrooted to '%s'", rootfs);
+	}
+	
 	ret = 0;
 out:
 	rmdir(tmpname);
@@ -816,6 +929,7 @@
 int lxc_conf_init(struct lxc_conf *conf)
 {
 	conf->rootfs = NULL;
+	conf->pivotdir = NULL;
 	conf->fstab = NULL;
 	conf->utsname = NULL;
 	conf->tty = 0;
@@ -1065,7 +1179,7 @@
 		ERROR("failed to setup the cgroups for '%s'", name);
 		return -1;
 	}
-
+	
 	if (setup_mount(lxc_conf->fstab)) {
 		ERROR("failed to setup the mounts for '%s'", name);
 		return -1;
@@ -1076,6 +1190,7 @@
 		return -1;
 	}
 
+
 	if (setup_console(lxc_conf->rootfs, lxc_conf->console)) {
 		ERROR("failed to setup the console for '%s'", name);
 		return -1;
@@ -1086,7 +1201,7 @@
 		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 -Naur lxc-0.6.4/src/lxc/conf.h lxc-0.6.4-pivot/src/lxc/conf.h
--- lxc-0.6.4/src/lxc/conf.h	2009-11-19 15:06:02.000000000 +0100
+++ lxc-0.6.4-pivot/src/lxc/conf.h	2010-01-04 22:58:50.000000000 +0100
@@ -133,9 +133,11 @@
  */
 struct lxc_conf {
 	char *rootfs;
+	char *pivotdir;
 	char *fstab;
 	int tty;
 	int pts;
+	int do_pivot;
 	struct utsname *utsname;
 	struct lxc_list cgroup;
 	struct lxc_list network;
@@ -167,6 +169,7 @@
 
 #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 -Naur lxc-0.6.4/src/lxc/confile.c lxc-0.6.4-pivot/src/lxc/confile.c
--- lxc-0.6.4/src/lxc/confile.c	2009-11-19 15:06:02.000000000 +0100
+++ lxc-0.6.4-pivot/src/lxc/confile.c	2010-01-04 22:59:18.000000000 +0100
@@ -44,6 +44,7 @@
 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 *);
@@ -68,6 +69,7 @@
 	{ "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  },
@@ -494,6 +496,22 @@
 	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->rootfs) {
+		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;


More information about the lxc-devel mailing list