chroot系统调用及示例

chroot函数详解

1. 函数介绍

chroot函数是Linux系统中用于改变进程根目录的系统调用函数,它的名字来源于”change root”。可以把chroot想象成一个”虚拟监狱管理员”,它能够为进程创建一个隔离的文件系统环境,让进程认为某个指定的目录就是文件系统的根目录(/)。

chroot通过改变进程的根目录视图,创建了一个受限的执行环境。在这个环境中,进程无法访问指定根目录之外的文件系统,从而提供了一定程度的安全隔离。这就像给进程戴上了一副”有色眼镜”,让它只能看到特定范围内的文件系统。

重要说明: chroot本身不是安全边界,经验丰富的攻击者可能通过各种方式”跳出”chroot环境。

使用场景:

  • 系统维护和修复
  • 软件构建环境隔离
  • 测试环境搭建
  • 简单的沙箱环境
  • 系统恢复和救援
  • 旧版容器技术的基础

2. 函数原型

#include <unistd.h>

int chroot(const char *path);

3. 功能

chroot函数的主要功能是改变调用进程及其子进程的根目录。调用成功后,指定的目录将成为新的文件系统根目录(/),所有相对路径和绝对路径的解析都会基于这个新的根目录。

4. 参数

  • path: 新的根目录路径
    • 类型:const char*
    • 含义:指向新根目录的路径字符串
    • 该路径必须是一个已存在的目录

5. 返回值

  • 成功: 返回0
  • 失败: 返回-1,并设置errno错误码
    • EACCES:权限不足(需要CAP_SYS_CHROOT能力)
    • EBUSY:当前目录是文件系统的根目录且忙
    • EFAULT:path指向无效内存
    • EIO:I/O错误
    • ELOOP:符号链接循环
    • ENAMETOOLONG:路径名过长
    • ENOENT:目录不存在
    • ENOTDIR:path不是目录
    • EPERM:操作不被允许

6. 相似函数或关联函数

  • chdir(): 改变当前工作目录
  • pivot_root(): 更现代的根目录切换函数
  • mount(): 挂载文件系统
  • unshare(): 创建新的命名空间
  • clone(): 创建进程时指定命名空间
  • setuid()/setgid(): 改变用户/组ID
  • capset(): 设置进程能力

7. 示例代码

示例1:基础chroot使用 – 简单环境切换

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <dirent.h>

// 创建chroot环境
int create_chroot_environment(const char* chroot_path) {
    printf("创建chroot环境: %s\n", chroot_path);
    
    // 创建根目录
    if (mkdir(chroot_path, 0755) == -1 && errno != EEXIST) {
        perror("创建根目录失败");
        return -1;
    }
    
    // 创建基本目录结构
    const char* dirs[] = {
        "bin", "lib", "lib64", "usr", "etc", "dev", "tmp", "proc"
    };
    
    for (int i = 0; i < 8; i++) {
        char full_path[256];
        snprintf(full_path, sizeof(full_path), "%s/%s", chroot_path, dirs[i]);
        if (mkdir(full_path, 0755) == -1 && errno != EEXIST) {
            perror("创建目录失败");
            return -1;
        }
    }
    
    // 创建基本设备文件(简化版本)
    char dev_path[256];
    snprintf(dev_path, sizeof(dev_path), "%s/dev", chroot_path);
    
    // 创建null设备节点
    char null_path[256];
    snprintf(null_path, sizeof(null_path), "%s/dev/null", chroot_path);
    if (mknod(null_path, S_IFCHR | 0666, makedev(1, 3)) == -1 && errno != EEXIST) {
        printf("警告: 创建/dev/null失败: %s\n", strerror(errno));
    }
    
    // 创建zero设备节点
    char zero_path[256];
    snprintf(zero_path, sizeof(zero_path), "%s/dev/zero", chroot_path);
    if (mknod(zero_path, S_IFCHR | 0666, makedev(1, 5)) == -1 && errno != EEXIST) {
        printf("警告: 创建/dev/zero失败: %s\n", strerror(errno));
    }
    
    printf("chroot环境创建完成\n");
    return 0;
}

// 显示当前目录结构
void show_directory_tree(const char* path, int depth) {
    DIR* dir = opendir(path);
    if (dir == NULL) {
        printf("无法打开目录: %s\n", path);
        return;
    }
    
    // 显示缩进
    for (int i = 0; i < depth; i++) {
        printf("  ");
    }
    printf("%s/\n", path);
    
    struct dirent* entry;
    while ((entry = readdir(dir)) != NULL) {
        if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
            continue;
        }
        
        // 显示文件/目录
        for (int i = 0; i < depth + 1; i++) {
            printf("  ");
        }
        printf("%s%s\n", entry->d_name, 
               entry->d_type == DT_DIR ? "/" : "");
    }
    
    closedir(dir);
}

