LSM BPF 程序

这些 BPF 程序允许特权用户在运行时对 LSM 钩子进行检测,以使用 eBPF 实现系统范围的 MAC(强制访问控制)和审计策略。

结构

该示例展示了一个可以附加到 file_mprotect LSM 钩子的 eBPF 程序

int file_mprotect(struct vm_area_struct *vma, unsigned long reqprot, unsigned long prot);

可以在 security/security.c 中找到可以检测的其他 LSM 钩子。

使用 BPF 类型格式 (BTF) 的 eBPF 程序不需要包含内核头文件,就可以从附加的 eBPF 程序的上下文中访问信息。它们可以简单地在 eBPF 程序中声明结构,并且只指定需要访问的字段。

struct mm_struct {
        unsigned long start_brk, brk, start_stack;
} __attribute__((preserve_access_index));

struct vm_area_struct {
        unsigned long start_brk, brk, start_stack;
        unsigned long vm_start, vm_end;
        struct mm_struct *vm_mm;
} __attribute__((preserve_access_index));

注意

字段的顺序无关紧要。

如果可以在构建时访问 BTF 信息,可以通过生成 vmlinux.h 来进一步简化此过程,使用

# bpftool btf dump file <path-to-btf-vmlinux> format c > vmlinux.h

注意

如果构建环境与部署 BPF 程序的环境相匹配,则 path-to-btf-vmlinux 可以是 /sys/kernel/btf/vmlinux

然后,可以在 BPF 程序中直接包含 vmlinux.h,而无需定义类型。

可以使用 tools/lib/bpf/bpf_tracing.h 中定义的 ``BPF_PROG`` 宏来声明 eBPF 程序。在此示例中

  • "lsm/file_mprotect" 指示程序必须附加到的 LSM 钩子

  • mprotect_audit 是 eBPF 程序的名称

SEC("lsm/file_mprotect")
int BPF_PROG(mprotect_audit, struct vm_area_struct *vma,
             unsigned long reqprot, unsigned long prot, int ret)
{
        /* ret is the return value from the previous BPF program
         * or 0 if it's the first hook.
         */
        if (ret != 0)
                return ret;

        int is_heap;

        is_heap = (vma->vm_start >= vma->vm_mm->start_brk &&
                   vma->vm_end <= vma->vm_mm->brk);

        /* Return an -EPERM or write information to the perf events buffer
         * for auditing
         */
        if (is_heap)
                return -EPERM;
}

__attribute__((preserve_access_index)) 是一个 clang 功能,允许 BPF 验证器使用 BPF 类型格式 (BTF) 信息在运行时更新访问的偏移量。由于 BPF 验证器知道类型,它还会验证 eBPF 程序中对各种类型的所有访问。

加载

可以使用 bpf(2) 系统调用的 BPF_PROG_LOAD 操作来加载 eBPF 程序

struct bpf_object *obj;

obj = bpf_object__open("./my_prog.o");
bpf_object__load(obj);

使用 bpftool 生成的骨架头文件可以简化此过程

# bpftool gen skeleton my_prog.o > my_prog.skel.h

通过包含 my_prog.skel.h 并使用生成的助手 my_prog__open_and_load 可以加载程序。

附加到 LSM 钩子

LSM 允许使用 bpf(2) 系统调用的 BPF_RAW_TRACEPOINT_OPEN 操作,或者更简单地使用 libbpf 助手 bpf_program__attach_lsm,将 eBPF 程序作为 LSM 钩子附加。

可以使用 bpf_link__destroy销毁 bpf_program__attach_lsm 返回的 link 链接,从而将程序从 LSM 钩子中分离。

还可以使用 my_prog.skel.h 中生成的助手,即 my_prog__attach 进行附加和 my_prog__destroy 进行清理。

示例

可以在 tools/testing/selftests/bpf/progs/lsm.c 中找到 eBPF 程序的示例,在 tools/testing/selftests/bpf/prog_tests/test_lsm.c 中找到相应的用户空间代码