swtich_code for hbbs (#15615)

* swtich_code for hbbs to bypass ACL

* improve register_switch_grant: skip public server, log at error level

Also document why registration is fire-and-forget with no retry: the
peer connects within seconds, so a late retry would land after its
punch request was already rejected; a failed switch is recovered by
the user triggering it again, which registers a fresh grant.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* add timestamp

Signed-off-by: 21pages <sunboeasy@gmail.com>

* fix(switch-sides): handle grant registration clock skew

  - retry registration once with the server-provided timestamp
  - require an explicit accepted response from hbbs
  - report malformed or incomplete responses

Signed-off-by: 21pages <sunboeasy@gmail.com>

* fix(switch-sides): register grants with code verifiers

  - send a derived verifier instead of the raw switch code
  - use detached signatures for grant registration
  - add verifier and signed-message tests

Signed-off-by: 21pages <sunboeasy@gmail.com>

---------

Signed-off-by: 21pages <sunboeasy@gmail.com>
Co-authored-by: 21pages <sunboeasy@gmail.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
RustDesk
2026-08-04 11:08:59 +08:00
committed by GitHub
parent 2f8822ec7a
commit d752823b8c
3 changed files with 151 additions and 4 deletions

View File

@@ -426,8 +426,8 @@ impl Client {
NatType::from_i32(my_nat_type).unwrap_or(NatType::UNKNOWN_NAT)
};
if !key.is_empty() && !token.is_empty() {
// mainly for the security of token
let switch_code = interface.get_switch_code();
if !key.is_empty() && (!token.is_empty() || !switch_code.is_empty()) {
secure_tcp(&mut socket, &key)
.await
.map_err(|e| anyhow!("Failed to secure tcp: {}", e))?;
@@ -469,6 +469,7 @@ impl Client {
udp_port: udp_nat_port as _,
force_relay: interface.is_force_relay(),
socket_addr_v6: ipv6.1.unwrap_or_default(),
switch_code,
..Default::default()
});
for i in 1..=3 {
@@ -716,6 +717,7 @@ impl Client {
let mut direct = !conn.is_err();
if interface.is_force_relay() || conn.is_err() {
if !relay_server.is_empty() {
let switch_code = interface.get_switch_code();
conn = Self::request_relay(
peer_id,
relay_server.to_owned(),
@@ -724,6 +726,7 @@ impl Client {
key,
token,
conn_type,
&switch_code,
)
.await;
if let Err(e) = conn {
@@ -844,6 +847,7 @@ impl Client {
key: &str,
token: &str,
conn_type: ConnType,
switch_code: &str,
) -> ResultType<Stream> {
let mut succeed = false;
let mut uuid = "".to_owned();
@@ -855,8 +859,7 @@ impl Client {
.await
.with_context(|| "Failed to connect to rendezvous server")?;
if !key.is_empty() && !token.is_empty() {
// mainly for the security of token
if !key.is_empty() && (!token.is_empty() || !switch_code.is_empty()) {
secure_tcp(&mut socket, key).await?;
}
@@ -877,6 +880,7 @@ impl Client {
uuid: uuid.clone(),
relay_server: relay_server.clone(),
secure,
switch_code: switch_code.to_owned(),
..Default::default()
});
socket.send(&msg_out).await?;
@@ -3754,6 +3758,16 @@ pub trait Interface: Send + Clone + 'static + Sized {
self.get_lch().read().unwrap().force_relay
}
fn get_switch_code(&self) -> String {
match self.get_lch().read().unwrap().switch_uuid.clone() {
Some(u) if !u.is_empty() => {
use hbb_common::sodiumoxide::crypto::hash::sha256;
crate::encode64(sha256::hash(u.as_bytes()).0)
}
_ => String::new(),
}
}
fn swap_modifier_mouse(&self, _msg: &mut hbb_common::protos::message::MouseEvent) {}
fn update_direct(&self, direct: Option<bool>) {