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",
|
||||||
"passwd.txt",
|
"passwd.txt",
|
||||||
];
|
];
|
||||||
|
const PROPAGATION_FILES: [&str; 3] = [
|
||||||
|
"RustDesk.toml",
|
||||||
|
"RustDesk_local.toml",
|
||||||
|
"RustDesk2.toml",
|
||||||
|
];
|
||||||
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(()));
|
||||||
|
|
@ -355,9 +360,13 @@ key = "{key}"
|
||||||
relay-server = "{host}"
|
relay-server = "{host}"
|
||||||
custom-rendezvous-server = "{host}"
|
custom-rendezvous-server = "{host}"
|
||||||
api-server = "https://{host}"
|
api-server = "https://{host}"
|
||||||
|
verification-method = "{verification}"
|
||||||
|
approve-mode = "{approve}"
|
||||||
"#,
|
"#,
|
||||||
host = SERVER_HOST,
|
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> {
|
fn stop_rustdesk_processes() -> Result<(), RustdeskError> {
|
||||||
if let Err(error) = run_sc(&["stop", SERVICE_NAME]) {
|
if let Err(error) = try_stop_service() {
|
||||||
match error {
|
log_event(&format!(
|
||||||
RustdeskError::CommandFailed { status: Some(code), .. } if code == 1062 || code == 1060 => {}
|
"Não foi possível parar o serviço RustDesk antes da sincronização: {error}"
|
||||||
_ => log_event(&format!("Não foi possível parar o serviço RustDesk antes da sincronização: {error}")),
|
));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let status = hidden_command("taskkill")
|
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> {
|
fn run_sc(args: &[&str]) -> Result<(), RustdeskError> {
|
||||||
let status = hidden_command("sc")
|
let status = hidden_command("sc")
|
||||||
.args(args)
|
.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) {
|
if let Err(error) = write_toml_kv(&local_path, "approve-mode", SECURITY_APPROVE_MODE_VALUE) {
|
||||||
log_event(&format!(
|
log_event(&format!(
|
||||||
"Falha ao ajustar approve-mode em {}: {error}",
|
"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> {
|
fn propagate_password_profile() -> io::Result<bool> {
|
||||||
let Some(src_dir) = user_appdata_config_dir() else {
|
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)");
|
log_event("AppData do usuário não disponível para copiar RustDesk.toml (propagação ignorada)");
|
||||||
return Ok(false);
|
return Ok(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
let files = ["RustDesk.toml", "RustDesk_local.toml"];
|
|
||||||
let mut propagated = false;
|
let mut propagated = false;
|
||||||
|
|
||||||
for filename in files {
|
for filename in PROPAGATION_FILES {
|
||||||
let src_path = src_dir.join(filename);
|
let src_path = src_dir.join(filename);
|
||||||
if !src_path.exists() {
|
if !src_path.exists() {
|
||||||
continue;
|
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 {
|
fn can_write_dir(dir: &Path) -> bool {
|
||||||
if fs::create_dir_all(dir).is_err() {
|
if fs::create_dir_all(dir).is_err() {
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -1062,12 +1112,20 @@ fn run_with_args(exe_path: &Path, args: &[&str]) -> Result<(), RustdeskError> {
|
||||||
|
|
||||||
fn log_password_replication(secret: &str) {
|
fn log_password_replication(secret: &str) {
|
||||||
for dir in remote_id_directories() {
|
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");
|
let local_path = dir.join("RustDesk_local.toml");
|
||||||
match read_password_from_file(&local_path) {
|
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 => {
|
Some(value) if value == secret => {
|
||||||
log_event(&format!(
|
log_event(&format!(
|
||||||
"Senha confirmada em {} ({})",
|
"Senha confirmada em {} ({})",
|
||||||
local_path.display(),
|
path.display(),
|
||||||
mask_secret(&value)
|
mask_secret(&value)
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
@ -1075,17 +1133,16 @@ fn log_password_replication(secret: &str) {
|
||||||
log_event(&format!(
|
log_event(&format!(
|
||||||
"Aviso: senha divergente ({}) em {}",
|
"Aviso: senha divergente ({}) em {}",
|
||||||
mask_secret(&value),
|
mask_secret(&value),
|
||||||
local_path.display()
|
path.display()
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
log_event(&format!(
|
log_event(&format!(
|
||||||
"Aviso: chave 'password' não encontrada em {}",
|
"Aviso: chave 'password' não encontrada em {}",
|
||||||
local_path.display()
|
path.display()
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_password_from_file(path: &Path) -> Option<String> {
|
fn read_password_from_file(path: &Path) -> Option<String> {
|
||||||
|
|
|
||||||
|
|
@ -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.
|
- 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.
|
- **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.
|
- **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):
|
- **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
|
```powershell
|
||||||
$targets = @(
|
$targets = @(
|
||||||
|
"$env:APPDATA\RustDesk\config\RustDesk.toml",
|
||||||
"$env:APPDATA\RustDesk\config\RustDesk_local.toml",
|
"$env:APPDATA\RustDesk\config\RustDesk_local.toml",
|
||||||
|
"$env:ProgramData\RustDesk\config\RustDesk.toml",
|
||||||
"$env:ProgramData\RustDesk\config\RustDesk_local.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\\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"
|
"C:\\Windows\\System32\\config\\systemprofile\\AppData\\Roaming\\RustDesk\\config\\RustDesk_local.toml"
|
||||||
)
|
)
|
||||||
foreach ($path in $targets) {
|
foreach ($path in $targets) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue