Stop RustDesk service via UAC and sync RustDesk2 security
This commit is contained in:
parent
daca17a93d
commit
05a273466a
2 changed files with 91 additions and 30 deletions
|
|
@ -42,6 +42,11 @@ const RUSTDESK_CONFIG_FILES: [&str; 6] = [
|
|||
"passwd",
|
||||
"passwd.txt",
|
||||
];
|
||||
const PROPAGATION_FILES: [&str; 3] = [
|
||||
"RustDesk.toml",
|
||||
"RustDesk_local.toml",
|
||||
"RustDesk2.toml",
|
||||
];
|
||||
const CREATE_NO_WINDOW: u32 = 0x08000000;
|
||||
|
||||
static PROVISION_MUTEX: Lazy<Mutex<()>> = Lazy::new(|| Mutex::new(()));
|
||||
|
|
@ -355,9 +360,13 @@ key = "{key}"
|
|||
relay-server = "{host}"
|
||||
custom-rendezvous-server = "{host}"
|
||||
api-server = "https://{host}"
|
||||
verification-method = "{verification}"
|
||||
approve-mode = "{approve}"
|
||||
"#,
|
||||
host = SERVER_HOST,
|
||||
key = SERVER_KEY
|
||||
key = SERVER_KEY,
|
||||
verification = SECURITY_VERIFICATION_VALUE,
|
||||
approve = SECURITY_APPROVE_MODE_VALUE,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -432,11 +441,10 @@ fn ensure_service_running(exe_path: &Path) -> Result<(), RustdeskError> {
|
|||
}
|
||||
|
||||
fn stop_rustdesk_processes() -> Result<(), RustdeskError> {
|
||||
if let Err(error) = run_sc(&["stop", SERVICE_NAME]) {
|
||||
match error {
|
||||
RustdeskError::CommandFailed { status: Some(code), .. } if code == 1062 || code == 1060 => {}
|
||||
_ => log_event(&format!("Não foi possível parar o serviço RustDesk antes da sincronização: {error}")),
|
||||
}
|
||||
if let Err(error) = try_stop_service() {
|
||||
log_event(&format!(
|
||||
"Não foi possível parar o serviço RustDesk antes da sincronização: {error}"
|
||||
));
|
||||
}
|
||||
|
||||
let status = hidden_command("taskkill")
|
||||
|
|
@ -455,6 +463,23 @@ fn stop_rustdesk_processes() -> Result<(), RustdeskError> {
|
|||
}
|
||||
}
|
||||
|
||||
fn try_stop_service() -> Result<(), RustdeskError> {
|
||||
match run_sc(&["stop", SERVICE_NAME]) {
|
||||
Ok(_) => {
|
||||
thread::sleep(Duration::from_secs(2));
|
||||
Ok(())
|
||||
}
|
||||
Err(RustdeskError::CommandFailed { status: Some(code), .. }) if code == 1060 || code == 1062 => Ok(()),
|
||||
Err(RustdeskError::CommandFailed { status: Some(5), .. }) => {
|
||||
stop_service_elevated().map_err(|error| RustdeskError::CommandFailed {
|
||||
command: format!("stop_service_elevated ({error})"),
|
||||
status: Some(5),
|
||||
})
|
||||
}
|
||||
Err(error) => Err(error),
|
||||
}
|
||||
}
|
||||
|
||||
fn run_sc(args: &[&str]) -> Result<(), RustdeskError> {
|
||||
let status = hidden_command("sc")
|
||||
.args(args)
|
||||
|
|
@ -600,6 +625,14 @@ fn ensure_password_files(secret: &str) -> Result<(), String> {
|
|||
));
|
||||
}
|
||||
|
||||
let rustdesk2_path = dir.join("RustDesk2.toml");
|
||||
if let Err(error) = enforce_security_in_rustdesk2(&rustdesk2_path) {
|
||||
log_event(&format!(
|
||||
"Falha ao ajustar flags no RustDesk2.toml em {}: {error}",
|
||||
rustdesk2_path.display()
|
||||
));
|
||||
}
|
||||
|
||||
if let Err(error) = write_toml_kv(&local_path, "approve-mode", SECURITY_APPROVE_MODE_VALUE) {
|
||||
log_event(&format!(
|
||||
"Falha ao ajustar approve-mode em {}: {error}",
|
||||
|
|
@ -653,16 +686,21 @@ fn enforce_security_flags() -> Result<(), String> {
|
|||
}
|
||||
}
|
||||
|
||||
fn enforce_security_in_rustdesk2(path: &Path) -> io::Result<()> {
|
||||
write_toml_kv(path, "verification-method", SECURITY_VERIFICATION_VALUE)?;
|
||||
write_toml_kv(path, "approve-mode", SECURITY_APPROVE_MODE_VALUE)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn propagate_password_profile() -> io::Result<bool> {
|
||||
let Some(src_dir) = user_appdata_config_dir() else {
|
||||
log_event("AppData do usuário não disponível para copiar RustDesk.toml (propagação ignorada)");
|
||||
return Ok(false);
|
||||
};
|
||||
|
||||
let files = ["RustDesk.toml", "RustDesk_local.toml"];
|
||||
let mut propagated = false;
|
||||
|
||||
for filename in files {
|
||||
for filename in PROPAGATION_FILES {
|
||||
let src_path = src_dir.join(filename);
|
||||
if !src_path.exists() {
|
||||
continue;
|
||||
|
|
@ -916,6 +954,18 @@ fn ensure_service_profiles_writable_preflight() -> Result<(), String> {
|
|||
}
|
||||
}
|
||||
|
||||
fn stop_service_elevated() -> Result<(), String> {
|
||||
let script = r#"
|
||||
$ErrorActionPreference='Stop'
|
||||
$service = Get-Service -Name 'RustDesk' -ErrorAction SilentlyContinue
|
||||
if ($service -and $service.Status -ne 'Stopped') {
|
||||
Stop-Service -Name 'RustDesk' -Force -ErrorAction Stop
|
||||
$service.WaitForStatus('Stopped','00:00:10')
|
||||
}
|
||||
"#;
|
||||
run_powershell_elevated(script)
|
||||
}
|
||||
|
||||
fn can_write_dir(dir: &Path) -> bool {
|
||||
if fs::create_dir_all(dir).is_err() {
|
||||
return false;
|
||||
|
|
@ -1062,28 +1112,35 @@ fn run_with_args(exe_path: &Path, args: &[&str]) -> Result<(), RustdeskError> {
|
|||
|
||||
fn log_password_replication(secret: &str) {
|
||||
for dir in remote_id_directories() {
|
||||
let primary = dir.join("RustDesk.toml");
|
||||
log_password_match(&primary, secret);
|
||||
|
||||
let local_path = dir.join("RustDesk_local.toml");
|
||||
match read_password_from_file(&local_path) {
|
||||
Some(value) if value == secret => {
|
||||
log_event(&format!(
|
||||
"Senha confirmada em {} ({})",
|
||||
local_path.display(),
|
||||
mask_secret(&value)
|
||||
));
|
||||
}
|
||||
Some(value) => {
|
||||
log_event(&format!(
|
||||
"Aviso: senha divergente ({}) em {}",
|
||||
mask_secret(&value),
|
||||
local_path.display()
|
||||
));
|
||||
}
|
||||
None => {
|
||||
log_event(&format!(
|
||||
"Aviso: chave 'password' não encontrada em {}",
|
||||
local_path.display()
|
||||
));
|
||||
}
|
||||
log_password_match(&local_path, secret);
|
||||
}
|
||||
}
|
||||
|
||||
fn log_password_match(path: &Path, secret: &str) {
|
||||
match read_password_from_file(path) {
|
||||
Some(value) if value == secret => {
|
||||
log_event(&format!(
|
||||
"Senha confirmada em {} ({})",
|
||||
path.display(),
|
||||
mask_secret(&value)
|
||||
));
|
||||
}
|
||||
Some(value) => {
|
||||
log_event(&format!(
|
||||
"Aviso: senha divergente ({}) em {}",
|
||||
mask_secret(&value),
|
||||
path.display()
|
||||
));
|
||||
}
|
||||
None => {
|
||||
log_event(&format!(
|
||||
"Aviso: chave 'password' não encontrada em {}",
|
||||
path.display()
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,14 +54,18 @@ Fluxo ideal:
|
|||
- Reinicia serviço `RustDesk` (`sc start RustDesk` — pode falhar com `status Some(5)` se não há privilégio admin). Mesmo com falha, a CLI continua e grava o ID nos arquivos.
|
||||
- **Autoelevação única**: na primeira execução do botão “Preparar”, o Raven dispara um PowerShell elevado (`takeown + icacls`) para liberar ACL dos perfis `LocalService` e `LocalSystem`. O sucesso grava `rustdeskAclUnlockedAt` dentro de `%LOCALAPPDATA%\br.com.esdrasrenan.sistemadechamados\machine-agent.json` e cria o flag `rustdesk_acl_unlocked.flag`, evitando novos prompts de UAC nas execuções seguintes.
|
||||
- **Kill/restart seguro**: antes de tocar nos TOML, o Raven roda `sc stop RustDesk` + `taskkill /F /T /IM rustdesk.exe`. Isso garante que nenhum cliente sobrescreva o `RustDesk_local.toml` enquanto aplicamos a senha.
|
||||
- **Replicação completa de perfis**: após aplicar `--password`, limpamos quaisquer arquivos antigos (`RustDesk.toml`, `RustDesk_local.toml`, `password`, `passwd*`) em `%APPDATA%`, `ProgramData`, `LocalService` e `LocalSystem` e, em seguida, reescrevemos tudo (via `write_toml_kv`). `verification-method = "use-permanent-password"` e `approve-mode = "password"` são aplicados em todos os perfis, junto com a cópia dos artefatos `password/passwd/passwd.txt`.
|
||||
- **Replicação completa de perfis**: após aplicar `--password`, limpamos quaisquer arquivos antigos (`RustDesk*.toml`, `password`, `passwd*`) em `%APPDATA%`, `ProgramData`, `LocalService` e `LocalSystem` e, em seguida, reescrevemos tudo. Agora os `RustDesk2.toml` herdados recebem `verification-method = "use-permanent-password"` e `approve-mode = "password"` no bloco `[options]` antes mesmo do serviço subir, evitando que o RustDesk volte para "Use both".
|
||||
- **Validação por arquivo**: o Raven agora registra no `rustdesk.log` se o `password` gravado em cada `RustDesk_local.toml` coincide com o PIN configurado. Para inspecionar manualmente, rode no PowerShell (e valide que todas as linhas exibem o PIN esperado):
|
||||
|
||||
```powershell
|
||||
$targets = @(
|
||||
"$env:APPDATA\RustDesk\config\RustDesk.toml",
|
||||
"$env:APPDATA\RustDesk\config\RustDesk_local.toml",
|
||||
"$env:ProgramData\RustDesk\config\RustDesk.toml",
|
||||
"$env:ProgramData\RustDesk\config\RustDesk_local.toml",
|
||||
"C:\\Windows\\ServiceProfiles\\LocalService\\AppData\\Roaming\\RustDesk\\config\\RustDesk.toml",
|
||||
"C:\\Windows\\ServiceProfiles\\LocalService\\AppData\\Roaming\\RustDesk\\config\\RustDesk_local.toml",
|
||||
"C:\\Windows\\System32\\config\\systemprofile\\AppData\\Roaming\\RustDesk\\config\\RustDesk.toml",
|
||||
"C:\\Windows\\System32\\config\\systemprofile\\AppData\\Roaming\\RustDesk\\config\\RustDesk_local.toml"
|
||||
)
|
||||
foreach ($path in $targets) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue