使用pivot_root更改root后,exec无法找到文件

问题描述 投票:0回答:1

我正在尝试使用命名空间和 chroot 模拟,并成功创建了一个仅包含新根的环境(通过目录遍历确认),但由于某种原因,我似乎无法在其中执行任何操作它。

这是一个最小可重复的示例:

mkdir /jail
mkdir /jail/bin
mkdir /jail/usr
mkdir /jail/lib
cp /bin/bash /jail/bin
/* isoroot.c */
#define _GNU_SOURCE

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/resource.h>
#include <sched.h> /* use clone */
#include <syscall.h>
#include <sys/mount.h>
#include <sched.h>

#ifndef pivot_root
#define pivot_root(new, old) syscall(SYS_pivot_root, new, old)
#endif

int main(int argc, char *argv[])
{
    int res = 0;
    const char *new_root = "/jail";
    const char *old_root = "/oldroot";

    (void) argc;

    if (unshare(CLONE_NEWNS)) { /* Must be executed with privileges. Don't forget this, or we basically blow up the system since we'll remove the real mount */
        fprintf(stderr, "unshare: %s\n", strerror(errno));
        _exit(errno);
    }
    if (mount(NULL, "/", NULL, MS_REC | MS_PRIVATE, NULL)) { /* Prevent shared propagation */
        fprintf(stderr, "mount: %s\n", strerror(errno));
        _exit(errno);
    }
    if (mount(new_root, new_root, NULL, MS_BIND, NULL)) { /* Ensure new_root is a mount point */ 
        fprintf(stderr, "mount: %s\n", strerror(errno));
        _exit(errno);
    }
    if (mkdir("/jail/oldroot", 0777)) { /* should be new_root/old_root */
        fprintf(stderr, "mkdir: %s\n", strerror(errno));
        _exit(errno);
    }

    /* Mount some things we need before we dismount the old root */
    /* /usr has /bin, /sbin, /lib, and /lib64 */
    res = mount("/usr", "/jail/usr", NULL, MS_BIND | MS_REC | MS_RDONLY, NULL);
    if (res) {
        fprintf(stderr, "mount usr failed: %s\n", strerror(errno));
    }

    if (pivot_root(new_root, "/jail/oldroot")) { /* Actually make it the new root */
        fprintf(stderr, "pivot_root: %s\n", strerror(errno));
        _exit(errno);
    }
    if (chdir("/")) { /* Switch to new root */
        fprintf(stderr, "chdir failed: %s\n", strerror(errno));
        _exit(errno);
    }

    if (umount2(old_root, MNT_DETACH)) { /* Unmount old root */
        fprintf(stderr, "umount2 failed: %s\n", strerror(errno));
        _exit(errno);
    }
    if (rmdir(old_root)) { /* Remove old mount point */
        fprintf(stderr, "rmdir failed: %s\n", strerror(errno));
        _exit(errno);
    }
    if (access("/bin/bash", X_OK)) {
        fprintf(stderr, "bash4 not found: %s\n", strerror(errno));
        _exit(errno);
    }
    printf("Got all the way here\n");
    execvp("/bin/bash", argv);
    fprintf(stderr, "%s\n", strerror(errno));
    exit(errno);
}

编译:

gcc -Wall -Werror -Wunused -Wextra -Wmaybe-uninitialized -Wstrict-prototypes -Wmissing-prototypes -Wdeclaration-after-statement -Wmissing-declarations -Wmissing-format-attribute -Wnull-dereference -Wformat=2 -Wshadow -Wsizeof-pointer-memaccess -std=gnu99 -pthread -O0 -g -Wstack-protector -fno-omit-frame-pointer -fwrapv -D_FORTIFY_SOURCE=2 -c isoroot.c
gcc -Wall -Werror -Wunused -Wextra -Wmaybe-uninitialized -Wstrict-prototypes -Wmissing-prototypes -Wdeclaration-after-statement -Wmissing-declarations -Wmissing-format-attribute -Wnull-dereference -Wformat=2 -Wshadow -Wsizeof-pointer-memaccess -std=gnu99 -pthread -O0 -g -Wstack-protector -fno-omit-frame-pointer -fwrapv -D_FORTIFY_SOURCE=2 -o isoroot *.o

看起来特别奇怪的是,最后一个

access
成功了,说明
access
可以找到
/bin/bash
。然而,
execvp
总是失败,并出现
No such file or directory

我假设由于

access
此时正在使用新的命名空间,
exec
也会使用新的命名空间,但由于某种原因,这似乎并不成立。是否有一些技巧可以让 exec 在更改根目录后找到东西?

作为比较,这里的示例确实在 exec 成功的意义上起作用,但它似乎并没有真正旋转根,因为真正根的所有内容仍然可见:https://lkml.iu.edu/ hypermail/linux/kernel/0803.0/1805.html

chdir("/jail"); 
unshare(CLONE_NEWNS);
mount("/jail", "/jail", NULL, MS_BIND, NULL);
pivot_root("/jail", "/jail/old_root");
chdir("/");
mount("/old_root/bin", "bin", NULL, MS_BIND, NULL);
mount("/old_root/usr", "usr", NULL, MS_BIND, NULL);
mount("/old_root/lib", "lib", NULL, MS_BIND, NULL);
umount2("/old_root", MNT_DETACH);
exec("/busybox");

使用

ldd
,我已经确认我应该拥有运行类似
/bin/sh
之类的东西所需的一切。
/bin
/lib
,以及
/lib64
都只是指向

中的文件夹的符号链接
# ldd /bin/bash
        linux-vdso.so.1 (0x00007ffc99116000)
        libtinfo.so.6 => /lib/x86_64-linux-gnu/libtinfo.so.6 (0x00007fd041cb9000)
        libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007fd041cb3000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fd041ade000)
        /lib64/ld-linux-x86-64.so.2 (0x00007fd041e3a000)

我也尝试手动添加符号链接,以防万一,但当我在最后执行此操作时,就在 exec 之前,我只是得到“错误:文件已存在”:

    if (symlink("/bin", "/usr/bin")) {
        fprintf(stderr, "symlink failed: %s\n", strerror(errno));
    }
    if (symlink("/lib", "/usr/lib")) {
        fprintf(stderr, "symlink failed: %s\n", strerror(errno));
    }
    if (symlink("/lib64", "/usr/lib64")) {
        fprintf(stderr, "symlink failed: %s\n", strerror(errno));
    }

尝试

mount
/proc
/lib64
失败,尽管
/bin
/lib
成功(尽管除
/proc
之外的所有这些都是到
/usr/<dir>

的符号链接)
c chroot linux-namespaces
1个回答
-1
投票

我也遇到了同样的问题,有更新吗?

© www.soinside.com 2019 - 2024. All rights reserved.