//! Modulo USB Policy - Controle de dispositivos USB //! //! Implementa o controle de armazenamento USB no Windows. //! Como o servico roda como LocalSystem, nao precisa de elevacao. use serde::{Deserialize, Serialize}; use std::io; use thiserror::Error; use tracing::{error, info, warn}; use winreg::enums::*; use winreg::RegKey; // GUID para Removable Storage Devices (Disk) const REMOVABLE_STORAGE_GUID: &str = "{53f56307-b6bf-11d0-94f2-00a0c91efb8b}"; // Chaves de registro const REMOVABLE_STORAGE_PATH: &str = r"Software\Policies\Microsoft\Windows\RemovableStorageDevices"; const USBSTOR_PATH: &str = r"SYSTEM\CurrentControlSet\Services\USBSTOR"; const STORAGE_POLICY_PATH: &str = r"SYSTEM\CurrentControlSet\Control\StorageDevicePolicies"; #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "SCREAMING_SNAKE_CASE")] pub enum UsbPolicy { Allow, BlockAll, Readonly, } impl UsbPolicy { pub fn from_str(s: &str) -> Option { match s.to_uppercase().as_str() { "ALLOW" => Some(Self::Allow), "BLOCK_ALL" => Some(Self::BlockAll), "READONLY" => Some(Self::Readonly), _ => None, } } pub fn as_str(&self) -> &'static str { match self { Self::Allow => "ALLOW", Self::BlockAll => "BLOCK_ALL", Self::Readonly => "READONLY", } } } #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct UsbPolicyResult { pub success: bool, pub policy: String, pub error: Option, pub applied_at: Option, } #[derive(Error, Debug)] pub enum UsbControlError { #[error("Politica USB invalida: {0}")] InvalidPolicy(String), #[error("Erro de registro do Windows: {0}")] RegistryError(String), #[error("Permissao negada")] PermissionDenied, #[error("Erro de I/O: {0}")] Io(#[from] io::Error), } /// Aplica uma politica de USB pub fn apply_policy(policy_str: &str) -> Result { let policy = UsbPolicy::from_str(policy_str) .ok_or_else(|| UsbControlError::InvalidPolicy(policy_str.to_string()))?; let now = chrono::Utc::now().timestamp_millis(); info!("Aplicando politica USB: {:?}", policy); // 1. Aplicar Removable Storage Policy apply_removable_storage_policy(policy)?; // 2. Aplicar USBSTOR apply_usbstor_policy(policy)?; // 3. Aplicar WriteProtect se necessario if policy == UsbPolicy::Readonly { apply_write_protect(true)?; } else { apply_write_protect(false)?; } // 4. Atualizar Group Policy (opcional) if let Err(e) = refresh_group_policy() { warn!("Falha ao atualizar group policy: {}", e); } info!("Politica USB aplicada com sucesso: {:?}", policy); Ok(UsbPolicyResult { success: true, policy: policy.as_str().to_string(), error: None, applied_at: Some(now), }) } /// Retorna a politica USB atual pub fn get_current_policy() -> Result { let hklm = RegKey::predef(HKEY_LOCAL_MACHINE); // Verifica Removable Storage Policy primeiro let full_path = format!(r"{}\{}", REMOVABLE_STORAGE_PATH, REMOVABLE_STORAGE_GUID); if let Ok(key) = hklm.open_subkey_with_flags(&full_path, KEY_READ) { let deny_read: u32 = key.get_value("Deny_Read").unwrap_or(0); let deny_write: u32 = key.get_value("Deny_Write").unwrap_or(0); if deny_read == 1 && deny_write == 1 { return Ok("BLOCK_ALL".to_string()); } if deny_read == 0 && deny_write == 1 { return Ok("READONLY".to_string()); } } // Verifica USBSTOR como fallback if let Ok(key) = hklm.open_subkey_with_flags(USBSTOR_PATH, KEY_READ) { let start: u32 = key.get_value("Start").unwrap_or(3); if start == 4 { return Ok("BLOCK_ALL".to_string()); } } Ok("ALLOW".to_string()) } fn apply_removable_storage_policy(policy: UsbPolicy) -> Result<(), UsbControlError> { let hklm = RegKey::predef(HKEY_LOCAL_MACHINE); let full_path = format!(r"{}\{}", REMOVABLE_STORAGE_PATH, REMOVABLE_STORAGE_GUID); match policy { UsbPolicy::Allow => { // Tenta remover as restricoes, se existirem if let Ok(key) = hklm.open_subkey_with_flags(&full_path, KEY_ALL_ACCESS) { let _ = key.delete_value("Deny_Read"); let _ = key.delete_value("Deny_Write"); let _ = key.delete_value("Deny_Execute"); } // Tenta remover a chave inteira se estiver vazia let _ = hklm.delete_subkey(&full_path); } UsbPolicy::BlockAll => { let (key, _) = hklm .create_subkey(&full_path) .map_err(map_winreg_error)?; key.set_value("Deny_Read", &1u32) .map_err(map_winreg_error)?; key.set_value("Deny_Write", &1u32) .map_err(map_winreg_error)?; key.set_value("Deny_Execute", &1u32) .map_err(map_winreg_error)?; } UsbPolicy::Readonly => { let (key, _) = hklm .create_subkey(&full_path) .map_err(map_winreg_error)?; // Permite leitura, bloqueia escrita key.set_value("Deny_Read", &0u32) .map_err(map_winreg_error)?; key.set_value("Deny_Write", &1u32) .map_err(map_winreg_error)?; key.set_value("Deny_Execute", &0u32) .map_err(map_winreg_error)?; } } Ok(()) } fn apply_usbstor_policy(policy: UsbPolicy) -> Result<(), UsbControlError> { let hklm = RegKey::predef(HKEY_LOCAL_MACHINE); let key = hklm .open_subkey_with_flags(USBSTOR_PATH, KEY_ALL_ACCESS) .map_err(map_winreg_error)?; match policy { UsbPolicy::Allow => { // Start = 3 habilita o driver key.set_value("Start", &3u32) .map_err(map_winreg_error)?; } UsbPolicy::BlockAll => { // Start = 4 desabilita o driver key.set_value("Start", &4u32) .map_err(map_winreg_error)?; } UsbPolicy::Readonly => { // Readonly mantem driver ativo key.set_value("Start", &3u32) .map_err(map_winreg_error)?; } } Ok(()) } fn apply_write_protect(enable: bool) -> Result<(), UsbControlError> { let hklm = RegKey::predef(HKEY_LOCAL_MACHINE); if enable { let (key, _) = hklm .create_subkey(STORAGE_POLICY_PATH) .map_err(map_winreg_error)?; key.set_value("WriteProtect", &1u32) .map_err(map_winreg_error)?; } else if let Ok(key) = hklm.open_subkey_with_flags(STORAGE_POLICY_PATH, KEY_ALL_ACCESS) { let _ = key.set_value("WriteProtect", &0u32); } Ok(()) } fn refresh_group_policy() -> Result<(), UsbControlError> { use std::os::windows::process::CommandExt; use std::process::Command; const CREATE_NO_WINDOW: u32 = 0x08000000; let output = Command::new("gpupdate") .args(["/target:computer", "/force"]) .creation_flags(CREATE_NO_WINDOW) .output() .map_err(UsbControlError::Io)?; if !output.status.success() { warn!( "gpupdate retornou erro: {}", String::from_utf8_lossy(&output.stderr) ); } Ok(()) } fn map_winreg_error(error: io::Error) -> UsbControlError { if let Some(code) = error.raw_os_error() { if code == 5 { return UsbControlError::PermissionDenied; } } UsbControlError::RegistryError(error.to_string()) }