⚠️ VeridianOS Kernel Documentation - This is low-level kernel code. All functions are unsafe unless explicitly marked otherwise. no_std

veridian_kernel/security/
audit.rs

1//! Security audit framework
2//!
3//! Tracks and logs security-relevant events for compliance and forensics.
4//!
5//! # Features
6//!
7//! - Structured audit events with timestamps, PIDs, UIDs, and action details
8//! - Configurable event filtering via bitmask
9//! - Persistent storage to VFS-backed audit log (`/var/log/audit.log`)
10//! - Serialization to pipe-delimited text format
11//! - Convenience functions for syscall, capability, and MAC audit logging
12//! - Real-time alert callbacks for critical security events
13//! - Statistics tracking
14
15use alloc::{format, string::String};
16use core::sync::atomic::{AtomicBool, AtomicU64, AtomicUsize, Ordering};
17
18use spin::Mutex;
19
20use crate::error::KernelError;
21
22// ---------------------------------------------------------------------------
23// Audit Event Types
24// ---------------------------------------------------------------------------
25
26/// Audit event type.
27#[derive(Debug, Clone, Copy, PartialEq, Eq)]
28#[repr(u8)]
29pub enum AuditEventType {
30    /// Process creation
31    ProcessCreate = 0,
32    /// Process termination
33    ProcessExit = 1,
34    /// File access
35    FileAccess = 2,
36    /// Network connection
37    NetworkConnect = 3,
38    /// Authentication attempt
39    AuthAttempt = 4,
40    /// Permission denied
41    PermissionDenied = 5,
42    /// System call
43    Syscall = 6,
44    /// Capability operation
45    CapabilityOp = 7,
46    /// MAC policy decision
47    MacDecision = 8,
48    /// Privilege escalation attempt
49    PrivilegeEscalation = 9,
50    /// Security configuration change
51    SecurityConfigChange = 10,
52}
53
54impl AuditEventType {
55    /// Convert to bitmask flag for filtering.
56    pub fn to_flag(self) -> u32 {
57        1u32 << (self as u8)
58    }
59
60    /// Get a human-readable name for this event type.
61    pub fn as_str(self) -> &'static str {
62        match self {
63            Self::ProcessCreate => "PROCESS_CREATE",
64            Self::ProcessExit => "PROCESS_EXIT",
65            Self::FileAccess => "FILE_ACCESS",
66            Self::NetworkConnect => "NETWORK_CONNECT",
67            Self::AuthAttempt => "AUTH_ATTEMPT",
68            Self::PermissionDenied => "PERMISSION_DENIED",
69            Self::Syscall => "SYSCALL",
70            Self::CapabilityOp => "CAPABILITY_OP",
71            Self::MacDecision => "MAC_DECISION",
72            Self::PrivilegeEscalation => "PRIVILEGE_ESCALATION",
73            Self::SecurityConfigChange => "SECURITY_CONFIG_CHANGE",
74        }
75    }
76}
77
78// ---------------------------------------------------------------------------
79// Audit Action (structured action descriptions)
80// ---------------------------------------------------------------------------
81
82/// Structured audit action for detailed event logging.
83#[derive(Debug, Clone, Copy, PartialEq, Eq)]
84pub enum AuditAction {
85    /// Process created (with parent PID as data)
86    Create,
87    /// Process exited (with exit code as data)
88    Exit,
89    /// Read access
90    Read,
91    /// Write access
92    Write,
93    /// Execute access
94    Execute,
95    /// Delete operation
96    Delete,
97    /// Login attempt
98    Login,
99    /// Logout
100    Logout,
101    /// Capability created
102    CapCreate,
103    /// Capability revoked
104    CapRevoke,
105    /// Capability derived
106    CapDerive,
107    /// MAC allow decision
108    MacAllow,
109    /// MAC deny decision
110    MacDeny,
111    /// Privilege escalation
112    Escalate,
113    /// Configuration change
114    ConfigChange,
115    /// Generic operation
116    Other,
117}
118
119impl AuditAction {
120    /// Get a human-readable name.
121    pub fn as_str(self) -> &'static str {
122        match self {
123            Self::Create => "CREATE",
124            Self::Exit => "EXIT",
125            Self::Read => "READ",
126            Self::Write => "WRITE",
127            Self::Execute => "EXECUTE",
128            Self::Delete => "DELETE",
129            Self::Login => "LOGIN",
130            Self::Logout => "LOGOUT",
131            Self::CapCreate => "CAP_CREATE",
132            Self::CapRevoke => "CAP_REVOKE",
133            Self::CapDerive => "CAP_DERIVE",
134            Self::MacAllow => "MAC_ALLOW",
135            Self::MacDeny => "MAC_DENY",
136            Self::Escalate => "ESCALATE",
137            Self::ConfigChange => "CONFIG_CHANGE",
138            Self::Other => "OTHER",
139        }
140    }
141}
142
143// ---------------------------------------------------------------------------
144// Audit Event (structured)
145// ---------------------------------------------------------------------------
146
147/// Structured audit event record.
148///
149/// Fields are serialized in pipe-delimited format for persistent storage.
150#[derive(Debug, Clone)]
151pub struct AuditEvent {
152    /// Event type
153    pub event_type: AuditEventType,
154    /// Timestamp (seconds since boot)
155    pub timestamp: u64,
156    /// Process ID
157    pub pid: u64,
158    /// User ID
159    pub uid: u32,
160    /// Structured action
161    pub action: AuditAction,
162    /// Target resource or object name
163    pub target: String,
164    /// Whether the operation succeeded
165    pub result: bool,
166    /// Extra data / details
167    pub extra_data: String,
168}
169
170impl AuditEvent {
171    /// Create a new structured audit event with automatic timestamp.
172    pub fn new(
173        event_type: AuditEventType,
174        pid: u64,
175        uid: u32,
176        action: AuditAction,
177        target: &str,
178        result: bool,
179        extra_data: &str,
180    ) -> Self {
181        Self {
182            event_type,
183            timestamp: get_audit_timestamp(),
184            pid,
185            uid,
186            action,
187            target: String::from(target),
188            result,
189            extra_data: String::from(extra_data),
190        }
191    }
192
193    /// Create from legacy parameters (backward compat with old
194    /// AuditEvent::new).
195    pub fn from_legacy(
196        event_type: AuditEventType,
197        pid: u64,
198        uid: u32,
199        result_code: i32,
200        data: u64,
201    ) -> Self {
202        let action = match event_type {
203            AuditEventType::ProcessCreate => AuditAction::Create,
204            AuditEventType::ProcessExit => AuditAction::Exit,
205            AuditEventType::FileAccess => AuditAction::Read,
206            AuditEventType::AuthAttempt => AuditAction::Login,
207            AuditEventType::PermissionDenied => AuditAction::MacDeny,
208            AuditEventType::CapabilityOp => AuditAction::CapCreate,
209            _ => AuditAction::Other,
210        };
211
212        Self {
213            event_type,
214            timestamp: get_audit_timestamp(),
215            pid,
216            uid,
217            action,
218            target: format!("data:{:#x}", data),
219            result: result_code == 0,
220            extra_data: format!("result_code:{}", result_code),
221        }
222    }
223
224    /// Serialize to pipe-delimited text format.
225    ///
226    /// Format: `timestamp|event_type|pid|uid|action|target|result|extra_data\n`
227    pub fn serialize(&self) -> String {
228        format!(
229            "{}|{}|{}|{}|{}|{}|{}|{}\n",
230            self.timestamp,
231            self.event_type.as_str(),
232            self.pid,
233            self.uid,
234            self.action.as_str(),
235            self.target,
236            if self.result { "OK" } else { "FAIL" },
237            self.extra_data,
238        )
239    }
240}
241
242// ---------------------------------------------------------------------------
243// Audit Filter
244// ---------------------------------------------------------------------------
245
246/// Configurable audit event filter using a bitmask.
247///
248/// Each event type corresponds to a bit. If the bit is set, events of
249/// that type will be logged. If 0, all events are filtered out.
250#[derive(Debug, Clone, Copy)]
251pub struct AuditFilter {
252    /// Bitmask of enabled event types
253    pub enabled_types: u32,
254}
255
256impl AuditFilter {
257    /// Create a filter that allows all event types.
258    pub const fn allow_all() -> Self {
259        Self {
260            enabled_types: 0xFFFF_FFFF,
261        }
262    }
263
264    /// Create a filter that blocks all event types.
265    pub const fn deny_all() -> Self {
266        Self { enabled_types: 0 }
267    }
268
269    /// Create a filter from an explicit bitmask.
270    pub const fn from_mask(mask: u32) -> Self {
271        Self {
272            enabled_types: mask,
273        }
274    }
275
276    /// Check if a given event type is enabled.
277    pub fn is_enabled(&self, event_type: AuditEventType) -> bool {
278        (self.enabled_types & event_type.to_flag()) != 0
279    }
280
281    /// Enable an event type.
282    pub fn enable(&mut self, event_type: AuditEventType) {
283        self.enabled_types |= event_type.to_flag();
284    }
285
286    /// Disable an event type.
287    pub fn disable(&mut self, event_type: AuditEventType) {
288        self.enabled_types &= !event_type.to_flag();
289    }
290}
291
292// ---------------------------------------------------------------------------
293// Alert Callback System
294// ---------------------------------------------------------------------------
295
296/// Trait for real-time audit alert handlers.
297///
298/// Implementations receive critical audit events as they occur. This
299/// enables immediate response to security-relevant events such as
300/// authentication failures and privilege escalation attempts.
301pub trait AlertCallback: Send + Sync {
302    /// Called when a critical audit event is generated.
303    fn on_alert(&self, event: &AuditEvent);
304}
305
306/// Maximum number of registered alert callbacks.
307const MAX_ALERT_CALLBACKS: usize = 8;
308
309// ---------------------------------------------------------------------------
310// Audit Statistics
311// ---------------------------------------------------------------------------
312
313/// Audit statistics tracking.
314struct AuditStats {
315    /// Total events logged
316    total_events: AtomicU64,
317    /// Events filtered out
318    filtered_events: AtomicU64,
319    /// Events written to persistent storage
320    persisted_events: AtomicU64,
321    /// Alerts triggered
322    alerts_triggered: AtomicU64,
323    /// Per-type event counts (indexed by AuditEventType discriminant)
324    per_type_counts: [AtomicU64; 16],
325}
326
327impl AuditStats {
328    const fn new() -> Self {
329        Self {
330            total_events: AtomicU64::new(0),
331            filtered_events: AtomicU64::new(0),
332            persisted_events: AtomicU64::new(0),
333            alerts_triggered: AtomicU64::new(0),
334            per_type_counts: [
335                AtomicU64::new(0),
336                AtomicU64::new(0),
337                AtomicU64::new(0),
338                AtomicU64::new(0),
339                AtomicU64::new(0),
340                AtomicU64::new(0),
341                AtomicU64::new(0),
342                AtomicU64::new(0),
343                AtomicU64::new(0),
344                AtomicU64::new(0),
345                AtomicU64::new(0),
346                AtomicU64::new(0),
347                AtomicU64::new(0),
348                AtomicU64::new(0),
349                AtomicU64::new(0),
350                AtomicU64::new(0),
351            ],
352        }
353    }
354
355    fn record_event(&self, event_type: AuditEventType) {
356        self.total_events.fetch_add(1, Ordering::Relaxed);
357        let idx = event_type as usize;
358        if idx < self.per_type_counts.len() {
359            self.per_type_counts[idx].fetch_add(1, Ordering::Relaxed);
360        }
361    }
362
363    fn record_filtered(&self) {
364        self.filtered_events.fetch_add(1, Ordering::Relaxed);
365    }
366
367    fn record_persisted(&self) {
368        self.persisted_events.fetch_add(1, Ordering::Relaxed);
369    }
370
371    fn record_alert(&self) {
372        self.alerts_triggered.fetch_add(1, Ordering::Relaxed);
373    }
374}
375
376// ---------------------------------------------------------------------------
377// Global State
378// ---------------------------------------------------------------------------
379
380/// Maximum audit log size (circular buffer).
381const MAX_AUDIT_LOG: usize = 4096;
382
383/// Audit log buffer (circular), protected by a Mutex.
384static AUDIT_LOG: Mutex<[Option<AuditEvent>; MAX_AUDIT_LOG]> =
385    Mutex::new([const { None }; MAX_AUDIT_LOG]);
386static AUDIT_HEAD: AtomicUsize = AtomicUsize::new(0);
387static AUDIT_COUNT: AtomicUsize = AtomicUsize::new(0);
388static AUDIT_ENABLED: AtomicBool = AtomicBool::new(false);
389
390/// Event filter (protected by Mutex since AuditFilter is not atomic).
391static AUDIT_FILTER: Mutex<AuditFilter> = Mutex::new(AuditFilter::allow_all());
392
393/// Alert callbacks (protected by Mutex).
394static ALERT_CALLBACKS: Mutex<[Option<&'static dyn AlertCallback>; MAX_ALERT_CALLBACKS]> =
395    Mutex::new([None; MAX_ALERT_CALLBACKS]);
396
397/// Global statistics.
398static AUDIT_STATS: AuditStats = AuditStats::new();
399
400/// Path for the persistent audit log file.
401const AUDIT_LOG_PATH: &str = "/var/log/audit.log";
402
403// ---------------------------------------------------------------------------
404// Timestamp Helper
405// ---------------------------------------------------------------------------
406
407/// Get a timestamp for audit events.
408fn get_audit_timestamp() -> u64 {
409    #[cfg(any(
410        target_arch = "x86_64",
411        target_arch = "aarch64",
412        target_arch = "riscv64"
413    ))]
414    {
415        crate::arch::timer::get_timestamp_secs()
416    }
417    #[cfg(not(any(
418        target_arch = "x86_64",
419        target_arch = "aarch64",
420        target_arch = "riscv64"
421    )))]
422    {
423        0
424    }
425}
426
427// ---------------------------------------------------------------------------
428// Core Logging
429// ---------------------------------------------------------------------------
430
431/// Log a structured audit event.
432///
433/// Events are checked against the active filter. If accepted, they are
434/// stored in the circular in-memory buffer and optionally written to
435/// persistent storage.
436///
437/// Uses try_lock() with graceful degradation to avoid deadlocks when called
438/// from syscall return paths or interrupt contexts. If locks are held,
439/// the event is skipped rather than blocking.
440pub fn log_event(event: AuditEvent) {
441    if !AUDIT_ENABLED.load(Ordering::Acquire) {
442        return;
443    }
444
445    // Try to acquire filter lock without blocking. If lock contention occurs,
446    // skip this event to avoid deadlock during syscall return path or
447    // interrupt context. This is safe because audit logging is best-effort.
448    let filter = match AUDIT_FILTER.try_lock() {
449        Some(f) => f,
450        None => {
451            // Lock contention - skip event to avoid deadlock
452            AUDIT_STATS.record_filtered();
453            return;
454        }
455    };
456
457    if !filter.is_enabled(event.event_type) {
458        AUDIT_STATS.record_filtered();
459        return;
460    }
461    drop(filter); // Release early
462
463    // Record statistics
464    AUDIT_STATS.record_event(event.event_type);
465
466    // Check for critical events and fire alerts
467    if is_critical_event(&event) {
468        fire_alerts(&event);
469    }
470
471    // Write to persistent storage (best-effort, don't fail if VFS not ready)
472    persist_event(&event);
473
474    // Store in circular buffer with try_lock. If the buffer lock is held,
475    // the event is dropped (acceptable for non-critical events).
476    if let Some(mut log) = AUDIT_LOG.try_lock() {
477        let head = AUDIT_HEAD.load(Ordering::Relaxed);
478        log[head] = Some(event);
479        AUDIT_HEAD.store((head + 1) % MAX_AUDIT_LOG, Ordering::Relaxed);
480
481        let count = AUDIT_COUNT.load(Ordering::Relaxed);
482        if count < MAX_AUDIT_LOG {
483            AUDIT_COUNT.store(count + 1, Ordering::Relaxed);
484        }
485    }
486    // If try_lock fails, event is dropped (graceful degradation)
487}
488
489/// Check if an event is critical enough to trigger alerts.
490fn is_critical_event(event: &AuditEvent) -> bool {
491    matches!(
492        event.event_type,
493        AuditEventType::PermissionDenied
494            | AuditEventType::PrivilegeEscalation
495            | AuditEventType::SecurityConfigChange
496    ) || (event.event_type == AuditEventType::AuthAttempt && !event.result)
497}
498
499/// Fire all registered alert callbacks for a critical event.
500fn fire_alerts(event: &AuditEvent) {
501    let callbacks = ALERT_CALLBACKS.lock();
502    for cb in callbacks.iter().flatten() {
503        cb.on_alert(event);
504        AUDIT_STATS.record_alert();
505    }
506}
507
508/// Write an event to persistent VFS-backed audit log (best-effort).
509fn persist_event(event: &AuditEvent) {
510    let serialized = event.serialize();
511    let bytes = serialized.as_bytes();
512
513    // VFS is only available on bare-metal; skip persistence in host tests.
514    #[cfg(not(test))]
515    {
516        // Try to append to the audit log file; ignore errors if VFS is not
517        // mounted or the path does not exist yet.
518        if crate::fs::append_file(AUDIT_LOG_PATH, bytes).is_ok() {
519            AUDIT_STATS.record_persisted();
520        }
521    }
522    #[cfg(test)]
523    let _ = bytes;
524}
525
526// ---------------------------------------------------------------------------
527// Convenience Logging Functions
528// ---------------------------------------------------------------------------
529
530/// Log a process creation event.
531pub fn log_process_create(pid: u64, uid: u32, result: i32) {
532    log_event(AuditEvent::new(
533        AuditEventType::ProcessCreate,
534        pid,
535        uid,
536        AuditAction::Create,
537        "process",
538        result == 0,
539        "",
540    ));
541}
542
543/// Log a process exit event.
544pub fn log_process_exit(pid: u64, exit_code: i32) {
545    log_event(AuditEvent::new(
546        AuditEventType::ProcessExit,
547        pid,
548        0,
549        AuditAction::Exit,
550        "process",
551        true,
552        &format!("exit_code:{}", exit_code),
553    ));
554}
555
556/// Log a file access event.
557pub fn log_file_access(pid: u64, uid: u32, path_hash: u64, access_type: u32) {
558    let action = match access_type {
559        0 => AuditAction::Read,
560        1 => AuditAction::Write,
561        2 => AuditAction::Execute,
562        _ => AuditAction::Other,
563    };
564    log_event(AuditEvent::new(
565        AuditEventType::FileAccess,
566        pid,
567        uid,
568        action,
569        &format!("file:{:#x}", path_hash),
570        true,
571        "",
572    ));
573}
574
575/// Log a permission denial event.
576pub fn log_permission_denied(pid: u64, uid: u32, target: &str) {
577    log_event(AuditEvent::new(
578        AuditEventType::PermissionDenied,
579        pid,
580        uid,
581        AuditAction::MacDeny,
582        target,
583        false,
584        "denied",
585    ));
586}
587
588/// Log a capability operation (create, revoke, derive).
589pub fn log_capability_op(pid: u64, cap_id: u64, result: i32) {
590    log_event(AuditEvent::new(
591        AuditEventType::CapabilityOp,
592        pid,
593        0,
594        AuditAction::CapCreate,
595        &format!("cap:{:#x}", cap_id),
596        result == 0,
597        "",
598    ));
599}
600
601/// Log a system call event.
602///
603/// Called from the syscall dispatch path.
604pub fn log_syscall(pid: u64, uid: u32, syscall_nr: usize, result: bool) {
605    log_event(AuditEvent::new(
606        AuditEventType::Syscall,
607        pid,
608        uid,
609        AuditAction::Other,
610        &format!("syscall:{}", syscall_nr),
611        result,
612        "",
613    ));
614}
615
616/// Log a capability operation with a specific action.
617pub fn log_capability(pid: u64, cap_id: u64, action: AuditAction, result: bool) {
618    log_event(AuditEvent::new(
619        AuditEventType::CapabilityOp,
620        pid,
621        0,
622        action,
623        &format!("cap:{:#x}", cap_id),
624        result,
625        "",
626    ));
627}
628
629/// Log a MAC policy decision.
630pub fn log_mac_decision(
631    pid: u64,
632    uid: u32,
633    source_type: &str,
634    target_type: &str,
635    access: &str,
636    allowed: bool,
637) {
638    let action = if allowed {
639        AuditAction::MacAllow
640    } else {
641        AuditAction::MacDeny
642    };
643    log_event(AuditEvent::new(
644        AuditEventType::MacDecision,
645        pid,
646        uid,
647        action,
648        &format!("{}:{}", source_type, target_type),
649        allowed,
650        access,
651    ));
652}
653
654/// Log an authentication attempt.
655pub fn log_auth_attempt(pid: u64, uid: u32, username: &str, success: bool) {
656    log_event(AuditEvent::new(
657        AuditEventType::AuthAttempt,
658        pid,
659        uid,
660        AuditAction::Login,
661        username,
662        success,
663        if success {
664            "login_success"
665        } else {
666            "login_failure"
667        },
668    ));
669}
670
671// ---------------------------------------------------------------------------
672// Alert Callback Registration
673// ---------------------------------------------------------------------------
674
675/// Register a real-time alert callback.
676///
677/// The callback will be invoked for critical events such as authentication
678/// failures, permission denials, and privilege escalation attempts.
679///
680/// Returns `Ok(())` if registered, or `Err` if all callback slots are full.
681pub fn register_alert_callback(callback: &'static dyn AlertCallback) -> Result<(), KernelError> {
682    let mut callbacks = ALERT_CALLBACKS.lock();
683    for slot in callbacks.iter_mut() {
684        if slot.is_none() {
685            *slot = Some(callback);
686            return Ok(());
687        }
688    }
689    Err(KernelError::ResourceExhausted {
690        resource: "audit alert callbacks",
691    })
692}
693
694// ---------------------------------------------------------------------------
695// Filter Management
696// ---------------------------------------------------------------------------
697
698/// Set the audit event filter.
699pub fn set_filter(filter: AuditFilter) {
700    let mut f = AUDIT_FILTER.lock();
701    *f = filter;
702}
703
704/// Get the current audit event filter.
705pub fn get_filter() -> AuditFilter {
706    *AUDIT_FILTER.lock()
707}
708
709/// Enable a specific event type in the filter.
710pub fn enable_event_type(event_type: AuditEventType) {
711    let mut f = AUDIT_FILTER.lock();
712    f.enable(event_type);
713}
714
715/// Disable a specific event type in the filter.
716pub fn disable_event_type(event_type: AuditEventType) {
717    let mut f = AUDIT_FILTER.lock();
718    f.disable(event_type);
719}
720
721// ---------------------------------------------------------------------------
722// Statistics and Query
723// ---------------------------------------------------------------------------
724
725/// Get audit log statistics: (current_count, max_capacity).
726pub fn get_stats() -> (usize, usize) {
727    (AUDIT_COUNT.load(Ordering::Relaxed), MAX_AUDIT_LOG)
728}
729
730/// Get detailed audit statistics.
731pub fn get_detailed_stats() -> AuditStatistics {
732    AuditStatistics {
733        total_events: AUDIT_STATS.total_events.load(Ordering::Relaxed),
734        filtered_events: AUDIT_STATS.filtered_events.load(Ordering::Relaxed),
735        persisted_events: AUDIT_STATS.persisted_events.load(Ordering::Relaxed),
736        alerts_triggered: AUDIT_STATS.alerts_triggered.load(Ordering::Relaxed),
737        buffer_count: AUDIT_COUNT.load(Ordering::Relaxed) as u64,
738        buffer_capacity: MAX_AUDIT_LOG as u64,
739    }
740}
741
742/// Detailed audit statistics snapshot.
743#[derive(Debug, Clone, Copy)]
744pub struct AuditStatistics {
745    /// Total events processed
746    pub total_events: u64,
747    /// Events dropped by filter
748    pub filtered_events: u64,
749    /// Events written to persistent storage
750    pub persisted_events: u64,
751    /// Alerts triggered
752    pub alerts_triggered: u64,
753    /// Events currently in buffer
754    pub buffer_count: u64,
755    /// Buffer capacity
756    pub buffer_capacity: u64,
757}
758
759// ---------------------------------------------------------------------------
760// Enable / Disable
761// ---------------------------------------------------------------------------
762
763/// Enable audit logging.
764pub fn enable() {
765    AUDIT_ENABLED.store(true, Ordering::Release);
766    println!("[AUDIT] Audit logging enabled");
767}
768
769/// Disable audit logging.
770pub fn disable() {
771    AUDIT_ENABLED.store(false, Ordering::Release);
772    println!("[AUDIT] Audit logging disabled");
773}
774
775// ---------------------------------------------------------------------------
776// Initialization
777// ---------------------------------------------------------------------------
778
779/// Initialize audit system.
780pub fn init() -> Result<(), KernelError> {
781    println!("[AUDIT] Initializing audit framework...");
782
783    // Clear audit log
784    AUDIT_HEAD.store(0, Ordering::Relaxed);
785    AUDIT_COUNT.store(0, Ordering::Relaxed);
786
787    // Set default filter (all events enabled)
788    set_filter(AuditFilter::allow_all());
789
790    // Try to create the audit log directory (best-effort)
791    // The VFS may not be mounted yet; persistent logging will start
792    // once the filesystem is available.
793    let _ = ensure_audit_log_dir();
794
795    // Enable auditing
796    enable();
797
798    println!("[AUDIT] Audit framework initialized");
799    Ok(())
800}
801
802/// Best-effort creation of `/var/log/` directory for audit log storage.
803fn ensure_audit_log_dir() -> Result<(), KernelError> {
804    // Try to create /var, /var/log directories if they don't exist.
805    // This is best-effort; if VFS is not mounted we silently skip.
806    if let Some(vfs_lock) = crate::fs::try_get_vfs() {
807        let vfs = vfs_lock.read();
808        let perms = crate::fs::Permissions::default();
809        let _ = vfs.mkdir("/var", perms);
810        let _ = vfs.mkdir("/var/log", perms);
811    }
812    Ok(())
813}
814
815// ---------------------------------------------------------------------------
816// Internal helpers
817// ---------------------------------------------------------------------------
818
819/// Simple string hash function (kept for backward compatibility).
820fn _simple_hash(s: &str) -> u64 {
821    let mut hash = 0u64;
822    for byte in s.bytes() {
823        hash = hash.wrapping_mul(31).wrapping_add(byte as u64);
824    }
825    hash
826}
827
828// ---------------------------------------------------------------------------
829// Tests
830// ---------------------------------------------------------------------------
831
832#[cfg(test)]
833mod tests {
834    use super::*;
835
836    #[test]
837    fn test_audit_event() {
838        let event = AuditEvent::new(
839            AuditEventType::ProcessCreate,
840            123,
841            1000,
842            AuditAction::Create,
843            "process",
844            true,
845            "",
846        );
847        assert_eq!(event.pid, 123);
848        assert_eq!(event.uid, 1000);
849        assert!(event.result);
850    }
851
852    #[test]
853    fn test_log_event() {
854        enable(); // Auditing is disabled by default; must enable before logging
855        log_process_create(456, 1000, 0);
856        let (count, _) = get_stats();
857        assert!(count > 0);
858    }
859
860    #[test]
861    fn test_audit_filter() {
862        let mut filter = AuditFilter::deny_all();
863        assert!(!filter.is_enabled(AuditEventType::ProcessCreate));
864
865        filter.enable(AuditEventType::ProcessCreate);
866        assert!(filter.is_enabled(AuditEventType::ProcessCreate));
867        assert!(!filter.is_enabled(AuditEventType::FileAccess));
868
869        filter.disable(AuditEventType::ProcessCreate);
870        assert!(!filter.is_enabled(AuditEventType::ProcessCreate));
871    }
872
873    #[test]
874    fn test_event_serialization() {
875        let event = AuditEvent::new(
876            AuditEventType::FileAccess,
877            42,
878            1000,
879            AuditAction::Read,
880            "/etc/passwd",
881            true,
882            "mode:0644",
883        );
884        let serialized = event.serialize();
885        assert!(serialized.contains("FILE_ACCESS"));
886        assert!(serialized.contains("42"));
887        assert!(serialized.contains("READ"));
888        assert!(serialized.contains("/etc/passwd"));
889        assert!(serialized.contains("OK"));
890    }
891}