Projects STRLCPY ebpfguard Commits 5e05a60d
🤬
  • guardity: Use `thiserror` instead of `anyhow`

    The unwritten rule in Rust is to use `thiserror` for library code (where
    errors need to be distinguished) and `anyhow` in binary code (where we
    just want to handle any errors that libraries return). Let's stick to
    it.
  • Loading...
  • Michal Rostecki committed with vadorovsky 1 year ago
    5e05a60d
    1 parent 955652e2
Revision indexing in progress... (symbol navigation in revisions will be accurate after indexed)
  • ■ ■ ■ ■ ■
    guardity/Cargo.toml
    skipped 3 lines
    4 4  edition = "2021"
    5 5   
    6 6  [dependencies]
    7  -anyhow = { version = "1", features = ["backtrace"] }
    8 7  aya = { git = "https://github.com/aya-rs/aya", branch = "main", features=["async_tokio"] }
    9 8  bytes = "1.4"
    10 9  clap = { version = "4.2", features = ["derive"] }
    skipped 14 lines
  • ■ ■ ■ ■ ■
    guardity/src/error.rs
    skipped 1 lines
    2 2   
    3 3  #[derive(Debug, Error)]
    4 4  pub enum GuardityError {
    5  - #[error("Failed to find an inode for the given path")]
    6  - Inode(#[from] std::io::Error),
     5 + #[error("Failed to load BPF program: {0}")]
     6 + Bpf(#[from] aya::BpfError),
     7 + 
     8 + #[error("Failed to get BTF info from the system: {0}")]
     9 + Btf(#[from] aya::BtfError),
     10 + 
     11 + #[error("Failed to load BPF program: {0}")]
     12 + BpfProgramError(#[from] aya::programs::ProgramError),
     13 + 
     14 + #[error("I/O error: {0}")]
     15 + IO(#[from] std::io::Error),
     16 + 
     17 + #[error("Map error: {0}")]
     18 + Map(#[from] aya::maps::MapError),
     19 + 
     20 + #[error("Failed to open a perf buffer: {0}")]
     21 + PerfBuffer(#[from] aya::maps::perf::PerfBufferError),
     22 + 
     23 + #[error("Failed to parse policies from YAML: {0}")]
     24 + YAML(#[from] serde_yaml::Error),
    7 25  }
    8 26   
  • ■ ■ ■ ■ ■ ■
    guardity/src/hooks.rs
    skipped 21 lines
    22 22   task,
    23 23  };
    24 24   
    25  -use crate::{alerts, policy, InodeSubjectMap};
     25 +use crate::{alerts, error::GuardityError, policy, InodeSubjectMap};
    26 26   
    27 27  static INODE_SUBJECT_MAP: Lazy<Mutex<InodeSubjectMap>> =
    28 28   Lazy::new(|| Mutex::new(InodeSubjectMap::default()));
    skipped 7 lines
    36 36  }
    37 37   
    38 38  impl All {
    39  - pub async fn add_policy(&mut self, policy: policy::Policy) -> anyhow::Result<()> {
     39 + pub async fn add_policy(&mut self, policy: policy::Policy) -> Result<(), GuardityError> {
    40 40   match policy {
    41 41   policy::Policy::FileOpen(policy) => self.file_open.add_policy(policy).await?,
    42 42   policy::Policy::TaskFixSetuid(policy) => {
    skipped 14 lines
    57 57  }
    58 58   
    59 59  impl BprmCheckSecurity {
    60  - pub async fn alerts(&mut self) -> anyhow::Result<Receiver<alerts::BprmCheckSecurity>> {
     60 + pub async fn alerts(&mut self) -> Result<Receiver<alerts::BprmCheckSecurity>, GuardityError> {
    61 61   perf_array_alerts::<ebpf_alerts::BprmCheckSecurity, alerts::BprmCheckSecurity>(
    62 62   &mut self.perf_array,
    63 63   )
    skipped 10 lines
    74 74  }
    75 75   
    76 76  impl FileOpen {
    77  - pub async fn add_policy(&mut self, policy: policy::FileOpen) -> anyhow::Result<()> {
     77 + pub async fn add_policy(&mut self, policy: policy::FileOpen) -> Result<(), GuardityError> {
    78 78   let bin_inode = {
    79 79   let mut map = INODE_SUBJECT_MAP.lock().await;
    80 80   map.resolve_path(policy.subject)?
    skipped 8 lines
    89 89   Ok(())
    90 90   }
    91 91   
    92  - pub async fn list_policies(&self) -> anyhow::Result<Vec<policy::FileOpen>> {
     92 + pub async fn list_policies(&self) -> Result<Vec<policy::FileOpen>, GuardityError> {
    93 93   let mut policies = Vec::new();
    94 94   
    95 95   for res in self.allowed_map.iter() {
    skipped 15 lines
    111 111   Ok(policies)
    112 112   }
    113 113   
    114  - pub async fn alerts(&mut self) -> anyhow::Result<Receiver<alerts::FileOpen>> {
     114 + pub async fn alerts(&mut self) -> Result<Receiver<alerts::FileOpen>, GuardityError> {
    115 115   perf_array_alerts::<ebpf_alerts::FileOpen, alerts::FileOpen>(&mut self.perf_array).await
    116 116   }
    117 117  }
    skipped 7 lines
    125 125  }
    126 126   
    127 127  impl TaskFixSetuid {
    128  - pub async fn add_policy(&mut self, policy: policy::TaskFixSetuid) -> anyhow::Result<()> {
     128 + pub async fn add_policy(&mut self, policy: policy::TaskFixSetuid) -> Result<(), GuardityError> {
    129 129   let bin_inode = {
    130 130   let mut map = INODE_SUBJECT_MAP.lock().await;
    131 131   map.resolve_path(policy.subject)?
    skipped 8 lines
    140 140   Ok(())
    141 141   }
    142 142   
    143  - pub async fn list_policies(&self) -> anyhow::Result<Vec<policy::TaskFixSetuid>> {
     143 + pub async fn list_policies(&self) -> Result<Vec<policy::TaskFixSetuid>, GuardityError> {
    144 144   let mut policies = Vec::new();
    145 145   
    146 146   for res in self.allowed_map.iter() {
    skipped 27 lines
    174 174   Ok(policies)
    175 175   }
    176 176   
    177  - pub async fn alerts(&mut self) -> anyhow::Result<Receiver<alerts::TaskFixSetuid>> {
     177 + pub async fn alerts(&mut self) -> Result<Receiver<alerts::TaskFixSetuid>, GuardityError> {
    178 178   perf_array_alerts::<ebpf_alerts::TaskFixSetuid, alerts::TaskFixSetuid>(&mut self.perf_array)
    179 179   .await
    180 180   }
    skipped 8 lines
    189 189  }
    190 190   
    191 191  impl SocketBind {
    192  - pub async fn add_policy(&mut self, policy: policy::SocketBind) -> anyhow::Result<()> {
     192 + pub async fn add_policy(&mut self, policy: policy::SocketBind) -> Result<(), GuardityError> {
    193 193   let bin_inode = {
    194 194   let mut map = INODE_SUBJECT_MAP.lock().await;
    195 195   map.resolve_path(policy.subject)?
    skipped 8 lines
    204 204   Ok(())
    205 205   }
    206 206   
    207  - pub async fn list_policies(&self) -> anyhow::Result<Vec<policy::SocketBind>> {
     207 + pub async fn list_policies(&self) -> Result<Vec<policy::SocketBind>, GuardityError> {
    208 208   let mut policies = Vec::new();
    209 209   
    210 210   for res in self.allowed_map.iter() {
    skipped 15 lines
    226 226   Ok(policies)
    227 227   }
    228 228   
    229  - pub async fn alerts(&mut self) -> anyhow::Result<Receiver<alerts::SocketBind>> {
     229 + pub async fn alerts(&mut self) -> Result<Receiver<alerts::SocketBind>, GuardityError> {
    230 230   perf_array_alerts::<ebpf_alerts::SocketBind, alerts::SocketBind>(&mut self.perf_array).await
    231 231   }
    232 232  }
    skipped 9 lines
    242 242  }
    243 243   
    244 244  impl SocketConnect {
    245  - pub async fn add_policy(&mut self, policy: policy::SocketConnect) -> anyhow::Result<()> {
     245 + pub async fn add_policy(&mut self, policy: policy::SocketConnect) -> Result<(), GuardityError> {
    246 246   let bin_inode = {
    247 247   let mut map = INODE_SUBJECT_MAP.lock().await;
    248 248   map.resolve_path(policy.subject)?
    skipped 10 lines
    259 259   Ok(())
    260 260   }
    261 261   
    262  - pub async fn list_policies(&self) -> anyhow::Result<Vec<policy::SocketConnect>> {
     262 + pub async fn list_policies(&self) -> Result<Vec<policy::SocketConnect>, GuardityError> {
    263 263   let mut policies = Vec::new();
    264 264   
    265 265   for res in self.allowed_map_v4.iter() {
    skipped 7 lines
    273 273   map.resolve_inode(bin_inode)
    274 274   };
    275 275   
    276  - let allow = if allow_v4.all() {
    277  - if allow_v6.all() {
    278  - policy::Addresses::All
    279  - } else {
    280  - return Err(anyhow::anyhow!("Inconsistent policy state"));
    281  - }
     276 + let allow = if allow_v4.all() && allow_v6.all() {
     277 + policy::Addresses::All
    282 278   } else {
    283  - if allow_v4.all() {
    284  - return Err(anyhow::anyhow!("Inconsistent policy state"));
    285  - }
    286 279   let mut addrs = Vec::new();
    287 280   for addr in allow_v4.addrs.iter() {
     281 + if *addr == 0 {
     282 + break;
     283 + }
    288 284   addrs.push(IpAddr::V4(Ipv4Addr::from(addr.to_owned())));
    289 285   }
    290 286   for addr in allow_v6.addrs.iter() {
     287 + if *addr == [0u8; 16] {
     288 + break;
     289 + }
    291 290   addrs.push(IpAddr::V6(Ipv6Addr::from(addr.to_owned())));
    292 291   }
    293 292   policy::Addresses::Addresses(addrs)
    294 293   };
    295  - let deny = if deny_v4.all() {
    296  - if deny_v6.all() {
    297  - policy::Addresses::All
    298  - } else {
    299  - return Err(anyhow::anyhow!("Inconsistent policy state"));
    300  - }
     294 + let deny = if deny_v4.all() && deny_v6.all() {
     295 + policy::Addresses::All
    301 296   } else {
    302  - if deny_v4.all() {
    303  - return Err(anyhow::anyhow!("Inconsistent policy state"));
    304  - }
    305 297   let mut addrs = Vec::new();
    306 298   for addr in deny_v4.addrs.iter() {
     299 + if *addr == 0 {
     300 + break;
     301 + }
    307 302   addrs.push(IpAddr::V4(Ipv4Addr::from(addr.to_owned())));
    308 303   }
    309 304   for addr in deny_v6.addrs.iter() {
     305 + if *addr == [0u8; 16] {
     306 + break;
     307 + }
    310 308   addrs.push(IpAddr::V6(Ipv6Addr::from(addr.to_owned())));
    311 309   }
    312 310   policy::Addresses::Addresses(addrs)
    skipped 9 lines
    322 320   Ok(policies)
    323 321   }
    324 322   
    325  - pub async fn alerts(&mut self) -> anyhow::Result<Receiver<alerts::SocketConnect>> {
     323 + pub async fn alerts(&mut self) -> Result<Receiver<alerts::SocketConnect>, GuardityError> {
    326 324   perf_array_alerts::<ebpf_alerts::SocketConnect, alerts::SocketConnect>(&mut self.perf_array)
    327 325   .await
    328 326   }
    skipped 1 lines
    330 328   
    331 329  pub async fn perf_array_alerts<E, U>(
    332 330   perf_array: &mut AsyncPerfEventArray<MapData>,
    333  -) -> anyhow::Result<Receiver<U>>
     331 +) -> Result<Receiver<U>, GuardityError>
    334 332  where
    335 333   E: ebpf_alerts::Alert,
    336 334   U: alerts::Alert + Debug + Send + From<E> + 'static,
    skipped 29 lines
  • ■ ■ ■ ■ ■ ■
    guardity/src/lib.rs
    skipped 162 lines
    163 163  pub mod hooks;
    164 164  pub mod policy;
    165 165   
     166 +use error::GuardityError;
    166 167  use hooks::{All, BprmCheckSecurity, FileOpen, SocketBind, SocketConnect, TaskFixSetuid};
    167 168  use policy::inode::InodeSubjectMap;
    168 169   
    skipped 12 lines
    181 182   ///
    182 183   /// let mut policy_manager = PolicyManager::new(Path::new("/sys/fs/bpf/mypolicies")).unwrap();
    183 184   /// ```
    184  - pub fn new<P: AsRef<Path>>(bpf_path: P) -> anyhow::Result<Self> {
     185 + pub fn new<P: AsRef<Path>>(bpf_path: P) -> Result<Self, GuardityError> {
    185 186   #[cfg(debug_assertions)]
    186 187   let bpf = BpfLoader::new()
    187 188   .map_pin_path(&bpf_path)
    skipped 11 lines
    199 200   }
    200 201   
    201 202   /// Attaches and returns a handle to all LSM hooks.
    202  - pub fn attach_all(&mut self) -> anyhow::Result<All> {
     203 + pub fn attach_all(&mut self) -> Result<All, GuardityError> {
    203 204   let bprm_check_security = self.attach_bprm_check_security()?;
    204 205   let file_open = self.attach_file_open()?;
    205 206   let task_fix_setuid = self.attach_task_fix_setuid()?;
    skipped 9 lines
    215 216   })
    216 217   }
    217 218   
    218  - pub fn manage_all(&mut self) -> anyhow::Result<All> {
     219 + pub fn manage_all(&mut self) -> Result<All, GuardityError> {
    219 220   let bprm_check_security = self.manage_bprm_check_security()?;
    220 221   let file_open = self.manage_file_open()?;
    221 222   let task_fix_setuid = self.manage_task_fix_setuid()?;
    skipped 9 lines
    231 232   })
    232 233   }
    233 234   
    234  - pub fn attach_bprm_check_security(&mut self) -> anyhow::Result<BprmCheckSecurity> {
     235 + pub fn attach_bprm_check_security(&mut self) -> Result<BprmCheckSecurity, GuardityError> {
    235 236   let mut bprm_check_security = self.manage_bprm_check_security()?;
    236 237   let program_link = self.attach_program("bprm_check_security")?;
    237 238   bprm_check_security.program_link = Some(program_link);
    skipped 1 lines
    239 240   Ok(bprm_check_security)
    240 241   }
    241 242   
    242  - pub fn manage_bprm_check_security(&mut self) -> anyhow::Result<BprmCheckSecurity> {
     243 + pub fn manage_bprm_check_security(&mut self) -> Result<BprmCheckSecurity, GuardityError> {
    243 244   let perf_array = self
    244 245   .bpf
    245 246   .take_map("ALERT_BPRM_CHECK_SECURITY")
    skipped 6 lines
    252 253   })
    253 254   }
    254 255   
    255  - pub fn attach_file_open(&mut self) -> anyhow::Result<FileOpen> {
     256 + pub fn attach_file_open(&mut self) -> Result<FileOpen, GuardityError> {
    256 257   let mut file_open = self.manage_file_open()?;
    257 258   let program_link = self.attach_program("file_open")?;
    258 259   file_open.program_link = Some(program_link);
    skipped 1 lines
    260 261   Ok(file_open)
    261 262   }
    262 263   
    263  - pub fn manage_file_open(&mut self) -> anyhow::Result<FileOpen> {
     264 + pub fn manage_file_open(&mut self) -> Result<FileOpen, GuardityError> {
    264 265   let allowed_map = self.bpf.take_map("ALLOWED_FILE_OPEN").unwrap().try_into()?;
    265 266   let denied_map = self.bpf.take_map("DENIED_FILE_OPEN").unwrap().try_into()?;
    266 267   let perf_array = self.bpf.take_map("ALERT_FILE_OPEN").unwrap().try_into()?;
    skipped 6 lines
    273 274   })
    274 275   }
    275 276   
    276  - pub fn attach_task_fix_setuid(&mut self) -> anyhow::Result<TaskFixSetuid> {
     277 + pub fn attach_task_fix_setuid(&mut self) -> Result<TaskFixSetuid, GuardityError> {
    277 278   let mut task_fix_setuid = self.manage_task_fix_setuid()?;
    278 279   let program_link = self.attach_program("task_fix_setuid")?;
    279 280   task_fix_setuid.program_link = Some(program_link);
    skipped 1 lines
    281 282   Ok(task_fix_setuid)
    282 283   }
    283 284   
    284  - pub fn manage_task_fix_setuid(&mut self) -> anyhow::Result<TaskFixSetuid> {
     285 + pub fn manage_task_fix_setuid(&mut self) -> Result<TaskFixSetuid, GuardityError> {
    285 286   let allowed_map = self
    286 287   .bpf
    287 288   .take_map("ALLOWED_TASK_FIX_SETUID")
    skipped 18 lines
    306 307   })
    307 308   }
    308 309   
    309  - pub fn attach_socket_bind(&mut self) -> anyhow::Result<SocketBind> {
     310 + pub fn attach_socket_bind(&mut self) -> Result<SocketBind, GuardityError> {
    310 311   let mut socket_bind = self.manage_socket_bind()?;
    311 312   let program_link = self.attach_program("socket_bind")?;
    312 313   socket_bind.program_link = Some(program_link);
    skipped 1 lines
    314 315   Ok(socket_bind)
    315 316   }
    316 317   
    317  - pub fn manage_socket_bind(&mut self) -> anyhow::Result<SocketBind> {
     318 + pub fn manage_socket_bind(&mut self) -> Result<SocketBind, GuardityError> {
    318 319   let allowed_map = self
    319 320   .bpf
    320 321   .take_map("ALLOWED_SOCKET_BIND")
    skipped 14 lines
    335 336   })
    336 337   }
    337 338   
    338  - pub fn attach_socket_connect(&mut self) -> anyhow::Result<SocketConnect> {
     339 + pub fn attach_socket_connect(&mut self) -> Result<SocketConnect, GuardityError> {
    339 340   let mut socket_connect = self.manage_socket_connect()?;
    340 341   let program_link = self.attach_program("socket_connect")?;
    341 342   socket_connect.program_link = Some(program_link);
    skipped 1 lines
    343 344   Ok(socket_connect)
    344 345   }
    345 346   
    346  - pub fn manage_socket_connect(&mut self) -> anyhow::Result<SocketConnect> {
     347 + pub fn manage_socket_connect(&mut self) -> Result<SocketConnect, GuardityError> {
    347 348   let allowed_map_v4 = self
    348 349   .bpf
    349 350   .take_map("ALLOWED_SOCKET_CONNECT_V4")
    skipped 30 lines
    380 381   })
    381 382   }
    382 383   
    383  - fn attach_program(&mut self, name: &str) -> anyhow::Result<LsmLink> {
     384 + fn attach_program(&mut self, name: &str) -> Result<LsmLink, GuardityError> {
    384 385   let btf = Btf::from_sys_fs()?;
    385 386   let program: &mut Lsm = self.bpf.program_mut(name).unwrap().try_into()?;
    386 387   program.load(name, &btf)?;
    skipped 7 lines
  • ■ ■ ■ ■ ■ ■
    guardity/src/policy/inode.rs
    1 1  use std::{collections::HashMap, path::PathBuf};
    2 2   
    3  -use crate::fs;
     3 +use crate::{error::GuardityError, fs};
    4 4   
    5 5  use super::PolicySubject;
    6 6   
    skipped 3 lines
    10 10  }
    11 11   
    12 12  impl InodeSubjectMap {
    13  - pub fn resolve_path(&mut self, subject: PolicySubject) -> anyhow::Result<u64> {
     13 + pub fn resolve_path(&mut self, subject: PolicySubject) -> Result<u64, GuardityError> {
    14 14   match subject {
    15 15   PolicySubject::Binary(path) => {
    16 16   let inode = fs::inode(&path)?;
    skipped 19 lines
  • ■ ■ ■ ■ ■
    guardity/src/policy/reader.rs
    1 1  use std::{fs, path::Path};
    2 2   
     3 +use crate::error::GuardityError;
     4 + 
    3 5  use super::Policy;
    4 6   
    5  -pub fn read_policies<P: AsRef<Path>>(path: P) -> anyhow::Result<Vec<Policy>> {
     7 +pub fn read_policies<P: AsRef<Path>>(path: P) -> Result<Vec<Policy>, GuardityError> {
    6 8   let path = path.as_ref();
    7 9   let yaml = fs::read_to_string(path)?;
    8 10   let policies = serde_yaml::from_str::<Vec<Policy>>(&yaml)?;
    skipped 3 lines
Please wait...
Page is in error, reload to recover