Skip to content

Commit

Permalink
lsm :: cgroup attachment type support
Browse files Browse the repository at this point in the history
  • Loading branch information
Altug Bozkurt authored and altugbozkurt07 committed Jan 14, 2025
1 parent 356cf45 commit 91ff050
Show file tree
Hide file tree
Showing 15 changed files with 450 additions and 22 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ indoc = { version = "2.0", default-features = false }
libc = { version = "0.2.105", default-features = false }
log = { version = "0.4", default-features = false }
netns-rs = { version = "0.1", default-features = false }
nix = { version = "0.29.0", default-features = false }
nix = { version = "0.29.0", default-features = true }
num_enum = { version = "0.7", default-features = false }
object = { version = "0.36", default-features = false }
octorust = { version = "0.9.0", default-features = false }
Expand Down
45 changes: 45 additions & 0 deletions aya-ebpf-macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ mod fentry;
mod fexit;
mod kprobe;
mod lsm;
mod lsm_cgroup;
mod map;
mod perf_event;
mod raw_tracepoint;
Expand All @@ -34,6 +35,7 @@ use fentry::FEntry;
use fexit::FExit;
use kprobe::{KProbe, KProbeKind};
use lsm::Lsm;
use lsm_cgroup::LsmCgroup;
use map::Map;
use perf_event::PerfEvent;
use proc_macro::TokenStream;
Expand Down Expand Up @@ -326,6 +328,49 @@ pub fn lsm(attrs: TokenStream, item: TokenStream) -> TokenStream {
.into()
}

/// Marks a function as an LSM program that can be attached to cgroups.
/// This program will only trigger for workloads in the attached cgroups.
/// Used to implement security policy and audit logging.
///
/// The hook name is the first and only argument to the macro.
///
/// LSM probes can be attached to the kernel's security hooks 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 6.0.
///
/// # Examples
///
/// ```no_run
/// use aya_ebpf::{macros::lsm_cgroup, programs::LsmContext};
///
/// #[lsm_cgroup(hook = "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<i32, i32> {
/// Err(0)
/// }
/// ```
#[proc_macro_attribute]
pub fn lsm_cgroup(attrs: TokenStream, item: TokenStream) -> TokenStream {
match LsmCgroup::parse(attrs.into(), item.into()) {
Ok(prog) => prog.expand(),
Err(err) => err.into_compile_error(),
}
.into()
}

/// Marks a function as a [BTF-enabled raw tracepoint][1] eBPF program that can be attached at
/// a pre-defined kernel trace point.
///
Expand Down
7 changes: 4 additions & 3 deletions aya-ebpf-macros/src/lsm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ impl Lsm {
let hook = pop_string_arg(&mut args, "hook");
let sleepable = pop_bool_arg(&mut args, "sleepable");
err_on_unknown_args(&args)?;

Ok(Self {
item,
hook,
Expand All @@ -39,15 +40,15 @@ impl Lsm {
block: _,
} = item;
let section_prefix = if *sleepable { "lsm.s" } else { "lsm" };
let section_name: Cow<'_, _> = if let Some(hook) = hook {
format!("{}/{}", section_prefix, hook).into()
let section_name: Cow<'_, _> = if let Some(name) = hook {
format!("{}/{}", section_prefix, name).into()
} else {
section_prefix.into()
};
let fn_name = &sig.ident;
// 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.
let fn_name = &sig.ident;
quote! {
#[no_mangle]
#[link_section = #section_name]
Expand Down
87 changes: 87 additions & 0 deletions aya-ebpf-macros/src/lsm_cgroup.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
use std::borrow::Cow;

use proc_macro2::TokenStream;
use quote::quote;
use syn::{ItemFn, Result};

use crate::args::{err_on_unknown_args, pop_string_arg};

pub(crate) struct LsmCgroup {
item: ItemFn,
hook: Option<String>,
}

impl LsmCgroup {
pub(crate) fn parse(attrs: TokenStream, item: TokenStream) -> Result<Self> {
let item = syn::parse2(item)?;
let mut args = syn::parse2(attrs)?;
let hook = pop_string_arg(&mut args, "hook");
err_on_unknown_args(&args)?;

Ok(Self { item, hook })
}

pub(crate) fn expand(&self) -> TokenStream {
let Self { item, hook } = self;
let ItemFn {
attrs: _,
vis,
sig,
block: _,
} = item;
let section_prefix = "lsm_cgroup";
let section_name: Cow<'_, _> = if let Some(name) = hook {
format!("{}/{}", section_prefix, name).into()
} else {
section_prefix.into()
};
let fn_name = &sig.ident;
// 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.
quote! {
#[no_mangle]
#[link_section = #section_name]
#vis fn #fn_name(ctx: *mut ::core::ffi::c_void) -> i32 {
return #fn_name(::aya_ebpf::programs::LsmContext::new(ctx));

#item
}
}
}
}

#[cfg(test)]
mod tests {
use syn::parse_quote;

use super::*;

#[test]
fn test_lsm_cgroup() {
let prog = LsmCgroup::parse(
parse_quote! {
hook = "bprm_committed_creds",
},
parse_quote! {
fn bprm_committed_creds(ctx: &mut ::aya_ebpf::programs::LsmContext) -> i32 {
0
}
},
)
.unwrap();
let expanded = prog.expand();
let expected = quote! {
#[no_mangle]
#[link_section = "lsm_cgroup/bprm_committed_creds"]
fn bprm_committed_creds(ctx: *mut ::core::ffi::c_void) -> i32 {
return bprm_committed_creds(::aya_ebpf::programs::LsmContext::new(ctx));

fn bprm_committed_creds(ctx: &mut ::aya_ebpf::programs::LsmContext) -> i32 {
0
}
}
};
assert_eq!(expected.to_string(), expanded.to_string());
}
}
47 changes: 43 additions & 4 deletions aya-obj/src/obj.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ use crate::{
},
maps::{bpf_map_def, BtfMap, BtfMapDef, LegacyMap, Map, PinningType, MINIMUM_MAP_SIZE},
programs::{
CgroupSockAddrAttachType, CgroupSockAttachType, CgroupSockoptAttachType, XdpAttachType,
CgroupSockAddrAttachType, CgroupSockAttachType, CgroupSockoptAttachType, LsmAttachType,
XdpAttachType,
},
relocation::*,
util::HashMap,
Expand Down Expand Up @@ -274,6 +275,10 @@ pub enum ProgramSection {
RawTracePoint,
Lsm {
sleepable: bool,
attach_type: LsmAttachType,
},
LsmCgroup {
attach_type: LsmAttachType,
},
BtfTracePoint,
FEntry {
Expand Down Expand Up @@ -434,8 +439,17 @@ impl FromStr for ProgramSection {
"lirc_mode2" => LircMode2,
"perf_event" => PerfEvent,
"raw_tp" | "raw_tracepoint" => RawTracePoint,
"lsm" => Lsm { sleepable: false },
"lsm.s" => Lsm { sleepable: true },
"lsm" => Lsm {
sleepable: false,
attach_type: LsmAttachType::Mac,
},
"lsm.s" => Lsm {
sleepable: true,
attach_type: LsmAttachType::Mac,
},
"lsm_cgroup" => LsmCgroup {
attach_type: LsmAttachType::Cgroup,
},
"fentry" => FEntry { sleepable: false },
"fentry.s" => FEntry { sleepable: true },
"fexit" => FExit { sleepable: false },
Expand Down Expand Up @@ -2190,7 +2204,7 @@ mod tests {
Some(Program {
section: ProgramSection::Lsm {
sleepable: false,
..
attach_type: LsmAttachType::Mac
},
..
})
Expand Down Expand Up @@ -2223,6 +2237,31 @@ mod tests {
);
}

#[test]
fn test_parse_section_lsm_cgroup() {
let mut obj = fake_obj();
fake_sym(&mut obj, 0, 0, "foo", FAKE_INS_LEN);

assert_matches!(
obj.parse_section(fake_section(
EbpfSectionKind::Program,
"lsm_cgroup/foo",
bytes_of(&fake_ins()),
None
)),
Ok(())
);
assert_matches!(
obj.programs.get("foo"),
Some(Program {
section: ProgramSection::LsmCgroup {
attach_type: LsmAttachType::Cgroup,
},
..
})
);
}

#[test]
fn test_parse_section_btf_tracepoint() {
let mut obj = fake_obj();
Expand Down
21 changes: 21 additions & 0 deletions aya-obj/src/programs/lsm.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
//! XDP programs.
use crate::generated::bpf_attach_type;

/// Defines where to attach an `XDP` program.
#[derive(Copy, Clone, Debug)]
pub enum LsmAttachType {
/// Cgroup based LSM program
Cgroup,
/// MAC based LSM program
Mac,
}

impl From<LsmAttachType> for bpf_attach_type {
fn from(value: LsmAttachType) -> Self {
match value {
LsmAttachType::Cgroup => bpf_attach_type::BPF_LSM_CGROUP,
LsmAttachType::Mac => bpf_attach_type::BPF_LSM_MAC,
}
}
}
2 changes: 2 additions & 0 deletions aya-obj/src/programs/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@
pub mod cgroup_sock;
pub mod cgroup_sock_addr;
pub mod cgroup_sockopt;
pub mod lsm;
mod types;
pub mod xdp;

pub use cgroup_sock::CgroupSockAttachType;
pub use cgroup_sock_addr::CgroupSockAddrAttachType;
pub use cgroup_sockopt::CgroupSockoptAttachType;
pub use lsm::LsmAttachType;
pub use xdp::XdpAttachType;
27 changes: 22 additions & 5 deletions aya/src/bpf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,9 @@ use crate::{
programs::{
BtfTracePoint, CgroupDevice, CgroupSkb, CgroupSkbAttachType, CgroupSock, CgroupSockAddr,
CgroupSockopt, CgroupSysctl, Extension, FEntry, FExit, Iter, KProbe, LircMode2, Lsm,
PerfEvent, ProbeKind, Program, ProgramData, ProgramError, RawTracePoint, SchedClassifier,
SkLookup, SkMsg, SkSkb, SkSkbKind, SockOps, SocketFilter, TracePoint, UProbe, Xdp,
LsmCgroup, PerfEvent, ProbeKind, Program, ProgramData, ProgramError, RawTracePoint,
SchedClassifier, SkLookup, SkMsg, SkSkb, SkSkbKind, SockOps, SocketFilter, TracePoint,
UProbe, Xdp,
},
sys::{
bpf_load_btf, is_bpf_cookie_supported, is_bpf_global_data_supported,
Expand Down Expand Up @@ -411,7 +412,11 @@ impl<'a> EbpfLoader<'a> {
ProgramSection::Extension
| ProgramSection::FEntry { sleepable: _ }
| ProgramSection::FExit { sleepable: _ }
| ProgramSection::Lsm { sleepable: _ }
| ProgramSection::Lsm {
sleepable: _,
attach_type: _,
}
| ProgramSection::LsmCgroup { attach_type: _ }
| ProgramSection::BtfTracePoint
| ProgramSection::Iter { sleepable: _ } => {
return Err(EbpfError::BtfError(err))
Expand Down Expand Up @@ -649,13 +654,25 @@ impl<'a> EbpfLoader<'a> {
ProgramSection::RawTracePoint => Program::RawTracePoint(RawTracePoint {
data: ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level),
}),
ProgramSection::Lsm { sleepable } => {
ProgramSection::Lsm {
sleepable,
attach_type,
} => {
let mut data =
ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level);
if *sleepable {
data.flags = BPF_F_SLEEPABLE;
}
Program::Lsm(Lsm { data })
Program::Lsm(Lsm {
data,
attach_type: *attach_type,
})
}
ProgramSection::LsmCgroup { attach_type } => {
Program::LsmCgroup(LsmCgroup {
data: ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level),
attach_type: *attach_type,
})
}
ProgramSection::BtfTracePoint => Program::BtfTracePoint(BtfTracePoint {
data: ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level),
Expand Down
Loading

0 comments on commit 91ff050

Please sign in to comment.