// SPDX-License-Identifier: MIT use anyhow::Context; use byteorder::{ByteOrder, NativeEndian}; use netlink_packet_utils::{traits::Parseable, DecodeError}; use crate::{constants::*, rules::*, Field}; // FIXME: when const fn are stable, use them, instead of defining a macro // const fn u32_array(start: usize, len: usize) -> Field { // start..(start + 4 * len) // } macro_rules! u32_array { ($start:expr, $len:expr) => { $start..($start + 4 * $len) }; } const FLAGS: Field = 0..4; const ACTION: Field = 4..8; const FIELD_COUNT: Field = 8..12; const SYSCALLS: Field = u32_array!(FIELD_COUNT.end, AUDIT_BITMASK_SIZE); const FIELDS: Field = u32_array!(SYSCALLS.end, AUDIT_MAX_FIELDS); const VALUES: Field = u32_array!(FIELDS.end, AUDIT_MAX_FIELDS); const FIELD_FLAGS: Field = u32_array!(VALUES.end, AUDIT_MAX_FIELDS); const BUFLEN: Field = FIELD_FLAGS.end..FIELD_FLAGS.end + 4; pub(crate) const RULE_BUF_MIN_LEN: usize = BUFLEN.end; #[allow(non_snake_case)] fn BUF(len: usize) -> Field { BUFLEN.end..(BUFLEN.end + len) } #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub struct RuleBuffer { buffer: T, } impl> RuleBuffer { pub fn new(buffer: T) -> RuleBuffer { RuleBuffer { buffer } } pub fn new_checked(buffer: T) -> Result { let packet = Self::new(buffer); packet.check_len()?; Ok(packet) } pub(crate) fn check_len(&self) -> Result<(), DecodeError> { let len = self.buffer.as_ref().len(); if len < BUFLEN.end { Err(format!( "buffer size is {}, whereas a rule buffer is at least {} long", len, BUFLEN.end ) .into()) } else if len < BUFLEN.end + self.buflen() as usize { Err(format!( "buffer length is {}, but it should be {} (header) + {} \ (length field)", len, BUFLEN.end, self.buflen() ) .into()) } else { Ok(()) } } pub fn flags(&self) -> u32 { NativeEndian::read_u32(&self.buffer.as_ref()[FLAGS]) } pub fn action(&self) -> u32 { NativeEndian::read_u32(&self.buffer.as_ref()[ACTION]) } pub fn field_count(&self) -> u32 { NativeEndian::read_u32(&self.buffer.as_ref()[FIELD_COUNT]) } pub fn buflen(&self) -> u32 { NativeEndian::read_u32(&self.buffer.as_ref()[BUFLEN]) } } impl<'a, T: AsRef<[u8]> + ?Sized> RuleBuffer<&'a T> { pub fn syscalls(&self) -> &'a [u8] { &self.buffer.as_ref()[SYSCALLS] } pub fn fields(&self) -> &'a [u8] { &self.buffer.as_ref()[FIELDS] } pub fn values(&self) -> &'a [u8] { &self.buffer.as_ref()[VALUES] } pub fn field_flags(&self) -> &'a [u8] { &self.buffer.as_ref()[FIELD_FLAGS] } pub fn buf(&self) -> &'a [u8] { let field = BUF(self.buflen() as usize); &self.buffer.as_ref()[field.start..field.end] } } impl + AsMut<[u8]>> RuleBuffer { pub fn set_flags(&mut self, value: u32) { NativeEndian::write_u32(&mut self.buffer.as_mut()[FLAGS], value) } pub fn set_action(&mut self, value: u32) { NativeEndian::write_u32(&mut self.buffer.as_mut()[ACTION], value) } pub fn set_field_count(&mut self, value: u32) { NativeEndian::write_u32(&mut self.buffer.as_mut()[FIELD_COUNT], value) } pub fn set_buflen(&mut self, value: u32) { NativeEndian::write_u32(&mut self.buffer.as_mut()[BUFLEN], value) } pub fn syscalls_mut(&mut self) -> &mut [u8] { &mut self.buffer.as_mut()[SYSCALLS] } pub fn fields_mut(&mut self) -> &mut [u8] { &mut self.buffer.as_mut()[FIELDS] } pub fn set_field(&mut self, position: usize, value: u32) { let offset = FIELDS.start + (position * 4); assert!(position <= FIELDS.end - 4); NativeEndian::write_u32( &mut self.buffer.as_mut()[offset..offset + 4], value, ) } pub fn values_mut(&mut self) -> &mut [u8] { &mut self.buffer.as_mut()[VALUES] } pub fn set_value(&mut self, position: usize, value: u32) { let offset = VALUES.start + (position * 4); assert!(position <= VALUES.end - 4); NativeEndian::write_u32( &mut self.buffer.as_mut()[offset..offset + 4], value, ) } pub fn field_flags_mut(&mut self) -> &mut [u8] { &mut self.buffer.as_mut()[FIELD_FLAGS] } pub fn set_field_flags(&mut self, position: usize, value: u32) { let offset = FIELD_FLAGS.start + (position * 4); assert!(position <= FIELD_FLAGS.end - 4); NativeEndian::write_u32( &mut self.buffer.as_mut()[offset..offset + 4], value, ) } pub fn buf_mut(&mut self) -> &mut [u8] { let field = BUF(self.buflen() as usize); &mut self.buffer.as_mut()[field.start..field.end] } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for RuleMessage { fn parse(buf: &RuleBuffer<&'a T>) -> Result { use self::RuleField::*; buf.check_len().context("invalid rule message buffer")?; let mut rule = RuleMessage::new(); rule.flags = buf.flags().into(); rule.action = buf.action().into(); rule.syscalls = RuleSyscalls::from_slice(buf.syscalls())?; let mut offset = 0; let fields = buf.fields().chunks(4).map(NativeEndian::read_u32); let values = buf.values().chunks(4).map(NativeEndian::read_u32); let field_flags = buf .field_flags() .chunks(4) .map(|chunk| RuleFieldFlags::from(NativeEndian::read_u32(chunk))); for (field, value, flags) in fields .zip(values.zip(field_flags)) .map(|(field, (value, flags))| (field, value, flags)) .take(buf.field_count() as usize) { let field = match field { AUDIT_PID => Pid(value), AUDIT_UID => Uid(value), AUDIT_EUID => Euid(value), AUDIT_SUID => Suid(value), AUDIT_FSUID => Fsuid(value), AUDIT_GID => Gid(value), AUDIT_EGID => Egid(value), AUDIT_SGID => Sgid(value), AUDIT_FSGID => Fsgid(value), AUDIT_LOGINUID => Loginuid(value), AUDIT_PERS => Pers(value), AUDIT_ARCH => Arch(value), AUDIT_MSGTYPE => Msgtype(value), AUDIT_PPID => Ppid(value), AUDIT_LOGINUID_SET => LoginuidSet(value), AUDIT_SESSIONID => Sessionid(value), AUDIT_FSTYPE => Fstype(value), AUDIT_DEVMAJOR => Devmajor(value), AUDIT_DEVMINOR => Devminor(value), AUDIT_INODE => Inode(value), AUDIT_EXIT => Exit(value), AUDIT_SUCCESS => Success(value), AUDIT_PERM => Perm(value), AUDIT_FILETYPE => Filetype(value), AUDIT_OBJ_UID => ObjUid(value), AUDIT_OBJ_GID => ObjGid(value), AUDIT_FIELD_COMPARE => FieldCompare(value), AUDIT_EXE => Exe(value), AUDIT_ARG0 => Arg0(value), AUDIT_ARG1 => Arg1(value), AUDIT_ARG2 => Arg2(value), AUDIT_ARG3 => Arg3(value), _ => { // For all the other fields, the value is a string let str_end = offset + value as usize; if str_end > buf.buf().len() { return Err(format!( "failed to decode field. type={field} \ (value should be a string?)" ) .into()); } let s: String = String::from_utf8_lossy(&buf.buf()[offset..str_end]) .into(); offset = str_end; match field { AUDIT_WATCH => Watch(s), AUDIT_DIR => Dir(s), AUDIT_FILTERKEY => Filterkey(s), AUDIT_SUBJ_USER => SubjUser(s), AUDIT_SUBJ_ROLE => SubjRole(s), AUDIT_SUBJ_TYPE => SubjType(s), AUDIT_SUBJ_SEN => SubjSen(s), AUDIT_SUBJ_CLR => SubjClr(s), AUDIT_OBJ_USER => ObjUser(s), AUDIT_OBJ_ROLE => ObjRole(s), AUDIT_OBJ_TYPE => ObjType(s), AUDIT_OBJ_LEV_LOW => ObjLevLow(s), AUDIT_OBJ_LEV_HIGH => ObjLevHigh(s), _ => { return Err(format!( "failed to decode field (unknown type) \ type={field}, value={s}" ) .into()); } } } }; rule.fields.push((field, flags)); } Ok(rule) } }