Commit bf601689 by Michael Holtz Committed by Daniel Lezcano

use pivot_root instead of chroot

lxc currently does a chroot into the target rootfs. chroot is insecure and can easily be broken, as demonstrated here: | root@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@synergy:~# grep pivotdir /var/lib/lxc/webhost/config | lxc.pivotdir = /oldrootfs | root@synergy:~# ls -lad /container/webhost/oldrootfs | drwxr-xr-x 2 root root 4096 2010-01-02 03:59 /container/webhost/oldrootfs | root@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 Signed-off-by: 's avatarDaniel Lezcano <dlezcano@fr.ibm.com> Signed-off-by: 's avatarMichael Holtz <lxc@my.fqdn.org>
parent 7b379ab3
...@@ -65,6 +65,8 @@ lxc_log_define(lxc_conf, lxc); ...@@ -65,6 +65,8 @@ lxc_log_define(lxc_conf, lxc);
#define MS_REC 16384 #define MS_REC 16384
#endif #endif
extern int pivot_root(const char * new_root, const char * put_old);
typedef int (*instanciate_cb)(struct lxc_netdev *); typedef int (*instanciate_cb)(struct lxc_netdev *);
struct mount_opt { struct mount_opt {
...@@ -336,7 +338,183 @@ static int setup_tty(const char *rootfs, const struct lxc_tty_info *tty_info) ...@@ -336,7 +338,183 @@ static int setup_tty(const char *rootfs, const struct lxc_tty_info *tty_info)
return 0; 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; char *tmpname;
int ret = -1; int ret = -1;
...@@ -360,18 +538,11 @@ static int setup_rootfs(const char *rootfs) ...@@ -360,18 +538,11 @@ static int setup_rootfs(const char *rootfs)
goto out; goto out;
} }
if (chroot(tmpname)) { if (setup_rootfs_pivot_root(tmpname, pivotdir)) {
SYSERROR("failed to set chroot %s", tmpname); ERROR("failed to pivot_root to '%s'", rootfs);
goto out; goto out;
} }
if (chdir(getenv("HOME")) && chdir("/")) {
SYSERROR("failed to change to home directory");
goto out;
}
INFO("chrooted to '%s'", rootfs);
ret = 0; ret = 0;
out: out:
rmdir(tmpname); rmdir(tmpname);
...@@ -827,6 +998,7 @@ struct lxc_conf *lxc_conf_init(void) ...@@ -827,6 +998,7 @@ struct lxc_conf *lxc_conf_init(void)
memset(new, 0, sizeof(*new)); memset(new, 0, sizeof(*new));
new->rootfs = NULL; new->rootfs = NULL;
new->pivotdir = NULL;
new->fstab = NULL; new->fstab = NULL;
new->utsname = NULL; new->utsname = NULL;
new->tty = 0; new->tty = 0;
...@@ -1128,7 +1300,7 @@ int lxc_setup(const char *name, struct lxc_conf *lxc_conf) ...@@ -1128,7 +1300,7 @@ int lxc_setup(const char *name, struct lxc_conf *lxc_conf)
return -1; 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); ERROR("failed to set rootfs for '%s'", name);
return -1; return -1;
} }
......
...@@ -157,6 +157,7 @@ struct lxc_tty_info { ...@@ -157,6 +157,7 @@ struct lxc_tty_info {
*/ */
struct lxc_conf { struct lxc_conf {
char *rootfs; char *rootfs;
char *pivotdir;
char *fstab; char *fstab;
int tty; int tty;
int pts; int pts;
...@@ -191,6 +192,7 @@ extern int conf_has(const char *name, const char *info); ...@@ -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_fstab(__name) conf_has(__name, "fstab")
#define conf_has_rootfs(__name) conf_has(__name, "rootfs") #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_utsname(__name) conf_has(__name, "utsname")
#define conf_has_network(__name) conf_has(__name, "network") #define conf_has_network(__name) conf_has(__name, "network")
#define conf_has_console(__name) conf_has(__name, "console") #define conf_has_console(__name) conf_has(__name, "console")
......
...@@ -45,6 +45,7 @@ static int config_tty(const char *, char *, struct lxc_conf *); ...@@ -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_cgroup(const char *, char *, struct lxc_conf *);
static int config_mount(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_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_utsname(const char *, char *, struct lxc_conf *);
static int config_network_type(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 *); static int config_network_flags(const char *, char *, struct lxc_conf *);
...@@ -72,6 +73,7 @@ static struct config config[] = { ...@@ -72,6 +73,7 @@ static struct config config[] = {
{ "lxc.cgroup", config_cgroup }, { "lxc.cgroup", config_cgroup },
{ "lxc.mount", config_mount }, { "lxc.mount", config_mount },
{ "lxc.rootfs", config_rootfs }, { "lxc.rootfs", config_rootfs },
{ "lxc.pivotdir", config_pivotdir },
{ "lxc.utsname", config_utsname }, { "lxc.utsname", config_utsname },
{ "lxc.network.type", config_network_type }, { "lxc.network.type", config_network_type },
{ "lxc.network.flags", config_network_flags }, { "lxc.network.flags", config_network_flags },
...@@ -580,6 +582,22 @@ static int config_rootfs(const char *key, char *value, struct lxc_conf *lxc_conf ...@@ -580,6 +582,22 @@ static int config_rootfs(const char *key, char *value, struct lxc_conf *lxc_conf
return 0; 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) static int config_utsname(const char *key, char *value, struct lxc_conf *lxc_conf)
{ {
struct utsname *utsname; struct utsname *utsname;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment