mirror of https://github.com/aya-rs/aya
Add support for LSM programs attached to cgroups
Kernel 6.0 provides a new attachment type - `BPF_LSM_CGROUP`. When using it, a program attaches to LSM hooks, but only within a cgroup. `BPF_LSM_CGROUP` requires programs to we present in the `lsm_cgroup` section, therefore we provide a new `cgroup_lsm` macro for defining such programs in aya-bpf. We also provide a new `CgroupLsm` structure in userspace aya, which uses the new attachment type and stores the information about cgroup in links. Fixes: #423 Signed-off-by: Michal Rostecki <vadorovsky@gmail.com>pull/439/head
parent
49404367d8
commit
accd9d2a2a
@ -0,0 +1,134 @@
|
||||
//! LSM probes attached to cgroups.
|
||||
use std::os::unix::prelude::{AsRawFd, RawFd};
|
||||
|
||||
use crate::{
|
||||
generated::{bpf_attach_type::BPF_LSM_CGROUP, bpf_prog_type::BPF_PROG_TYPE_LSM},
|
||||
obj::btf::{Btf, BtfKind},
|
||||
programs::{define_link_wrapper, load_program, FdLink, Link, ProgramData, ProgramError},
|
||||
sys::bpf_link_create,
|
||||
};
|
||||
|
||||
/// A program that attaches to Linux LSM hooks within a [cgroup]. 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 `lsm` option. If your kernel
|
||||
/// is not built with `lsm=[...],bpf` option, BPF LSM needs to be enabled
|
||||
/// through the kernel's boot parameter (like `lsm=lockdown,yama,bpf`).
|
||||
///
|
||||
/// # Minimum kernel version
|
||||
///
|
||||
/// The minimum kernel version required to use this feature is 6.0.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// # #[derive(thiserror::Error, Debug)]
|
||||
/// # enum LsmError {
|
||||
/// # #[error(transparent)]
|
||||
/// # BtfError(#[from] aya::BtfError),
|
||||
/// # #[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::LsmCgroup, BtfError, Btf};
|
||||
/// use std::{fs::File, os::unix::prelude::AsRawFd, path::Path};
|
||||
///
|
||||
/// let btf = Btf::from_sys_fs()?;
|
||||
/// let program: &mut LsmCgroup = bpf.program_mut("lsm_prog").unwrap().try_into()?;
|
||||
/// program.load("security_bprm_exec", &btf)?;
|
||||
/// let cgroup = File::open(Path::new("/sys/fs/cgroup/unified/aya"))?;
|
||||
/// program.attach(cgroup.as_raw_fd())?;
|
||||
/// # Ok::<(), LsmError>(())
|
||||
/// ```
|
||||
#[derive(Debug)]
|
||||
pub struct CgroupLsm {
|
||||
pub(crate) data: ProgramData<CgroupLsmLink>,
|
||||
}
|
||||
|
||||
impl CgroupLsm {
|
||||
/// Loads the program inside the kernel.
|
||||
pub fn load(&mut self, lsm_hook_name: &str, btf: &Btf) -> Result<(), ProgramError> {
|
||||
self.data.expected_attach_type = Some(BPF_LSM_CGROUP);
|
||||
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)
|
||||
}
|
||||
|
||||
/// Attaches the program.
|
||||
///
|
||||
/// The returned value can be used to detach, see [CgroupLsm::detach].
|
||||
pub fn attach<T: AsRawFd>(&mut self, cgroup: T) -> Result<CgroupLsmLinkId, ProgramError> {
|
||||
let prog_fd = self.data.fd_or_err()?;
|
||||
let cgroup_fd = cgroup.as_raw_fd();
|
||||
let attach_type = self.data.expected_attach_type.unwrap();
|
||||
let btf_id = self.data.attach_btf_id;
|
||||
|
||||
let link_fd = bpf_link_create(prog_fd, cgroup_fd, attach_type, btf_id, 0).map_err(
|
||||
|(_, io_error)| ProgramError::SyscallError {
|
||||
call: "bpf_link_create".to_owned(),
|
||||
io_error,
|
||||
},
|
||||
)? as RawFd;
|
||||
self.data
|
||||
.links
|
||||
.insert(CgroupLsmLink(CgroupLsmLinkInner::Fd(FdLink::new(link_fd))))
|
||||
}
|
||||
|
||||
/// Detaches the program.
|
||||
///
|
||||
/// See [CgroupLsm::attach].
|
||||
pub fn detach(&mut self, link_id: CgroupLsmLinkId) -> Result<(), ProgramError> {
|
||||
self.data.links.remove(link_id)
|
||||
}
|
||||
|
||||
/// Takes ownership of the link referenced by the provided link_id.
|
||||
///
|
||||
/// The link will be detached on `Drop` and the caller is now responsible
|
||||
/// for managing its lifetime.
|
||||
pub fn take_link(&mut self, link_id: CgroupLsmLinkId) -> Result<CgroupLsmLink, ProgramError> {
|
||||
self.data.take_link(link_id)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Hash, Eq, PartialEq)]
|
||||
enum CgroupLsmLinkIdInner {
|
||||
Fd(<FdLink as Link>::Id),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum CgroupLsmLinkInner {
|
||||
Fd(FdLink),
|
||||
}
|
||||
|
||||
impl Link for CgroupLsmLinkInner {
|
||||
type Id = CgroupLsmLinkIdInner;
|
||||
|
||||
fn id(&self) -> Self::Id {
|
||||
match self {
|
||||
CgroupLsmLinkInner::Fd(fd) => CgroupLsmLinkIdInner::Fd(fd.id()),
|
||||
}
|
||||
}
|
||||
|
||||
fn detach(self) -> Result<(), ProgramError> {
|
||||
match self {
|
||||
CgroupLsmLinkInner::Fd(fd) => fd.detach(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
define_link_wrapper!(
|
||||
/// The link used by [CgroupLsm] programs.
|
||||
CgroupLsmLink,
|
||||
/// The type returned by [CgroupLsm::attach]. Can be passed to [CgroupLsm::detach].
|
||||
CgroupLsmLinkId,
|
||||
CgroupLsmLinkInner,
|
||||
CgroupLsmLinkIdInner
|
||||
);
|
Loading…
Reference in New Issue