// 显示文件系统信息
void show_filesystem_info() {
    char cwd[1024];
    if (getcwd(cwd, sizeof(cwd)) != NULL) {
        printf("当前工作目录: %s\n", cwd);
    }
    
    // 显示根目录内容
    printf("根目录内容:\n");
    DIR* root_dir = opendir("/");
    if (root_dir) {
        struct dirent* entry;
        int count = 0;
        while ((entry = readdir(root_dir)) != NULL && count < 10) {
            if (strcmp(entry->d_name, ".") != 0 && strcmp(entry->d_name, "..") != 0) {
                printf("  %s%s\n", entry->d_name, 
                       entry->d_type == DT_DIR ? "/" : "");
                count++;
            }
        }
        if (count >= 10) {
            printf("  ... (更多文件)\n");
        }
        closedir(root_dir);
    }
}

int main() {
    printf("=== 基础chroot使用示例 ===\n");
    
    const char* chroot_dir = "/tmp/my_chroot";
    
    // 检查是否具有root权限
    if (geteuid() != 0) {
        printf("警告: chroot需要root权限运行\n");
        printf("请使用sudo运行此程序\n");
        exit(EXIT_FAILURE);
    }
    
    // 创建chroot环境
    if (create_chroot_environment(chroot_dir) == -1) {
        exit(EXIT_FAILURE);
    }
    
    printf("\n1. chroot前的文件系统状态:\n");
    show_filesystem_info();
    show_directory_tree(chroot_dir, 0);
    
    // 获取当前工作目录
    char original_cwd[1024];
    if (getcwd(original_cwd, sizeof(original_cwd)) == NULL) {
        perror("获取当前目录失败");
        exit(EXIT_FAILURE);
    }
    printf("原始工作目录: %s\n", original_cwd);
    
    // 执行chroot
    printf("\n2. 执行chroot操作:\n");
    printf("切换根目录到: %s\n", chroot_dir);
    
    if (chroot(chroot_dir) == -1) {
        perror("chroot失败");
        exit(EXIT_FAILURE);
    }
    
    printf("✓ chroot操作成功\n");
    
    // 改变工作目录到新的根目录
    if (chdir("/") == -1) {
        perror("改变工作目录失败");
        exit(EXIT_FAILURE);
    }
    
    printf("\n3. chroot后的文件系统状态:\n");
    show_filesystem_info();
    
    // 创建一些测试文件
    printf("\n4. 在chroot环境中创建文件:\n");
    int fd = open("/test_file.txt", O_CREAT | O_WRONLY | O_TRUNC, 0644);
    if (fd != -1) {
        write(fd, "这是chroot环境中的测试文件\n", 26);
        close(fd);
        printf("创建文件: /test_file.txt\n");
    }
    
    // 创建目录
    if (mkdir("/mydir", 0755) == 0) {
        printf("创建目录: /mydir\n");
    }
    
    // 显示chroot环境内容
    show_directory_tree("/", 0);
    
    // 尝试访问原始系统文件(应该失败)
    printf("\n5. 尝试访问原始系统文件:\n");
    if (access("/etc/passwd", F_OK) == -1) {
        printf("✓ 无法访问原始系统文件 /etc/passwd (预期行为)\n");
    } else {
        printf("✗ 仍然可以访问原始系统文件\n");
    }
    
    // 清理测试文件
    unlink("/test_file.txt");
    rmdir("/mydir");
    
    printf("\n=== 基础chroot演示完成 ===\n");
    printf("注意: 此程序在chroot环境中结束\n");
    
    return 0;
}

示例2:安全chroot实现 – 防止逃逸

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <dirent.h>
#include <pwd.h>

// 安全chroot函数
int secure_chroot(const char* new_root) {
    struct stat root_stat, cwd_stat;
    char cwd[4096];
    
    printf("执行安全chroot到: %s\n", new_root);
    
    // 1. 验证新根目录存在且是目录
    if (stat(new_root, &root_stat) == -1) {
        perror("无法访问根目录");
        return -1;
    }
    
    if (!S_ISDIR(root_stat.st_mode)) {
        fprintf(stderr, "指定路径不是目录\n");
        return -1;
    }
    
    // 2. 验证新根目录权限
    if (access(new_root, R_OK | X_OK) == -1) {
        perror("根目录权限不足");
        return -1;
    }
    
    // 3. 改变当前工作目录到根目录
    if (chdir(new_root) == -1) {
        perror("改变到根目录失败");
        return -1;
    }
    
    // 4. 获取当前目录的inode信息
    if (getcwd(cwd, sizeof(cwd)) == NULL) {
        perror("获取当前目录失败");
        return -1;
    }
    
    if (stat(".", &cwd_stat) == -1) {
        perror("获取当前目录状态失败");
        return -1;
    }
    
    // 5. 执行chroot
    if (chroot(".") == -1) {
        perror("chroot失败");
        return -1;
    }
    
    // 6. 再次改变到根目录(防止某些逃逸技术)
    if (chdir("/") == -1) {
        perror("最终改变目录失败");
        return -1;
    }
    
    printf("✓ 安全chroot完成\n");
    return 0;
}

// 在chroot环境中运行的函数
void run_in_chroot() {
    printf("\n=== 在chroot环境中运行 ===\n");
    
    // 显示环境信息
    printf("进程ID: %d\n", getpid());
    printf("父进程ID: %d\n", getppid());
    
    char cwd[4096];
    if (getcwd(cwd, sizeof(cwd)) != NULL) {
        printf("当前工作目录: %s\n", cwd);
    }
    
    // 显示用户信息
    printf("用户ID: %d\n", getuid());
    printf("有效用户ID: %d\n", geteuid());
    printf("组ID: %d\n", getgid());
    
    // 显示根目录内容
    printf("根目录内容:\n");
    DIR* dir = opendir("/");
    if (dir) {
        struct dirent* entry;
        while ((entry = readdir(dir)) != NULL) {
            if (strcmp(entry->d_name, ".") != 0 && strcmp(entry->d_name, "..") != 0) {
                printf("  %s%s\n", entry->d_name, 
                       entry->d_type == DT_DIR ? "/" : "");
            }
        }
        closedir(dir);
    }
    
    // 创建测试文件
    int fd = open("/chroot_test.txt", O_CREAT | O_WRONLY | O_TRUNC, 0644);
    if (fd != -1) {
        const char* test_content = "Chroot环境测试文件\n创建时间: ";
        write(fd, test_content, strlen(test_content));
        
        // 添加时间戳
        time_t now = time(NULL);
        char time_str[64];
        snprintf(time_str, sizeof(time_str), "%s", ctime(&now));
        // 移除换行符
        char* newline = strchr(time_str, '\n');
        if (newline) *newline = '\0';
        write(fd, time_str, strlen(time_str));
        write(fd, "\n", 1);
        close(fd);
        printf("创建测试文件: /chroot_test.txt\n");
    }
    
    // 显示测试文件内容
    fd = open("/chroot_test.txt", O_RDONLY);
    if (fd != -1) {
        char buffer[256];
        ssize_t bytes_read = read(fd, buffer, sizeof(buffer) - 1);
        if (bytes_read > 0) {
            buffer[bytes_read] = '\0';
            printf("测试文件内容:\n%s", buffer);
        }
        close(fd);
    }
    
    // 演示环境隔离
    printf("\n环境隔离测试:\n");
    
    // 尝试访问原始系统文件
    const char* system_files[] = {
        "/etc/passwd",
        "/etc/shadow",
        "/proc/1/cmdline",
        "/sys/kernel",
        "/dev/sda"
    };
    
    for (int i = 0; i < 5; i++) {
        if (access(system_files[i], F_OK) == 0) {
            printf("  能够访问: %s\n", system_files[i]);
        } else {
            printf("  无法访问: %s (%s)\n", system_files[i], strerror(errno));
        }
    }
    
    // 清理测试文件
    unlink("/chroot_test.txt");
}

// 演示chroot逃逸防护
void demonstrate_escape_protection() {
    printf("\n=== chroot逃逸防护演示 ===\n");
    
    // 这些是常见的chroot逃逸尝试
    printf("尝试常见的逃逸方法:\n");
    
    // 1. 尝试通过..访问上级目录
    if (chdir("..") == 0) {
        char cwd[4096];
        if (getcwd(cwd, sizeof(cwd)) != NULL) {
            printf("  cd .. 后的目录: %s\n", cwd);
        }
        // 回到根目录
        chdir("/");
    } else {
        printf("  cd .. 失败 (预期行为)\n");
    }
    
    // 2. 尝试通过绝对路径访问
    if (access("/etc/passwd", F_OK) == 0) {
        printf("  能够访问 /etc/passwd (可能存在问题)\n");
    } else {
        printf("  无法访问 /etc/passwd (正常隔离)\n");
    }
    
    // 3. 尝试创建符号链接到外部
    if (symlink("/etc/passwd", "/passwd_link") == 0) {
        printf("  创建符号链接成功\n");
        // 测试符号链接是否有效
        if (access("/passwd_link", F_OK) == 0) {
            printf("  符号链接指向有效文件\n");
        } else {
            printf("  符号链接无效或被隔离\n");
        }
        unlink("/passwd_link");
    } else {
        printf("  无法创建符号链接 (正常)\n");
    }
}

int main() {
    printf("=== 安全chroot实现示例 ===\n");
    
    // 检查权限
    if (geteuid() != 0) {
        printf("错误: 此程序需要root权限运行\n");
        exit(EXIT_FAILURE);
    }
    
    const char* chroot_path = "/tmp/secure_chroot";
    
    // 创建安全的chroot环境
    printf("1. 创建安全chroot环境:\n");
    
    // 创建基本目录结构
    const char* dirs[] = {"bin", "etc", "dev", "tmp", "usr", "lib", "lib64"};
    if (mkdir(chroot_path, 0755) == -1 && errno != EEXIST) {
        perror("创建根目录失败");
        exit(EXIT_FAILURE);
    }
    
    for (int i = 0; i < 7; i++) {
        char full_path[256];
        snprintf(full_path, sizeof(full_path), "%s/%s", chroot_path, dirs[i]);
        if (mkdir(full_path, 0755) == -1 && errno != EEXIST) {
            perror("创建目录失败");
            exit(EXIT_FAILURE);
        }
    }
    
    // 创建基本设备文件
    char null_path[256];
    snprintf(null_path, sizeof(null_path), "%s/dev/null", chroot_path);
    if (mknod(null_path, S_IFCHR | 0666, makedev(1, 3)) == -1 && errno != EEXIST) {
        printf("警告: 创建/dev/null失败\n");
    }
    
    printf("chroot环境创建完成: %s\n", chroot_path);
    
    // 执行安全chroot
    printf("\n2. 执行安全chroot:\n");
    if (secure_chroot(chroot_path) == -1) {
        exit(EXIT_FAILURE);
    }
    
    // 在chroot环境中运行
    run_in_chroot();
    
    // 演示逃逸防护
    demonstrate_escape_protection();
    
    // 显示最终状态
    printf("\n=== chroot环境演示完成 ===\n");
    printf("当前仍在chroot环境中\n");
    
    return 0;
}

