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 { 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 = unsafe { format_context((*header).pid_tgid, (*header).context) };
118        let ktime = unsafe { Duration::from_nanos((*header).ktime) };
119        let event = match unsafe { (*header).type_ } {
120            audit_event_type_t::AUDIT_EVENT_NEW_CONTEXT => {
121                let raw_new_context = bytes.as_ptr() as *mut audit_new_context_event_st;
122                let parent =
123                    unsafe { format_context((*header).pid_tgid, (*raw_new_context).parent) };
124                let origin = unsafe {
125                    (&(*raw_new_context).origin)[..(*raw_new_context).origin_size as usize].to_vec()
126                };
127                EventGroup {
128                    context,
129                    start: ktime,
130                    end: ktime,
131                    events: vec![Event::NewContext { parent, origin }],
132                }
133            }
134            audit_event_type_t::AUDIT_EVENT_DATA => unsafe {
135                let data = bytes.as_ptr() as *mut audit_data_event_st;
136                match (*data).type_ {
137                    audit_data_type_t::AUDIT_DATA_WORD => {
138                        let raw_word_data = bytes.as_ptr() as *mut audit_word_data_event_st;
139                        let key = CStr::from_ptr((*raw_word_data).base.key.as_ptr());
140                        EventGroup {
141                            context,
142                            start: ktime,
143                            end: ktime,
144                            events: vec![Event::Data {
145                                key: key.to_str()?.to_string(),
146                                value: EventData::Word((*raw_word_data).value),
147                            }],
148                        }
149                    }
150                    audit_data_type_t::AUDIT_DATA_STRING => {
151                        let raw_string_data = bytes.as_ptr() as *mut audit_blob_data_event_st;
152                        let key = CStr::from_ptr((*raw_string_data).base.key.as_ptr());
153                        let len = (*raw_string_data).size as usize;
154                        let string = std::str::from_utf8(&(&(*raw_string_data).value)[..len - 1])?
155                            .to_string();
156                        EventGroup {
157                            context,
158                            start: ktime,
159                            end: ktime,
160                            events: vec![Event::Data {
161                                key: key.to_str()?.to_string(),
162                                value: EventData::String(string),
163                            }],
164                        }
165                    }
166                    audit_data_type_t::AUDIT_DATA_BLOB => {
167                        let raw_blob_data = bytes.as_ptr() as *mut audit_blob_data_event_st;
168                        let key = CStr::from_ptr((*raw_blob_data).base.key.as_ptr());
169                        let len = (*raw_blob_data).size as usize;
170                        let data = (&(*raw_blob_data).value)[..len].to_vec();
171                        EventGroup {
172                            context,
173                            start: ktime,
174                            end: ktime,
175                            events: vec![Event::Data {
176                                key: key.to_str()?.to_string(),
177                                value: EventData::Blob(data),
178                            }],
179                        }
180                    }
181                    _ => unreachable!(),
182                }
183            },
184            _ => unreachable!(),
185        };
186        Ok(event)
187    }
188}