Skip to main content

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::{
5    Deserialize, Serialize,
6    ser::{SerializeSeq, Serializer},
7};
8use serde_with::{hex::Hex, serde_as};
9use std::cell::RefCell;
10use std::collections::BTreeMap;
11use std::ffi::CStr;
12use std::rc::Rc;
13use std::time::{Duration, SystemTime, UNIX_EPOCH};
14use sysinfo::System;
15
16include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
17
18pub type ContextId = [u8; 16];
19
20fn only_values<K, V, S>(source: &BTreeMap<K, V>, serializer: S) -> Result<S::Ok, S::Error>
21where
22    S: Serializer,
23    V: Serialize,
24{
25    let mut seq = serializer.serialize_seq(Some(source.len()))?;
26    for value in source.values() {
27        seq.serialize_element(value)?;
28    }
29    seq.end()
30}
31
32#[serde_as]
33#[derive(Debug, Serialize)]
34pub struct Context {
35    #[serde_as(as = "Hex")]
36    #[serde(rename = "context")]
37    pub id: ContextId,
38    #[serde_as(as = "Hex")]
39    pub origin: Vec<u8>,
40    #[serde_as(as = "serde_with::TimestampSecondsWithFrac<f64>")]
41    pub start: SystemTime,
42    #[serde_as(as = "serde_with::TimestampSecondsWithFrac<f64>")]
43    pub end: SystemTime,
44    pub events: BTreeMap<String, EventData>,
45    #[serde(skip_serializing_if = "BTreeMap::is_empty")]
46    #[serde(serialize_with = "only_values")]
47    pub spans: BTreeMap<ContextId, Rc<RefCell<Context>>>,
48}
49
50#[derive(Debug)]
51pub struct ContextTracker {
52    all_contexts: BTreeMap<ContextId, Rc<RefCell<Context>>>,
53    root_contexts: Vec<Rc<RefCell<Context>>>,
54    boot_time: SystemTime,
55}
56
57impl ContextTracker {
58    pub fn new(boot_time: Option<SystemTime>) -> Self {
59        Self {
60            all_contexts: BTreeMap::new(),
61            root_contexts: Vec::new(),
62            boot_time: boot_time.unwrap_or_else(|| {
63                UNIX_EPOCH
64                    .checked_add(Duration::from_secs(System::boot_time()))
65                    .unwrap()
66            }),
67        }
68    }
69
70    pub fn flush(&mut self, before: Option<SystemTime>) -> impl IntoIterator<Item = Context> {
71        let mut removed = Vec::new();
72        self.root_contexts.retain(|context| {
73            if let Some(before) = before
74                && context.borrow().start > before
75            {
76                true
77            } else {
78                self.all_contexts.remove(&context.borrow().id[..]);
79                removed.push(context.clone());
80                false
81            }
82        });
83        removed
84            .into_iter()
85            .map(|context| Rc::into_inner(context).unwrap().into_inner())
86    }
87
88    pub fn handle_event_group(&mut self, group: &EventGroup) -> usize {
89        let start = self
90            .boot_time
91            .checked_add(group.start())
92            .unwrap_or(UNIX_EPOCH);
93        let end = self
94            .boot_time
95            .checked_add(group.end())
96            .unwrap_or(UNIX_EPOCH);
97        let mut count = 0;
98        for event in group.events() {
99            match event {
100                Event::NewContext {
101                    parent: parent_context,
102                    origin,
103                } => {
104                    let context = Rc::new(RefCell::new(Context {
105                        id: *group.context(),
106                        origin: origin.to_owned(),
107                        start,
108                        end,
109                        events: Default::default(),
110                        spans: Default::default(),
111                    }));
112                    if let Some(parent) = self.all_contexts.get(&parent_context[..]) {
113                        parent
114                            .borrow_mut()
115                            .spans
116                            .insert(*group.context(), context.clone());
117                    } else {
118                        self.root_contexts.push(context.clone());
119                        count += 1;
120                    }
121                    self.all_contexts.insert(*group.context(), context);
122                }
123                Event::Data { key, value } => {
124                    if !self.all_contexts.contains_key(group.context()) {
125                        // Either this library did not do a new_context for this context, or the
126                        // log we have is truncated at the beginning. Just assume that this context
127                        // has no parent and create a new one so we don't loose the information in
128                        // this message.
129                        let context_obj = Rc::new(RefCell::new(Context {
130                            id: *group.context(),
131                            origin: Default::default(),
132                            start,
133                            end,
134                            events: Default::default(),
135                            spans: Default::default(),
136                        }));
137                        self.root_contexts.push(context_obj.clone());
138                        self.all_contexts.insert(*group.context(), context_obj);
139                        count += 1;
140                    }
141                    if let Some(parent) = self.all_contexts.get(group.context()) {
142                        parent
143                            .borrow_mut()
144                            .events
145                            .insert(key.to_string(), value.clone());
146                    }
147                }
148            }
149        }
150        count
151    }
152}
153
154#[serde_as]
155#[derive(Serialize, Deserialize, Clone, Debug)]
156#[serde(untagged)]
157pub enum EventData {
158    Word(i64),
159    String(String),
160    Blob(
161        #[serde_as(as = "serde_with::Bytes")] Vec<u8>, // TODO: try ArrayVec?
162    ),
163}
164
165#[serde_as]
166#[derive(Serialize, Deserialize, Clone, Debug)]
167pub enum Event {
168    NewContext {
169        #[serde_as(as = "serde_with::Bytes")]
170        parent: ContextId,
171        #[serde_as(as = "serde_with::Bytes")]
172        origin: Vec<u8>,
173    },
174    Data {
175        key: String,
176        value: EventData,
177    },
178}
179
180#[serde_as]
181#[derive(Serialize, Deserialize, Clone, Debug)]
182pub struct EventGroup {
183    #[serde_as(as = "serde_with::Bytes")]
184    context: ContextId,
185    #[serde_as(as = "serde_with::DurationNanoSeconds<u64>")]
186    start: Duration,
187    #[serde_as(as = "serde_with::DurationNanoSeconds<u64>")]
188    end: Duration,
189    events: Vec<Event>,
190}
191
192fn format_context_id(pid_tgid: u64, context: i64) -> ContextId {
193    let mut result: ContextId = Default::default();
194    result[..8].copy_from_slice(&u64::to_le_bytes(pid_tgid));
195    result[8..].copy_from_slice(&i64::to_le_bytes(context));
196    result
197}
198
199impl EventGroup {
200    /// Returns the context ID associated with the event group
201    pub fn context(&self) -> &ContextId {
202        &self.context
203    }
204
205    /// Returns the start time of the event group
206    pub fn start(&self) -> Duration {
207        self.start
208    }
209
210    /// Returns the end time of the event group
211    pub fn end(&self) -> Duration {
212        self.end
213    }
214
215    /// Returns the events contained in the event group
216    pub fn events(&self) -> &Vec<Event> {
217        &self.events
218    }
219
220    /// Returns true if this event group is associated with the given process ID
221    pub fn matches_pid(&self, pid: libc::pid_t) -> bool {
222        (u64::from_le_bytes(self.context()[..8].try_into().unwrap()) & 0xffffffff)
223            == <i32 as TryInto<u64>>::try_into(pid).unwrap()
224    }
225
226    /// Returns encrypted context ID associated with the event group
227    pub fn encrypt_context<F>(&mut self, f: F) -> Result<(), Box<dyn std::error::Error>>
228    where
229        F: Fn(&mut ContextId) -> Result<(), Box<dyn std::error::Error>>,
230    {
231        f(&mut self.context)?;
232
233        if let Some(Event::NewContext { parent, .. }) = self.events.last_mut() {
234            f(parent)?;
235        }
236        Ok(())
237    }
238
239    /// Merges this event group with another which shares the same context ID
240    pub fn coalesce(&mut self, other: &mut Self) {
241        self.end = other.end;
242        self.events.append(&mut other.events);
243    }
244
245    /// Removes events which do not match the given scopes
246    pub fn events_filtered(&mut self, scopes: &[String]) {
247        self.events.retain(|event| match event {
248            Event::NewContext { .. } => true,
249            Event::Data { key, .. } => scopes
250                .iter()
251                .any(|scope| !key.contains("::") || key.starts_with(&format!("{}::", scope))),
252        });
253    }
254
255    /// Deserializes an event group from bytes
256    pub fn from_bytes(bytes: &[u8]) -> Result<Self, Box<dyn std::error::Error>> {
257        let header = bytes.as_ptr() as *mut audit_event_header_st;
258        let context =
259            unsafe { format_context_id((*header).pid_tgid.into(), (*header).context.into()) };
260        let ktime = unsafe { Duration::from_nanos((*header).ktime.into()) };
261        let event = match unsafe { (*header).type_ } {
262            audit_event_type_t::AUDIT_EVENT_NEW_CONTEXT => {
263                let raw_new_context = bytes.as_ptr() as *mut audit_new_context_event_st;
264                let parent = unsafe {
265                    format_context_id((*header).pid_tgid.into(), (*raw_new_context).parent.into())
266                };
267                let origin = unsafe {
268                    (&(*raw_new_context).origin)[..(*raw_new_context).origin_size as usize].to_vec()
269                };
270                EventGroup {
271                    context,
272                    start: ktime,
273                    end: ktime,
274                    events: vec![Event::NewContext { parent, origin }],
275                }
276            }
277            audit_event_type_t::AUDIT_EVENT_DATA => unsafe {
278                let data = bytes.as_ptr() as *mut audit_data_event_st;
279                match (*data).type_ {
280                    audit_data_type_t::AUDIT_DATA_WORD => {
281                        let raw_word_data = bytes.as_ptr() as *mut audit_word_data_event_st;
282                        let key = CStr::from_ptr((*raw_word_data).base.key.as_ptr());
283                        EventGroup {
284                            context,
285                            start: ktime,
286                            end: ktime,
287                            events: vec![Event::Data {
288                                key: key.to_str()?.to_string(),
289                                value: EventData::Word((*raw_word_data).value.into()),
290                            }],
291                        }
292                    }
293                    audit_data_type_t::AUDIT_DATA_STRING => {
294                        let raw_string_data = bytes.as_ptr() as *mut audit_blob_data_event_st;
295                        let key = CStr::from_ptr((*raw_string_data).base.key.as_ptr());
296                        let len = (*raw_string_data).size as usize;
297                        let string = std::str::from_utf8(&(&(*raw_string_data).value)[..len - 1])?
298                            .to_string();
299                        EventGroup {
300                            context,
301                            start: ktime,
302                            end: ktime,
303                            events: vec![Event::Data {
304                                key: key.to_str()?.to_string(),
305                                value: EventData::String(string),
306                            }],
307                        }
308                    }
309                    audit_data_type_t::AUDIT_DATA_BLOB => {
310                        let raw_blob_data = bytes.as_ptr() as *mut audit_blob_data_event_st;
311                        let key = CStr::from_ptr((*raw_blob_data).base.key.as_ptr());
312                        let len = (*raw_blob_data).size as usize;
313                        let data = (&(*raw_blob_data).value)[..len].to_vec();
314                        EventGroup {
315                            context,
316                            start: ktime,
317                            end: ktime,
318                            events: vec![Event::Data {
319                                key: key.to_str()?.to_string(),
320                                value: EventData::Blob(data),
321                            }],
322                        }
323                    }
324                    _ => unreachable!(),
325                }
326            },
327            _ => unreachable!(),
328        };
329        Ok(event)
330    }
331}