示例3:chroot环境构建与程序执行

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <dirent.h>
#include <sys/wait.h>

// 复制文件到chroot环境
int copy_file_to_chroot(const char* src, const char* dst_chroot, const char* dst_path) {
    char full_dst_path[512];
    snprintf(full_dst_path, sizeof(full_dst_path), "%s%s", dst_chroot, dst_path);
    
    // 确保目标目录存在
    char* last_slash = strrchr(full_dst_path, '/');
    if (last_slash) {
        *last_slash = '\0';
        // 创建目录(简化实现)
        mkdir(full_dst_path, 0755);
        *last_slash = '/';
    }
    
    // 打开源文件
    int src_fd = open(src, O_RDONLY);
    if (src_fd == -1) {
        printf("警告: 无法打开源文件 %s: %s\n", src, strerror(errno));
        return -1;
    }
    
    // 创建目标文件
    int dst_fd = open(full_dst_path, O_CREAT | O_WRONLY | O_TRUNC, 0755);
    if (dst_fd == -1) {
        printf("警告: 无法创建目标文件 %s: %s\n", full_dst_path, strerror(errno));
        close(src_fd);
        return -1;
    }
    
    // 复制文件内容
    char buffer[8192];
    ssize_t bytes_read, bytes_written;
    
    while ((bytes_read = read(src_fd, buffer, sizeof(buffer))) > 0) {
        bytes_written = write(dst_fd, buffer, bytes_read);
        if (bytes_written != bytes_read) {
            perror("写入目标文件失败");
            close(src_fd);
            close(dst_fd);
            return -1;
        }
    }
    
    close(src_fd);
    close(dst_fd);
    
    printf("复制文件: %s -> %s\n", src, full_dst_path);
    return 0;
}

// 构建基本的chroot环境
int build_basic_chroot(const char* chroot_path) {
    printf("构建基本chroot环境: %s\n", chroot_path);
    
    // 创建目录结构
    const char* dirs[] = {
        "", "bin", "sbin", "etc", "dev", "usr", "usr/bin", 
        "usr/sbin", "lib", "lib64", "tmp", "var", "var/tmp"
    };
    
    for (int i = 0; i < 13; i++) {
        char full_path[256];
        snprintf(full_path, sizeof(full_path), "%s/%s", chroot_path, dirs[i]);
        if (mkdir(full_path, 0755) == -1 && errno != EEXIST) {
            if (errno != EEXIST) {
                printf("警告: 创建目录失败 %s: %s\n", full_path, strerror(errno));
            }
        }
    }
    
    // 创建基本设备文件
    char dev_path[256];
    snprintf(dev_path, sizeof(dev_path), "%s/dev/null", chroot_path);
    if (mknod(dev_path, S_IFCHR | 0666, makedev(1, 3)) == -1 && errno != EEXIST) {
        printf("警告: 创建/dev/null失败\n");
    }
    
    snprintf(dev_path, sizeof(dev_path), "%s/dev/zero", chroot_path);
    if (mknod(dev_path, S_IFCHR | 0666, makedev(1, 5)) == -1 && errno != EEXIST) {
        printf("警告: 创建/dev/zero失败\n");
    }
    
    snprintf(dev_path, sizeof(dev_path), "%s/dev/random", chroot_path);
    if (mknod(dev_path, S_IFCHR | 0666, makedev(1, 8)) == -1 && errno != EEXIST) {
        printf("警告: 创建/dev/random失败\n");
    }
    
    snprintf(dev_path, sizeof(dev_path), "%s/dev/urandom", chroot_path);
    if (mknod(dev_path, S_IFCHR | 0666, makedev(1, 9)) == -1 && errno != EEXIST) {
        printf("警告: 创建/dev/urandom失败\n");
    }
    
    // 复制基本命令(根据系统实际情况调整)
    printf("复制基本命令...\n");
    
    // 复制shell
    copy_file_to_chroot("/bin/sh", chroot_path, "/bin/sh");
    
    // 复制基本命令
    const char* basic_commands[] = {
        "/bin/ls", "/bin/cat", "/bin/echo", "/bin/pwd",
        "/usr/bin/id", "/bin/ps"
    };
    
    for (int i = 0; i < 6; i++) {
        copy_file_to_chroot(basic_commands[i], chroot_path, basic_commands[i]);
    }
    
    // 创建基本配置文件
    char etc_passwd[256];
    snprintf(etc_passwd, sizeof(etc_passwd), "%s/etc/passwd", chroot_path);
    int fd = open(etc_passwd, O_CREAT | O_WRONLY | O_TRUNC, 0644);
    if (fd != -1) {
        const char* passwd_content = 
            "root:x:0:0:root:/root:/bin/sh\n"
            "nobody:x:65534:65534:nobody:/:/bin/sh\n";
        write(fd, passwd_content, strlen(passwd_content));
        close(fd);
        printf("创建 /etc/passwd\n");
    }
    
    char etc_group[256];
    snprintf(etc_group, sizeof(etc_group), "%s/etc/group", chroot_path);
    fd = open(etc_group, O_CREAT | O_WRONLY | O_TRUNC, 0644);
    if (fd != -1) {
        const char* group_content = 
            "root:x:0:\n"
            "nobody:x:65534:\n";
        write(fd, group_content, strlen(group_content));
        close(fd);
        printf("创建 /etc/group\n");
    }
    
    printf("基本chroot环境构建完成\n");
    return 0;
}

