- Implementa Windows Service (raven-service) para operacoes privilegiadas - Comunicacao via Named Pipes sem necessidade de UAC adicional - Adiciona single-instance para evitar multiplos icones na bandeja - Corrige todos os warnings do clippy (rustdesk, lib, usb_control, agent) - Remove fallback de elevacao para evitar UAC desnecessario - USB Policy e RustDesk provisioning agora usam o servico quando disponivel 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
259 lines
7.6 KiB
Rust
259 lines
7.6 KiB
Rust
//! 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<Self> {
|
|
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<String>,
|
|
pub applied_at: Option<i64>,
|
|
}
|
|
|
|
#[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<UsbPolicyResult, UsbControlError> {
|
|
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<String, UsbControlError> {
|
|
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())
|
|
}
|