197 lines
4.4 KiB
C++
197 lines
4.4 KiB
C++
/*
|
|
* Copyright (c) Facebook, Inc.
|
|
* Licensed under the Apache License, Version 2.0 (the "License")
|
|
*
|
|
* Usage:
|
|
* $ ./KModRetExample
|
|
* opened file: /bin/true
|
|
* security_file_open() is called 1 times, expecting 1
|
|
*
|
|
* Kfunc modify_ret support is only available at kernel version 5.6 and later.
|
|
* This example only works for x64. Currently, only the kernel functions can
|
|
* be attached with BPF_MODIFY_RETURN:
|
|
* - Whitelisted for error injection by checking within_error_injection_list.
|
|
* Similar discussions happened for the bpf_override_return helper.
|
|
* - The LSM security hooks (kernel global function with prefix "security_").
|
|
*/
|
|
|
|
#include <fstream>
|
|
#include <iostream>
|
|
#include <iomanip>
|
|
#include <string>
|
|
|
|
#include <error.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
|
|
#include "bcc_version.h"
|
|
#include "BPF.h"
|
|
|
|
const std::string BPF_PROGRAM = R"(
|
|
#include <linux/fs.h>
|
|
#include <asm/errno.h>
|
|
|
|
BPF_ARRAY(target_pid, u32, 1);
|
|
static bool match_target_pid()
|
|
{
|
|
int key = 0, *val, tpid, cpid;
|
|
|
|
val = target_pid.lookup(&key);
|
|
if (!val)
|
|
return false;
|
|
|
|
tpid = *val;
|
|
cpid = bpf_get_current_pid_tgid() >> 32;
|
|
if (tpid == 0 || tpid != cpid)
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
struct fname_buf {
|
|
char buf[16];
|
|
};
|
|
BPF_ARRAY(fname_table, struct fname_buf, 1);
|
|
|
|
KMOD_RET(__x64_sys_openat, struct pt_regs *regs, int ret)
|
|
{
|
|
if (!match_target_pid())
|
|
return 0;
|
|
|
|
// openat syscall arguments:
|
|
// int dfd, const char __user * filename, int flags, umode_t mode
|
|
char *filename = (char *)PT_REGS_PARM2_SYSCALL(regs);
|
|
|
|
int key = 0;
|
|
struct fname_buf *val;
|
|
val = fname_table.lookup(&key);
|
|
if (!val)
|
|
return false;
|
|
|
|
if (bpf_copy_from_user(val, sizeof(*val), filename) < 0)
|
|
return 0;
|
|
|
|
/* match target_pid, return -EINVAL. */
|
|
return -EINVAL;
|
|
}
|
|
|
|
BPF_ARRAY(count, u32, 1);
|
|
KMOD_RET(security_file_open, struct file *file, int ret)
|
|
{
|
|
if (!match_target_pid())
|
|
return 0;
|
|
|
|
int key = 0, *val;
|
|
val = count.lookup(&key);
|
|
if (!val)
|
|
return 0;
|
|
|
|
/* no modification, kernel func continues to execute after this. */
|
|
lock_xadd(val, 1);
|
|
return 0;
|
|
}
|
|
)";
|
|
|
|
struct fname_buf {
|
|
char buf[16];
|
|
};
|
|
|
|
static int modify_return(ebpf::BPF &bpf) {
|
|
int prog_fd;
|
|
auto res = bpf.load_func("kmod_ret____x64_sys_openat",
|
|
BPF_PROG_TYPE_TRACING, prog_fd, BPF_F_SLEEPABLE);
|
|
if (!res.ok()) {
|
|
std::cerr << res.msg() << std::endl;
|
|
return 1;
|
|
}
|
|
|
|
int attach_fd = bpf_attach_kfunc(prog_fd);
|
|
if (attach_fd < 0) {
|
|
std::cerr << "bpf_attach_kfunc failed: " << attach_fd << std::endl;
|
|
return 1;
|
|
}
|
|
|
|
int ret = open("/bin/true", O_RDONLY);
|
|
if (ret >= 0 || errno != EINVAL) {
|
|
close(attach_fd);
|
|
std::cerr << "incorrect open result" << std::endl;
|
|
return 1;
|
|
}
|
|
|
|
auto fname_table = bpf.get_array_table<struct fname_buf>("fname_table");
|
|
uint32_t key = 0;
|
|
struct fname_buf val;
|
|
res = fname_table.get_value(key, val);
|
|
if (!res.ok()) {
|
|
close(attach_fd);
|
|
std::cerr << res.msg() << std::endl;
|
|
return 1;
|
|
}
|
|
std::cout << "opened file: " << val.buf << std::endl;
|
|
|
|
// detach the kfunc.
|
|
close(attach_fd);
|
|
return 0;
|
|
}
|
|
|
|
static int not_modify_return(ebpf::BPF &bpf) {
|
|
int prog_fd;
|
|
auto res = bpf.load_func("kmod_ret__security_file_open",
|
|
BPF_PROG_TYPE_TRACING, prog_fd);
|
|
if (!res.ok()) {
|
|
std::cerr << res.msg() << std::endl;
|
|
return 1;
|
|
}
|
|
|
|
int attach_fd = bpf_attach_kfunc(prog_fd);
|
|
if (attach_fd < 0) {
|
|
std::cerr << "bpf_attach_kfunc failed: " << attach_fd << std::endl;
|
|
return 1;
|
|
}
|
|
|
|
int ret = open("/bin/true", O_RDONLY);
|
|
if (ret < 0) {
|
|
close(attach_fd);
|
|
std::cerr << "incorrect open result" << std::endl;
|
|
return 1;
|
|
}
|
|
|
|
auto count_table = bpf.get_array_table<uint32_t>("count");
|
|
uint32_t key = 0, val = 0;
|
|
res = count_table.get_value(key, val);
|
|
if (!res.ok()) {
|
|
close(attach_fd);
|
|
std::cerr << res.msg() << std::endl;
|
|
return 1;
|
|
}
|
|
|
|
close(attach_fd);
|
|
std::cout << "security_file_open() is called " << val << " times, expecting 1\n";
|
|
return 0;
|
|
}
|
|
|
|
int main() {
|
|
ebpf::BPF bpf;
|
|
auto res = bpf.init(BPF_PROGRAM);
|
|
if (!res.ok()) {
|
|
std::cerr << res.msg() << std::endl;
|
|
return 1;
|
|
}
|
|
|
|
uint32_t key = 0, val = getpid();
|
|
auto pid_table = bpf.get_array_table<uint32_t>("target_pid");
|
|
res = pid_table.update_value(key, val);
|
|
if (!res.ok()) {
|
|
std::cerr << res.msg() << std::endl;
|
|
return 1;
|
|
}
|
|
|
|
if (modify_return(bpf))
|
|
return 1;
|
|
|
|
if (not_modify_return(bpf))
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|