// 在chroot环境中执行命令
int execute_in_chroot(const char* chroot_path, const char* command) {
    pid_t pid = fork();
    
    if (pid == -1) {
        perror("fork失败");
        return -1;
    }
    
    if (pid == 0) {
        // 子进程
        // 执行chroot
        if (chroot(chroot_path) == -1) {
            perror("chroot失败");
            exit(EXIT_FAILURE);
        }
        
        // 改变到根目录
        if (chdir("/") == -1) {
            perror("chdir失败");
            exit(EXIT_FAILURE);
        }
        
        // 执行命令
        execl("/bin/sh", "sh", "-c", command, (char*)NULL);
        perror("执行命令失败");
        exit(EXIT_FAILURE);
    } else {
        // 父进程等待子进程结束
        int status;
        waitpid(pid, &status, 0);
        
        if (WIFEXITED(status)) {
            int exit_code = WEXITSTATUS(status);
            printf("命令执行完成,退出码: %d\n", exit_code);
            return exit_code;
        } else if (WIFSIGNALED(status)) {
            int signal = WTERMSIG(status);
            printf("命令被信号终止: %d\n", signal);
            return -1;
        }
    }
    
    return 0;
}

// 显示chroot环境内容
void show_chroot_contents(const char* chroot_path) {
    printf("\nchroot环境内容:\n");
    
    DIR* dir = opendir(chroot_path);
    if (dir) {
        struct dirent* entry;
        while ((entry = readdir(dir)) != NULL) {
            if (strcmp(entry->d_name, ".") != 0 && strcmp(entry->d_name, "..") != 0) {
                printf("  /%s%s\n", entry->d_name, 
                       entry->d_type == DT_DIR ? "/" : "");
                
                // 显示子目录内容(仅一层)
                if (entry->d_type == DT_DIR) {
                    char sub_path[512];
                    snprintf(sub_path, sizeof(sub_path), "%s/%s", chroot_path, entry->d_name);
                    DIR* sub_dir = opendir(sub_path);
                    if (sub_dir) {
                        struct dirent* sub_entry;
                        int count = 0;
                        while ((sub_entry = readdir(sub_dir)) != NULL && count < 5) {
                            if (strcmp(sub_entry->d_name, ".") != 0 && 
                                strcmp(sub_entry->d_name, "..") != 0) {
                                printf("    %s%s\n", sub_entry->d_name, 
                                       sub_entry->d_type == DT_DIR ? "/" : "");
                                count++;
                            }
                        }
                        if (count >= 5) {
                            printf("    ...\n");
                        }
                        closedir(sub_dir);
                    }
                }
            }
        }
        closedir(dir);
    }
}

