mirror of
https://github.com/rustdesk/rustdesk.git
synced 2026-10-19 16:17:03 +00:00
fix: a refused privacy-mode request no longer turns off another connection's (#16507)
* fix: a refused privacy-mode request no longer turns off another connection's `Connection::turn_on_privacy`'s error arm tore down whatever privacy mode was active, whoever owned it. With connection A in privacy mode, connection B's request is correctly refused with OCCUPIED, and the error arm then called `turn_off_privacy_to_msg(INVALID_PRIVACY_MODE_CONN_ID, ..)`. That id bypasses the ownership check in `check_off_conn_id`, so A's privacy mode went away: the screen was un-blanked and local input un-hooked. The call passes `state: None`, so no `set_privacy_mode_state` goes out either and A was never told, leaving its client showing privacy mode as on. `check_privacy_mode_changed` doesn't cover it, as it only sends PrvOnByOther while a privacy mode is still active. Pass `self.inner.id`, as the sibling error path above already does. `check_off_conn_id` then cleans up after this connection when it owns privacy mode, and refuses when another connection does. The bypass itself stays: `ipc::Data::Close`, `reset_all` and `TurnOnGuard` need an unconditional teardown. Its contract is now noted on `check_off_conn_id`, since that is what the buggy call site got wrong. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: Steven Storie <sstangle73@gmail.com> * fix: disconnect other remote sessions when privacy mode starts Signed-off-by: fufesou <linlong1266@gmail.com> * fix: exclude privacy disconnect hook from iOS builds Signed-off-by: fufesou <linlong1266@gmail.com> * fix: validate privacy mode before disconnecting other sessions Signed-off-by: fufesou <linlong1266@gmail.com> * chore Signed-off-by: fufesou <linlong1266@gmail.com> --------- Signed-off-by: Steven Storie <sstangle73@gmail.com> Signed-off-by: fufesou <linlong1266@gmail.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com> Co-authored-by: fufesou <linlong1266@gmail.com>
This commit is contained in:
co-authored by
Claude Opus 5
fufesou
parent
001b0e9617
commit
67090d3aa1
@@ -75,6 +75,11 @@ pub trait PrivacyMode: Sync + Send {
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
/// `INVALID_PRIVACY_MODE_CONN_ID` deliberately bypasses the ownership check, meaning
|
||||
/// "turn it off whoever owns it": shutdown (`ipc::Data::Close`), `reset_all`, and
|
||||
/// `TurnOnGuard`, which undoes a half-finished turn-on however far it got. A caller
|
||||
/// acting for one connection must pass that connection's own id, or it can turn off
|
||||
/// privacy mode belonging to another.
|
||||
#[inline]
|
||||
fn check_off_conn_id(&self, conn_id: i32) -> ResultType<()> {
|
||||
let pre_conn_id = self.pre_conn_id();
|
||||
@@ -289,6 +294,29 @@ fn turn_on_privacy_sync(impl_key: &str, conn_id: i32) -> Option<ResultType<bool>
|
||||
Some(privacy_mode_lock.as_mut()?.turn_on_privacy(conn_id))
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "ios"))]
|
||||
pub(crate) fn close_other_remote_connections_for_privacy(owner_id: i32) {
|
||||
use crate::server::{AuthConnType, AUTHED_CONNS};
|
||||
|
||||
let privacy_mode_lock = PRIVACY_MODE.lock().unwrap();
|
||||
// Ownership can change while the caller validates the capturer.
|
||||
if privacy_mode_lock.as_ref().map(|mode| mode.pre_conn_id()) != Some(owner_id) {
|
||||
return;
|
||||
}
|
||||
for connection in AUTHED_CONNS.lock().unwrap().iter() {
|
||||
if connection.conn_type != AuthConnType::Remote || connection.conn_id == owner_id {
|
||||
continue;
|
||||
}
|
||||
if let Err(err) = connection.sender.send(crate::ipc::Data::Close) {
|
||||
hbb_common::log::debug!(
|
||||
"Failed to close connection {} for privacy mode: {}",
|
||||
connection.conn_id,
|
||||
err
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn turn_off_privacy(conn_id: i32, state: Option<PrivacyModeState>) -> Option<ResultType<()>> {
|
||||
Some(
|
||||
|
||||
@@ -1145,6 +1145,14 @@ impl Connection {
|
||||
},
|
||||
Some(data) = rx_from_authed.recv() => {
|
||||
match data {
|
||||
ipc::Data::Close => {
|
||||
conn.send(crate::common::make_privacy_mode_msg(
|
||||
back_notification::PrivacyModeState::PrvOnByOther,
|
||||
String::new(),
|
||||
)).await;
|
||||
conn.on_close("privacy mode", false).await;
|
||||
break;
|
||||
}
|
||||
#[cfg(all(target_os = "windows", feature = "flutter"))]
|
||||
ipc::Data::PrinterData(data) => {
|
||||
if Self::permission(keys::OPTION_ENABLE_REMOTE_PRINTER, &conn.control_permissions) {
|
||||
@@ -5142,6 +5150,7 @@ impl Connection {
|
||||
5_000,
|
||||
);
|
||||
if err_msg.is_empty() {
|
||||
privacy_mode::close_other_remote_connections_for_privacy(self.inner.id);
|
||||
crate::common::make_privacy_mode_msg(
|
||||
back_notification::PrivacyModeState::PrvOnSucceeded,
|
||||
impl_key,
|
||||
@@ -5168,10 +5177,10 @@ impl Connection {
|
||||
Some(Err(e)) => {
|
||||
log::error!("Failed to turn on privacy mode. {}", e);
|
||||
if privacy_mode::is_in_privacy_mode() {
|
||||
let _ = Self::turn_off_privacy_to_msg(
|
||||
privacy_mode::INVALID_PRIVACY_MODE_CONN_ID,
|
||||
String::new(),
|
||||
);
|
||||
// Use this connection's ID so a failed request cannot disable another
|
||||
// connection's privacy mode: INVALID_PRIVACY_MODE_CONN_ID would bypass
|
||||
// the ownership check (see `check_off_conn_id`).
|
||||
let _ = Self::turn_off_privacy_to_msg(self.inner.id, String::new());
|
||||
}
|
||||
crate::common::make_privacy_mode_msg_with_details(
|
||||
back_notification::PrivacyModeState::PrvOnFailed,
|
||||
|
||||
Reference in New Issue
Block a user