// Copyright (C) 2024 Bellande Architecture Mechanism Research Innovation Center, Ronaldson Bellande // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with this program. If not, see . use chrono::Utc; use regex::Regex; use std::collections::HashMap; use std::fs::{self, File}; use std::io::{BufRead, BufReader}; use std::os::unix::fs::PermissionsExt; use std::path::{Path, PathBuf}; use std::process::Command; use anyhow::{Context, Result}; use log::{info, warn}; use serde::{Deserialize, Serialize}; use sha2::{Digest, Sha256}; use std::os::unix::fs::MetadataExt; use crate::audit::audit::log_audit_event; use crate::config::config::Config; #[derive(Debug)] pub struct NetworkRequirements { pub required_protocols: Vec, pub minimum_networks: usize, pub required_firewall: bool, pub required_encryption: bool, } #[derive(Debug)] pub struct ComplianceConfig { // Original fields pub min_password_length: usize, pub min_password_entropy: f64, pub password_complexity_regex: String, pub critical_files: Vec, pub required_services: Vec, pub required_kernel_params: Vec, pub audit_file_hashes: PathBuf, pub network_requirements: NetworkRequirements, // New password policy fields pub password_max_days: u32, pub password_min_days: u32, pub password_warn_days: u32, pub max_repeated_chars: usize, } #[derive(Debug)] struct PasswordViolation { description: String, severity: ViolationSeverity, } #[derive(Debug)] enum ViolationSeverity { Low, Medium, High, Critical, } impl Default for ComplianceConfig { fn default() -> Self { let security_paths = get_security_paths(); let critical_files = security_paths.get("critical").cloned().unwrap_or_default(); let services = get_security_services(); let required_services = services.get("required").cloned().unwrap_or_default(); ComplianceConfig { min_password_length: 12, min_password_entropy: 50.0, max_repeated_chars: 3, password_max_days: 90, password_min_days: 1, password_warn_days: 7, password_complexity_regex: String::from( r"^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{12,}$", ), critical_files, required_services, // Fix the type mismatch by converting HashMap to Vec required_kernel_params: get_required_kernel_params() .into_iter() .map(|(key, value)| format!("{}={}", key, value)) .collect(), audit_file_hashes: PathBuf::from("audit_hashes.db"), network_requirements: NetworkRequirements { required_protocols: vec![ "TLSv1.3".to_string(), "SSHv2".to_string(), "TLS_AES_256_GCM_SHA384".to_string(), "TLS_CHACHA20_POLY1305_SHA256".to_string(), ], minimum_networks: 1, required_firewall: true, required_encryption: true, }, } } } fn get_security_paths() -> HashMap> { let mut paths = HashMap::new(); match std::env::consts::OS { "linux" => { paths.insert( "critical".to_string(), vec![ PathBuf::from("/etc/security/limits.conf"), PathBuf::from("/etc/security/pwquality.conf"), PathBuf::from("/etc/passwd"), PathBuf::from("/etc/shadow"), PathBuf::from("/etc/group"), PathBuf::from("/etc/sudoers"), PathBuf::from("/etc/ssh/sshd_config"), ], ); } "bellandeos" => { paths.insert( "critical".to_string(), vec![ PathBuf::from("/bell/security/audit.conf"), PathBuf::from("/bell/security/password.conf"), PathBuf::from("/bell/security/users"), PathBuf::from("/bell/security/access"), PathBuf::from("/bell/security/keys"), PathBuf::from("/bell/config/system"), ], ); } "macos" => { paths.insert( "critical".to_string(), vec![ PathBuf::from("/etc/security/audit_control"), PathBuf::from("/etc/security/pwpolicy"), PathBuf::from("/etc/pam.d"), PathBuf::from("/Library/Security"), PathBuf::from("/etc/ssh/sshd_config"), ], ); } _ => {} } paths } fn get_required_services() -> Vec { match std::env::consts::OS { "macos" => vec![ "com.apple.auditd".to_string(), "com.apple.security".to_string(), ], "linux" => vec!["auditd".to_string(), "sshd".to_string(), "ufw".to_string()], "bellandeos" => vec![ "bell.audit".to_string(), "bell.security".to_string(), "bell.firewall".to_string(), ], _ => vec![], } } fn get_required_kernel_params() -> HashMap { let mut params = HashMap::new(); match std::env::consts::OS { "linux" => { // Memory protection params.insert("kernel.randomize_va_space".to_string(), "2".to_string()); params.insert("kernel.kptr_restrict".to_string(), "1".to_string()); params.insert("kernel.yama.ptrace_scope".to_string(), "1".to_string()); params.insert("vm.mmap_min_addr".to_string(), "65536".to_string()); // Network security params.insert("net.ipv4.tcp_syncookies".to_string(), "1".to_string()); params.insert("net.ipv4.conf.all.rp_filter".to_string(), "1".to_string()); params.insert( "net.ipv4.conf.default.rp_filter".to_string(), "1".to_string(), ); params.insert( "net.ipv4.conf.all.accept_redirects".to_string(), "0".to_string(), ); params.insert( "net.ipv6.conf.all.accept_redirects".to_string(), "0".to_string(), ); params.insert( "net.ipv4.conf.all.send_redirects".to_string(), "0".to_string(), ); params.insert( "net.ipv4.conf.all.accept_source_route".to_string(), "0".to_string(), ); params.insert( "net.ipv6.conf.all.accept_source_route".to_string(), "0".to_string(), ); // Core dumps params.insert("kernel.core_pattern".to_string(), "|/bin/false".to_string()); params.insert("fs.suid_dumpable".to_string(), "0".to_string()); // System security params.insert("kernel.sysrq".to_string(), "0".to_string()); params.insert("kernel.dmesg_restrict".to_string(), "1".to_string()); params.insert( "kernel.unprivileged_bpf_disabled".to_string(), "1".to_string(), ); // Module loading params.insert("kernel.modules_disabled".to_string(), "1".to_string()); // IPv6 security params.insert( "net.ipv6.conf.all.disable_ipv6".to_string(), "1".to_string(), ); params.insert( "net.ipv6.conf.default.disable_ipv6".to_string(), "1".to_string(), ); } "bellandeos" => { // General security params.insert("bell.security.level".to_string(), "high".to_string()); params.insert("bell.memory.protection".to_string(), "strict".to_string()); params.insert("bell.process.isolation".to_string(), "enforced".to_string()); // System hardening params.insert("bell.kernel.hardening".to_string(), "maximum".to_string()); params.insert("bell.syscall.filtering".to_string(), "strict".to_string()); params.insert( "bell.exploit.prevention".to_string(), "aggressive".to_string(), ); // Memory security params.insert("bell.memory.aslr".to_string(), "full".to_string()); params.insert("bell.stack.protection".to_string(), "strong".to_string()); params.insert("bell.heap.protection".to_string(), "strict".to_string()); // Network security params.insert("bell.network.filtering".to_string(), "strict".to_string()); params.insert("bell.network.isolation".to_string(), "enforced".to_string()); params.insert( "bell.network.encryption".to_string(), "required".to_string(), ); // Access control params.insert("bell.access.control".to_string(), "mandatory".to_string()); params.insert( "bell.privilege.escalation".to_string(), "restricted".to_string(), ); params.insert("bell.capability.control".to_string(), "strict".to_string()); // Monitoring and auditing params.insert("bell.audit.level".to_string(), "comprehensive".to_string()); params.insert("bell.monitoring.mode".to_string(), "active".to_string()); params.insert("bell.incident.detection".to_string(), "enabled".to_string()); } "macos" => { // While macOS doesn't use sysctl for all security settings, params.insert("kern.sugid_coredump".to_string(), "0".to_string()); params.insert( "kern.bootargs".to_string(), "cs_enforcement_disable=0".to_string(), ); params.insert("kern.secure_kernel".to_string(), "1".to_string()); params.insert("net.inet.tcp.blackhole".to_string(), "2".to_string()); params.insert("net.inet.udp.blackhole".to_string(), "1".to_string()); params.insert("net.inet.icmp.icmplim".to_string(), "50".to_string()); params.insert("net.inet.ip.forwarding".to_string(), "0".to_string()); params.insert("net.inet.ip.redirect".to_string(), "0".to_string()); params.insert("net.inet.tcp.always_keepalive".to_string(), "0".to_string()); params.insert("net.inet.tcp.drop_synfin".to_string(), "1".to_string()); } _ => {} } params } fn get_security_services() -> HashMap> { let mut services = HashMap::new(); match std::env::consts::OS { "linux" => { services.insert( "required".to_string(), vec![ "auditd".to_string(), "fail2ban".to_string(), "ufw".to_string(), "apparmor".to_string(), "systemd-journald".to_string(), ], ); services.insert( "prohibited".to_string(), vec![ "telnet".to_string(), "rsh".to_string(), "rlogin".to_string(), "rexec".to_string(), ], ); } "bellandeos" => { services.insert( "required".to_string(), vec![ "bell.audit".to_string(), "bell.security".to_string(), "bell.firewall".to_string(), "bell.intrusion_detection".to_string(), "bell.integrity_monitor".to_string(), "bell.endpoint_protection".to_string(), ], ); services.insert( "prohibited".to_string(), vec![ "bell.legacy_protocols".to_string(), "bell.unsecured_services".to_string(), ], ); } "macos" => { services.insert( "required".to_string(), vec![ "com.apple.auditd".to_string(), "com.apple.security.firewall".to_string(), "com.apple.security.fdesetup".to_string(), "com.apple.security.SecureIO".to_string(), ], ); services.insert( "prohibited".to_string(), vec!["com.apple.tftp".to_string(), "com.apple.ftp".to_string()], ); } _ => {} } services } pub async fn check_compliance(config: &Config) -> Result<()> { let compliance_config = ComplianceConfig::default(); info!("Starting compliance check for {}", std::env::consts::OS); // Password compliance check_password_complexity(config, &compliance_config).await?; // File permissions check_file_permissions(&compliance_config).await?; // System configurations check_system_configurations(&compliance_config).await?; // Audit log integrity check_audit_log_integrity(&compliance_config).await?; // Network configurations check_network_configurations(config, &compliance_config).await?; // OS-specific checks perform_os_specific_checks(&compliance_config).await?; log_audit_event( "COMPLIANCE_CHECK", "SYSTEM", &format!("Completed compliance check on {}", std::env::consts::OS), ) .await?; Ok(()) } async fn check_password_complexity( config: &Config, compliance_config: &ComplianceConfig, ) -> Result<()> { let regex = Regex::new(&compliance_config.password_complexity_regex) .context("Failed to compile password complexity regex")?; for user in &config.users { let mut violations: Vec = Vec::new(); // Check hash length (Argon2) if user.password_hash.len() < 60 { violations.push(PasswordViolation { description: "Password hash does not meet length requirements".to_string(), severity: ViolationSeverity::High, }); } // Check password expiry let days_since_change = (Utc::now() - user.password_changed_at).num_days(); if days_since_change > compliance_config.password_max_days as i64 { violations.push(PasswordViolation { description: format!( "Password expired {} days ago (max: {} days)", days_since_change, compliance_config.password_max_days ), severity: ViolationSeverity::Medium, }); } // Check if password will expire soon let days_until_expiry = compliance_config.password_max_days as i64 - days_since_change; if days_until_expiry <= compliance_config.password_warn_days as i64 && days_until_expiry > 0 { violations.push(PasswordViolation { description: format!("Password will expire in {} days", days_until_expiry), severity: ViolationSeverity::Low, }); } // Check additional password requirements if let Some(raw_password) = get_last_password_change(&user.username).await? { if !regex.is_match(&raw_password) { violations.push(PasswordViolation { description: "Password does not meet complexity requirements".to_string(), severity: ViolationSeverity::Critical, }); } // Check minimum length if raw_password.len() < compliance_config.min_password_length { violations.push(PasswordViolation { description: format!( "Password length ({}) below minimum required ({})", raw_password.len(), compliance_config.min_password_length ), severity: ViolationSeverity::High, }); } // Add detailed password validation let strength_violations = validate_password_strength(&raw_password, compliance_config); violations.extend(strength_violations); // Check for common passwords if is_common_password(&raw_password).await? { violations.push(PasswordViolation { description: "Password found in common password list".to_string(), severity: ViolationSeverity::Critical, }); } // Check password entropy let entropy = calculate_password_entropy(&raw_password); if entropy < compliance_config.min_password_entropy { violations.push(PasswordViolation { description: format!( "Password entropy too low: {:.2} bits (minimum: {} bits)", entropy, compliance_config.min_password_entropy ), severity: ViolationSeverity::High, }); } } // Log all violations for violation in violations { log_audit_event( "COMPLIANCE_VIOLATION", &user.username, &format!( "{} (Severity: {:?})", violation.description, violation.severity ), ) .await?; } } Ok(()) } async fn is_common_password(password: &str) -> Result { // This could be implemented by checking against a BellandeSQL of common passwords // For now, we'll just check some basic patterns let common_patterns = [ "password", "123456", "qwerty", "admin", "letmein", "welcome", ]; Ok(common_patterns .iter() .any(|&pattern| password.contains(pattern))) } fn validate_password_strength(password: &str, config: &ComplianceConfig) -> Vec { let mut violations: Vec = Vec::new(); // Check length if password.len() < config.min_password_length { violations.push(PasswordViolation { description: format!( "Password too short: {} chars (minimum {})", password.len(), config.min_password_length ), severity: ViolationSeverity::High, }); } // Check for uppercase letters if !password.chars().any(|c| c.is_uppercase()) { violations.push(PasswordViolation { description: "Password must contain at least one uppercase letter".to_string(), severity: ViolationSeverity::Medium, }); } // Check for lowercase letters if !password.chars().any(|c| c.is_lowercase()) { violations.push(PasswordViolation { description: "Password must contain at least one lowercase letter".to_string(), severity: ViolationSeverity::Medium, }); } // Check for numbers if !password.chars().any(|c| c.is_numeric()) { violations.push(PasswordViolation { description: "Password must contain at least one number".to_string(), severity: ViolationSeverity::Medium, }); } // Check for special characters if !password.chars().any(|c| !c.is_alphanumeric()) { violations.push(PasswordViolation { description: "Password must contain at least one special character".to_string(), severity: ViolationSeverity::Medium, }); } // Check for repeating characters if has_repeating_chars(password, 3) { violations.push(PasswordViolation { description: "Password contains repeating characters".to_string(), severity: ViolationSeverity::Low, }); } violations } // Helper function to check for repeating characters fn has_repeating_chars(password: &str, max_repeats: usize) -> bool { let chars: Vec = password.chars().collect(); let mut repeat_count = 1; for i in 1..chars.len() { if chars[i] == chars[i - 1] { repeat_count += 1; if repeat_count > max_repeats { return true; } } else { repeat_count = 1; } } false } // Entropy calculation for password strength fn calculate_password_entropy(password: &str) -> f64 { let mut charset_size = 0; // Check what types of characters are used let has_lowercase = password.chars().any(|c| c.is_ascii_lowercase()); let has_uppercase = password.chars().any(|c| c.is_ascii_uppercase()); let has_numbers = password.chars().any(|c| c.is_ascii_digit()); let has_symbols = password.chars().any(|c| !c.is_alphanumeric()); // Calculate charset size if has_lowercase { charset_size += 26; } if has_uppercase { charset_size += 26; } if has_numbers { charset_size += 10; } if has_symbols { charset_size += 32; } // Calculate entropy (password.len() as f64) * (charset_size as f64).log2() } #[cfg(target_family = "unix")] async fn check_file_permissions(compliance_config: &ComplianceConfig) -> Result<()> { for file_path in &compliance_config.critical_files { let metadata = fs::metadata(file_path) .context(format!("Failed to get metadata for {:?}", file_path))?; // Use direct metadata mode() for Unix systems let mode = metadata.mode() & 0o777; // Check for secure permissions if mode != 0o600 { log_audit_event( "COMPLIANCE_VIOLATION", "SYSTEM", &format!("Incorrect permissions on {:?}: {:o}", file_path, mode), ) .await?; } // Check ownership let uid = metadata.uid(); if uid != 0 { log_audit_event( "COMPLIANCE_VIOLATION", "SYSTEM", &format!("Incorrect ownership on {:?}", file_path), ) .await?; } // Check group ownership let gid = metadata.gid(); if gid != 0 { log_audit_event( "COMPLIANCE_VIOLATION", "SYSTEM", &format!("Incorrect group ownership on {:?}: GID {}", file_path, gid), ) .await?; } } Ok(()) } #[cfg(target_family = "unix")] async fn check_single_file_permissions(file_path: &Path) -> Result<()> { let metadata = fs::metadata(file_path).context(format!("Failed to get metadata for {:?}", file_path))?; let mode = metadata.mode() & 0o777; let uid = metadata.uid(); let gid = metadata.gid(); let mut violations = Vec::new(); // Check basic permissions if mode != 0o600 { violations.push(format!("incorrect permissions: {:o}", mode)); } // Check ownership if uid != 0 { violations.push(format!("incorrect owner UID: {}", uid)); } // Check group if gid != 0 { violations.push(format!("incorrect group GID: {}", gid)); } // Special bits check if mode & 0o7000 != 0 { violations.push(format!("special bits set: {:o}", mode & 0o7000)); } // Log all violations if any found if !violations.is_empty() { log_audit_event( "COMPLIANCE_VIOLATION", "SYSTEM", &format!( "Security violations for {:?}: {}", file_path, violations.join(", ") ), ) .await?; } Ok(()) } async fn check_system_configurations(compliance_config: &ComplianceConfig) -> Result<()> { match std::env::consts::OS { "macos" => check_macos_configurations().await?, "linux" => check_linux_configurations(compliance_config).await?, "bellandeos" => check_bellande_configurations().await?, _ => warn!("System configuration checking not implemented for this OS"), } // Check required services for service in &compliance_config.required_services { if !is_service_running(service).await? { log_audit_event( "COMPLIANCE_VIOLATION", "SYSTEM", &format!("Required service not running: {}", service), ) .await?; } } // Check kernel parameters for param in &compliance_config.required_kernel_params { let expected_value = get_expected_value(param)?; check_kernel_parameter(param, &expected_value).await?; } Ok(()) } async fn check_kernel_parameter(param: &str, expected_value: &str) -> Result<()> { match std::env::consts::OS { "linux" => { let output = Command::new("sysctl") .arg(param) .output() .context(format!("Failed to check kernel parameter: {}", param))?; let value = String::from_utf8_lossy(&output.stdout); if !value.contains(expected_value) { log_audit_event( "COMPLIANCE_VIOLATION", "SYSTEM", &format!("Kernel parameter {} has incorrect value", param), ) .await?; } } "bellandeos" => { let output = Command::new("bellctl") .args(&["kernel", "param", param]) .output() .context(format!("Failed to check BellandeOS parameter: {}", param))?; let value = String::from_utf8_lossy(&output.stdout); if !value.contains(expected_value) { log_audit_event( "COMPLIANCE_VIOLATION", "SYSTEM", &format!("BellandeOS parameter {} has incorrect value", param), ) .await?; } } _ => warn!("Kernel parameter checking not implemented for this OS"), } Ok(()) } fn get_expected_value(param: &str) -> Result { match param { "kernel.randomize_va_space" => Ok("2".to_string()), "net.ipv4.ip_forward" => Ok("0".to_string()), "kernel.yama.ptrace_scope" => Ok("1".to_string()), "kernel.kptr_restrict" => Ok("2".to_string()), "net.ipv4.conf.all.accept_redirects" => Ok("0".to_string()), "net.ipv4.conf.all.send_redirects" => Ok("0".to_string()), _ => Ok("0".to_string()), } } async fn get_kernel_parameter(param: &str) -> Result { match std::env::consts::OS { "linux" => { let path = format!("/proc/sys/{}", param.replace(".", "/")); Ok(fs::read_to_string(path)?.trim().to_string()) } _ => Ok("0".to_string()), } } async fn check_audit_log_integrity(compliance_config: &ComplianceConfig) -> Result<()> { // Load stored hashes let stored_hashes = load_file_hashes(&compliance_config.audit_file_hashes)?; // Check audit log file let audit_log_path = Path::new("audit_log.txt"); if !audit_log_path.exists() { log_audit_event( "COMPLIANCE_VIOLATION", "SYSTEM", "Audit log file is missing", ) .await?; return Ok(()); } // Calculate current hash let current_hash = calculate_file_hash(audit_log_path)?; // Compare with stored hash if let Some(stored_hash) = stored_hashes.get(audit_log_path) { if current_hash != *stored_hash { log_audit_event( "COMPLIANCE_VIOLATION", "SYSTEM", "Audit log integrity check failed", ) .await?; } } else { // Store initial hash save_file_hash( &compliance_config.audit_file_hashes, audit_log_path, ¤t_hash, )?; } Ok(()) } async fn check_network_configurations( config: &Config, compliance_config: &ComplianceConfig, ) -> Result<()> { // Check network restrictions if config.allowed_networks.len() < compliance_config.network_requirements.minimum_networks { log_audit_event( "COMPLIANCE_VIOLATION", "SYSTEM", "Insufficient network restrictions configured", ) .await?; } // Check firewall if compliance_config.network_requirements.required_firewall { check_firewall_status().await?; } // Check encryption requirements if compliance_config.network_requirements.required_encryption { check_network_encryption(&compliance_config.network_requirements).await?; } Ok(()) } async fn perform_os_specific_checks(compliance_config: &ComplianceConfig) -> Result<()> { match std::env::consts::OS { "macos" => { // Check SIP status let sip_output = Command::new("csrutil") .arg("status") .output() .context("Failed to check SIP status")?; if !String::from_utf8_lossy(&sip_output.stdout).contains("enabled") { log_audit_event( "COMPLIANCE_VIOLATION", "SYSTEM", "System Integrity Protection is disabled", ) .await?; } // Check FileVault let filevault_output = Command::new("fdesetup") .arg("status") .output() .context("Failed to check FileVault status")?; if !String::from_utf8_lossy(&filevault_output.stdout).contains("On") { log_audit_event("COMPLIANCE_VIOLATION", "SYSTEM", "FileVault is not enabled") .await?; } } "linux" => { // Check SELinux if !Path::new("/sys/fs/selinux/enforce").exists() { log_audit_event("COMPLIANCE_VIOLATION", "SYSTEM", "SELinux is not enabled").await?; } // Check AppArmor let apparmor_output = Command::new("aa-status") .output() .context("Failed to check AppArmor status")?; if !apparmor_output.status.success() { log_audit_event( "COMPLIANCE_VIOLATION", "SYSTEM", "AppArmor is not properly configured", ) .await?; } } "bellandeos" => { // Check BellandeOS security module let security_output = Command::new("bellctl") .args(&["security", "status"]) .output() .context("Failed to check BellandeOS security status")?; if !String::from_utf8_lossy(&security_output.stdout).contains("enabled") { log_audit_event( "COMPLIANCE_VIOLATION", "SYSTEM", "BellandeOS security module is not enabled", ) .await?; } // Check BellandeOS integrity let integrity_output = Command::new("bellctl") .args(&["verify", "system"]) .output() .context("Failed to verify BellandeOS integrity")?; if !integrity_output.status.success() { log_audit_event( "COMPLIANCE_VIOLATION", "SYSTEM", "BellandeOS system integrity check failed", ) .await?; } } _ => warn!("OS-specific checks not implemented for this operating system"), } Ok(()) } async fn check_macos_configurations() -> Result<()> { // Check System Integrity Protection (SIP) let sip_output = Command::new("csrutil") .arg("status") .output() .context("Failed to check SIP status")?; if !String::from_utf8_lossy(&sip_output.stdout).contains("enabled") { log_audit_event( "COMPLIANCE_VIOLATION", "SYSTEM", "System Integrity Protection (SIP) is disabled", ) .await?; } // Check FileVault encryption let filevault_output = Command::new("fdesetup") .arg("status") .output() .context("Failed to check FileVault status")?; if !String::from_utf8_lossy(&filevault_output.stdout).contains("FileVault is On") { log_audit_event( "COMPLIANCE_VIOLATION", "SYSTEM", "FileVault encryption is not enabled", ) .await?; } // Check Gatekeeper status let gatekeeper_output = Command::new("spctl") .args(&["--status"]) .output() .context("Failed to check Gatekeeper status")?; if !String::from_utf8_lossy(&gatekeeper_output.stdout).contains("assessments enabled") { log_audit_event( "COMPLIANCE_VIOLATION", "SYSTEM", "Gatekeeper is not enabled", ) .await?; } // Check software update settings let update_output = Command::new("softwareupdate") .args(&["--schedule"]) .output() .context("Failed to check software update schedule")?; if !String::from_utf8_lossy(&update_output.stdout).contains("enabled") { log_audit_event( "COMPLIANCE_VIOLATION", "SYSTEM", "Automatic software updates are not enabled", ) .await?; } // Check firewall status let firewall_output = Command::new("defaults") .args(&["read", "/Library/Preferences/com.apple.alf", "globalstate"]) .output() .context("Failed to check firewall status")?; if !String::from_utf8_lossy(&firewall_output.stdout) .trim() .eq("1") { log_audit_event("COMPLIANCE_VIOLATION", "SYSTEM", "Firewall is not enabled").await?; } Ok(()) } async fn check_linux_configurations(compliance_config: &ComplianceConfig) -> Result<()> { // Check SELinux status if Path::new("/etc/selinux/config").exists() { let selinux_output = Command::new("getenforce") .output() .context("Failed to check SELinux status")?; if !String::from_utf8_lossy(&selinux_output.stdout).contains("Enforcing") { log_audit_event( "COMPLIANCE_VIOLATION", "SYSTEM", "SELinux is not in enforcing mode", ) .await?; } } // Check firewall status (UFW) let ufw_output = Command::new("ufw") .arg("status") .output() .context("Failed to check UFW status")?; if !String::from_utf8_lossy(&ufw_output.stdout).contains("active") { log_audit_event( "COMPLIANCE_VIOLATION", "SYSTEM", "UFW firewall is not active", ) .await?; } // Check system security settings check_sysctl_settings().await?; // Check important security files let security_files = ["/etc/shadow", "/etc/passwd", "/etc/group", "/etc/sudoers"]; for file in &security_files { let metadata = fs::metadata(file).context(format!("Failed to check permissions for {}", file))?; let mode = metadata.permissions().mode(); if mode & 0o777 != 0o600 { log_audit_event( "COMPLIANCE_VIOLATION", "SYSTEM", &format!("Incorrect permissions on {}: {:o}", file, mode & 0o777), ) .await?; } } // Check for password policies check_password_policies().await?; // Check for core dumps check_core_dumps().await?; Ok(()) } async fn check_bellande_configurations() -> Result<()> { // Check BellandeOS security module status let security_status = Command::new("bellctl") .args(&["security", "status"]) .output() .context("Failed to check BellandeOS security status")?; if !String::from_utf8_lossy(&security_status.stdout).contains("enabled") { log_audit_event( "COMPLIANCE_VIOLATION", "SYSTEM", "BellandeOS security module is not enabled", ) .await?; } // Check BellandeOS integrity let integrity_check = Command::new("bellctl") .args(&["verify", "system"]) .output() .context("Failed to verify BellandeOS integrity")?; if !integrity_check.status.success() { log_audit_event( "COMPLIANCE_VIOLATION", "SYSTEM", "BellandeOS system integrity check failed", ) .await?; } // Check BellandeOS encryption status let encryption_status = Command::new("bellctl") .args(&["encryption", "status"]) .output() .context("Failed to check encryption status")?; if !String::from_utf8_lossy(&encryption_status.stdout).contains("enabled") { log_audit_event( "COMPLIANCE_VIOLATION", "SYSTEM", "BellandeOS encryption is not enabled", ) .await?; } // Check BellandeOS firewall configuration let firewall_status = Command::new("bellctl") .args(&["firewall", "status"]) .output() .context("Failed to check firewall status")?; if !firewall_status.status.success() { log_audit_event( "COMPLIANCE_VIOLATION", "SYSTEM", "BellandeOS firewall is not properly configured", ) .await?; } // Check security policies check_bellande_security_policies().await?; Ok(()) } // Helper functions async fn check_sysctl_settings() -> Result<()> { let sysctl_checks = [ ("kernel.randomize_va_space", "2"), ("kernel.kptr_restrict", "2"), ("kernel.dmesg_restrict", "1"), ("kernel.yama.ptrace_scope", "1"), ("net.ipv4.conf.all.rp_filter", "1"), ("net.ipv4.conf.default.rp_filter", "1"), ]; for (setting, expected_value) in &sysctl_checks { let output = Command::new("sysctl") .arg("-n") .arg(setting) .output() .context(format!("Failed to check sysctl setting: {}", setting))?; let value = String::from_utf8_lossy(&output.stdout).trim().to_string(); if value != *expected_value { log_audit_event( "COMPLIANCE_VIOLATION", "SYSTEM", &format!( "Incorrect sysctl setting {}: expected {}, got {}", setting, expected_value, value ), ) .await?; } } Ok(()) } async fn check_password_policies() -> Result<()> { let login_defs_path = "/etc/login.defs"; if Path::new(login_defs_path).exists() { let content = fs::read_to_string(login_defs_path).context("Failed to read login.defs")?; let checks = [ ("PASS_MAX_DAYS", "90"), ("PASS_MIN_DAYS", "1"), ("PASS_MIN_LEN", "12"), ("PASS_WARN_AGE", "7"), ]; for (setting, expected) in &checks { if !content.lines().any(|line| { line.starts_with(setting) && line.split_whitespace().nth(1) == Some(expected) }) { log_audit_event( "COMPLIANCE_VIOLATION", "SYSTEM", &format!("Incorrect password policy setting: {}", setting), ) .await?; } } } Ok(()) } async fn check_core_dumps() -> Result<()> { let limits_conf = "/etc/security/limits.conf"; if Path::new(limits_conf).exists() { let content = fs::read_to_string(limits_conf).context("Failed to read limits.conf")?; if !content.lines().any(|line| line.contains("* hard core 0")) { log_audit_event( "COMPLIANCE_VIOLATION", "SYSTEM", "Core dumps are not disabled", ) .await?; } } Ok(()) } async fn check_bellande_security_policies() -> Result<()> { let policies = [ ("password-complexity", "high"), ("session-timeout", "enabled"), ("audit-level", "full"), ("network-isolation", "enforced"), ]; for (policy, expected_value) in &policies { let output = Command::new("bellctl") .args(&["policy", "get", policy]) .output() .context(format!("Failed to check policy: {}", policy))?; let value = String::from_utf8_lossy(&output.stdout).trim().to_string(); if value != *expected_value { log_audit_event( "COMPLIANCE_VIOLATION", "SYSTEM", &format!( "Incorrect security policy {}: expected {}, got {}", policy, expected_value, value ), ) .await?; } } Ok(()) } async fn is_service_running(service: &str) -> Result { match std::env::consts::OS { "macos" => { let output = Command::new("launchctl") .args(&["list", service]) .output()?; Ok(output.status.success()) } "linux" => { let output = Command::new("systemctl") .args(&["is-active", service]) .output()?; Ok(output.status.success()) } "bellandeos" => { let output = Command::new("bellctl") .args(&["service", "status", service]) .output()?; Ok(output.status.success()) } _ => Ok(false), } } async fn check_firewall_status() -> Result<()> { match std::env::consts::OS { "macos" => { let output = Command::new("defaults") .args(&["read", "/Library/Preferences/com.apple.alf", "globalstate"]) .output() .context("Failed to check macOS firewall status")?; if !String::from_utf8_lossy(&output.stdout).contains("1") { log_audit_event( "COMPLIANCE_VIOLATION", "SYSTEM", "macOS firewall is not enabled", ) .await?; } } "linux" => { let output = Command::new("ufw") .arg("status") .output() .context("Failed to check UFW status")?; if !String::from_utf8_lossy(&output.stdout).contains("Status: active") { log_audit_event( "COMPLIANCE_VIOLATION", "SYSTEM", "UFW firewall is not active", ) .await?; } } "bellandeos" => { let output = Command::new("bellctl") .args(&["firewall", "status"]) .output() .context("Failed to check BellandeOS firewall status")?; if !String::from_utf8_lossy(&output.stdout).contains("enabled") { log_audit_event( "COMPLIANCE_VIOLATION", "SYSTEM", "BellandeOS firewall is not enabled", ) .await?; } } _ => warn!("Firewall checking not implemented for this OS"), } Ok(()) } async fn check_network_encryption(network_requirements: &NetworkRequirements) -> Result<()> { // Check SSL/TLS versions check_ssl_versions(&network_requirements.required_protocols).await?; // Check SSH configuration check_ssh_configuration().await?; // Check encrypted protocols check_encrypted_protocols().await?; Ok(()) } async fn check_ssl_versions(required_protocols: &[String]) -> Result<()> { match std::env::consts::OS { "macos" | "linux" | "bellandeos" => { let output = Command::new("openssl") .args(&["version"]) .output() .context("Failed to check OpenSSL version")?; let version = String::from_utf8_lossy(&output.stdout); for protocol in required_protocols { if !version.contains(protocol) { log_audit_event( "COMPLIANCE_VIOLATION", "SYSTEM", &format!("Required protocol {} not available", protocol), ) .await?; } } } _ => warn!("SSL version checking not implemented for this OS"), } Ok(()) } async fn check_ssh_configuration() -> Result<()> { let ssh_config_path = match std::env::consts::OS { "macos" | "linux" => Path::new("/etc/ssh/sshd_config"), "bellandeos" => Path::new("/bell/security/ssh/sshd_config"), _ => return Ok(()), }; if !ssh_config_path.exists() { log_audit_event( "COMPLIANCE_VIOLATION", "SYSTEM", "SSH configuration file not found", ) .await?; return Ok(()); } let file = File::open(ssh_config_path)?; let reader = BufReader::new(file); let required_settings = [ ("PermitRootLogin", "no"), ("PasswordAuthentication", "no"), ("X11Forwarding", "no"), ("Protocol", "2"), ]; for line in reader.lines() { let line = line?; for (setting, expected_value) in &required_settings { if line.starts_with(setting) && !line.contains(expected_value) { log_audit_event( "COMPLIANCE_VIOLATION", "SYSTEM", &format!("SSH setting {} has incorrect value", setting), ) .await?; } } } Ok(()) } async fn check_encrypted_protocols() -> Result<()> { // Check for unencrypted protocols let unsafe_protocols = ["telnet", "ftp", "http"]; let output = Command::new("netstat") .args(&["-tulpn"]) .output() .context("Failed to check network protocols")?; let output_str = String::from_utf8_lossy(&output.stdout); for protocol in unsafe_protocols { if output_str.contains(protocol) { log_audit_event( "COMPLIANCE_VIOLATION", "SYSTEM", &format!("Unsafe protocol in use: {}", protocol), ) .await?; } } Ok(()) } async fn get_last_password_change(username: &str) -> Result> { match std::env::consts::OS { "macos" => { let output = Command::new("dscl") .args(&[ ".", "-read", &format!("/Users/{}", username), "passwordLastSetTime", ]) .output()?; Ok(Some(String::from_utf8_lossy(&output.stdout).to_string())) } "linux" => { let output = Command::new("chage").args(&["-l", username]).output()?; Ok(Some(String::from_utf8_lossy(&output.stdout).to_string())) } "bellandeos" => { let output = Command::new("bellctl") .args(&["user", "password-info", username]) .output()?; Ok(Some(String::from_utf8_lossy(&output.stdout).to_string())) } _ => Ok(None), } } fn calculate_file_hash(path: &Path) -> Result { let mut file = File::open(path)?; let mut hasher = Sha256::new(); std::io::copy(&mut file, &mut hasher)?; Ok(format!("{:x}", hasher.finalize())) } fn load_file_hashes(path: &Path) -> Result> { if path.exists() { let file = File::open(path)?; Ok(serde_json::from_reader(file)?) } else { Ok(HashMap::new()) } } fn save_file_hash(path: &Path, file_path: &Path, hash: &str) -> Result<()> { let mut hashes = load_file_hashes(path)?; hashes.insert(file_path.to_path_buf(), hash.to_string()); let file = File::create(path)?; serde_json::to_writer_pretty(file, &hashes)?; Ok(()) }