int main() {
    printf("=== chroot环境构建与程序执行示例 ===\n");
    
    if (geteuid() != 0) {
        printf("错误: 此程序需要root权限运行\n");
        exit(EXIT_FAILURE);
    }
    
    const char* chroot_path = "/tmp/full_chroot";
    
    // 构建chroot环境
    printf("1. 构建完整的chroot环境:\n");
    if (build_basic_chroot(chroot_path) == -1) {
        exit(EXIT_FAILURE);
    }
    
    show_chroot_contents(chroot_path);
    
    // 在chroot环境中执行命令
    printf("\n2. 在chroot环境中执行命令:\n");
    
    // 执行基本命令
    const char* commands[] = {
        "echo 'Hello from chroot!'",
        "ls -la /",
        "pwd",
        "id",
        "cat /etc/passwd"
    };
    
    for (int i = 0; i < 5; i++) {
        printf("\n执行命令: %s\n", commands[i]);
        printf("--- 输出开始 ---\n");
        execute_in_chroot(chroot_path, commands[i]);
        printf("--- 输出结束 ---\n");
    }
    
    // 创建和运行简单脚本
    printf("\n3. 创建和运行脚本:\n");
    
    // 创建脚本文件
    char script_path[256];
    snprintf(script_path, sizeof(script_path), "%s/test_script.sh", chroot_path);
    int fd = open(script_path, O_CREAT | O_WRONLY | O_TRUNC, 0755);
    if (fd != -1) {
        const char* script_content = 
            "#!/bin/sh\n"
            "echo '=== 测试脚本开始 ==='\n"
            "echo '当前时间:' $(date)\n"
            "echo '当前用户:' $(id)\n"
            "echo '当前目录:' $(pwd)\n"
            "ls -la /\n"
            "echo '=== 测试脚本结束 ==='\n";
        write(fd, script_content, strlen(script_content));
        close(fd);
        printf("创建测试脚本: /test_script.sh\n");
    }
    
    // 执行脚本
    printf("执行测试脚本:\n");
    printf("--- 脚本输出开始 ---\n");
    execute_in_chroot(chroot_path, "/test_script.sh");
    printf("--- 脚本输出结束 ---\n");
    
    // 演示安全性
    printf("\n4. 安全性演示:\n");
    
    // 尝试访问宿主系统文件
    printf("尝试访问宿主系统文件:\n");
    const char* dangerous_commands[] = {
        "ls -la /etc",
        "cat /etc/shadow 2>/dev/null || echo '无法访问/etc/shadow'",
        "ls -la /root 2>/dev/null || echo '无法访问/root'",
        "find /proc -maxdepth 2 2>/dev/null | head -5"
    };
    
    for (int i = 0; i < 4; i++) {
        printf("\n执行安全测试命令: %s\n", dangerous_commands[i]);
        printf("--- 输出开始 ---\n");
        execute_in_chroot(chroot_path, dangerous_commands[i]);
        printf("--- 输出结束 ---\n");
    }
    
    // 清理测试脚本
    unlink(script_path);
    
    printf("\n=== chroot环境演示完成 ===\n");
    printf("环境路径: %s\n", chroot_path);
    printf("注意: 环境文件仍保留在系统中\n");
    
    return 0;
}

示例4:chroot高级应用 – 系统维护工具

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <dirent.h>
#include <sys/mount.h>
#include <sys/wait.h>
#include <mntent.h>

// chroot环境管理器
typedef struct {
    char path[512];
    int is_active;
    pid_t original_pid;
    time_t create_time;
} chroot_manager_t;

static chroot_manager_t manager = {0};

// 创建完整的系统恢复环境
int create_recovery_environment(const char* chroot_path) {
    printf("创建系统恢复环境: %s\n", chroot_path);
    
    // 创建完整的目录结构
    const char* essential_dirs[] = {
        "", "bin", "sbin", "etc", "dev", "proc", "sys", "tmp",
        "var", "var/log", "var/run", "usr", "usr/bin", "usr/sbin",
        "usr/lib", "lib", "lib64", "mnt", "media", "root", "home"
    };
    
    for (int i = 0; i < 21; i++) {
        char full_path[512];
        snprintf(full_path, sizeof(full_path), "%s/%s", chroot_path, essential_dirs[i]);
        if (mkdir(full_path, 0755) == -1 && errno != EEXIST) {
            printf("警告: 创建目录失败 %s: %s\n", full_path, strerror(errno));
        }
    }
    
    // 创建设备文件
    printf("创建基本设备文件...\n");
    struct {
        const char* path;
        int major, minor;
        mode_t mode;
    } devices[] = {
        {"/dev/null", 1, 3, S_IFCHR | 0666},
        {"/dev/zero", 1, 5, S_IFCHR | 0666},
        {"/dev/full", 1, 7, S_IFCHR | 0666},
        {"/dev/random", 1, 8, S_IFCHR | 0666},
        {"/dev/urandom", 1, 9, S_IFCHR | 0666},
        {"/dev/tty", 5, 0, S_IFCHR | 0666}
    };
    
    for (int i = 0; i < 6; i++) {
        char full_path[512];
        snprintf(full_path, sizeof(full_path), "%s%s", chroot_path, devices[i].path);
        if (mknod(full_path, devices[i].mode, makedev(devices[i].major, devices[i].minor)) == -1 && errno != EEXIST) {
            printf("警告: 创建设备文件失败 %s: %s\n", full_path, strerror(errno));
        }
    }
    
    // 复制系统管理工具
    printf("复制系统管理工具...\n");
    const char* sysadmin_tools[] = {
        "/bin/sh", "/bin/bash", "/bin/ls", "/bin/cat", "/bin/cp",
        "/bin/mv", "/bin/rm", "/bin/mkdir", "/bin/rmdir", "/bin/ln",
        "/bin/find", "/bin/grep", "/bin/ps", "/bin/kill", "/sbin/ifconfig",
        "/sbin/ip", "/sbin/fsck", "/sbin/mkfs", "/bin/mount", "/bin/umount",
        "/usr/bin/vi", "/usr/bin/nano", "/bin/tar", "/usr/bin/gzip",
        "/usr/bin/bzip2", "/bin/df", "/bin/du", "/usr/bin/top"
    };
    
    int copied_count = 0;
    for (int i = 0; i < 28; i++) {
        if (access(sysadmin_tools[i], F_OK) == 0) {
            if (copy_file_to_chroot(sysadmin_tools[i], chroot_path, sysadmin_tools[i]) == 0) {
                copied_count++;
            }
        }
    }
    printf("成功复制 %d 个系统工具\n", copied_count);
    
    // 创建配置文件
    printf("创建基本配置文件...\n");
    
    // /etc/passwd
    char passwd_path[512];
    snprintf(passwd_path, sizeof(passwd_path), "%s/etc/passwd", chroot_path);
    int fd = open(passwd_path, O_CREAT | O_WRONLY | O_TRUNC, 0644);
    if (fd != -1) {
        const char* passwd_content = 
            "root:x:0:0:root:/root:/bin/bash\n"
            "admin:x:1000:1000:Admin User:/home/admin:/bin/bash\n";
        write(fd, passwd_content, strlen(passwd_content));
        close(fd);
    }
    
    // /etc/group
    char group_path[512];
    snprintf(group_path, sizeof(group_path), "%s/etc/group", chroot_path);
    fd = open(group_path, O_CREAT | O_WRONLY | O_TRUNC, 0644);
    if (fd != -1) {
        const char* group_content = 
            "root:x:0:\n"
            "admin:x:1000:\n";
        write(fd, group_content, strlen(group_content));
        close(fd);
    }
    
    // /etc/hosts
    char hosts_path[512];
    snprintf(hosts_path, sizeof(hosts_path), "%s/etc/hosts", chroot_path);
    fd = open(hosts_path, O_CREAT | O_WRONLY | O_TRUNC, 0644);
    if (fd != -1) {
        const char* hosts_content = 
            "127.0.0.1\tlocalhost\n"
            "::1\tlocalhost ip6-localhost ip6-loopback\n";
        write(fd, hosts_content, strlen(hosts_content));
        close(fd);
    }
    
    printf("系统恢复环境创建完成\n");
    return 0;
}

