Skip to main content

starnix_core/security/
audit.rs

1// Copyright 2025 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use 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::{AuditQueueLock, 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;
22use std::u32;
23use zx::MonotonicDuration;
24
25use crate::task::{ArgNameAndValue, CurrentTask, Kernel};
26const DEFAULT_BACKLOG_LIMIT: u32 = 128;
27
28/// Supported requests that manipulate the `AuditLogger`
29pub enum AuditRequest {
30    AuditGet,
31    AuditSet,
32    AuditUser,
33}
34
35impl TryFrom<u32> for AuditRequest {
36    type Error = Errno;
37
38    fn try_from(value: u32) -> Result<Self, Self::Error> {
39        match value {
40            AUDIT_GET => Ok(Self::AuditGet),
41            AUDIT_SET => Ok(Self::AuditSet),
42            AUDIT_USER
43            | AUDIT_FIRST_USER_MSG..=AUDIT_LAST_USER_MSG
44            | AUDIT_FIRST_USER_MSG2..=AUDIT_LAST_USER_MSG2 => Ok(Self::AuditUser),
45            _ => error!(ENOTSUP),
46        }
47    }
48}
49
50/// Possible modes of the audit framework.
51#[derive(PartialEq)]
52enum AuditMode {
53    Disabled,
54    Unspecified,
55    Enabled,
56}
57
58/// The audit sink reference structure.
59#[derive(Default)]
60struct AuditNetlinkClientRef {
61    /// Inner reference to the registered audit sink, if any.
62    client: Option<Arc<AuditNetlinkClient>>,
63    /// The PID of the registered audit sink.
64    pid: pid_t,
65    /// Deque for the audit messages, always present.
66    messages: VecDeque<AuditMessage>,
67}
68
69/// Audit status structure defining the behaviour of the logger.
70struct AuditConfig {
71    /// The audit mode set by kernel command line.
72    audit_mode: AuditMode,
73    /// The maximum number of audit messages that can be stored by the logger.
74    backlog_limit: AtomicU32,
75    /// Action to take in case of audit failure.
76    fail_action: AtomicU8,
77    /// Socket to which the logger writes audit messages.
78    audit_sink: LockDepMutex<AuditNetlinkClientRef, AuditSinkLock>,
79}
80
81impl Default for AuditConfig {
82    fn default() -> Self {
83        Self {
84            audit_mode: AuditMode::Unspecified,
85            backlog_limit: AtomicU32::new(DEFAULT_BACKLOG_LIMIT),
86            fail_action: AtomicU8::new(AUDIT_FAIL_PRINTK as u8),
87            audit_sink: Default::default(),
88        }
89    }
90}
91
92impl AuditConfig {
93    pub fn new<'a>(cmdline_iter: impl Iterator<Item = ArgNameAndValue<'a>>) -> Self {
94        let mut config = Self::default();
95        // The logger may be disabled by the kernel command line.
96        config.apply_kernel_cmdline(cmdline_iter);
97        config
98    }
99
100    /// Function to apply the optional kernel command line arguments.
101    fn apply_kernel_cmdline<'a>(
102        &mut self,
103        cmdline_iter: impl Iterator<Item = ArgNameAndValue<'a>>,
104    ) {
105        for arg in cmdline_iter {
106            match arg {
107                ArgNameAndValue { name: "audit", value: Some(value) } => match value {
108                    "0" | "off" => self.audit_mode = AuditMode::Disabled,
109                    // If the audit option is "1"/"on"/anything else, fully enable auditing.
110                    _ => self.audit_mode = AuditMode::Enabled,
111                },
112                ArgNameAndValue { name: "audit_backlog_limit", value: Some(value) } => self
113                    .backlog_limit
114                    .store(value.parse().unwrap_or(DEFAULT_BACKLOG_LIMIT), Ordering::Release),
115                _ => (),
116            }
117        }
118    }
119}
120
121/// Audit logging structure.
122pub struct AuditLogger {
123    /// Audit status structure.
124    configuration: AuditConfig,
125    /// The number of audit messages lost due to writing errors.
126    lost_audit_messages: AtomicU32,
127    /// Monotonic counter for audit serial numbers
128    serial_counter: AtomicCounter<u64>,
129    /// Audit message deque containing (audit type, audit string) up to `backlog_limit` messages.
130    /// TODO: https://fxbug.dev/438677236 - confirm single queue behaviour is valid.
131    audit_queue: LockDepMutex<VecDeque<AuditMessage>, AuditQueueLock>,
132}
133
134impl AuditLogger {
135    pub fn new(kernel: &Kernel) -> Self {
136        Self {
137            configuration: AuditConfig::new(kernel.cmdline_args_iter()),
138            lost_audit_messages: Default::default(),
139            serial_counter: Default::default(),
140            audit_queue: Default::default(),
141        }
142    }
143
144    pub fn is_disabled(&self) -> bool {
145        self.configuration.audit_mode == AuditMode::Disabled
146    }
147
148    /// Audit logging function that adds an audit message to the queue.
149    ///
150    /// The `audit_formatter` function is called only if the auditing is enabled.
151    pub fn audit_log<M: Display, T: FnOnce() -> M>(&self, audit_type: u16, audit_formatter: T) {
152        if self.configuration.audit_mode == AuditMode::Disabled {
153            return;
154        }
155        self.add_audit_to_backlog(audit_type, audit_formatter);
156    }
157
158    /// Called by the `NetlinkAuditClient` to pull the next audit log from the backlog.
159    pub fn read_audit_log(&self, client: &Arc<AuditNetlinkClient>) -> Option<AuditMessage> {
160        let mut client_guard = self.configuration.audit_sink.lock();
161        let Some(current_client) = client_guard.client.as_ref() else {
162            return None;
163        };
164        // Check if the current client is reading the backlog.
165        if !Arc::ptr_eq(&current_client, client) {
166            return None;
167        }
168        client_guard.messages.pop_front()
169    }
170
171    /// Function to detach the `AuditNetlinkClient` from the `AuditLogger` if
172    /// the provided client matches the one registered.
173    pub fn detach_client(&self, client: &Arc<AuditNetlinkClient>) {
174        let mut client_guard = self.configuration.audit_sink.lock();
175        if client_guard
176            .client
177            .as_ref()
178            .is_some_and(|current_client| Arc::ptr_eq(client, &current_client))
179        {
180            let pid = client_guard.pid;
181            client_guard.client = None;
182            client_guard.pid = 0;
183            client_guard.messages.clear();
184            drop(client_guard);
185            self.audit_log(AUDIT_CONFIG_CHANGE as u16, || format!("audit sink detached pid={pid}"));
186        }
187    }
188
189    /// Applies the specified changes to the audit logger settings.
190    pub fn set_status(
191        &self,
192        current_task: &CurrentTask,
193        status: audit_status,
194        client: &Arc<AuditNetlinkClient>,
195    ) -> Result<(), Errno> {
196        // Dummy check for enable/disable request. This should be used again if other
197        // subsystems will use the audit logger.
198        if status.mask & AUDIT_STATUS_ENABLED != 0 && status.enabled > 1 {
199            return error!(EINVAL);
200        }
201        if status.mask & AUDIT_STATUS_BACKLOG_LIMIT != 0 {
202            self.configuration.backlog_limit.store(status.backlog_limit, Ordering::Release);
203        }
204        if status.mask & AUDIT_STATUS_FAILURE != 0 {
205            self.configuration.fail_action.store(status.failure as u8, Ordering::Release);
206        }
207        if status.mask & AUDIT_STATUS_LOST != 0 {
208            self.lost_audit_messages.store(0, Ordering::Release);
209        }
210        if status.mask & AUDIT_STATUS_PID != 0 {
211            self.update_client(current_task.get_pid(), status.pid as pid_t, client)?;
212        }
213        Ok(())
214    }
215
216    /// Retrieve the `AuditConfig` as `audit_status` struct.
217    pub fn get_status(&self) -> audit_status {
218        let pid = self.configuration.audit_sink.lock().pid as u32;
219        let backlog = self.audit_queue.lock().len() as u32;
220        audit_status {
221            mask: Default::default(),
222            enabled: Default::default(),
223            failure: self.configuration.fail_action.load(Ordering::Acquire) as u32,
224            pid,
225            rate_limit: u32::MAX,
226            backlog_limit: self.configuration.backlog_limit.load(Ordering::Acquire),
227            lost: self.lost_audit_messages.load(Ordering::Acquire),
228            backlog,
229            __bindgen_anon_1: Default::default(),
230            backlog_wait_time: Default::default(),
231            backlog_wait_time_actual: Default::default(),
232        }
233    }
234
235    /// Retrieve the number of audit messages in the backlog.
236    pub fn get_backlog_count(&self, client: &Arc<AuditNetlinkClient>) -> usize {
237        let client_guard = self.configuration.audit_sink.lock();
238        if let Some(current_client) = &client_guard.client {
239            if Arc::ptr_eq(&current_client, client) {
240                return client_guard.messages.len();
241            }
242        }
243        0
244    }
245
246    /// Function to update the attached `client` and its PID
247    fn update_client(
248        &self,
249        pid: pid_t,
250        request_pid: pid_t,
251        client: &Arc<AuditNetlinkClient>,
252    ) -> Result<(), Errno> {
253        if request_pid == 0 {
254            let client_ref = {
255                let client_guard = self.configuration.audit_sink.lock();
256                // If there is no audit client registered and unregister is requested, return without error.
257                if client_guard.pid == 0 {
258                    return Ok(());
259                } else if pid != client_guard.pid {
260                    return error!(EPERM);
261                }
262                client_guard.client.clone()
263            };
264            client_ref.inspect(|client_ref| self.detach_client(&client_ref));
265            return Ok(());
266        }
267        if pid != request_pid {
268            return error!(EINVAL);
269        }
270
271        let mut client_guard = self.configuration.audit_sink.lock();
272        if client_guard.client.is_some() {
273            return error!(EEXIST);
274        }
275        client_guard.client = Some(client.clone());
276        client_guard.pid = pid;
277        drop(client_guard);
278        self.audit_log(AUDIT_CONFIG_CHANGE as u16, || format!("new audit sink attached pid={pid}"));
279        Ok(())
280    }
281
282    /// Add an audit message to the backlog if it is enabled.
283    fn add_audit_to_backlog<M: Display, T: FnOnce() -> M>(
284        &self,
285        audit_type: u16,
286        audit_formatter: T,
287    ) {
288        // At this point, we know that the audit framework is not disabled until reboot.
289        let audit_message = self.prepend_audit_metadata(audit_formatter);
290
291        let mut client_guard = self.configuration.audit_sink.lock();
292        // If there is no audit sink and the auditing is partially enabled, print and return
293        // without pushing the message to the backlog.
294        if client_guard.client.is_none() {
295            log_warn!("audit: type={audit_type} msg={audit_message}");
296        }
297
298        if client_guard.client.is_some() || self.configuration.audit_mode == AuditMode::Enabled {
299            self.push_back_audit(audit_type, audit_message, &mut client_guard);
300            let client = client_guard.client.clone();
301            drop(client_guard);
302            if let Some(client) = client {
303                client.notify();
304            }
305        }
306    }
307
308    /// Push the audit message in the backlog after checking its limit.
309    fn push_back_audit(
310        &self,
311        audit_type: u16,
312        audit_message: String,
313        client_guard: &mut LockDepGuard<'_, AuditNetlinkClientRef>,
314    ) {
315        // TODO: https://fxbug.dev/440090442 - implement backlog waiting.
316        if self.check_backlog(client_guard.messages.len() as u32) {
317            return;
318        }
319        client_guard.messages.push_back(AuditMessage { audit_type, message: audit_message.into() });
320    }
321
322    /// Function to check the backlog size against the backlog limit.
323    /// If the limit is set to 0, ignore the check.
324    ///
325    /// Return true if the limit is reached, false otherwise.
326    fn check_backlog(&self, backlog_size: u32) -> bool {
327        let limit = self.configuration.backlog_limit.load(Ordering::Acquire);
328        if limit != 0 && backlog_size >= limit {
329            let lost = self.lost_audit_messages.fetch_add(1, Ordering::Release) + 1;
330            log_warn!("audit_lost={lost} backlog_limit={limit}");
331            // If the backlog is full, use failure-to-log action.
332            match self.configuration.fail_action.load(Ordering::Acquire) as u32 {
333                AUDIT_FAIL_PANIC => panic!("backlog limit exceeded"),
334                AUDIT_FAIL_PRINTK => log_warn!("backlog limit exceeded"),
335                AUDIT_FAIL_SILENT | _ => (),
336            }
337            return true;
338        }
339        false
340    }
341
342    /// Function to prepend an audit message with a timestamp and serial number.
343    fn prepend_audit_metadata<M: Display, T: FnOnce() -> M>(&self, audit: T) -> String {
344        let epoch_time = MonotonicDuration::from(
345            SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap_or_default(),
346        )
347        .into_millis();
348
349        format!(
350            "audit({}.{}:{}): {}",
351            epoch_time / 1000,
352            epoch_time % 1000,
353            self.serial_counter.next(),
354            audit()
355        )
356    }
357}
358
359/// Audit message structure.
360pub struct AuditMessage {
361    /// The type of the audit message (e.g., AUDIT_AVC).
362    pub audit_type: u16,
363    /// The message to be audit-logged.
364    pub message: Vec<u8>,
365}