netlink_packet_audit/rules/
buffer.rs

1// SPDX-License-Identifier: MIT
2
3use anyhow::Context;
4use byteorder::{ByteOrder, NativeEndian};
5use netlink_packet_utils::{traits::Parseable, DecodeError};
6
7use crate::{constants::*, rules::*, Field};
8
9// FIXME: when const fn are stable, use them, instead of defining a macro
10// const fn u32_array(start: usize, len: usize) -> Field {
11//     start..(start + 4 * len)
12// }
13macro_rules! u32_array {
14    ($start:expr, $len:expr) => {
15        $start..($start + 4 * $len)
16    };
17}
18
19const FLAGS: Field = 0..4;
20const ACTION: Field = 4..8;
21const FIELD_COUNT: Field = 8..12;
22const SYSCALLS: Field = u32_array!(FIELD_COUNT.end, AUDIT_BITMASK_SIZE);
23const FIELDS: Field = u32_array!(SYSCALLS.end, AUDIT_MAX_FIELDS);
24const VALUES: Field = u32_array!(FIELDS.end, AUDIT_MAX_FIELDS);
25const FIELD_FLAGS: Field = u32_array!(VALUES.end, AUDIT_MAX_FIELDS);
26const BUFLEN: Field = FIELD_FLAGS.end..FIELD_FLAGS.end + 4;
27
28pub(crate) const RULE_BUF_MIN_LEN: usize = BUFLEN.end;
29
30#[allow(non_snake_case)]
31fn BUF(len: usize) -> Field {
32    BUFLEN.end..(BUFLEN.end + len)
33}
34
35#[derive(Debug, PartialEq, Eq, Clone)]
36#[non_exhaustive]
37pub struct RuleBuffer<T> {
38    buffer: T,
39}
40
41impl<T: AsRef<[u8]>> RuleBuffer<T> {
42    pub fn new(buffer: T) -> RuleBuffer<T> {
43        RuleBuffer { buffer }
44    }
45
46    pub fn new_checked(buffer: T) -> Result<Self, DecodeError> {
47        let packet = Self::new(buffer);
48        packet.check_len()?;
49        Ok(packet)
50    }
51
52    pub(crate) fn check_len(&self) -> Result<(), DecodeError> {
53        let len = self.buffer.as_ref().len();
54        if len < BUFLEN.end {
55            Err(format!(
56                "buffer size is {}, whereas a rule buffer is at least {} long",
57                len, BUFLEN.end
58            )
59            .into())
60        } else if len < BUFLEN.end + self.buflen() as usize {
61            Err(format!(
62                "buffer length is {}, but it should be {} (header) + {} \
63                (length field)",
64                len,
65                BUFLEN.end,
66                self.buflen()
67            )
68            .into())
69        } else {
70            Ok(())
71        }
72    }
73
74    pub fn flags(&self) -> u32 {
75        NativeEndian::read_u32(&self.buffer.as_ref()[FLAGS])
76    }
77
78    pub fn action(&self) -> u32 {
79        NativeEndian::read_u32(&self.buffer.as_ref()[ACTION])
80    }
81
82    pub fn field_count(&self) -> u32 {
83        NativeEndian::read_u32(&self.buffer.as_ref()[FIELD_COUNT])
84    }
85
86    pub fn buflen(&self) -> u32 {
87        NativeEndian::read_u32(&self.buffer.as_ref()[BUFLEN])
88    }
89}
90
91impl<'a, T: AsRef<[u8]> + ?Sized> RuleBuffer<&'a T> {
92    pub fn syscalls(&self) -> &'a [u8] {
93        &self.buffer.as_ref()[SYSCALLS]
94    }
95
96    pub fn fields(&self) -> &'a [u8] {
97        &self.buffer.as_ref()[FIELDS]
98    }
99
100    pub fn values(&self) -> &'a [u8] {
101        &self.buffer.as_ref()[VALUES]
102    }
103
104    pub fn field_flags(&self) -> &'a [u8] {
105        &self.buffer.as_ref()[FIELD_FLAGS]
106    }
107
108    pub fn buf(&self) -> &'a [u8] {
109        let field = BUF(self.buflen() as usize);
110        &self.buffer.as_ref()[field.start..field.end]
111    }
112}
113
114impl<T: AsRef<[u8]> + AsMut<[u8]>> RuleBuffer<T> {
115    pub fn set_flags(&mut self, value: u32) {
116        NativeEndian::write_u32(&mut self.buffer.as_mut()[FLAGS], value)
117    }
118
119    pub fn set_action(&mut self, value: u32) {
120        NativeEndian::write_u32(&mut self.buffer.as_mut()[ACTION], value)
121    }
122
123    pub fn set_field_count(&mut self, value: u32) {
124        NativeEndian::write_u32(&mut self.buffer.as_mut()[FIELD_COUNT], value)
125    }
126
127    pub fn set_buflen(&mut self, value: u32) {
128        NativeEndian::write_u32(&mut self.buffer.as_mut()[BUFLEN], value)
129    }
130
131    pub fn syscalls_mut(&mut self) -> &mut [u8] {
132        &mut self.buffer.as_mut()[SYSCALLS]
133    }
134
135    pub fn fields_mut(&mut self) -> &mut [u8] {
136        &mut self.buffer.as_mut()[FIELDS]
137    }
138
139    pub fn set_field(&mut self, position: usize, value: u32) {
140        let offset = FIELDS.start + (position * 4);
141        assert!(position <= FIELDS.end - 4);
142        NativeEndian::write_u32(
143            &mut self.buffer.as_mut()[offset..offset + 4],
144            value,
145        )
146    }
147
148    pub fn values_mut(&mut self) -> &mut [u8] {
149        &mut self.buffer.as_mut()[VALUES]
150    }
151
152    pub fn set_value(&mut self, position: usize, value: u32) {
153        let offset = VALUES.start + (position * 4);
154        assert!(position <= VALUES.end - 4);
155        NativeEndian::write_u32(
156            &mut self.buffer.as_mut()[offset..offset + 4],
157            value,
158        )
159    }
160
161    pub fn field_flags_mut(&mut self) -> &mut [u8] {
162        &mut self.buffer.as_mut()[FIELD_FLAGS]
163    }
164
165    pub fn set_field_flags(&mut self, position: usize, value: u32) {
166        let offset = FIELD_FLAGS.start + (position * 4);
167        assert!(position <= FIELD_FLAGS.end - 4);
168        NativeEndian::write_u32(
169            &mut self.buffer.as_mut()[offset..offset + 4],
170            value,
171        )
172    }
173
174    pub fn buf_mut(&mut self) -> &mut [u8] {
175        let field = BUF(self.buflen() as usize);
176        &mut self.buffer.as_mut()[field.start..field.end]
177    }
178}
179
180impl<'a, T: AsRef<[u8]> + ?Sized> Parseable<RuleBuffer<&'a T>> for RuleMessage {
181    fn parse(buf: &RuleBuffer<&'a T>) -> Result<Self, DecodeError> {
182        use self::RuleField::*;
183
184        buf.check_len().context("invalid rule message buffer")?;
185        let mut rule = RuleMessage::new();
186        rule.flags = buf.flags().into();
187        rule.action = buf.action().into();
188        rule.syscalls = RuleSyscalls::from_slice(buf.syscalls())?;
189
190        let mut offset = 0;
191
192        let fields = buf.fields().chunks(4).map(NativeEndian::read_u32);
193        let values = buf.values().chunks(4).map(NativeEndian::read_u32);
194        let field_flags = buf
195            .field_flags()
196            .chunks(4)
197            .map(|chunk| RuleFieldFlags::from(NativeEndian::read_u32(chunk)));
198        for (field, value, flags) in fields
199            .zip(values.zip(field_flags))
200            .map(|(field, (value, flags))| (field, value, flags))
201            .take(buf.field_count() as usize)
202        {
203            let field = match field {
204                AUDIT_PID => Pid(value),
205                AUDIT_UID => Uid(value),
206                AUDIT_EUID => Euid(value),
207                AUDIT_SUID => Suid(value),
208                AUDIT_FSUID => Fsuid(value),
209                AUDIT_GID => Gid(value),
210                AUDIT_EGID => Egid(value),
211                AUDIT_SGID => Sgid(value),
212                AUDIT_FSGID => Fsgid(value),
213                AUDIT_LOGINUID => Loginuid(value),
214                AUDIT_PERS => Pers(value),
215                AUDIT_ARCH => Arch(value),
216                AUDIT_MSGTYPE => Msgtype(value),
217                AUDIT_PPID => Ppid(value),
218                AUDIT_LOGINUID_SET => LoginuidSet(value),
219                AUDIT_SESSIONID => Sessionid(value),
220                AUDIT_FSTYPE => Fstype(value),
221                AUDIT_DEVMAJOR => Devmajor(value),
222                AUDIT_DEVMINOR => Devminor(value),
223                AUDIT_INODE => Inode(value),
224                AUDIT_EXIT => Exit(value),
225                AUDIT_SUCCESS => Success(value),
226                AUDIT_PERM => Perm(value),
227                AUDIT_FILETYPE => Filetype(value),
228                AUDIT_OBJ_UID => ObjUid(value),
229                AUDIT_OBJ_GID => ObjGid(value),
230                AUDIT_FIELD_COMPARE => FieldCompare(value),
231                AUDIT_EXE => Exe(value),
232                AUDIT_ARG0 => Arg0(value),
233                AUDIT_ARG1 => Arg1(value),
234                AUDIT_ARG2 => Arg2(value),
235                AUDIT_ARG3 => Arg3(value),
236                _ => {
237                    // For all the other fields, the value is a string
238                    let str_end = offset + value as usize;
239                    if str_end > buf.buf().len() {
240                        return Err(format!(
241                            "failed to decode field. type={field} \
242                            (value should be a string?)"
243                        )
244                        .into());
245                    }
246                    let s: String =
247                        String::from_utf8_lossy(&buf.buf()[offset..str_end])
248                            .into();
249                    offset = str_end;
250                    match field {
251                        AUDIT_WATCH => Watch(s),
252                        AUDIT_DIR => Dir(s),
253                        AUDIT_FILTERKEY => Filterkey(s),
254                        AUDIT_SUBJ_USER => SubjUser(s),
255                        AUDIT_SUBJ_ROLE => SubjRole(s),
256                        AUDIT_SUBJ_TYPE => SubjType(s),
257                        AUDIT_SUBJ_SEN => SubjSen(s),
258                        AUDIT_SUBJ_CLR => SubjClr(s),
259                        AUDIT_OBJ_USER => ObjUser(s),
260                        AUDIT_OBJ_ROLE => ObjRole(s),
261                        AUDIT_OBJ_TYPE => ObjType(s),
262                        AUDIT_OBJ_LEV_LOW => ObjLevLow(s),
263                        AUDIT_OBJ_LEV_HIGH => ObjLevHigh(s),
264                        _ => {
265                            return Err(format!(
266                                "failed to decode field (unknown type) \
267                                type={field}, value={s}"
268                            )
269                            .into());
270                        }
271                    }
272                }
273            };
274            rule.fields.push((field, flags));
275        }
276        Ok(rule)
277    }
278}