// 在chroot中挂载特殊文件系统
int mount_special_filesystems(const char* chroot_path) {
    printf("挂载特殊文件系统...\n");
    
    char proc_path[512], sys_path[512], dev_path[512];
    snprintf(proc_path, sizeof(proc_path), "%s/proc", chroot_path);
    snprintf(sys_path, sizeof(sys_path), "%s/sys", chroot_path);
    snprintf(dev_path, sizeof(dev_path), "%s/dev", chroot_path);
    
    // 挂载/proc
    if (mount("proc", proc_path, "proc", 0, NULL) == -1) {
        printf("警告: 挂载/proc失败: %s\n", strerror(errno));
    } else {
        printf("挂载 /proc 到 %s\n", proc_path);
    }
    
    // 挂载/sys
    if (mount("sysfs", sys_path, "sysfs", 0, NULL) == -1) {
        printf("警告: 挂载/sys失败: %s\n", strerror(errno));
    } else {
        printf("挂载 /sys 到 %s\n", sys_path);
    }
    
    // 创建并挂载tmpfs到/tmp
    char tmp_path[512];
    snprintf(tmp_path, sizeof(tmp_path), "%s/tmp", chroot_path);
    if (mount("tmpfs", tmp_path, "tmpfs", 0, "size=100M") == -1) {
        printf("警告: 挂载/tmp失败: %s\n", strerror(errno));
    } else {
        printf("挂载 tmpfs 到 %s\n", tmp_path);
    }
    
    return 0;
}

// 卸载特殊文件系统
int unmount_special_filesystems(const char* chroot_path) {
    printf("卸载特殊文件系统...\n");
    
    char mounts[][512] = {
        "%s/tmp",
        "%s/sys",
        "%s/proc"
    };
    
    for (int i = 0; i < 3; i++) {
        char mount_point[512];
        snprintf(mount_point, sizeof(mount_point), mounts[i], chroot_path);
        if (umount(mount_point) == -1) {
            if (errno != EINVAL) {  // 忽略未挂载的错误
                printf("警告: 卸载 %s 失败: %s\n", mount_point, strerror(errno));
            }
        } else {
            printf("卸载 %s\n", mount_point);
        }
    }
    
    return 0;
}

// 初始化chroot管理器
int init_chroot_manager(const char* chroot_path) {
    strncpy(manager.path, chroot_path, sizeof(manager.path) - 1);
    manager.is_active = 0;
    manager.original_pid = getpid();
    manager.create_time = time(NULL);
    
    printf("初始化chroot管理器\n");
    printf("  环境路径: %s\n", manager.path);
    printf("  管理器PID: %d\n", manager.original_pid);
    
    return 0;
}

// 激活chroot环境
int activate_chroot_environment() {
    if (manager.is_active) {
        printf("chroot环境已激活\n");
        return 0;
    }
    
    printf("激活chroot环境: %s\n", manager.path);
    
    // 挂载特殊文件系统
    mount_special_filesystems(manager.path);
    
    // 执行chroot
    if (chroot(manager.path) == -1) {
        perror("chroot失败");
        return -1;
    }
    
    // 改变到根目录
    if (chdir("/") == -1) {
        perror("chdir失败");
        return -1;
    }
    
    manager.is_active = 1;
    printf("✓ chroot环境已激活\n");
    
    return 0;
}

// 交互式shell
int start_interactive_shell() {
    printf("\n=== 启动交互式shell ===\n");
    printf("提示: 输入 'exit' 退出shell\n");
    printf("当前环境: chroot @ %s\n", manager.path);
    printf("========================\n");
    
    // 启动shell
    execl("/bin/bash", "bash", "--norc", "--noprofile", (char*)NULL);
    
    // 如果execl失败
    perror("启动shell失败");
    return -1;
}

