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

然后可以将 vmlinux.h 简单地包含在 BPF 程序中,而无需定义类型。

可以使用 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 操作将 eBPF 程序附加为 LSM 钩子,或者更简单地使用 libbpf 帮助程序 bpf_program__attach_lsm

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

也可以使用 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 中找到相应的用户空间代码