refactor: preflight ACL repair for RustDesk
This commit is contained in:
parent
7972ac207d
commit
a8cbfee03b
1 changed files with 43 additions and 74 deletions
|
|
@ -64,6 +64,12 @@ 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() {
|
||||||
|
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."
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
let exe_path = detect_executable_path();
|
let exe_path = detect_executable_path();
|
||||||
let (installed_version, freshly_installed) = ensure_installed(&exe_path)?;
|
let (installed_version, freshly_installed) = ensure_installed(&exe_path)?;
|
||||||
log_event(if freshly_installed {
|
log_event(if freshly_installed {
|
||||||
|
|
@ -478,16 +484,11 @@ fn remote_id_directories() -> Vec<PathBuf> {
|
||||||
|
|
||||||
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();
|
||||||
let mut need_acl_fix = false;
|
|
||||||
|
|
||||||
for dir in remote_id_directories() {
|
for dir in remote_id_directories() {
|
||||||
let password_path = dir.join("RustDesk.toml");
|
let password_path = dir.join("RustDesk.toml");
|
||||||
if let Err(error) = write_toml_kv(&password_path, "password", secret) {
|
if let Err(error) = write_toml_kv(&password_path, "password", secret) {
|
||||||
if is_localservice_permission_issue(&dir, &error) {
|
errors.push(format!("{} -> {}", password_path.display(), error));
|
||||||
need_acl_fix = true;
|
|
||||||
} else {
|
|
||||||
errors.push(format!("{} -> {}", password_path.display(), error));
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
log_event(&format!(
|
log_event(&format!(
|
||||||
"Senha escrita via fallback em {}",
|
"Senha escrita via fallback em {}",
|
||||||
|
|
@ -501,9 +502,6 @@ fn ensure_password_files(secret: &str) -> Result<(), String> {
|
||||||
"Falha ao ajustar verification-method em {}: {error}",
|
"Falha ao ajustar verification-method em {}: {error}",
|
||||||
local_path.display()
|
local_path.display()
|
||||||
));
|
));
|
||||||
if is_localservice_permission_issue(&dir, &error) {
|
|
||||||
need_acl_fix = true;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
log_event(&format!(
|
log_event(&format!(
|
||||||
"verification-method atualizado para use-both-passwords em {}",
|
"verification-method atualizado para use-both-passwords em {}",
|
||||||
|
|
@ -512,40 +510,6 @@ fn ensure_password_files(secret: &str) -> Result<(), String> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if need_acl_fix {
|
|
||||||
log_event("Tentando corrigir ACL do perfil LocalService via UAC...");
|
|
||||||
fix_localservice_acl().map_err(|error| format!("fix acl: {error}"))?;
|
|
||||||
|
|
||||||
for dir in remote_id_directories() {
|
|
||||||
if !is_localservice_dir(&dir) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
let password_path = dir.join("RustDesk.toml");
|
|
||||||
if let Err(error) = write_toml_kv(&password_path, "password", secret) {
|
|
||||||
errors.push(format!("{} -> {}", password_path.display(), error));
|
|
||||||
} else {
|
|
||||||
log_event(&format!(
|
|
||||||
"Senha escrita via fallback em {}",
|
|
||||||
password_path.display()
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
let local_path = dir.join("RustDesk_local.toml");
|
|
||||||
if let Err(error) = write_toml_kv(&local_path, "verification-method", "use-both-passwords") {
|
|
||||||
errors.push(format!("{} -> {}", local_path.display(), error));
|
|
||||||
log_event(&format!(
|
|
||||||
"Falha ao ajustar verification-method em {} mesmo após ACL: {error}",
|
|
||||||
local_path.display()
|
|
||||||
));
|
|
||||||
} else {
|
|
||||||
log_event(&format!(
|
|
||||||
"verification-method atualizado para use-both-passwords em {}",
|
|
||||||
local_path.display()
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if errors.is_empty() {
|
if errors.is_empty() {
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -620,20 +584,44 @@ fn replicate_password_artifacts() -> io::Result<()> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_powershell_elevated(script: &str) -> Result<(), String> {
|
fn run_powershell_elevated(script: &str) -> Result<(), String> {
|
||||||
let tmp = env::temp_dir().join("raven_fix_acl.ps1");
|
use std::io::Write;
|
||||||
fs::write(&tmp, script).map_err(|error| format!("write ps1: {error}"))?;
|
|
||||||
let args = format!(
|
let temp_dir = env::temp_dir();
|
||||||
"-NoProfile -ExecutionPolicy Bypass -Command \"Start-Process -FilePath PowerShell -Verb RunAs -WindowStyle Hidden -Wait -ArgumentList '-NoProfile','-ExecutionPolicy','Bypass','-File','{}'\"",
|
let payload = temp_dir.join("raven_payload.ps1");
|
||||||
tmp.display()
|
fs::write(&payload, script).map_err(|error| format!("write payload: {error}"))?;
|
||||||
|
|
||||||
|
let launcher = temp_dir.join("raven_launcher.ps1");
|
||||||
|
let launcher_body = format!(
|
||||||
|
r#"
|
||||||
|
$ErrorActionPreference='Stop'
|
||||||
|
$psi = New-Object System.Diagnostics.ProcessStartInfo
|
||||||
|
$psi.FileName = 'powershell.exe'
|
||||||
|
$psi.Arguments = '-NoProfile -ExecutionPolicy Bypass -File "{payload}"'
|
||||||
|
$psi.Verb = 'runas'
|
||||||
|
$psi.WindowStyle = 'Hidden'
|
||||||
|
$process = [System.Diagnostics.Process]::Start($psi)
|
||||||
|
$process.WaitForExit()
|
||||||
|
exit $process.ExitCode
|
||||||
|
"#,
|
||||||
|
payload = payload.display()
|
||||||
);
|
);
|
||||||
|
fs::write(&launcher, launcher_body).map_err(|error| format!("write launcher: {error}"))?;
|
||||||
|
|
||||||
let status = Command::new("powershell")
|
let status = Command::new("powershell")
|
||||||
.arg(args)
|
|
||||||
.creation_flags(CREATE_NO_WINDOW)
|
.creation_flags(CREATE_NO_WINDOW)
|
||||||
.stdout(Stdio::null())
|
.args([
|
||||||
.stderr(Stdio::null())
|
"-NoProfile",
|
||||||
|
"-ExecutionPolicy",
|
||||||
|
"Bypass",
|
||||||
|
"-File",
|
||||||
|
&launcher.to_string_lossy(),
|
||||||
|
])
|
||||||
.status()
|
.status()
|
||||||
.map_err(|error| format!("spawn ps: {error}"))?;
|
.map_err(|error| format!("spawn ps: {error}"))?;
|
||||||
let _ = fs::remove_file(&tmp);
|
|
||||||
|
let _ = fs::remove_file(&launcher);
|
||||||
|
let _ = fs::remove_file(&payload);
|
||||||
|
|
||||||
if status.success() {
|
if status.success() {
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -647,8 +635,10 @@ fn fix_localservice_acl() -> Result<(), String> {
|
||||||
r#"
|
r#"
|
||||||
$ErrorActionPreference = 'Stop'
|
$ErrorActionPreference = 'Stop'
|
||||||
if (-not (Test-Path '{target}')) {{ New-Item -ItemType Directory -Force -Path '{target}' | Out-Null }}
|
if (-not (Test-Path '{target}')) {{ New-Item -ItemType Directory -Force -Path '{target}' | Out-Null }}
|
||||||
|
$admins = (New-Object Security.Principal.SecurityIdentifier('S-1-5-32-544')).Translate([Security.Principal.NTAccount]).Value
|
||||||
|
$localSvc = (New-Object Security.Principal.SecurityIdentifier('S-1-5-19')).Translate([Security.Principal.NTAccount]).Value
|
||||||
takeown /F '{target}' /R /D Y | Out-Null
|
takeown /F '{target}' /R /D Y | Out-Null
|
||||||
icacls '{target}' /grant *S-1-5-32-544:(OI)(CI)F /T /C | Out-Null
|
icacls '{target}' /grant "${{admins}}":(OI)(CI)F "${{localSvc}}":(OI)(CI)F /T /C | Out-Null
|
||||||
"#
|
"#
|
||||||
);
|
);
|
||||||
run_powershell_elevated(&script)
|
run_powershell_elevated(&script)
|
||||||
|
|
@ -772,27 +762,6 @@ fn run_with_args(exe_path: &Path, args: &[&str]) -> Result<(), RustdeskError> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_localservice_dir(path: &Path) -> bool {
|
|
||||||
path.as_os_str()
|
|
||||||
.to_string_lossy()
|
|
||||||
.to_lowercase()
|
|
||||||
.contains("serviceprofiles\\localservice")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_localservice_permission_issue(dir: &Path, error: &io::Error) -> bool {
|
|
||||||
if !is_localservice_dir(dir) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if let Some(code) = error.raw_os_error() {
|
|
||||||
return code == 5 || code == 183;
|
|
||||||
}
|
|
||||||
let message = error.to_string().to_lowercase();
|
|
||||||
message.contains("permission denied")
|
|
||||||
|| message.contains("acesso negado")
|
|
||||||
|| message.contains("os error 5")
|
|
||||||
|| message.contains("os error 183")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn hidden_command(program: impl AsRef<OsStr>) -> Command {
|
fn hidden_command(program: impl AsRef<OsStr>) -> Command {
|
||||||
let mut cmd = Command::new(program);
|
let mut cmd = Command::new(program);
|
||||||
cmd.creation_flags(CREATE_NO_WINDOW);
|
cmd.creation_flags(CREATE_NO_WINDOW);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue