1use crate::vfs::socket::AuditNetlinkClient;
6use linux_uapi::{
7 AUDIT_CONFIG_CHANGE, AUDIT_FAIL_PANIC, AUDIT_FAIL_PRINTK, AUDIT_FAIL_SILENT,
8 AUDIT_FIRST_USER_MSG, AUDIT_FIRST_USER_MSG2, AUDIT_GET, AUDIT_LAST_USER_MSG,
9 AUDIT_LAST_USER_MSG2, AUDIT_SET, AUDIT_STATUS_BACKLOG_LIMIT, AUDIT_STATUS_ENABLED,
10 AUDIT_STATUS_FAILURE, AUDIT_STATUS_LOST, AUDIT_STATUS_PID, AUDIT_USER,
11};
12use starnix_lifecycle::AtomicCounter;
13use starnix_logging::log_warn;
14use starnix_sync::{AuditSinkLock, LockDepGuard, LockDepMutex};
15use starnix_uapi::errors::Errno;
16use starnix_uapi::{audit_status, error, pid_t};
17use std::collections::VecDeque;
18use std::fmt::Display;
19use std::sync::Arc;
20use std::sync::atomic::{AtomicU8, AtomicU32, Ordering};
21use std::time::SystemTime;
22
23use crate::task::{ArgNameAndValue, CurrentTask, Kernel};
24const DEFAULT_BACKLOG_LIMIT: u32 = 128;
25
26pub enum AuditRequest {
28 AuditGet,
29 AuditSet,
30 AuditUser,
31}
32
33impl TryFrom<u32> for AuditRequest {
34 type Error = Errno;
35
36 fn try_from(value: u32) -> Result<Self, Self::Error> {
37 match value {
38 AUDIT_GET => Ok(Self::AuditGet),
39 AUDIT_SET => Ok(Self::AuditSet),
40 AUDIT_USER
41 | AUDIT_FIRST_USER_MSG..=AUDIT_LAST_USER_MSG
42 | AUDIT_FIRST_USER_MSG2..=AUDIT_LAST_USER_MSG2 => Ok(Self::AuditUser),
43 _ => error!(ENOTSUP),
44 }
45 }
46}
47
48#[derive(PartialEq)]
50enum AuditMode {
51 Disabled,
52 Unspecified,
53 Enabled,
54}
55
56#[derive(Default)]
58struct AuditNetlinkClientRef {
59 client: Option<Arc<AuditNetlinkClient>>,
61 pid: pid_t,
63 messages: VecDeque<AuditMessage>,
66}
67
68struct AuditConfig {
70 audit_mode: AuditMode,
72 backlog_limit: AtomicU32,
74 fail_action: AtomicU8,
76 audit_sink: LockDepMutex<AuditNetlinkClientRef, AuditSinkLock>,
78}
79
80impl Default for AuditConfig {
81 fn default() -> Self {
82 Self {
83 audit_mode: AuditMode::Unspecified,
84 backlog_limit: AtomicU32::new(DEFAULT_BACKLOG_LIMIT),
85 fail_action: AtomicU8::new(AUDIT_FAIL_PRINTK as u8),
86 audit_sink: Default::default(),
87 }
88 }
89}
90
91impl AuditConfig {
92 pub fn new<'a>(cmdline_iter: impl Iterator<Item = ArgNameAndValue<'a>>) -> Self {
93 let mut config = Self::default();
94 config.apply_kernel_cmdline(cmdline_iter);
96 config
97 }
98
99 fn apply_kernel_cmdline<'a>(
101 &mut self,
102 cmdline_iter: impl Iterator<Item = ArgNameAndValue<'a>>,
103 ) {
104 for arg in cmdline_iter {
105 match arg {
106 ArgNameAndValue { name: "audit", value: Some(value) } => match value {
107 "0" | "off" => self.audit_mode = AuditMode::Disabled,
108 _ => self.audit_mode = AuditMode::Enabled,
110 },
111 ArgNameAndValue { name: "audit_backlog_limit", value: Some(value) } => self
112 .backlog_limit
113 .store(value.parse().unwrap_or(DEFAULT_BACKLOG_LIMIT), Ordering::Release),
114 _ => (),
115 }
116 }
117 }
118}
119
120pub struct AuditLogger {
122 configuration: AuditConfig,
124 lost_audit_messages: AtomicU32,
126 serial_counter: AtomicCounter<u64>,
128}
129
130impl AuditLogger {
131 pub fn new(kernel: &Kernel) -> Self {
132 Self {
133 configuration: AuditConfig::new(kernel.cmdline_args_iter()),
134 lost_audit_messages: Default::default(),
135 serial_counter: AtomicCounter::new(1),
138 }
139 }
140
141 pub fn is_disabled(&self) -> bool {
142 self.configuration.audit_mode == AuditMode::Disabled
143 }
144
145 pub fn audit_log<M: Display, T: FnOnce() -> M>(&self, audit_type: u16, audit_formatter: T) {
149 if self.configuration.audit_mode == AuditMode::Disabled {
150 return;
151 }
152 self.add_audit_to_backlog(audit_type, audit_formatter);
153 }
154
155 pub fn read_audit_log(&self, client: &Arc<AuditNetlinkClient>) -> Option<AuditMessage> {
157 let mut client_guard = self.configuration.audit_sink.lock();
158 let Some(current_client) = client_guard.client.as_ref() else {
159 return None;
160 };
161 if !Arc::ptr_eq(¤t_client, client) {
163 return None;
164 }
165 client_guard.messages.pop_front()
166 }
167
168 pub fn detach_client(&self, client: &Arc<AuditNetlinkClient>) {
171 let mut client_guard = self.configuration.audit_sink.lock();
172 if client_guard
173 .client
174 .as_ref()
175 .is_some_and(|current_client| Arc::ptr_eq(client, ¤t_client))
176 {
177 let pid = client_guard.pid;
178 client_guard.client = None;
179 client_guard.pid = 0;
180 client_guard.messages.clear();
181 drop(client_guard);
182 self.audit_log(AUDIT_CONFIG_CHANGE as u16, || format!("audit sink detached pid={pid}"));
183 }
184 }
185
186 pub fn set_status(
188 &self,
189 current_task: &CurrentTask,
190 status: audit_status,
191 client: &Arc<AuditNetlinkClient>,
192 ) -> Result<(), Errno> {
193 if status.mask & AUDIT_STATUS_ENABLED != 0 && status.enabled > 1 {
196 return error!(EINVAL);
197 }
198 if status.mask & AUDIT_STATUS_BACKLOG_LIMIT != 0 {
199 self.configuration.backlog_limit.store(status.backlog_limit, Ordering::Release);
200 }
201 if status.mask & AUDIT_STATUS_FAILURE != 0 {
202 self.configuration.fail_action.store(status.failure as u8, Ordering::Release);
203 }
204 if status.mask & AUDIT_STATUS_LOST != 0 {
205 self.lost_audit_messages.store(0, Ordering::Release);
206 }
207 if status.mask & AUDIT_STATUS_PID != 0 {
208 self.update_client(current_task.get_pid(), status.pid as pid_t, client)?;
209 }
210 Ok(())
211 }
212
213 pub fn get_status(&self) -> audit_status {
215 let (pid, backlog) = {
216 let audit_sink = self.configuration.audit_sink.lock();
217 (audit_sink.pid as u32, audit_sink.messages.len() as u32)
218 };
219 audit_status {
220 mask: Default::default(),
221 enabled: u32::from(!self.is_disabled()),
222 failure: self.configuration.fail_action.load(Ordering::Acquire) as u32,
223 pid,
224 rate_limit: u32::MAX,
225 backlog_limit: self.configuration.backlog_limit.load(Ordering::Acquire),
226 lost: self.lost_audit_messages.load(Ordering::Acquire),
227 backlog,
228 __bindgen_anon_1: Default::default(),
229 backlog_wait_time: Default::default(),
230 backlog_wait_time_actual: Default::default(),
231 }
232 }
233
234 pub fn get_backlog_count(&self, client: &Arc<AuditNetlinkClient>) -> usize {
236 let client_guard = self.configuration.audit_sink.lock();
237 if let Some(current_client) = &client_guard.client {
238 if Arc::ptr_eq(¤t_client, client) {
239 return client_guard.messages.len();
240 }
241 }
242 0
243 }
244
245 fn update_client(
247 &self,
248 pid: pid_t,
249 request_pid: pid_t,
250 client: &Arc<AuditNetlinkClient>,
251 ) -> Result<(), Errno> {
252 if request_pid == 0 {
253 let client_ref = {
254 let client_guard = self.configuration.audit_sink.lock();
255 if client_guard.pid == 0 {
257 return Ok(());
258 } else if pid != client_guard.pid {
259 return error!(EPERM);
260 }
261 client_guard.client.clone()
262 };
263 client_ref.inspect(|client_ref| self.detach_client(&client_ref));
264 return Ok(());
265 }
266 if pid != request_pid {
267 return error!(EINVAL);
268 }
269
270 let mut client_guard = self.configuration.audit_sink.lock();
271 if client_guard.client.is_some() {
272 return error!(EEXIST);
273 }
274 client_guard.client = Some(client.clone());
275 client_guard.pid = pid;
276 drop(client_guard);
277 self.audit_log(AUDIT_CONFIG_CHANGE as u16, || format!("new audit sink attached pid={pid}"));
278 Ok(())
279 }
280
281 fn add_audit_to_backlog<M: Display, T: FnOnce() -> M>(
283 &self,
284 audit_type: u16,
285 audit_formatter: T,
286 ) {
287 let audit_message = self.prepend_audit_metadata(audit_formatter);
289
290 let mut client_guard = self.configuration.audit_sink.lock();
291 if client_guard.client.is_none() {
294 log_warn!("audit: type={audit_type} msg={audit_message}");
295 }
296
297 if client_guard.client.is_some() || self.configuration.audit_mode == AuditMode::Enabled {
298 self.push_back_audit(audit_type, audit_message, &mut client_guard);
299 let client = client_guard.client.clone();
300 drop(client_guard);
301 if let Some(client) = client {
302 client.notify();
303 }
304 }
305 }
306
307 fn push_back_audit(
309 &self,
310 audit_type: u16,
311 audit_message: String,
312 client_guard: &mut LockDepGuard<'_, AuditNetlinkClientRef>,
313 ) {
314 if self.check_backlog(client_guard.messages.len() as u32) {
316 return;
317 }
318 client_guard.messages.push_back(AuditMessage { audit_type, message: audit_message.into() });
319 }
320
321 fn check_backlog(&self, backlog_size: u32) -> bool {
326 let limit = self.configuration.backlog_limit.load(Ordering::Acquire);
327 if limit != 0 && backlog_size >= limit {
328 let lost = self.lost_audit_messages.fetch_add(1, Ordering::Release) + 1;
329 log_warn!("audit_lost={lost} backlog_limit={limit}");
330 match self.configuration.fail_action.load(Ordering::Acquire) as u32 {
332 AUDIT_FAIL_PANIC => panic!("backlog limit exceeded"),
333 AUDIT_FAIL_PRINTK => log_warn!("backlog limit exceeded"),
334 AUDIT_FAIL_SILENT | _ => (),
335 }
336 return true;
337 }
338 false
339 }
340
341 fn prepend_audit_metadata<M: Display, T: FnOnce() -> M>(&self, audit: T) -> String {
343 let duration = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap_or_default();
344
345 format!(
346 "audit({}.{:03}:{}): {}",
347 duration.as_secs(),
348 duration.subsec_millis(),
349 self.serial_counter.next(),
350 audit()
351 )
352 }
353}
354
355pub struct AuditMessage {
357 pub audit_type: u16,
359 pub message: Vec<u8>,
361}
362
363#[cfg(test)]
364mod tests {
365 use super::*;
366 use regex_lite::Regex;
367
368 fn test_logger(configuration: AuditConfig) -> AuditLogger {
369 AuditLogger {
370 configuration,
371 lost_audit_messages: Default::default(),
372 serial_counter: AtomicCounter::new(1),
373 }
374 }
375
376 #[::fuchsia::test]
377 fn prepend_audit_metadata_formats_millis_and_serial() {
378 let logger = test_logger(AuditConfig::default());
379
380 let re1 = Regex::new(r"^audit\(\d+\.\d{3}:1\): msg1$").unwrap();
381 let msg1 = logger.prepend_audit_metadata(|| "msg1");
382 assert!(re1.is_match(&msg1), "unexpected format: {msg1}");
383
384 let re2 = Regex::new(r"^audit\(\d+\.\d{3}:2\): msg2$").unwrap();
385 let msg2 = logger.prepend_audit_metadata(|| "msg2");
386 assert!(re2.is_match(&msg2), "unexpected format: {msg2}");
387 }
388
389 #[::fuchsia::test]
390 fn get_status_reports_backlog_len() {
391 let logger = test_logger(AuditConfig::new(
392 [ArgNameAndValue { name: "audit", value: Some("1") }].into_iter(),
393 ));
394 assert_eq!(logger.get_status().pid, 0);
395 assert_eq!(logger.get_status().backlog, 0);
396
397 logger.audit_log(AUDIT_USER as u16, || "test audit message 1");
398 assert_eq!(logger.get_status().backlog, 1);
399
400 logger.audit_log(AUDIT_USER as u16, || "test audit message 2");
401 assert_eq!(logger.get_status().backlog, 2);
402 }
403
404 #[::fuchsia::test]
405 fn get_status_reports_enabled() {
406 let enabled_logger = test_logger(AuditConfig::new(
407 [ArgNameAndValue { name: "audit", value: Some("1") }].into_iter(),
408 ));
409 assert_eq!(enabled_logger.get_status().enabled, 1);
410
411 let default_logger = test_logger(AuditConfig::default());
412 assert_eq!(default_logger.get_status().enabled, 1);
413
414 let disabled_logger = test_logger(AuditConfig::new(
415 [ArgNameAndValue { name: "audit", value: Some("0") }].into_iter(),
416 ));
417 assert_eq!(disabled_logger.get_status().enabled, 0);
418 }
419}