feat: support LocalSystem RustDesk profile auto-fix

This commit is contained in:
Esdras Renan 2025-11-12 11:43:07 -03:00
parent 2339d5010f
commit a535a6625b

View file

@ -26,6 +26,7 @@ const DEFAULT_PASSWORD: &str = "FMQ9MA>e73r.FI<b*34Vmx_8P";
const SERVICE_NAME: &str = "RustDesk"; const SERVICE_NAME: &str = "RustDesk";
const CACHE_DIR_NAME: &str = "Rever\\RustDeskCache"; const CACHE_DIR_NAME: &str = "Rever\\RustDeskCache";
const LOCAL_SERVICE_CONFIG: &str = r"C:\\Windows\\ServiceProfiles\\LocalService\\AppData\\Roaming\\RustDesk\\config"; const LOCAL_SERVICE_CONFIG: &str = r"C:\\Windows\\ServiceProfiles\\LocalService\\AppData\\Roaming\\RustDesk\\config";
const LOCAL_SYSTEM_CONFIG: &str = r"C:\\Windows\\System32\\config\\systemprofile\\AppData\\Roaming\\RustDesk\\config";
const CREATE_NO_WINDOW: u32 = 0x08000000; const CREATE_NO_WINDOW: u32 = 0x08000000;
static PROVISION_MUTEX: Lazy<Mutex<()>> = Lazy::new(|| Mutex::new(())); static PROVISION_MUTEX: Lazy<Mutex<()>> = Lazy::new(|| Mutex::new(()));
@ -64,9 +65,9 @@ pub fn ensure_rustdesk(
let _guard = PROVISION_MUTEX.lock(); let _guard = PROVISION_MUTEX.lock();
log_event("Iniciando preparo do RustDesk"); log_event("Iniciando preparo do RustDesk");
if let Err(error) = ensure_localservice_writable_preflight() { if let Err(error) = ensure_service_profiles_writable_preflight() {
log_event(&format!( log_event(&format!(
"Aviso: não foi possível preparar ACL do LocalService ({error}). Continuando mesmo assim; o serviço pode não aplicar a senha." "Aviso: não foi possível preparar ACL dos perfis do serviço ({error}). Continuando mesmo assim; o serviço pode não aplicar a senha."
)); ));
} }
@ -381,8 +382,8 @@ fn ensure_service_running() -> Result<(), RustdeskError> {
match start_sequence() { match start_sequence() {
Ok(_) => Ok(()), Ok(_) => Ok(()),
Err(RustdeskError::CommandFailed { command: _, status: Some(5), .. }) => { Err(RustdeskError::CommandFailed { command: _, status: Some(5), .. }) => {
log_event("SC retornou acesso negado; tentando ajustar ACL do LocalService..."); log_event("SC retornou acesso negado; tentando ajustar ACL dos perfis do serviço...");
fix_localservice_acl().map_err(|error| RustdeskError::CommandFailed { ensure_service_profiles_writable_preflight().map_err(|error| RustdeskError::CommandFailed {
command: format!("fix_acl ({error})"), command: format!("fix_acl ({error})"),
status: Some(5), status: Some(5),
})?; })?;
@ -476,13 +477,22 @@ fn ensure_remote_id_files(id: &str) {
fn remote_id_directories() -> Vec<PathBuf> { fn remote_id_directories() -> Vec<PathBuf> {
let mut dirs = Vec::new(); let mut dirs = Vec::new();
dirs.push(program_data_config_dir()); dirs.push(program_data_config_dir());
dirs.push(PathBuf::from(LOCAL_SERVICE_CONFIG)); for profile in service_profile_dirs() {
dirs.push(profile);
}
if let Some(appdir) = user_appdata_config_dir() { if let Some(appdir) = user_appdata_config_dir() {
dirs.push(appdir); dirs.push(appdir);
} }
dirs dirs
} }
fn service_profile_dirs() -> Vec<PathBuf> {
vec![
PathBuf::from(LOCAL_SERVICE_CONFIG),
PathBuf::from(LOCAL_SYSTEM_CONFIG),
]
}
fn ensure_password_files(secret: &str) -> Result<(), String> { fn ensure_password_files(secret: &str) -> Result<(), String> {
let mut errors = Vec::new(); let mut errors = Vec::new();
@ -630,9 +640,10 @@ exit $process.ExitCode
Err(format!("elevated ps exit {:?}", status.code())) Err(format!("elevated ps exit {:?}", status.code()))
} }
fn fix_localservice_acl() -> Result<(), String> { fn fix_profile_acl(target: &Path) -> Result<(), String> {
let target = r"C:\\Windows\\ServiceProfiles\\LocalService\\AppData\\Roaming\\RustDesk\\config"; let target_str = target.display().to_string().replace('\', "\\");
let transcript = env::temp_dir().join("raven_acl_ps.log"); let transcript = env::temp_dir().join("raven_acl_ps.log");
let log_str = transcript.display().to_string().replace('\', "\\");
let script = format!( let script = format!(
r#" r#"
$ErrorActionPreference='Stop' $ErrorActionPreference='Stop'
@ -656,32 +667,58 @@ try {{
try {{ Stop-Transcript | Out-Null }} catch {{ }} try {{ Stop-Transcript | Out-Null }} catch {{ }}
}} }}
"#, "#,
target = target, target = target_str,
log = transcript.display() log = log_str
); );
let result = run_powershell_elevated(&script); let result = run_powershell_elevated(&script);
if result.is_err() { if result.is_err() {
if let Ok(content) = fs::read_to_string(&transcript) { if let Ok(content) = fs::read_to_string(&transcript) {
log_event(&format!("ACL transcript:\n{content}")); log_event(&format!(
"ACL transcript para {}:\n{}",
target.display(), content
));
} }
} }
let _ = fs::remove_file(&transcript); let _ = fs::remove_file(&transcript);
result result
} }
fn ensure_localservice_writable_preflight() -> Result<(), String> { fn ensure_service_profiles_writable_preflight() -> Result<(), String> {
let dir = PathBuf::from(LOCAL_SERVICE_CONFIG); let mut success = false;
let mut last_error: Option<String> = None;
for dir in service_profile_dirs() {
if can_write_dir(&dir) { if can_write_dir(&dir) {
return Ok(()); success = true;
continue;
}
log_event(&format!(
"Tentando corrigir ACL via UAC (preflight) em {}...",
dir.display()
));
if let Err(error) = fix_profile_acl(&dir) {
last_error = Some(error);
continue;
} }
log_event("Tentando corrigir ACL do perfil LocalService via UAC (preflight)...");
fix_localservice_acl()?;
if can_write_dir(&dir) { if can_write_dir(&dir) {
log_event("ACL do LocalService ajustada com sucesso."); log_event(&format!(
"ACL ajustada com sucesso em {}",
dir.display()
));
success = true;
} else {
last_error = Some(format!(
"continua sem permissão para {} mesmo após preflight",
dir.display()
));
}
}
if success {
Ok(()) Ok(())
} else { } else {
Err("continua sem permissão para LocalService (após preflight)".into()) Err(last_error.unwrap_or_else(|| "nenhum perfil de serviço acessível".into()))
} }
} }