From 169478c863adea838ef9a73a8b3323b4815b4ee2 Mon Sep 17 00:00:00 2001 From: William Findlay Date: Sun, 20 Jun 2021 11:17:52 -0400 Subject: [PATCH] Add support for raw tracepoint and LSM programs This change adds support for the following program types: * raw tracepoint * LSM Supporting LSM programs involved a necessity of supporting more load_attrs for the BPF_PROG_LOAD operation, concretely: * expected_attach_type - for LSM programs, it has always to be set to BPF_LSM_MAC * attach_btf_obj_fd - it's often used to reference the file descriptor of program's BTF info, altough in case of LSM programs, it only has to contain the value 0, which means the vmlinux object file (usually /sys/kernel/btf/vmlinux) * attach_btf_id - ID of the BTF object, which in case of LSM programs is the ID of the function (the LSM hook) The example of LSM program using that functionality can be found here: https://github.com/vadorovsky/aya-example-lsm Fixes: #9 Signed-off-by: William Findlay Signed-off-by: Michal Rostecki --- aya/src/bpf.rs | 13 ++- aya/src/obj/btf/btf.rs | 32 +++++- aya/src/obj/mod.rs | 55 +++++++++++ aya/src/programs/lsm.rs | 109 +++++++++++++++++++++ aya/src/programs/mod.rs | 41 ++++++-- aya/src/programs/raw_trace_point.rs | 67 +++++++++++++ aya/src/sys/bpf.rs | 53 +++++++--- bpf/aya-bpf-macros/src/expand.rs | 60 ++++++++++++ bpf/aya-bpf-macros/src/lib.rs | 84 +++++++++++++++- bpf/aya-bpf/src/programs/lsm.rs | 19 ++++ bpf/aya-bpf/src/programs/mod.rs | 4 + bpf/aya-bpf/src/programs/raw_tracepoint.rs | 19 ++++ 12 files changed, 528 insertions(+), 28 deletions(-) create mode 100644 aya/src/programs/lsm.rs create mode 100644 aya/src/programs/raw_trace_point.rs create mode 100644 bpf/aya-bpf/src/programs/lsm.rs create mode 100644 bpf/aya-bpf/src/programs/raw_tracepoint.rs diff --git a/aya/src/bpf.rs b/aya/src/bpf.rs index b7070d29..9aec7fe7 100644 --- a/aya/src/bpf.rs +++ b/aya/src/bpf.rs @@ -20,9 +20,9 @@ use crate::{ Object, ParseError, ProgramSection, }, programs::{ - CgroupSkb, CgroupSkbAttachType, KProbe, LircMode2, PerfEvent, ProbeKind, Program, - ProgramData, ProgramError, SchedClassifier, SkMsg, SkSkb, SkSkbKind, SockOps, SocketFilter, - TracePoint, UProbe, Xdp, + CgroupSkb, CgroupSkbAttachType, KProbe, LircMode2, Lsm, PerfEvent, ProbeKind, Program, + ProgramData, ProgramError, RawTracePoint, SchedClassifier, SkMsg, SkSkb, SkSkbKind, + SockOps, SocketFilter, TracePoint, UProbe, Xdp, }, sys::bpf_map_update_elem_ptr, util::{possible_cpus, POSSIBLE_CPUS}, @@ -253,6 +253,9 @@ impl<'a> BpfLoader<'a> { name: name.clone(), fd: None, links: Vec::new(), + expected_attach_type: None, + attach_btf_obj_fd: None, + attach_btf_id: None, }; let program = match section { ProgramSection::KProbe { .. } => Program::KProbe(KProbe { @@ -299,6 +302,10 @@ impl<'a> BpfLoader<'a> { }), ProgramSection::LircMode2 { .. } => Program::LircMode2(LircMode2 { data }), ProgramSection::PerfEvent { .. } => Program::PerfEvent(PerfEvent { data }), + ProgramSection::RawTracePoint { .. } => { + Program::RawTracePoint(RawTracePoint { data }) + } + ProgramSection::Lsm { .. } => Program::Lsm(Lsm { data }), }; (name, program) diff --git a/aya/src/obj/btf/btf.rs b/aya/src/obj/btf/btf.rs index 32c15520..a6ba2990 100644 --- a/aya/src/obj/btf/btf.rs +++ b/aya/src/obj/btf/btf.rs @@ -12,7 +12,7 @@ use thiserror::Error; use crate::{ generated::{btf_ext_header, btf_header}, - obj::btf::{relocation::Relocation, BtfType}, + obj::btf::{relocation::Relocation, BtfKind, BtfType}, }; pub(crate) const MAX_RESOLVE_DEPTH: u8 = 32; @@ -66,6 +66,9 @@ pub enum BtfError { #[error("Unexpected BTF type id `{type_id}`")] UnexpectedBtfType { type_id: u32 }, + #[error("Unknown BTF type `{type_name}`")] + UnknownBtfTypeName { type_name: String }, + #[error("maximum depth reached resolving BTF type")] MaximumTypeDepthReached { type_id: u32 }, } @@ -221,6 +224,33 @@ impl Btf { .and_then(|off| self.string_at(off).ok().map(String::from)) } + pub(crate) fn id_by_type_name_kind(&self, name: &str, kind: BtfKind) -> Result { + for (type_id, ty) in self.types().enumerate() { + match ty.kind()? { + Some(k) => { + if k != kind { + continue; + } + } + None => continue, + } + + match self.type_name(ty)? { + Some(ty_name) => { + if ty_name == name { + return Ok(type_id as u32); + } + continue; + } + None => continue, + } + } + + Err(BtfError::UnknownBtfTypeName { + type_name: name.to_string(), + }) + } + pub(crate) fn type_size(&self, root_type_id: u32) -> Result { let mut type_id = root_type_id; let mut n_elems = 1; diff --git a/aya/src/obj/mod.rs b/aya/src/obj/mod.rs index edf6341e..8b6c89c4 100644 --- a/aya/src/obj/mod.rs +++ b/aya/src/obj/mod.rs @@ -85,6 +85,8 @@ pub enum ProgramSection { CgroupSkbEgress { name: String }, LircMode2 { name: String }, PerfEvent { name: String }, + RawTracePoint { name: String }, + Lsm { name: String }, } impl ProgramSection { @@ -106,6 +108,8 @@ impl ProgramSection { ProgramSection::CgroupSkbEgress { name } => name, ProgramSection::LircMode2 { name } => name, ProgramSection::PerfEvent { name } => name, + ProgramSection::RawTracePoint { name } => name, + ProgramSection::Lsm { name } => name, } } } @@ -147,6 +151,8 @@ impl FromStr for ProgramSection { "cgroup_skb/egress" => CgroupSkbEgress { name }, "lirc_mode2" => LircMode2 { name }, "perf_event" => PerfEvent { name }, + "raw_tp" | "raw_tracepoint" => RawTracePoint { name }, + "lsm" => Lsm { name }, _ => { return Err(ParseError::InvalidProgramSection { section: section.to_owned(), @@ -570,6 +576,9 @@ fn is_program_section(name: &str) -> bool { "uprobe", "uretprobe", "xdp", + "raw_tp", + "raw_tracepoint", + "lsm", ] { if name.starts_with(prefix) { return true; @@ -991,4 +1000,50 @@ mod tests { }) ); } + + #[test] + fn test_parse_section_raw_tp() { + let mut obj = fake_obj(); + + assert_matches!( + obj.parse_section(fake_section("raw_tp/foo", bytes_of(&fake_ins()))), + Ok(()) + ); + assert_matches!( + obj.programs.get("foo"), + Some(Program { + section: ProgramSection::RawTracePoint { .. }, + .. + }) + ); + + assert_matches!( + obj.parse_section(fake_section("raw_tracepoint/bar", bytes_of(&fake_ins()))), + Ok(()) + ); + assert_matches!( + obj.programs.get("bar"), + Some(Program { + section: ProgramSection::RawTracePoint { .. }, + .. + }) + ); + } + + #[test] + fn test_parse_section_lsm() { + let mut obj = fake_obj(); + + assert_matches!( + obj.parse_section(fake_section("lsm/foo", bytes_of(&fake_ins()))), + Ok(()) + ); + assert_matches!( + obj.programs.get("foo"), + Some(Program { + section: ProgramSection::Lsm { .. }, + .. + }) + ); + } } diff --git a/aya/src/programs/lsm.rs b/aya/src/programs/lsm.rs new file mode 100644 index 00000000..30f21aea --- /dev/null +++ b/aya/src/programs/lsm.rs @@ -0,0 +1,109 @@ +//! LSM probes. +use std::os::unix::io::RawFd; + +use thiserror::Error; + +use crate::{ + generated::{bpf_attach_type::BPF_LSM_MAC, bpf_prog_type::BPF_PROG_TYPE_LSM}, + obj::btf::{Btf, BtfError, BtfKind}, + programs::{load_program, FdLink, LinkRef, ProgramData, ProgramError}, + sys::bpf_raw_tracepoint_open, +}; + +/// A program that attaches to Linux LSM hooks. Used to implement security policy and +/// audit logging. +/// +/// LSM probes can be attached to the kernel's [security hooks][1] to implement mandatory +/// access control policy and security auditing. +/// +/// LSM probes require a kernel compiled with `CONFIG_BPF_LSM=y` and `CONFIG_DEBUG_INFO_BTF=y`. +/// In order for the probes to fire, you also need the BPF LSM to be enabled through your +/// kernel's boot paramters (like `lsm=lockdown,yama,bpf`). +/// +/// # Minimum kernel version +/// +/// The minimum kernel version required to use this feature is 5.7. +/// +/// # Examples +/// +/// ```no_run +/// # #[derive(thiserror::Error, Debug)] +/// # enum LsmError { +/// # #[error(transparent)] +/// # LsmLoad(#[from] aya::programs::LsmLoadError), +/// # #[error(transparent)] +/// # Program(#[from] aya::programs::ProgramError), +/// # #[error(transparent)] +/// # Bpf(#[from] aya::BpfError), +/// # } +/// # let mut bpf = Bpf::load_file("ebpf_programs.o")?; +/// use aya::{Bpf, programs::Lsm}; +/// use std::convert::TryInto; +/// +/// let program: &mut Lsm = bpf.program_mut("lsm_prog")?.try_into()?; +/// program.load("security_bprm_exec")?; +/// program.attach()?; +/// # Ok::<(), LsmError>(()) +/// ``` +/// +/// [1]: https://elixir.bootlin.com/linux/latest/source/include/linux/lsm_hook_defs.h +#[derive(Debug)] +#[doc(alias = "BPF_PROG_TYPE_LSM")] +pub struct Lsm { + pub(crate) data: ProgramData, +} + +/// Error type returned when loading LSM programs. +#[derive(Debug, Error)] +pub enum LsmLoadError { + #[error(transparent)] + Btf(#[from] BtfError), + + #[error(transparent)] + Program(#[from] ProgramError), +} + +impl Lsm { + /// Loads the program inside the kernel. + /// + /// See also [`Program::load`](crate::programs::Program::load). + /// + /// # Arguments + /// + /// * `lsm_hook_name` - full name of the LSM hook that the program should + /// be attached to + pub fn load(&mut self, lsm_hook_name: &str) -> Result<(), LsmLoadError> { + let btf = &Btf::from_sys_fs()?; + self.data.expected_attach_type = Some(BPF_LSM_MAC); + let type_name = format!("bpf_lsm_{}", lsm_hook_name); + self.data.attach_btf_id = + Some(btf.id_by_type_name_kind(type_name.as_str(), BtfKind::Func)?); + load_program(BPF_PROG_TYPE_LSM, &mut self.data).map_err(LsmLoadError::from) + } + + /// Returns the name of the program. + pub fn name(&self) -> String { + self.data.name.to_string() + } + + /// Attaches the program. + pub fn attach(&mut self) -> Result { + attach_btf_id(&mut self.data) + } +} + +/// Common logic for all BPF program types that attach to a BTF id. +pub(crate) fn attach_btf_id(program_data: &mut ProgramData) -> Result { + let prog_fd = program_data.fd_or_err()?; + + // Attaching LSM programs doesn't require providing attach name. LSM + // programs are attached to LSM hook which is specified in the program. + let pfd = bpf_raw_tracepoint_open(None, prog_fd).map_err(|(_code, io_error)| { + ProgramError::SyscallError { + call: "bpf_raw_tracepoint_open".to_owned(), + io_error, + } + })? as RawFd; + + Ok(program_data.link(FdLink { fd: Some(pfd) })) +} diff --git a/aya/src/programs/mod.rs b/aya/src/programs/mod.rs index 8d39c4e8..fecf0df2 100644 --- a/aya/src/programs/mod.rs +++ b/aya/src/programs/mod.rs @@ -39,9 +39,11 @@ mod cgroup_skb; mod kprobe; mod lirc_mode2; +mod lsm; mod perf_attach; pub mod perf_event; mod probe; +mod raw_trace_point; mod sk_msg; mod sk_skb; mod sock_ops; @@ -67,9 +69,11 @@ use thiserror::Error; pub use cgroup_skb::{CgroupSkb, CgroupSkbAttachType}; pub use kprobe::{KProbe, KProbeError}; pub use lirc_mode2::LircMode2; +pub use lsm::{Lsm, LsmLoadError}; use perf_attach::*; pub use perf_event::{PerfEvent, PerfEventScope, PerfTypeId, SamplePolicy}; pub use probe::ProbeKind; +pub use raw_trace_point::RawTracePoint; pub use sk_msg::SkMsg; pub use sk_skb::{SkSkb, SkSkbKind}; pub use sock_ops::SockOps; @@ -83,7 +87,7 @@ use crate::{ generated::{bpf_attach_type, bpf_prog_info, bpf_prog_type}, maps::MapError, obj::{self, Function}, - sys::{bpf_load_program, bpf_pin_object, bpf_prog_detach, bpf_prog_query}, + sys::{bpf_load_program, bpf_pin_object, bpf_prog_detach, bpf_prog_query, BpfLoadProgramAttrs}, }; /// Error type returned when working with programs. @@ -188,6 +192,8 @@ pub enum Program { CgroupSkb(CgroupSkb), LircMode2(LircMode2), PerfEvent(PerfEvent), + RawTracePoint(RawTracePoint), + Lsm(Lsm), } impl Program { @@ -221,6 +227,8 @@ impl Program { Program::CgroupSkb(_) => BPF_PROG_TYPE_CGROUP_SKB, Program::LircMode2(_) => BPF_PROG_TYPE_LIRC_MODE2, Program::PerfEvent(_) => BPF_PROG_TYPE_PERF_EVENT, + Program::RawTracePoint(_) => BPF_PROG_TYPE_RAW_TRACEPOINT, + Program::Lsm(_) => BPF_PROG_TYPE_LSM, } } @@ -248,6 +256,8 @@ impl Program { Program::CgroupSkb(p) => &p.data, Program::LircMode2(p) => &p.data, Program::PerfEvent(p) => &p.data, + Program::RawTracePoint(p) => &p.data, + Program::Lsm(p) => &p.data, } } @@ -265,6 +275,8 @@ impl Program { Program::CgroupSkb(p) => &mut p.data, Program::LircMode2(p) => &mut p.data, Program::PerfEvent(p) => &mut p.data, + Program::RawTracePoint(p) => &mut p.data, + Program::Lsm(p) => &mut p.data, } } } @@ -275,6 +287,9 @@ pub(crate) struct ProgramData { pub(crate) obj: obj::Program, pub(crate) fd: Option, pub(crate) links: Vec>>, + pub(crate) expected_attach_type: Option, + pub(crate) attach_btf_obj_fd: Option, + pub(crate) attach_btf_id: Option, } impl ProgramData { @@ -376,13 +391,17 @@ fn load_program(prog_type: bpf_prog_type, data: &mut ProgramData) -> Result<(), let mut retries = 0; let mut ret; loop { - ret = bpf_load_program( - prog_type, - instructions, + let attr = BpfLoadProgramAttrs { + ty: prog_type, + insns: instructions, license, - (*kernel_version).into(), - &mut log_buf, - ); + kernel_version: (*kernel_version).into(), + expected_attach_type: data.expected_attach_type, + attach_btf_obj_fd: data.attach_btf_obj_fd, + attach_btf_id: data.attach_btf_id, + log: &mut log_buf, + }; + ret = bpf_load_program(attr); match &ret { Ok(prog_fd) => { *fd = Some(*prog_fd as RawFd); @@ -568,7 +587,9 @@ impl_program_fd!( SchedClassifier, CgroupSkb, LircMode2, - PerfEvent + PerfEvent, + Lsm, + RawTracePoint, ); macro_rules! impl_try_from_program { @@ -611,7 +632,9 @@ impl_try_from_program!( SchedClassifier, CgroupSkb, LircMode2, - PerfEvent + PerfEvent, + Lsm, + RawTracePoint, ); /// Provides information about a loaded program, like name, id and statistics diff --git a/aya/src/programs/raw_trace_point.rs b/aya/src/programs/raw_trace_point.rs new file mode 100644 index 00000000..2ca95826 --- /dev/null +++ b/aya/src/programs/raw_trace_point.rs @@ -0,0 +1,67 @@ +//! Raw tracepoints. +use std::{ffi::CString, os::unix::io::RawFd}; + +use crate::{ + generated::bpf_prog_type::BPF_PROG_TYPE_RAW_TRACEPOINT, + programs::{load_program, FdLink, LinkRef, ProgramData, ProgramError}, + sys::bpf_raw_tracepoint_open, +}; + +/// A program that can be attached at a pre-defined kernel trace point, but also +/// has an access to kernel internal arguments of trace points, which +/// differentiates them from traditional tracepoint eBPF programs. +/// +/// The kernel provides a set of pre-defined trace points that eBPF programs can +/// be attached to. See`/sys/kernel/debug/tracing/events` for a list of which +/// events can be traced. +/// +/// # Minimum kernel version +/// +/// The minimum kernel version required to use this feature is 4.17. +/// +/// # Examples +/// +/// ```no_run +/// # let mut bpf = Bpf::load_file("ebpf_programs.o")?; +/// use aya::{Bpf, programs::RawTracePoint}; +/// use std::convert::TryInto; +/// +/// let program: &mut RawTracePoint = bpf.program_mut("sys_enter")?.try_into()?; +/// program.load()?; +/// program.attach("sys_enter")?; +/// # Ok::<(), aya::BpfError>(()) +/// ``` +#[derive(Debug)] +#[doc(alias = "BPF_PROG_TYPE_RAW_TRACEPOINT")] +pub struct RawTracePoint { + pub(crate) data: ProgramData, +} + +impl RawTracePoint { + /// Loads the program inside the kernel. + /// + /// See also [`Program::load`](crate::programs::Program::load). + pub fn load(&mut self) -> Result<(), ProgramError> { + load_program(BPF_PROG_TYPE_RAW_TRACEPOINT, &mut self.data) + } + + /// Returns the name of the program. + pub fn name(&self) -> String { + self.data.name.to_string() + } + + /// Attaches the program to the given tracepoint. + pub fn attach(&mut self, tp_name: &str) -> Result { + let prog_fd = self.data.fd_or_err()?; + let name = CString::new(tp_name).unwrap(); + + let pfd = bpf_raw_tracepoint_open(Some(&name), prog_fd).map_err(|(_code, io_error)| { + ProgramError::SyscallError { + call: "bpf_raw_tracepoint_open".to_owned(), + io_error, + } + })? as RawFd; + + Ok(self.data.link(FdLink { fd: Some(pfd) })) + } +} diff --git a/aya/src/sys/bpf.rs b/aya/src/sys/bpf.rs index a9902015..80728218 100644 --- a/aya/src/sys/bpf.rs +++ b/aya/src/sys/bpf.rs @@ -53,28 +53,41 @@ pub(crate) fn bpf_get_object(path: &CStr) -> SysResult { sys_bpf(bpf_cmd::BPF_OBJ_GET, &attr) } -pub(crate) fn bpf_load_program( - ty: bpf_prog_type, - insns: &[bpf_insn], - license: &CStr, - kernel_version: u32, - log: &mut VerifierLog, -) -> SysResult { +pub(crate) struct BpfLoadProgramAttrs<'a> { + pub(crate) ty: bpf_prog_type, + pub(crate) insns: &'a [bpf_insn], + pub(crate) license: &'a CStr, + pub(crate) kernel_version: u32, + pub(crate) expected_attach_type: Option, + pub(crate) attach_btf_obj_fd: Option, + pub(crate) attach_btf_id: Option, + pub(crate) log: &'a mut VerifierLog, +} + +pub(crate) fn bpf_load_program(aya_attr: BpfLoadProgramAttrs) -> SysResult { let mut attr = unsafe { mem::zeroed::() }; let u = unsafe { &mut attr.__bindgen_anon_3 }; - u.prog_type = ty as u32; - u.expected_attach_type = 0; - u.insns = insns.as_ptr() as u64; - u.insn_cnt = insns.len() as u32; - u.license = license.as_ptr() as u64; - u.kern_version = kernel_version; - let log_buf = log.buf(); + u.prog_type = aya_attr.ty as u32; + if let Some(v) = aya_attr.expected_attach_type { + u.expected_attach_type = v as u32; + } + u.insns = aya_attr.insns.as_ptr() as u64; + u.insn_cnt = aya_attr.insns.len() as u32; + u.license = aya_attr.license.as_ptr() as u64; + u.kern_version = aya_attr.kernel_version; + let log_buf = aya_attr.log.buf(); if log_buf.capacity() > 0 { u.log_level = 7; u.log_buf = log_buf.as_mut_ptr() as u64; u.log_size = log_buf.capacity() as u32; } + if let Some(v) = aya_attr.attach_btf_obj_fd { + u.__bindgen_anon_1.attach_btf_obj_fd = v; + } + if let Some(v) = aya_attr.attach_btf_id { + u.attach_btf_id = v; + } sys_bpf(bpf_cmd::BPF_PROG_LOAD, &attr) } @@ -332,6 +345,18 @@ pub(crate) fn bpf_obj_get_info_by_fd(prog_fd: RawFd) -> Result, prog_fd: RawFd) -> SysResult { + let mut attr = unsafe { mem::zeroed::() }; + + attr.raw_tracepoint.name = match name { + Some(n) => n.as_ptr() as u64, + None => 0, + }; + attr.raw_tracepoint.prog_fd = prog_fd as u32; + + sys_bpf(bpf_cmd::BPF_RAW_TRACEPOINT_OPEN, &attr) +} + fn sys_bpf(cmd: bpf_cmd, attr: &bpf_attr) -> SysResult { syscall(Syscall::Bpf { cmd, attr }) } diff --git a/bpf/aya-bpf-macros/src/expand.rs b/bpf/aya-bpf-macros/src/expand.rs index 279e9458..45c36b74 100644 --- a/bpf/aya-bpf-macros/src/expand.rs +++ b/bpf/aya-bpf-macros/src/expand.rs @@ -352,3 +352,63 @@ impl PerfEvent { }) } } + +pub struct RawTracePoint { + item: ItemFn, + name: String, +} + +impl RawTracePoint { + pub fn from_syn(mut args: Args, item: ItemFn) -> Result { + let name = name_arg(&mut args)?.unwrap_or_else(|| item.sig.ident.to_string()); + + Ok(RawTracePoint { item, name }) + } + + pub fn expand(&self) -> Result { + let section_name = format!("raw_tp/{}", self.name); + let fn_name = &self.item.sig.ident; + let item = &self.item; + Ok(quote! { + #[no_mangle] + #[link_section = #section_name] + fn #fn_name(ctx: *mut ::core::ffi::c_void) -> u32 { + let _ = #fn_name(::aya_bpf::programs::RawTracePointContext::new(ctx)); + return 0; + + #item + } + }) + } +} + +pub struct Lsm { + item: ItemFn, + name: String, +} + +impl Lsm { + pub fn from_syn(mut args: Args, item: ItemFn) -> Result { + let name = name_arg(&mut args)?.unwrap_or_else(|| item.sig.ident.to_string()); + + Ok(Lsm { item, name }) + } + + pub fn expand(&self) -> Result { + let section_name = format!("lsm/{}", self.name); + let fn_name = &self.item.sig.ident; + let item = &self.item; + // LSM probes need to return an integer corresponding to the correct + // policy decision. Therefore we do not simply default to a return value + // of 0 as in other program types. + Ok(quote! { + #[no_mangle] + #[link_section = #section_name] + fn #fn_name(ctx: *mut ::core::ffi::c_void) -> i32 { + return #fn_name(::aya_bpf::programs::LsmContext::new(ctx)); + + #item + } + }) + } +} diff --git a/bpf/aya-bpf-macros/src/lib.rs b/bpf/aya-bpf-macros/src/lib.rs index 1ccfe362..d11ed583 100644 --- a/bpf/aya-bpf-macros/src/lib.rs +++ b/bpf/aya-bpf-macros/src/lib.rs @@ -1,7 +1,8 @@ mod expand; use expand::{ - Args, Map, PerfEvent, Probe, ProbeKind, SchedClassifier, SkMsg, SockOps, TracePoint, Xdp, + Args, Lsm, Map, PerfEvent, Probe, ProbeKind, RawTracePoint, SchedClassifier, SkMsg, SockOps, + TracePoint, Xdp, }; use proc_macro::TokenStream; use syn::{parse_macro_input, ItemFn, ItemStatic}; @@ -125,3 +126,84 @@ pub fn perf_event(attrs: TokenStream, item: TokenStream) -> TokenStream { .unwrap_or_else(|err| err.to_compile_error()) .into() } + +/// Marks a function as a raw tracepoint eBPF program that can be attached at a +/// pre-defined kernel trace point. +/// +/// The kernel provides a set of pre-defined trace points that eBPF programs can +/// be attached to. See `/sys/kernel/debug/tracing/events` for a list of which +/// events can be traced. +/// +/// # Minimum kernel version +/// +/// The minimum kernel version required to use this feature is 4.7. +/// +/// # Examples +/// +/// ```no_run +/// use aya_bpf::{macros::raw_tracepoint, programs::RawTracePointContext}; +/// +/// #[raw_tracepoint(name = "sys_enter")] +/// pub fn sys_enter(ctx: RawTracePointContext) -> i32 { +/// match unsafe { try_sys_enter(ctx) } { +/// Ok(ret) => ret, +/// Err(ret) => ret, +/// } +/// } +/// +/// unsafe fn try_sys_enter(_ctx: RawTracePointContext) -> Result { +/// Ok(0) +/// } +/// ``` +#[proc_macro_attribute] +pub fn raw_tracepoint(attrs: TokenStream, item: TokenStream) -> TokenStream { + let args = parse_macro_input!(attrs as Args); + let item = parse_macro_input!(item as ItemFn); + + RawTracePoint::from_syn(args, item) + .and_then(|u| u.expand()) + .unwrap_or_else(|err| err.to_compile_error()) + .into() +} + +/// Marks a function as an LSM program that can be attached to Linux LSM hooks. +/// Used to implement security policy and audit logging. +/// +/// LSM probes can be attached to the kernel's [security hooks][1] to implement mandatory +/// access control policy and security auditing. +/// +/// LSM probes require a kernel compiled with `CONFIG_BPF_LSM=y` and `CONFIG_DEBUG_INFO_BTF=y`. +/// In order for the probes to fire, you also need the BPF LSM to be enabled through your +/// kernel's boot paramters (like `lsm=lockdown,yama,bpf`). +/// +/// # Minimum kernel version +/// +/// The minimum kernel version required to use this feature is 5.7. +/// +/// # Examples +/// +/// ```no_run +/// use aya_bpf::{macros::lsm, programs::LsmContext}; +/// +/// #[lsm(name = "file_open")] +/// pub fn file_open(ctx: LsmContext) -> i32 { +/// match unsafe { try_file_open(ctx) } { +/// Ok(ret) => ret, +/// Err(ret) => ret, +/// } +/// } +/// +/// unsafe fn try_file_open(_ctx: LsmContext) -> Result { +/// Ok(0) +/// } +/// ``` +#[proc_macro_attribute] +pub fn lsm(attrs: TokenStream, item: TokenStream) -> TokenStream { + let args = parse_macro_input!(attrs as Args); + let item = parse_macro_input!(item as ItemFn); + + Lsm::from_syn(args, item) + .and_then(|u| u.expand()) + .unwrap_or_else(|err| err.to_compile_error()) + .into() +} diff --git a/bpf/aya-bpf/src/programs/lsm.rs b/bpf/aya-bpf/src/programs/lsm.rs new file mode 100644 index 00000000..4a36cd51 --- /dev/null +++ b/bpf/aya-bpf/src/programs/lsm.rs @@ -0,0 +1,19 @@ +use core::ffi::c_void; + +use crate::BpfContext; + +pub struct LsmContext { + ctx: *mut c_void, +} + +impl LsmContext { + pub fn new(ctx: *mut c_void) -> LsmContext { + LsmContext { ctx } + } +} + +impl BpfContext for LsmContext { + fn as_ptr(&self) -> *mut c_void { + self.ctx + } +} diff --git a/bpf/aya-bpf/src/programs/mod.rs b/bpf/aya-bpf/src/programs/mod.rs index 2e55a4b1..16d53d05 100644 --- a/bpf/aya-bpf/src/programs/mod.rs +++ b/bpf/aya-bpf/src/programs/mod.rs @@ -1,13 +1,17 @@ +pub mod lsm; pub mod perf_event; pub mod probe; +pub mod raw_tracepoint; pub mod sk_msg; pub mod sk_skb; pub mod sock_ops; pub mod tracepoint; pub mod xdp; +pub use lsm::LsmContext; pub use perf_event::PerfEventContext; pub use probe::ProbeContext; +pub use raw_tracepoint::RawTracePointContext; pub use sk_msg::SkMsgContext; pub use sk_skb::SkSkbContext; pub use sock_ops::SockOpsContext; diff --git a/bpf/aya-bpf/src/programs/raw_tracepoint.rs b/bpf/aya-bpf/src/programs/raw_tracepoint.rs new file mode 100644 index 00000000..cc140244 --- /dev/null +++ b/bpf/aya-bpf/src/programs/raw_tracepoint.rs @@ -0,0 +1,19 @@ +use core::ffi::c_void; + +use crate::BpfContext; + +pub struct RawTracePointContext { + ctx: *mut c_void, +} + +impl RawTracePointContext { + pub fn new(ctx: *mut c_void) -> RawTracePointContext { + RawTracePointContext { ctx } + } +} + +impl BpfContext for RawTracePointContext { + fn as_ptr(&self) -> *mut c_void { + self.ctx + } +}