crypto_auditing/
types.rs

1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright (C) 2022-2023 The crypto-auditing developers.
3
4use serde::{Deserialize, Serialize};
5use serde_with::serde_as;
6use std::ffi::CStr;
7use std::time::Duration;
8
9include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
10
11pub type ContextID = [u8; 16];
12
13#[serde_as]
14#[derive(Serialize, Deserialize, Clone, Debug)]
15#[serde(untagged)]
16pub enum EventData {
17    Word(i64),
18    String(String),
19    Blob(
20        #[serde_as(as = "serde_with::Bytes")] Vec<u8>, // TODO: try ArrayVec?
21    ),
22}
23
24#[serde_as]
25#[derive(Serialize, Deserialize, Clone, Debug)]
26pub enum Event {
27    NewContext {
28        #[serde_as(as = "serde_with::Bytes")]
29        parent: ContextID,
30        #[serde_as(as = "serde_with::Bytes")]
31        origin: Vec<u8>,
32    },
33    Data {
34        key: String,
35        value: EventData,
36    },
37}
38
39#[serde_as]
40#[derive(Serialize, Deserialize, Clone, Debug)]
41pub struct EventGroup {
42    #[serde_as(as = "serde_with::Bytes")]
43    context: ContextID,
44    #[serde_as(as = "serde_with::DurationNanoSeconds<u64>")]
45    start: Duration,
46    #[serde_as(as = "serde_with::DurationNanoSeconds<u64>")]
47    end: Duration,
48    events: Vec<Event>,
49}
50
51fn format_context(pid_tgid: u64, context: i64) -> ContextID {
52    let mut result: ContextID = Default::default();
53    result[..8].copy_from_slice(&u64::to_le_bytes(pid_tgid));
54    result[8..].copy_from_slice(&i64::to_le_bytes(context));
55    result
56}
57
58impl EventGroup {
59    /// Returns the context ID associated with the event group
60    pub fn context(&self) -> &ContextID {
61        &self.context
62    }
63
64    /// Returns the start time of the event group
65    pub fn start(&self) -> Duration {
66        self.start
67    }
68
69    /// Returns the end time of the event group
70    pub fn end(&self) -> Duration {
71        self.end
72    }
73
74    /// Returns the events contained in the event group
75    pub fn events(&self) -> &Vec<Event> {
76        &self.events
77    }
78
79    /// Returns true if this event group is associated with the given process ID
80    pub fn matches_pid(&self, pid: libc::pid_t) -> bool {
81        (u64::from_le_bytes(self.context()[..8].try_into().unwrap()) & 0xffffffff)
82            == <i32 as TryInto<u64>>::try_into(pid).unwrap()
83    }
84
85    /// Returns encrypted context ID associated with the event group
86    pub fn encrypt_context<F>(&mut self, f: F) -> Result<(), Box<dyn std::error::Error>>
87    where
88        F: Fn(&mut ContextID) -> Result<(), Box<dyn std::error::Error>>,
89    {
90        f(&mut self.context)?;
91
92        if let Some(Event::NewContext { ref mut parent, .. }) = self.events.last_mut() {
93            f(parent)?;
94        }
95        Ok(())
96    }
97
98    /// Merges this event group with another which shares the same context ID
99    pub fn coalesce(&mut self, other: &mut Self) {
100        self.end = other.end;
101        self.events.append(&mut other.events);
102    }
103
104    /// Removes events which do not match the given scopes
105    pub fn events_filtered(&mut self, scopes: &[String]) {
106        self.events.retain(|event| match event {
107            Event::NewContext { .. } => true,
108            Event::Data { key, .. } => scopes
109                .iter()
110                .any(|scope| !key.contains("::") || key.starts_with(&format!("{}::", scope))),
111        });
112    }
113
114    /// Deserializes an event group from bytes
115    pub fn from_bytes(bytes: &[u8]) -> Result<Self, Box<dyn std::error::Error>> {
116        let header = bytes.as_ptr() as *mut audit_event_header_st;
117        let context =
118            unsafe { format_context((*header).pid_tgid.into(), (*header).context.into()) };
119        let ktime = unsafe { Duration::from_nanos((*header).ktime.into()) };
120        let event = match unsafe { (*header).type_ } {
121            audit_event_type_t::AUDIT_EVENT_NEW_CONTEXT => {
122                let raw_new_context = bytes.as_ptr() as *mut audit_new_context_event_st;
123                let parent = unsafe {
124                    format_context((*header).pid_tgid.into(), (*raw_new_context).parent.into())
125                };
126                let origin = unsafe {
127                    (*raw_new_context).origin[..(*raw_new_context).origin_size as usize].to_vec()
128                };
129                EventGroup {
130                    context,
131                    start: ktime,
132                    end: ktime,
133                    events: vec![Event::NewContext { parent, origin }],
134                }
135            }
136            audit_event_type_t::AUDIT_EVENT_DATA => unsafe {
137                let data = bytes.as_ptr() as *mut audit_data_event_st;
138                match (*data).type_ {
139                    audit_data_type_t::AUDIT_DATA_WORD => {
140                        let raw_word_data = bytes.as_ptr() as *mut audit_word_data_event_st;
141                        let key = CStr::from_ptr((*raw_word_data).base.key.as_ptr());
142                        EventGroup {
143                            context,
144                            start: ktime,
145                            end: ktime,
146                            events: vec![Event::Data {
147                                key: key.to_str()?.to_string(),
148                                value: EventData::Word((*raw_word_data).value.into()),
149                            }],
150                        }
151                    }
152                    audit_data_type_t::AUDIT_DATA_STRING => {
153                        let raw_string_data = bytes.as_ptr() as *mut audit_blob_data_event_st;
154                        let key = CStr::from_ptr((*raw_string_data).base.key.as_ptr());
155                        let len = (*raw_string_data).size as usize;
156                        let string =
157                            std::str::from_utf8(&(*raw_string_data).value[..len - 1])?.to_string();
158                        EventGroup {
159                            context,
160                            start: ktime,
161                            end: ktime,
162                            events: vec![Event::Data {
163                                key: key.to_str()?.to_string(),
164                                value: EventData::String(string),
165                            }],
166                        }
167                    }
168                    audit_data_type_t::AUDIT_DATA_BLOB => {
169                        let raw_blob_data = bytes.as_ptr() as *mut audit_blob_data_event_st;
170                        let key = CStr::from_ptr((*raw_blob_data).base.key.as_ptr());
171                        let len = (*raw_blob_data).size as usize;
172                        let data = (*raw_blob_data).value[..len].to_vec();
173                        EventGroup {
174                            context,
175                            start: ktime,
176                            end: ktime,
177                            events: vec![Event::Data {
178                                key: key.to_str()?.to_string(),
179                                value: EventData::Blob(data),
180                            }],
181                        }
182                    }
183                    _ => unreachable!(),
184                }
185            },
186            _ => unreachable!(),
187        };
188        Ok(event)
189    }
190}