diff --git a/rust/backend/common/src/lib.rs b/rust/backend/common/src/lib.rs index 750593f9..48340037 100644 --- a/rust/backend/common/src/lib.rs +++ b/rust/backend/common/src/lib.rs @@ -19,7 +19,13 @@ pub struct VfsWriteCall { impl VfsWriteCall { pub fn new(pid: u32, tid: u32, begin_time_stamp: u64, fp: u64, bytes_written: usize) -> Self { - Self { pid, tid, begin_time_stamp, fp, bytes_written} + Self { + pid, + tid, + begin_time_stamp, + fp, + bytes_written, + } } } @@ -35,7 +41,13 @@ pub struct SysSendmsgCall { impl SysSendmsgCall { pub fn new(pid: u32, tid: u32, begin_time_stamp: u64, fd: u64, duration_nano_sec: u64) -> Self { - Self { pid, tid, begin_time_stamp, fd, duration_nano_sec } + Self { + pid, + tid, + begin_time_stamp, + fd, + duration_nano_sec, + } } } @@ -58,7 +70,7 @@ pub struct JNICall { } #[inline(always)] -pub fn generate_id(pid: u32, tgid: u32) -> u64{ +pub fn generate_id(pid: u32, tgid: u32) -> u64 { let pid_u64 = pid as u64; let tgid_u64 = tgid as u64; diff --git a/rust/backend/daemon/src/ebpf_utils.rs b/rust/backend/daemon/src/ebpf_utils.rs index 9fa86034..5b531ae5 100644 --- a/rust/backend/daemon/src/ebpf_utils.rs +++ b/rust/backend/daemon/src/ebpf_utils.rs @@ -5,12 +5,9 @@ // // SPDX-License-Identifier: MIT -use aya::{Ebpf, EbpfError}; -use shared::config::Configuration; +use aya::EbpfError; use thiserror::Error; -use crate::features::{SysSendmsgFeature, VfsFeature, JNIReferencesFeature}; - #[derive(Debug, Error)] pub enum EbpfErrorWrapper { #[error(transparent)] @@ -23,40 +20,3 @@ impl From for tonic::Status { } } -pub struct State { - vfs_write_feature: VfsFeature, - sys_sendmsg_feature: SysSendmsgFeature, - jni_references_feature: JNIReferencesFeature, -} - -impl State { - pub fn new() -> State { - State { - vfs_write_feature: VfsFeature::new(), - sys_sendmsg_feature: SysSendmsgFeature::new(), - jni_references_feature: JNIReferencesFeature::new() - } - } - - pub fn init(&mut self, ebpf: &mut Ebpf) -> Result<(), EbpfError> { - self.vfs_write_feature.create(ebpf)?; - self.sys_sendmsg_feature.create(ebpf)?; - self.jni_references_feature.create(ebpf)?; - - - Ok(()) - } - - pub fn update_from_config( - &mut self, - ebpf: &mut Ebpf, - config: &Configuration, - ) -> Result<(), EbpfError> { - self.vfs_write_feature.apply(ebpf, config.vfs_write.as_ref())?; - self.sys_sendmsg_feature.apply(ebpf, config.sys_sendmsg.as_ref())?; - self.jni_references_feature.apply(ebpf, config.jni_references.as_ref())?; - - - Ok(()) - } -} diff --git a/rust/backend/daemon/src/features.rs b/rust/backend/daemon/src/features.rs deleted file mode 100644 index 5a924460..00000000 --- a/rust/backend/daemon/src/features.rs +++ /dev/null @@ -1,408 +0,0 @@ -// SPDX-FileCopyrightText: 2024 Felix Hilgers -// SPDX-FileCopyrightText: 2024 Robin Seidl -// SPDX-FileCopyrightText: 2024 Tom Weisshuhn -// -// SPDX-License-Identifier: MIT - -#![allow(unused)] - -use std::collections::BTreeSet; -use aya::{ - maps::HashMap, - programs::{kprobe::KProbeLinkId, uprobe::UProbeLink, trace_point::TracePointLink, KProbe, TracePoint, UProbe, ProgramError}, - Ebpf, EbpfError, -}; -use shared::config::{SysSendmsgConfig, VfsWriteConfig, JniReferencesConfig}; - - -pub struct JNIReferencesFeature { - trace_add_local_link: Option, - trace_del_local_link: Option, - trace_add_global_link: Option, - trace_del_global_link: Option, -} - -impl JNIReferencesFeature { - pub fn new() -> JNIReferencesFeature { - JNIReferencesFeature { - trace_add_local_link: None, - trace_del_local_link: None, - trace_add_global_link: None, - trace_del_global_link: None, - } - } - - fn jni_load_program_by_name(ebpf: &mut Ebpf, name: &str) -> Result<(), EbpfError> { - let jni_probe: &mut UProbe = ebpf - .program_mut(name) - .ok_or(EbpfError::ProgramError( - aya::programs::ProgramError::InvalidName { - name: name.to_string(), - }, - ))? - .try_into()?; - jni_probe.load()?; - Ok(()) - } - - fn jni_get_offset_by_name(name: &str) -> u64 { - todo!("get offset of symbol by name"); - } - - fn jni_get_target_by_name(name: &str) -> &str { - todo!("get absolute path to library/ binary"); - } - - fn jni_attach_program_by_name(&mut self, ebpf: &mut Ebpf, probe_name: &str, jni_method_name: &str) -> Result<(), EbpfError> { - let jni_program: &mut UProbe = ebpf - .program_mut(probe_name) - .ok_or(EbpfError::ProgramError( - aya::programs::ProgramError::InvalidName { - name: probe_name.to_string(), - }, - ))? - .try_into()?; - - let offset = Self::jni_get_offset_by_name(jni_method_name); - let target = Self::jni_get_target_by_name(jni_method_name); - let link_id = jni_program.attach( - Some(jni_method_name), - offset, - target, - None - ).map_err(|err| {EbpfError::ProgramError(err)})?; - self.trace_add_local_link = Some(jni_program.take_link(link_id).map_err(|err| {EbpfError::ProgramError(err)})?); - Ok(()) - } - - pub fn create(&mut self, ebpf: &mut Ebpf) -> Result<(), EbpfError> { - Self::jni_load_program_by_name(ebpf, "trace_add_local"); - Self::jni_load_program_by_name(ebpf, "trace_del_local"); - Self::jni_load_program_by_name(ebpf, "trace_add_global"); - Self::jni_load_program_by_name(ebpf, "trace_del_global"); - - Ok(()) - } - - #[allow(unreachable_code)] - pub fn attach(&mut self, ebpf: &mut Ebpf) -> Result<(), EbpfError> { - if self.trace_add_local_link.is_none() { - Self::jni_attach_program_by_name(self, ebpf, "trace_add_local", "AddLocalRef")? - } - - if self.trace_del_local_link.is_none() { - Self::jni_attach_program_by_name(self, ebpf, "trace_del_local", "DeleteLocalRef")? - } - - if self.trace_add_global_link.is_none() { - Self::jni_attach_program_by_name(self, ebpf, "trace_add_global", "AddGlobalRef")? - } - - if self.trace_del_global_link.is_none() { - Self::jni_attach_program_by_name(self, ebpf, "trace_del_global", "DeleteGlobalRef")? - } - - Ok(()) - } - - pub fn detach(&mut self, ebpf: &mut Ebpf) -> Result<(), EbpfError> { - let _ = self.trace_add_local_link.take(); - let _ = self.trace_del_local_link.take(); - let _ = self.trace_add_global_link.take(); - let _ = self.trace_del_global_link.take(); - - Ok(()) - } - - fn update_pids( - &mut self, - ebpf: &mut Ebpf, - pids: &[u32] - ) -> Result<(), EbpfError> { - let mut pids_to_track: HashMap<_, u32, u32> = ebpf - .map_mut("JNI_REF_PIDS") - .ok_or(EbpfError::MapError(aya::maps::MapError::InvalidName { - name: "JNI_REF_PIDS".to_string(), - }))? - .try_into()?; - - let new_keys = pids.iter().copied().collect::>(); - let existing_keys = pids_to_track.keys().collect::, _>>()?; - - for key_to_remove in existing_keys.difference(&new_keys) { - pids_to_track.remove(key_to_remove)?; - } - - for key_to_add in new_keys.difference(&existing_keys) { - pids_to_track.insert(key_to_add, 0, 0)?; - } - - Ok(()) - } - - pub fn apply( - &mut self, - ebpf: &mut Ebpf, - config: Option<&JniReferencesConfig>, - ) -> Result<(), EbpfError> { - match config { - Some(config) => { - self.attach(ebpf)?; - self.update_pids(ebpf, &config.pids)?; - } - None => { - self.detach(ebpf)?; - } - } - Ok(()) - } -} - - - - -pub struct SysSendmsgFeature { - sys_enter_sendmsg_id: Option, - sys_exit_sendmsg_id: Option, -} - -impl SysSendmsgFeature { - pub fn new() -> SysSendmsgFeature { - SysSendmsgFeature { - sys_enter_sendmsg_id: None, - sys_exit_sendmsg_id: None, - } - } - - fn get_tracepoint_by_name<'a>( - ebpf: &'a mut Ebpf, - name: &str, - ) -> Result<&'a mut TracePoint, EbpfError> { - Ok(ebpf - .program_mut(name) - .ok_or(EbpfError::ProgramError( - aya::programs::ProgramError::InvalidName { - name: name.to_string(), - }, - ))? - .try_into()?) - } - - fn get_sys_enter_sendmsg(ebpf: &mut Ebpf) -> Result<&mut TracePoint, EbpfError> { - Self::get_tracepoint_by_name(ebpf, "sys_enter_sendmsg") - } - - fn get_sys_exit_sendmsg(ebpf: &mut Ebpf) -> Result<&mut TracePoint, EbpfError> { - Self::get_tracepoint_by_name(ebpf, "sys_exit_sendmsg") - } - - fn detach(&mut self) -> Result<(), EbpfError> { - let _ = self.sys_enter_sendmsg_id.take(); - let _ = self.sys_exit_sendmsg_id.take(); - Ok(()) - } - - fn attach(&mut self, ebpf: &mut Ebpf) -> Result<(), EbpfError> { - if self.sys_enter_sendmsg_id.is_none() { - let p = SysSendmsgFeature::get_sys_enter_sendmsg(ebpf)?; - let link_id = p.attach("syscalls", "sys_enter_sendmsg")?; - self.sys_enter_sendmsg_id = Some(p.take_link(link_id)?); - } - - if self.sys_exit_sendmsg_id.is_none() { - let p = SysSendmsgFeature::get_sys_exit_sendmsg(ebpf)?; - let link_id = p.attach("syscalls", "sys_exit_sendmsg")?; - self.sys_exit_sendmsg_id = Some(p.take_link(link_id)?); - } - - Ok(()) - } - - fn update_pids( - &mut self, - ebpf: &mut Ebpf, - entries: &std::collections::HashMap, - ) -> Result<(), EbpfError> { - let mut pids_to_track: HashMap<_, u32, u64> = ebpf - .map_mut("SYS_SENDMSG_PIDS") - .ok_or(EbpfError::MapError(aya::maps::MapError::InvalidName { - name: "SYS_SENDMSG_PIDS".to_string(), - }))? - .try_into()?; - - let new_keys = entries.keys().copied().collect(); - let existing_keys = pids_to_track.keys().collect::, _>>()?; - - for key_to_remove in existing_keys.difference(&new_keys) { - pids_to_track.remove(key_to_remove)?; - } - - for (key, value) in entries { - pids_to_track.insert(key, value, 0)?; - } - - Ok(()) - } - - pub fn create(&mut self, ebpf: &mut Ebpf) -> Result<(), EbpfError> { - SysSendmsgFeature::get_sys_exit_sendmsg(ebpf)?.load()?; - SysSendmsgFeature::get_sys_enter_sendmsg(ebpf)?.load()?; - Ok(()) - } - - pub fn apply( - &mut self, - ebpf: &mut Ebpf, - config: Option<&SysSendmsgConfig>, - ) -> Result<(), EbpfError> { - match config { - Some(config) => { - self.attach(ebpf)?; - self.update_pids(ebpf, &config.entries)?; - } - None => { - self.detach()?; - } - } - Ok(()) - } -} - -pub struct VfsFeature { - vfs_write_id: Option, - vfs_write_ret_id: Option, -} - -impl VfsFeature { - pub fn new() -> VfsFeature { - VfsFeature { - vfs_write_id: None, - vfs_write_ret_id: None, - } - } - - pub fn create(&mut self, ebpf: &mut Ebpf) -> Result<(), EbpfError> { - let vfs_write: &mut KProbe = ebpf - .program_mut("vfs_write") - .ok_or(EbpfError::ProgramError( - aya::programs::ProgramError::InvalidName { - name: "vfs_write".to_string(), - }, - ))? - .try_into()?; - vfs_write.load()?; - - let vfs_write_ret: &mut KProbe = ebpf - .program_mut("vfs_write_ret") - .ok_or(EbpfError::ProgramError( - aya::programs::ProgramError::InvalidName { - name: "vfs_write_ret".to_string(), - }, - ))? - .try_into()?; - vfs_write_ret.load()?; - - Ok(()) - } - - pub fn attach(&mut self, ebpf: &mut Ebpf) -> Result<(), EbpfError> { - if self.vfs_write_id.is_none() { - let vfs_write: &mut KProbe = ebpf - .program_mut("vfs_write") - .ok_or(EbpfError::ProgramError( - aya::programs::ProgramError::InvalidName { - name: "vfs_write".to_string(), - }, - ))? - .try_into()?; - self.vfs_write_id = Some(vfs_write.attach("vfs_write", 0)?); - } - - if self.vfs_write_ret_id.is_none() { - let vfs_write_ret: &mut KProbe = ebpf - .program_mut("vfs_write_ret") - .ok_or(EbpfError::ProgramError( - aya::programs::ProgramError::InvalidName { - name: "vfs_write_ret".to_string(), - }, - ))? - .try_into()?; - self.vfs_write_ret_id = Some(vfs_write_ret.attach("vfs_write", 0)?); - } - - Ok(()) - } - - pub fn detach(&mut self, ebpf: &mut Ebpf) -> Result<(), EbpfError> { - let vfs_write: &mut KProbe = ebpf - .program_mut("vfs_write") - .ok_or(EbpfError::ProgramError( - aya::programs::ProgramError::InvalidName { - name: "vfs_write".to_string(), - }, - ))? - .try_into()?; - - if let Some(vfs_write_id) = self.vfs_write_id.take() { - vfs_write.detach(vfs_write_id)?; - } - - let vfs_write_ret: &mut KProbe = ebpf - .program_mut("vfs_write_ret") - .ok_or(EbpfError::ProgramError( - aya::programs::ProgramError::InvalidName { - name: "vfs_write_ret".to_string(), - }, - ))? - .try_into()?; - - if let Some(vfs_write_ret_id) = self.vfs_write_ret_id.take() { - vfs_write_ret.detach(vfs_write_ret_id)?; - } - - Ok(()) - } - - fn update_pids( - &mut self, - ebpf: &mut Ebpf, - entries: &std::collections::HashMap, - ) -> Result<(), EbpfError> { - let mut pids_to_track: HashMap<_, u32, u64> = ebpf - .map_mut("VFS_WRITE_PIDS") - .ok_or(EbpfError::MapError(aya::maps::MapError::InvalidName { - name: "VFS_WRITE_PIDS".to_string(), - }))? - .try_into()?; - - let new_keys = entries.keys().copied().collect(); - let existing_keys = pids_to_track.keys().collect::, _>>()?; - - for key_to_remove in existing_keys.difference(&new_keys) { - pids_to_track.remove(key_to_remove)?; - } - - for (key, value) in entries { - pids_to_track.insert(key, value, 0)?; - } - - Ok(()) - } - - pub fn apply( - &mut self, - ebpf: &mut Ebpf, - config: Option<&VfsWriteConfig>, - ) -> Result<(), EbpfError> { - match config { - Some(config) => { - self.attach(ebpf)?; - self.update_pids(ebpf, &config.entries)?; - } - None => { - self.detach(ebpf)?; - } - } - Ok(()) - } -} diff --git a/rust/backend/daemon/src/features/jni_reference_feature.rs b/rust/backend/daemon/src/features/jni_reference_feature.rs new file mode 100644 index 00000000..a0f93ee3 --- /dev/null +++ b/rust/backend/daemon/src/features/jni_reference_feature.rs @@ -0,0 +1,191 @@ +// SPDX-FileCopyrightText: 2024 Tom Weisshuhn +// SPDX-FileCopyrightText: 2024 Franz Schlicht +// SPDX-License-Identifier: MIT +#![allow(unused)] +use aya::{ + programs::uprobe::UProbeLink, + Ebpf, EbpfError, +}; +use aya::programs::UProbe; +use crate::features::{update_pids, Feature}; +use shared::config::JniReferencesConfig; + +pub struct JNIReferencesFeature { + trace_add_local_link: Option, + trace_del_local_link: Option, + trace_add_global_link: Option, + trace_del_global_link: Option, +} + +impl JNIReferencesFeature { + + fn create(ebpf: &mut Ebpf) -> Result { + Self::jni_load_program_by_name(ebpf, "trace_add_local")?; + Self::jni_load_program_by_name(ebpf, "trace_del_local")?; + Self::jni_load_program_by_name(ebpf, "trace_add_global")?; + Self::jni_load_program_by_name(ebpf, "trace_del_global")?; + Ok( + Self { + trace_add_local_link: None, + trace_del_local_link: None, + trace_add_global_link: None, + trace_del_global_link: None, + }) + } + + pub fn attach(&mut self, ebpf: &mut Ebpf) -> Result<(), EbpfError> { + if self.trace_add_local_link.is_none() { + Self::jni_attach_program_by_name(self, ebpf, "trace_add_local", "AddLocalRef")? + } + + if self.trace_del_local_link.is_none() { + Self::jni_attach_program_by_name(self, ebpf, "trace_del_local", "DeleteLocalRef")? + } + + if self.trace_add_global_link.is_none() { + Self::jni_attach_program_by_name(self, ebpf, "trace_add_global", "AddGlobalRef")? + } + + if self.trace_del_global_link.is_none() { + Self::jni_attach_program_by_name(self, ebpf, "trace_del_global", "DeleteGlobalRef")? + } + + Ok(()) + } + + pub fn detach(&mut self) { + let _ = self.trace_add_local_link.take(); + let _ = self.trace_del_local_link.take(); + let _ = self.trace_add_global_link.take(); + let _ = self.trace_del_global_link.take(); + } + + fn destroy(&mut self, ebpf: &mut Ebpf) -> Result<(), EbpfError> { + + self.detach(); + + // TODO Error handling + let trace_add_local: &mut UProbe = ebpf + .program_mut("trace_add_local") + .ok_or(EbpfError::ProgramError( + aya::programs::ProgramError::InvalidName { + name: "trace_add_local".to_string(), + }, + ))? + .try_into()?; + trace_add_local.unload()?; + + let trace_del_local: &mut UProbe = ebpf + .program_mut("trace_del_local") + .ok_or(EbpfError::ProgramError( + aya::programs::ProgramError::InvalidName { + name: "trace_del_local".to_string(), + }, + ))? + .try_into()?; + trace_del_local.unload()?; + + let trace_add_global: &mut UProbe = ebpf + .program_mut("trace_add_global") + .ok_or(EbpfError::ProgramError( + aya::programs::ProgramError::InvalidName { + name: "trace_add_global".to_string(), + }, + ))? + .try_into()?; + trace_add_global.unload()?; + + + + let trace_del_global: &mut UProbe = ebpf + .program_mut("trace_del_global") + .ok_or(EbpfError::ProgramError( + aya::programs::ProgramError::InvalidName { + name: "trace_del_global".to_string(), + }, + ))? + .try_into()?; + trace_del_global.unload()?; + + Ok(()) + } + + fn update_pids( + &mut self, + ebpf: &mut Ebpf, + pids: &[u32] + ) -> Result<(), EbpfError> { + + // the general update_pids function for all features works with hashmaps, so the list is converted into a hashmap with keys always being 0 + let pid_0_tuples: Vec<(u32, u64)> = pids.iter().map(|pid| (*pid, 0)).collect(); + let pids_as_hashmap: std::collections::HashMap = std::collections::HashMap::from_iter(pid_0_tuples); + + update_pids(ebpf, &pids_as_hashmap, "JNI_REF_PIDS") + } + fn jni_load_program_by_name(ebpf: &mut Ebpf, name: &str) -> Result<(), EbpfError> { + let jni_probe: &mut UProbe = ebpf + .program_mut(name) + .ok_or(EbpfError::ProgramError( + aya::programs::ProgramError::InvalidName { + name: name.to_string(), + }, + ))? + .try_into()?; + jni_probe.load()?; + Ok(()) + } + + fn jni_get_offset_by_name(name: &str) -> u64 { + todo!("get offset of symbol by name"); + } + + fn jni_get_target_by_name(name: &str) -> &str { + todo!("get absolute path to library/ binary"); + } + + fn jni_attach_program_by_name(&mut self, ebpf: &mut Ebpf, probe_name: &str, jni_method_name: &str) -> Result<(), EbpfError> { + let jni_program: &mut UProbe = ebpf + .program_mut(probe_name) + .ok_or(EbpfError::ProgramError( + aya::programs::ProgramError::InvalidName { + name: probe_name.to_string(), + }, + ))? + .try_into()?; + + let offset = Self::jni_get_offset_by_name(jni_method_name); + let target = Self::jni_get_target_by_name(jni_method_name); + let link_id = jni_program.attach( + Some(jni_method_name), + offset, + target, + None + ).map_err(|err| {EbpfError::ProgramError(err)})?; + self.trace_add_local_link = Some(jni_program.take_link(link_id).map_err(|err| {EbpfError::ProgramError(err)})?); + Ok(()) + } +} + +impl Feature for JNIReferencesFeature { + type Config = JniReferencesConfig; + + fn init(ebpf: &mut Ebpf) -> Self { + JNIReferencesFeature::create(ebpf).expect("Error initializing JNI reference feature") + } + fn apply( + &mut self, + ebpf: &mut Ebpf, + config: &Option, + ) -> Result<(), EbpfError> { + match config { + Some(config) => { + self.attach(ebpf)?; + self.update_pids(ebpf, &config.pids)?; + } + None => { + self.detach(); + } + } + Ok(()) + } +} \ No newline at end of file diff --git a/rust/backend/daemon/src/features/mod.rs b/rust/backend/daemon/src/features/mod.rs new file mode 100644 index 00000000..645bbbfd --- /dev/null +++ b/rust/backend/daemon/src/features/mod.rs @@ -0,0 +1,92 @@ +// SPDX-FileCopyrightText: 2024 Felix Hilgers +// SPDX-FileCopyrightText: 2024 Robin Seidl +// SPDX-FileCopyrightText: 2024 Tom Weisshuhn +// SPDX-FileCopyrightText: 2024 Franz Schlicht +// SPDX-License-Identifier: MIT + +mod jni_reference_feature; +mod vfs_write_feature; +mod sys_sendmsg_feature; + +use std::collections::BTreeSet; +use aya::{ + maps::HashMap, + Ebpf, EbpfError, +}; +use jni_reference_feature::JNIReferencesFeature; +use shared::config::Configuration; +use sys_sendmsg_feature::SysSendmsgFeature; +use vfs_write_feature::VfsWriteFeature; + + +pub trait Feature { + type Config; + + fn init(ebpf: &mut Ebpf) -> Self; + fn apply(&mut self, ebpf: &mut Ebpf, config: &Option) -> Result<(), EbpfError>; + +} + +pub struct Features { + sys_sendmsg_feature: SysSendmsgFeature, + vfs_write_feature: VfsWriteFeature, + jni_reference_feature: JNIReferencesFeature, +} + +impl Features { + + pub fn init_all_features(ebpf: &mut Ebpf) -> Self { + let sys_sendmsg_feature = SysSendmsgFeature::init(ebpf); + let vfs_write_feature = VfsWriteFeature::init(ebpf); + let jni_reference_feature = JNIReferencesFeature::init(ebpf); + + Self { + sys_sendmsg_feature, + vfs_write_feature, + jni_reference_feature, + } + } + + pub fn update_from_config( + &mut self, + ebpf: &mut Ebpf, + config: &Configuration, + ) -> Result<(), EbpfError> { + + + self.vfs_write_feature.apply(ebpf, &config.vfs_write)?; + self.sys_sendmsg_feature.apply(ebpf, &config.sys_sendmsg)?; + self.jni_reference_feature.apply(ebpf, &config.jni_references)?; + + Ok(()) + } +} + + + +pub fn update_pids( + ebpf: &mut Ebpf, + entries: &std::collections::HashMap, + map_name: &str +) -> Result<(), EbpfError> { + let mut pids_to_track: HashMap<_, u32, u64> = ebpf + .map_mut(map_name) + .ok_or(EbpfError::MapError(aya::maps::MapError::InvalidName { + name: map_name.to_string(), + }))? + .try_into()?; + + let new_keys = entries.keys().copied().collect(); + let existing_keys = pids_to_track.keys().collect::, _>>()?; + + for key_to_remove in existing_keys.difference(&new_keys) { + pids_to_track.remove(key_to_remove)?; + } + + for (key, value) in entries { + pids_to_track.insert(key, value, 0)?; + } + + Ok(()) +} + diff --git a/rust/backend/daemon/src/features/sys_sendmsg_feature.rs b/rust/backend/daemon/src/features/sys_sendmsg_feature.rs new file mode 100644 index 00000000..31e4a27d --- /dev/null +++ b/rust/backend/daemon/src/features/sys_sendmsg_feature.rs @@ -0,0 +1,131 @@ +// SPDX-FileCopyrightText: 2024 Robin Seidl +// SPDX-FileCopyrightText: 2024 Tom Weisshuhn +// SPDX-FileCopyrightText: 2024 Franz Schlicht +// SPDX-License-Identifier: MIT + +use aya::{Ebpf, EbpfError}; +use aya::programs::trace_point::TracePointLink; +use aya::programs::TracePoint; +use shared::config::SysSendmsgConfig; +use crate::features::{update_pids, Feature}; + +pub struct SysSendmsgFeature { + sys_enter_sendmsg: Option, + sys_exit_sendmsg: Option, +} + +impl SysSendmsgFeature { + fn create(ebpf: &mut Ebpf) -> Result { + SysSendmsgFeature::get_sys_enter_sendmsg(ebpf)?.load()?; + SysSendmsgFeature::get_sys_exit_sendmsg(ebpf)?.load()?; + Ok( + Self { + sys_enter_sendmsg: None, + sys_exit_sendmsg: None, + }) + } + + fn attach(&mut self, ebpf: &mut Ebpf) -> Result<(), EbpfError> { + if self.sys_enter_sendmsg.is_none() { + let p = SysSendmsgFeature::get_sys_enter_sendmsg(ebpf)?; + let link_id = p.attach("syscalls","sys_enter_sendmsg")?; + self.sys_enter_sendmsg = Some(p.take_link(link_id)?); + } + + if self.sys_exit_sendmsg.is_none() { + let p = SysSendmsgFeature::get_sys_exit_sendmsg(ebpf)?; + let link_id = p.attach("syscalls","sys_exit_sendmsg")?; + self.sys_exit_sendmsg = Some(p.take_link(link_id)?); + } + + Ok(()) + } + + fn detach(&mut self) { + // the TrakePointLinks will be automatically detached when the reference is dropped + let _ = self.sys_enter_sendmsg.take(); + let _ = self.sys_exit_sendmsg.take(); + } + + // make clippy happy + fn _destroy(&mut self, ebpf: &mut Ebpf) -> Result<(), EbpfError> { + + self.detach(); + + // TODO Error handling + let vfs_write: &mut TracePoint = ebpf + .program_mut("vfs_write") + .ok_or(EbpfError::ProgramError( + aya::programs::ProgramError::InvalidName { + name: "vfs_write".to_string(), + }, + ))? + .try_into()?; + vfs_write.unload()?; + + let vfs_write_ret: &mut TracePoint = ebpf + .program_mut("vfs_write_ret") + .ok_or(EbpfError::ProgramError( + aya::programs::ProgramError::InvalidName { + name: "vfs_write_ret".to_string(), + }, + ))? + .try_into()?; + vfs_write_ret.unload()?; + + Ok(()) + } + + fn update_pids( + &self, + ebpf: &mut Ebpf, + entries: &std::collections::HashMap, + ) -> Result<(), EbpfError> { + update_pids(ebpf, entries, "SYS_SENDMSG_PIDS") + } + + + fn get_tracepoint_by_name<'a>(ebpf: &'a mut Ebpf, name: &str) -> Result<&'a mut TracePoint, EbpfError> { + Ok(ebpf + .program_mut(name) + .ok_or(EbpfError::ProgramError( + aya::programs::ProgramError::InvalidName { + name: name.to_string(), + }, + ))? + .try_into()?) + } + + fn get_sys_enter_sendmsg(ebpf: &mut Ebpf) -> Result<&mut TracePoint, EbpfError> { + Self::get_tracepoint_by_name(ebpf, "sys_enter_sendmsg") + } + + fn get_sys_exit_sendmsg(ebpf: &mut Ebpf) -> Result<&mut TracePoint, EbpfError> { + Self::get_tracepoint_by_name(ebpf, "sys_exit_sendmsg") + } +} + +impl Feature for SysSendmsgFeature { + type Config = SysSendmsgConfig; + fn init(ebpf: &mut Ebpf) -> Self { + SysSendmsgFeature::create(ebpf).expect("Error initializing sys_sendmsg feature") + } + + fn apply(&mut self, ebpf: &mut Ebpf, config: &Option) -> Result<(), EbpfError> { + match config { + Some(config) => { + self.attach(ebpf)?; + self.update_pids(ebpf, &config.entries)?; + } + None => { + self.detach(); + } + } + Ok(()) + } +} + + + + + diff --git a/rust/backend/daemon/src/features/vfs_write_feature.rs b/rust/backend/daemon/src/features/vfs_write_feature.rs new file mode 100644 index 00000000..d377226e --- /dev/null +++ b/rust/backend/daemon/src/features/vfs_write_feature.rs @@ -0,0 +1,155 @@ +// SPDX-FileCopyrightText: 2024 Robin Seidl +// SPDX-FileCopyrightText: 2024 Tom Weisshuhn +// SPDX-FileCopyrightText: 2024 Franz Schlicht +// SPDX-License-Identifier: MIT + +use aya::{Ebpf, EbpfError}; +use aya::programs::KProbe; +use aya::programs::kprobe::KProbeLink; +use shared::config::VfsWriteConfig; +use crate::features::{Feature, update_pids}; + +pub struct VfsWriteFeature { + vfs_write: Option, + vfs_write_ret: Option, +} + +impl VfsWriteFeature { + + fn create(ebpf: &mut Ebpf) -> Result { + let vfs_write: &mut KProbe = ebpf + .program_mut("vfs_write") + .ok_or(EbpfError::ProgramError( + aya::programs::ProgramError::InvalidName { + name: "vfs_write".to_string(), + }, + ))? + .try_into()?; + vfs_write.load()?; + + let vfs_write_ret: &mut KProbe = ebpf + .program_mut("vfs_write_ret") + .ok_or(EbpfError::ProgramError( + aya::programs::ProgramError::InvalidName { + name: "vfs_write_ret".to_string(), + }, + ))? + .try_into()?; + vfs_write_ret.load()?; + + Ok( + VfsWriteFeature { + vfs_write: None, + vfs_write_ret: None + } + ) + } + + fn attach(&mut self, ebpf: &mut Ebpf) -> Result<(), EbpfError> { + if self.vfs_write.is_none() { + let program: &mut KProbe = ebpf + .program_mut("vfs_write") + .ok_or(EbpfError::ProgramError( + aya::programs::ProgramError::InvalidName { + name: "vfs_write".to_string(), + }, + ))? + .try_into()?; + + let link_id = program.attach("vfs_write",0)?; + self.vfs_write = Some(program.take_link(link_id)?); + } + + if self.vfs_write_ret.is_none() { + let program: &mut KProbe = ebpf + .program_mut("vfs_write_ret") + .ok_or(EbpfError::ProgramError( + aya::programs::ProgramError::InvalidName { + name: "vfs_write_ret".to_string(), + }, + ))? + .try_into()?; + let link_id = program.attach("vfs_write", 0)?; + self.vfs_write_ret = Some(program.take_link(link_id)?); + + } + + + Ok(()) + } + + fn detach(&mut self) { + let _ = self.vfs_write.take(); + let _ = self.vfs_write_ret.take(); + } + + fn _destroy(&mut self, ebpf: &mut Ebpf) -> Result<(), EbpfError> { + + self.detach(); + + // TODO Error handling + let vfs_write: &mut KProbe = ebpf + .program_mut("vfs_write") + .ok_or(EbpfError::ProgramError( + aya::programs::ProgramError::InvalidName { + name: "vfs_write".to_string(), + }, + ))? + .try_into()?; + vfs_write.unload()?; + + let vfs_write_ret: &mut KProbe = ebpf + .program_mut("vfs_write_ret") + .ok_or(EbpfError::ProgramError( + aya::programs::ProgramError::InvalidName { + name: "vfs_write_ret".to_string(), + }, + ))? + .try_into()?; + vfs_write_ret.unload()?; + + Ok(()) + } + + + fn update_pids( + &self, + ebpf: &mut Ebpf, + entries: &std::collections::HashMap, + ) -> Result<(), EbpfError> { + update_pids(ebpf, entries, "VFS_WRITE_PIDS") + } + + + + // fn update(&mut self, ebpf: &mut Ebpf) { + // // update pids that are attached + // !todo!(); + // } + + // fn events(&mut self, ebpf: &mut Ebpf) { + // // return buffered stream of events + // // will be discussed by Felix and Beni + // } +} + +impl Feature for VfsWriteFeature { + type Config = VfsWriteConfig; + + fn init(ebpf: &mut Ebpf) -> Self { + VfsWriteFeature::create(ebpf).expect("Error initializing vfs_write feature") + } + + fn apply(&mut self, ebpf: &mut Ebpf, config: &Option) -> Result<(), EbpfError> { + match config { + Some(config) => { + self.attach(ebpf)?; + self.update_pids(ebpf, &config.entries)?; + }, + None => { + self.detach(); + } + } + Ok(()) + } +} diff --git a/rust/backend/daemon/src/lib.rs b/rust/backend/daemon/src/lib.rs index bcd17baa..c65ea189 100644 --- a/rust/backend/daemon/src/lib.rs +++ b/rust/backend/daemon/src/lib.rs @@ -12,9 +12,8 @@ mod helpers; mod procfs_utils; mod server; mod features; -mod symbols; - mod collector; +mod symbols; pub async fn run_server() { helpers::bump_rlimit(); diff --git a/rust/backend/daemon/src/server.rs b/rust/backend/daemon/src/server.rs index 283158f1..ad22a5cc 100644 --- a/rust/backend/daemon/src/server.rs +++ b/rust/backend/daemon/src/server.rs @@ -10,8 +10,9 @@ use crate::symbols::SymbolHandler; use crate::{ configuration, constants, counter::Counter, - ebpf_utils::{EbpfErrorWrapper, State}, + ebpf_utils::EbpfErrorWrapper, procfs_utils::{list_processes, ProcErrorWrapper}, + features::Features, }; use async_broadcast::{broadcast, Receiver, Sender}; use aya::Ebpf; @@ -38,7 +39,7 @@ use tonic::{transport::Server, Request, Response, Status}; pub struct ZiofaImpl { // tx: Option>>, ebpf: Arc>, - state: Arc>, + features: Arc>, channel: Arc, symbol_handler: Arc>, } @@ -46,13 +47,13 @@ pub struct ZiofaImpl { impl ZiofaImpl { pub fn new( ebpf: Arc>, - state: Arc>, + features: Arc>, channel: Arc, symbol_handler: Arc>, ) -> ZiofaImpl { ZiofaImpl { ebpf, - state, + features, channel, symbol_handler, } @@ -102,10 +103,10 @@ impl Ziofa for ZiofaImpl { configuration::save_to_file(&config, constants::DEV_DEFAULT_FILE_PATH)?; let mut ebpf_guard = self.ebpf.lock().await; - let mut state_guard = self.state.lock().await; + let mut features_guard = self.features.lock().await; // TODO: set config path - state_guard + features_guard .update_from_config(ebpf_guard.deref_mut(), &config) .map_err(EbpfErrorWrapper::from)?; @@ -214,15 +215,13 @@ pub async fn serve_forever() { let (shutdown_tx, shutdown_rx) = tokio::sync::oneshot::channel(); let event_tx = channel.tx.clone(); - let mut state = State::new(); - state.init(&mut ebpf).expect("should work"); + let features = Features::init_all_features(&mut ebpf); let symbol_handler = Arc::new(Mutex::new(SymbolHandler::new())); let ebpf = Arc::new(Mutex::new(ebpf)); - let state = Arc::new(Mutex::new(state)); - let ziofa_server = - ZiofaServer::new(ZiofaImpl::new(ebpf.clone(), state, channel, symbol_handler)); + let features = Arc::new(Mutex::new(features)); + let ziofa_server = ZiofaServer::new(ZiofaImpl::new(ebpf.clone(), features, channel, symbol_handler)); let counter_server = CounterServer::new(Counter::new(ebpf).await); let serve = async move { diff --git a/rust/shared/proto/config.proto b/rust/shared/proto/config.proto index 4ec43e10..20a232be 100644 --- a/rust/shared/proto/config.proto +++ b/rust/shared/proto/config.proto @@ -18,9 +18,9 @@ message UprobeConfig { } message Configuration { - VfsWriteConfig vfs_write = 1; - SysSendmsgConfig sys_sendmsg = 2; - JniReferencesConfig jniReferences = 3; + optional VfsWriteConfig vfs_write = 1; + optional SysSendmsgConfig sys_sendmsg = 2; + optional JniReferencesConfig jniReferences = 3; repeated UprobeConfig uprobes = 4; }