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::AtomicU64Counter;
13use starnix_logging::log_warn;
14use starnix_sync::{Mutex, MutexGuard};
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: Mutex<AuditNetlinkClientRef>,
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: AtomicU64Counter,
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: Mutex<VecDeque<AuditMessage>>,
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(
156            audit_type,
157            audit_formatter,
158            &mut self.configuration.audit_sink.lock(),
159        );
160    }
161
162    /// Called by the `NetlinkAuditClient` to pull the next audit log from the backlog.
163    pub fn read_audit_log(&self, client: &Arc<AuditNetlinkClient>) -> Option<AuditMessage> {
164        let mut client_guard = self.configuration.audit_sink.lock();
165        let Some(current_client) = client_guard.client.as_ref() else {
166            return None;
167        };
168        // Check if the current client is reading the backlog.
169        if !Arc::ptr_eq(&current_client, client) {
170            return None;
171        }
172        client_guard.messages.pop_front()
173    }
174
175    /// Function to detach the `AuditNetlinkClient` from the `AuditLogger` if
176    /// the provided client matches the one registered.
177    pub fn detach_client(&self, client: &Arc<AuditNetlinkClient>) {
178        let mut client_guard = self.configuration.audit_sink.lock();
179        if client_guard
180            .client
181            .as_ref()
182            .is_some_and(|current_client| Arc::ptr_eq(client, &current_client))
183        {
184            let pid = client_guard.pid;
185            client_guard.client = None;
186            client_guard.pid = 0;
187            client_guard.messages.clear();
188            self.add_audit_to_backlog(
189                AUDIT_CONFIG_CHANGE as u16,
190                || format!("audit sink detached pid={pid}"),
191                &mut client_guard,
192            );
193        }
194    }
195
196    /// Applies the specified changes to the audit logger settings.
197    pub fn set_status(
198        &self,
199        current_task: &CurrentTask,
200        status: audit_status,
201        client: &Arc<AuditNetlinkClient>,
202    ) -> Result<(), Errno> {
203        // Dummy check for enable/disable request. This should be used again if other
204        // subsystems will use the audit logger.
205        if status.mask & AUDIT_STATUS_ENABLED != 0 && status.enabled > 1 {
206            return error!(EINVAL);
207        }
208        if status.mask & AUDIT_STATUS_BACKLOG_LIMIT != 0 {
209            self.configuration.backlog_limit.store(status.backlog_limit, Ordering::Release);
210        }
211        if status.mask & AUDIT_STATUS_FAILURE != 0 {
212            self.configuration.fail_action.store(status.failure as u8, Ordering::Release);
213        }
214        if status.mask & AUDIT_STATUS_LOST != 0 {
215            self.lost_audit_messages.store(0, Ordering::Release);
216        }
217        if status.mask & AUDIT_STATUS_PID != 0 {
218            self.update_client(current_task.get_pid(), status.pid as pid_t, client)?;
219        }
220        Ok(())
221    }
222
223    /// Retrieve the `AuditConfig` as `audit_status` struct.
224    pub fn get_status(&self) -> audit_status {
225        audit_status {
226            mask: Default::default(),
227            enabled: Default::default(),
228            failure: self.configuration.fail_action.load(Ordering::Acquire) as u32,
229            pid: self.configuration.audit_sink.lock().pid as u32,
230            rate_limit: u32::MAX,
231            backlog_limit: self.configuration.backlog_limit.load(Ordering::Acquire),
232            lost: self.lost_audit_messages.load(Ordering::Acquire),
233            backlog: self.audit_queue.lock().len() as u32,
234            __bindgen_anon_1: Default::default(),
235            backlog_wait_time: Default::default(),
236            backlog_wait_time_actual: Default::default(),
237        }
238    }
239
240    /// Retrieve the number of audit messages in the backlog.
241    pub fn get_backlog_count(&self, client: &Arc<AuditNetlinkClient>) -> usize {
242        let client_guard = self.configuration.audit_sink.lock();
243        if let Some(current_client) = &client_guard.client {
244            if Arc::ptr_eq(&current_client, client) {
245                return client_guard.messages.len();
246            }
247        }
248        0
249    }
250
251    /// Function to update the attached `client` and its PID
252    fn update_client(
253        &self,
254        pid: pid_t,
255        request_pid: pid_t,
256        client: &Arc<AuditNetlinkClient>,
257    ) -> Result<(), Errno> {
258        if request_pid == 0 {
259            let client_ref = {
260                let client_guard = self.configuration.audit_sink.lock();
261                // If there is no audit client registered and unregister is requested, return without error.
262                if client_guard.pid == 0 {
263                    return Ok(());
264                } else if pid != client_guard.pid {
265                    return error!(EPERM);
266                }
267                client_guard.client.clone()
268            };
269            client_ref.inspect(|client_ref| self.detach_client(&client_ref));
270            return Ok(());
271        }
272        if pid != request_pid {
273            return error!(EINVAL);
274        }
275
276        let mut client_guard = self.configuration.audit_sink.lock();
277        if client_guard.client.is_some() {
278            return error!(EEXIST);
279        }
280        client_guard.client = Some(client.clone());
281        client_guard.pid = pid;
282        self.add_audit_to_backlog(
283            AUDIT_CONFIG_CHANGE as u16,
284            || format!("new audit sink attached pid={pid}"),
285            &mut client_guard,
286        );
287        Ok(())
288    }
289
290    /// Add an audit message to the backlog if it is enabled.
291    fn add_audit_to_backlog<M: Display, T: FnOnce() -> M>(
292        &self,
293        audit_type: u16,
294        audit_formatter: T,
295        client_guard: &mut MutexGuard<'_, AuditNetlinkClientRef>,
296    ) {
297        // At this point, we know that the audit framework is not disabled until reboot.
298        let audit_message = self.prepend_audit_metadata(audit_formatter);
299        // If there is no audit sink and the auditing is partially enabled, print and return
300        // without pushing the message to the backlog.
301        if client_guard.client.is_none() {
302            log_warn!("{audit_message}");
303        }
304        if client_guard.client.is_some() || self.configuration.audit_mode == AuditMode::Enabled {
305            self.push_back_audit(audit_type, audit_message, client_guard);
306            client_guard.client.as_ref().inspect(|client| client.notify());
307        }
308    }
309
310    /// Push the audit message in the backlog after checking its limit.
311    fn push_back_audit(
312        &self,
313        audit_type: u16,
314        audit_message: String,
315        client_guard: &mut MutexGuard<'_, AuditNetlinkClientRef>,
316    ) {
317        // TODO: https://fxbug.dev/440090442 - implement backlog waiting.
318        if self.check_backlog(client_guard.messages.len() as u32) {
319            return;
320        }
321        client_guard.messages.push_back(AuditMessage { audit_type, message: audit_message.into() });
322    }
323
324    /// Function to check the backlog size against the backlog limit.
325    /// If the limit is set to 0, ignore the check.
326    ///
327    /// Return true if the limit is reached, false otherwise.
328    fn check_backlog(&self, backlog_size: u32) -> bool {
329        let limit = self.configuration.backlog_limit.load(Ordering::Acquire);
330        if limit != 0 && backlog_size >= limit {
331            let lost = self.lost_audit_messages.fetch_add(1, Ordering::Release) + 1;
332            log_warn!("audit_lost={lost} backlog_limit={limit}");
333            // If the backlog is full, use failure-to-log action.
334            match self.configuration.fail_action.load(Ordering::Acquire) as u32 {
335                AUDIT_FAIL_PANIC => panic!("backlog limit exceeded"),
336                AUDIT_FAIL_PRINTK => log_warn!("backlog limit exceeded"),
337                AUDIT_FAIL_SILENT | _ => (),
338            }
339            return true;
340        }
341        false
342    }
343
344    /// Function to prepend an audit message with a timestamp and serial number.
345    fn prepend_audit_metadata<M: Display, T: FnOnce() -> M>(&self, audit: T) -> String {
346        let epoch_time = MonotonicDuration::from(
347            SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap_or_default(),
348        )
349        .into_millis();
350
351        format!(
352            "audit({}.{}:{}): {}",
353            epoch_time / 1000,
354            epoch_time % 1000,
355            self.serial_counter.next(),
356            audit()
357        )
358    }
359}
360
361/// Audit message structure.
362pub struct AuditMessage {
363    /// The type of the audit message (e.g., AUDIT_AVC).
364    pub audit_type: u16,
365    /// The message to be audit-logged.
366    pub message: Vec<u8>,
367}