// 执行系统维护任务
int perform_system_maintenance() {
    printf("=== 系统维护任务 ===\n");
    
    // 检查文件系统
    printf("1. 检查文件系统:\n");
    system("df -h");
    
    // 检查磁盘使用情况
    printf("\n2. 磁盘使用情况:\n");
    system("du -sh /* 2>/dev/null | head -10");
    
    // 检查进程
    printf("\n3. 当前进程:\n");
    system("ps aux --forest | head -15");
    
    // 检查网络
    printf("\n4. 网络状态:\n");
    system("ip link show | head -10");
    
    // 检查系统日志
    printf("\n5. 系统日志检查:\n");
    system("dmesg | tail -10");
    
    return 0;
}

int main(int argc, char* argv[]) {
    printf("=== chroot高级应用 - 系统维护工具 ===\n");
    
    if (geteuid() != 0) {
        printf("错误: 此工具需要root权限运行\n");
        exit(EXIT_FAILURE);
    }
    
    const char* chroot_path = "/tmp/recovery_chroot";
    
    // 初始化管理器
    init_chroot_manager(chroot_path);
    
    // 检查命令行参数
    if (argc > 1) {
        if (strcmp(argv[1], "create") == 0) {
            // 创建恢复环境
            printf("创建恢复环境...\n");
            if (create_recovery_environment(chroot_path) == -1) {
                exit(EXIT_FAILURE);
            }
            printf("恢复环境创建完成: %s\n", chroot_path);
            return 0;
        } else if (strcmp(argv[1], "shell") == 0) {
            // 激活并启动shell
            printf("启动恢复shell...\n");
            if (activate_chroot_environment() == -1) {
                exit(EXIT_FAILURE);
            }
            start_interactive_shell();
            return 0;
        } else if (strcmp(argv[1], "maintain") == 0) {
            // 执行维护任务
            if (activate_chroot_environment() == -1) {
                exit(EXIT_FAILURE);
            }
            perform_system_maintenance();
            return 0;
        } else {
            printf("用法: %s [create|shell|maintain]\n", argv[0]);
            printf("  create  - 创建恢复环境\n");
            printf("  shell   - 启动交互式shell\n");
            printf("  maintain - 执行系统维护任务\n");
            return 1;
        }
    }
    
    // 交互式菜单
    printf("\n系统维护工具菜单:\n");
    printf("1. 创建恢复环境\n");
    printf("2. 启动恢复shell\n");
    printf("3. 执行系统维护\n");
    printf("4. 退出\n");
    
    int choice;
    printf("请选择操作 (1-4): ");
    if (scanf("%d", &choice) != 1) {
        printf("输入错误\n");
        return 1;
    }
    
    switch (choice) {
        case 1:
            printf("创建恢复环境...\n");
            create_recovery_environment(chroot_path);
            break;
        case 2:
            printf("启动恢复shell...\n");
            activate_chroot_environment();
            start_interactive_shell();
            break;
        case 3:
            printf("执行系统维护...\n");
            activate_chroot_environment();
            perform_system_maintenance();
            break;
        case 4:
            printf("退出工具\n");
            break;
        default:
            printf("无效选择\n");
            return 1;
    }
    
    printf("\n=== 系统维护工具结束 ===\n");
    
    return 0;
}

编译和运行

# 编译示例1
sudo gcc -o chroot_example1 chroot_example1.c
sudo ./chroot_example1

# 编译示例2
sudo gcc -o chroot_example2 chroot_example2.c
sudo ./chroot_example2

# 编译示例3
sudo gcc -o chroot_example3 chroot_example3.c
sudo ./chroot_example3

# 编译示例4
sudo gcc -o chroot_example4 chroot_example4.c
sudo ./chroot_example4 create
sudo ./chroot_example4 shell

重要注意事项

  1. 权限要求: chroot需要CAP_SYS_CHROOT能力,通常需要root权限
  2. 安全性限制: chroot不是安全边界,不能完全防止逃逸
  3. 目录验证: 必须确保新根目录的安全性和完整性
  4. 文件描述符: chroot不影响已打开的文件描述符
  5. 符号链接: 注意处理符号链接可能带来的安全问题
  6. 设备文件: 需要正确创建必要的设备文件
  7. 库依赖: 确保所需的共享库在chroot环境中可用

最佳实践

  1. 权限最小化: 在chroot后尽快降低权限
  2. 环境清理: 清理不必要的环境变量和文件描述符
  3. 目录验证: 验证新根目录的完整性和安全性
  4. 设备文件: 只创建必要的设备文件
  5. 库依赖: 确保所有依赖库都在chroot环境中
  6. 监控审计: 监控chroot环境中的活动
  7. 定期更新: 定期更新chroot环境中的软件包

通过这些示例,你可以理解chroot在系统管理和安全隔离方面的应用,虽然现代容器技术已经提供了更好的解决方案,但chroot仍然是一个重要的系统管理工具。

此条目发表在linux文章分类目录。将固定链接加入收藏夹。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注