mirror of
https://github.com/rustdesk/rustdesk.git
synced 2026-02-17 14:07:28 +08:00
Allow configuring remote control permissions for different users (#13974)
Signed-off-by: 21pages <sunboeasy@gmail.com>
This commit is contained in:
@@ -3039,10 +3039,21 @@ Future<void> start_service(bool is_start) async {
|
||||
}
|
||||
|
||||
Future<bool> canBeBlocked() async {
|
||||
var access_mode = await bind.mainGetOption(key: kOptionAccessMode);
|
||||
// First check control permission
|
||||
final controlPermission = await bind.mainGetCommon(
|
||||
key: "is-remote-modify-enabled-by-control-permissions");
|
||||
if (controlPermission == "true") {
|
||||
return false;
|
||||
} else if (controlPermission == "false") {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check local settings
|
||||
var accessMode = await bind.mainGetOption(key: kOptionAccessMode);
|
||||
var isCustomAccessMode = accessMode != 'full' && accessMode != 'view';
|
||||
var option = option2bool(kOptionAllowRemoteConfigModification,
|
||||
await bind.mainGetOption(key: kOptionAllowRemoteConfigModification));
|
||||
return access_mode == 'view' || (access_mode.isEmpty && !option);
|
||||
return accessMode == 'view' || (isCustomAccessMode && !option);
|
||||
}
|
||||
|
||||
// to-do: web not implemented
|
||||
|
||||
@@ -2277,6 +2277,28 @@ pub fn str2color(s: &str, alpha: u8) -> u32 {
|
||||
(alpha as u32) << 24 | rgb
|
||||
}
|
||||
|
||||
/// Check control permission state from a u64 bitmap.
|
||||
/// Each permission uses 2 bits: 0 = not set, 1 = disable, 2 = enable, 3 = invalid (treated as not set)
|
||||
/// Returns: Some(true) = enabled, Some(false) = disabled, None = not set or invalid
|
||||
pub fn get_control_permission(
|
||||
permissions: u64,
|
||||
permission: hbb_common::rendezvous_proto::control_permissions::Permission,
|
||||
) -> Option<bool> {
|
||||
use hbb_common::protobuf::Enum;
|
||||
let index = permission.value();
|
||||
if index >= 0 && index < 32 {
|
||||
let shift = index * 2;
|
||||
let value = (permissions >> shift) & 0b11;
|
||||
match value {
|
||||
1 => Some(false), // disable
|
||||
2 => Some(true), // enable
|
||||
_ => None, // 0 = not set, 3 = invalid
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
@@ -2600,6 +2600,13 @@ pub fn main_get_common(key: String) -> String {
|
||||
return false.to_string();
|
||||
} else if key == "transfer-job-id" {
|
||||
return hbb_common::fs::get_next_job_id().to_string();
|
||||
} else if key == "is-remote-modify-enabled-by-control-permissions" {
|
||||
return match is_remote_modify_enabled_by_control_permissions() {
|
||||
Some(true) => "true",
|
||||
Some(false) => "false",
|
||||
None => "",
|
||||
}
|
||||
.to_string();
|
||||
} else {
|
||||
if key.starts_with("download-data-") {
|
||||
let id = key.replace("download-data-", "");
|
||||
|
||||
34
src/ipc.rs
34
src/ipc.rs
@@ -23,7 +23,11 @@ pub use clipboard::ClipboardFile;
|
||||
use hbb_common::{
|
||||
allow_err, bail, bytes,
|
||||
bytes_codec::BytesCodec,
|
||||
config::{self, keys::OPTION_ALLOW_WEBSOCKET, Config, Config2},
|
||||
config::{
|
||||
self,
|
||||
keys::{self, OPTION_ALLOW_WEBSOCKET},
|
||||
Config, Config2,
|
||||
},
|
||||
futures::StreamExt as _,
|
||||
futures_util::sink::SinkExt,
|
||||
log, password_security as password, timeout,
|
||||
@@ -384,6 +388,9 @@ pub enum Data {
|
||||
SocksWs(Option<Box<(Option<config::Socks5Server>, String)>>),
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
Whiteboard((String, crate::whiteboard::CustomEvent)),
|
||||
ControlPermissionsRemoteModify(Option<bool>),
|
||||
#[cfg(target_os = "windows")]
|
||||
FileTransferEnabledState(Option<bool>),
|
||||
}
|
||||
|
||||
#[tokio::main(flavor = "current_thread")]
|
||||
@@ -862,6 +869,31 @@ async fn handle(data: Data, stream: &mut Connection) {
|
||||
// Port forward session count is only a get value.
|
||||
}
|
||||
},
|
||||
Data::ControlPermissionsRemoteModify(_) => {
|
||||
use hbb_common::rendezvous_proto::control_permissions::Permission;
|
||||
let state =
|
||||
crate::server::get_control_permission_state(Permission::remote_modify, true);
|
||||
allow_err!(
|
||||
stream
|
||||
.send(&Data::ControlPermissionsRemoteModify(state))
|
||||
.await
|
||||
);
|
||||
}
|
||||
#[cfg(target_os = "windows")]
|
||||
Data::FileTransferEnabledState(_) => {
|
||||
use hbb_common::rendezvous_proto::control_permissions::Permission;
|
||||
let state = crate::server::get_control_permission_state(Permission::file, false);
|
||||
let enabled = state.unwrap_or_else(|| {
|
||||
crate::server::Connection::is_permission_enabled_locally(
|
||||
config::keys::OPTION_ENABLE_FILE_TRANSFER,
|
||||
)
|
||||
});
|
||||
allow_err!(
|
||||
stream
|
||||
.send(&Data::FileTransferEnabledState(Some(enabled)))
|
||||
.await
|
||||
);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -427,6 +427,7 @@ impl RendezvousMediator {
|
||||
rr.secure,
|
||||
false,
|
||||
Default::default(),
|
||||
rr.control_permissions.clone().into_option(),
|
||||
)
|
||||
.await
|
||||
}
|
||||
@@ -440,6 +441,7 @@ impl RendezvousMediator {
|
||||
secure: bool,
|
||||
initiate: bool,
|
||||
socket_addr_v6: bytes::Bytes,
|
||||
control_permissions: Option<ControlPermissions>,
|
||||
) -> ResultType<()> {
|
||||
let peer_addr = AddrMangle::decode(&socket_addr);
|
||||
log::info!(
|
||||
@@ -473,6 +475,7 @@ impl RendezvousMediator {
|
||||
peer_addr,
|
||||
secure,
|
||||
is_ipv4(&self.addr),
|
||||
control_permissions,
|
||||
)
|
||||
.await;
|
||||
Ok(())
|
||||
@@ -491,7 +494,13 @@ impl RendezvousMediator {
|
||||
let relay = use_ws() || Config::is_proxy();
|
||||
let mut socket_addr_v6 = Default::default();
|
||||
if peer_addr_v6.port() > 0 && !relay {
|
||||
socket_addr_v6 = start_ipv6(peer_addr_v6, addr, server.clone()).await;
|
||||
socket_addr_v6 = start_ipv6(
|
||||
peer_addr_v6,
|
||||
addr,
|
||||
server.clone(),
|
||||
fla.control_permissions.clone().into_option(),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
if is_ipv4(&self.addr) && !relay && !config::is_disable_tcp_listen() {
|
||||
if let Err(err) = self
|
||||
@@ -517,6 +526,7 @@ impl RendezvousMediator {
|
||||
true,
|
||||
true,
|
||||
socket_addr_v6,
|
||||
fla.control_permissions.into_option(),
|
||||
)
|
||||
.await
|
||||
}
|
||||
@@ -547,7 +557,14 @@ impl RendezvousMediator {
|
||||
});
|
||||
let bytes = msg_out.write_to_bytes()?;
|
||||
socket.send_raw(bytes).await?;
|
||||
crate::accept_connection(server.clone(), socket, peer_addr, true).await;
|
||||
crate::accept_connection(
|
||||
server.clone(),
|
||||
socket,
|
||||
peer_addr,
|
||||
true,
|
||||
fla.control_permissions.into_option(),
|
||||
)
|
||||
.await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -562,8 +579,15 @@ impl RendezvousMediator {
|
||||
let peer_addr_v6 = hbb_common::AddrMangle::decode(&ph.socket_addr_v6);
|
||||
let relay = use_ws() || Config::is_proxy() || ph.force_relay;
|
||||
let mut socket_addr_v6 = Default::default();
|
||||
let control_permissions = ph.control_permissions.into_option();
|
||||
if peer_addr_v6.port() > 0 && !relay {
|
||||
socket_addr_v6 = start_ipv6(peer_addr_v6, peer_addr, server.clone()).await;
|
||||
socket_addr_v6 = start_ipv6(
|
||||
peer_addr_v6,
|
||||
peer_addr,
|
||||
server.clone(),
|
||||
control_permissions.clone(),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
let relay_server = self.get_relay_server(ph.relay_server);
|
||||
// for ensure, websocket go relay directly
|
||||
@@ -582,6 +606,7 @@ impl RendezvousMediator {
|
||||
true,
|
||||
true,
|
||||
socket_addr_v6.clone(),
|
||||
control_permissions,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
@@ -598,7 +623,8 @@ impl RendezvousMediator {
|
||||
};
|
||||
if ph.udp_port > 0 {
|
||||
peer_addr.set_port(ph.udp_port as u16);
|
||||
self.punch_udp_hole(peer_addr, server, msg_punch).await?;
|
||||
self.punch_udp_hole(peer_addr, server, msg_punch, control_permissions)
|
||||
.await?;
|
||||
return Ok(());
|
||||
}
|
||||
log::debug!("Punch tcp hole to {:?}", peer_addr);
|
||||
@@ -614,7 +640,8 @@ impl RendezvousMediator {
|
||||
msg_out.set_punch_hole_sent(msg_punch);
|
||||
let bytes = msg_out.write_to_bytes()?;
|
||||
socket.send_raw(bytes).await?;
|
||||
crate::accept_connection(server.clone(), socket, peer_addr, true).await;
|
||||
crate::accept_connection(server.clone(), socket, peer_addr, true, control_permissions)
|
||||
.await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -623,6 +650,7 @@ impl RendezvousMediator {
|
||||
peer_addr: SocketAddr,
|
||||
server: ServerPtr,
|
||||
msg_punch: PunchHoleSent,
|
||||
control_permissions: Option<ControlPermissions>,
|
||||
) -> ResultType<()> {
|
||||
let mut msg_out = Message::new();
|
||||
msg_out.set_punch_hole_sent(msg_punch);
|
||||
@@ -637,7 +665,14 @@ impl RendezvousMediator {
|
||||
socket.send_to(&data, addr).await.ok();
|
||||
}
|
||||
});
|
||||
udp_nat_listen(socket_cloned.clone(), peer_addr, peer_addr, server).await?;
|
||||
udp_nat_listen(
|
||||
socket_cloned.clone(),
|
||||
peer_addr,
|
||||
peer_addr,
|
||||
server,
|
||||
control_permissions,
|
||||
)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -778,6 +813,7 @@ async fn direct_server(server: ServerPtr) {
|
||||
hbb_common::Stream::from(stream, local_addr),
|
||||
addr,
|
||||
false,
|
||||
None, // Direct connections don't have control_permissions
|
||||
)
|
||||
.await
|
||||
);
|
||||
@@ -809,12 +845,22 @@ async fn start_ipv6(
|
||||
peer_addr_v6: SocketAddr,
|
||||
peer_addr_v4: SocketAddr,
|
||||
server: ServerPtr,
|
||||
control_permissions: Option<ControlPermissions>,
|
||||
) -> bytes::Bytes {
|
||||
crate::test_ipv6().await;
|
||||
if let Some((socket, local_addr_v6)) = crate::get_ipv6_socket().await {
|
||||
let server = server.clone();
|
||||
tokio::spawn(async move {
|
||||
allow_err!(udp_nat_listen(socket.clone(), peer_addr_v6, peer_addr_v4, server).await);
|
||||
allow_err!(
|
||||
udp_nat_listen(
|
||||
socket.clone(),
|
||||
peer_addr_v6,
|
||||
peer_addr_v4,
|
||||
server,
|
||||
control_permissions
|
||||
)
|
||||
.await
|
||||
);
|
||||
});
|
||||
return local_addr_v6;
|
||||
}
|
||||
@@ -826,6 +872,7 @@ async fn udp_nat_listen(
|
||||
peer_addr: SocketAddr,
|
||||
peer_addr_v4: SocketAddr,
|
||||
server: ServerPtr,
|
||||
control_permissions: Option<ControlPermissions>,
|
||||
) -> ResultType<()> {
|
||||
let tm = Instant::now();
|
||||
let socket_cloned = socket.clone();
|
||||
@@ -838,7 +885,14 @@ async fn udp_nat_listen(
|
||||
res,
|
||||
)
|
||||
.await?;
|
||||
crate::server::create_tcp_connection(server, stream.1, peer_addr_v4, true).await?;
|
||||
crate::server::create_tcp_connection(
|
||||
server,
|
||||
stream.1,
|
||||
peer_addr_v4,
|
||||
true,
|
||||
control_permissions,
|
||||
)
|
||||
.await?;
|
||||
Ok(())
|
||||
};
|
||||
func.await.map_err(|e: anyhow::Error| {
|
||||
|
||||
@@ -154,18 +154,30 @@ pub fn new() -> ServerPtr {
|
||||
Arc::new(RwLock::new(server))
|
||||
}
|
||||
|
||||
async fn accept_connection_(server: ServerPtr, socket: Stream, secure: bool) -> ResultType<()> {
|
||||
async fn accept_connection_(
|
||||
server: ServerPtr,
|
||||
socket: Stream,
|
||||
secure: bool,
|
||||
control_permissions: Option<ControlPermissions>,
|
||||
) -> ResultType<()> {
|
||||
let local_addr = socket.local_addr();
|
||||
drop(socket);
|
||||
// even we drop socket, below still may fail if not use reuse_addr,
|
||||
// there is TIME_WAIT before socket really released, so sometimes we
|
||||
// see “Only one usage of each socket address is normally permitted” on windows sometimes,
|
||||
// see "Only one usage of each socket address is normally permitted" on windows sometimes,
|
||||
let listener = new_listener(local_addr, true).await?;
|
||||
log::info!("Server listening on: {}", &listener.local_addr()?);
|
||||
if let Ok((stream, addr)) = timeout(CONNECT_TIMEOUT, listener.accept()).await? {
|
||||
stream.set_nodelay(true).ok();
|
||||
let stream_addr = stream.local_addr()?;
|
||||
create_tcp_connection(server, Stream::from(stream, stream_addr), addr, secure).await?;
|
||||
create_tcp_connection(
|
||||
server,
|
||||
Stream::from(stream, stream_addr),
|
||||
addr,
|
||||
secure,
|
||||
control_permissions,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -175,6 +187,7 @@ pub async fn create_tcp_connection(
|
||||
stream: Stream,
|
||||
addr: SocketAddr,
|
||||
secure: bool,
|
||||
control_permissions: Option<ControlPermissions>,
|
||||
) -> ResultType<()> {
|
||||
let mut stream = stream;
|
||||
let id = server.write().unwrap().get_new_id();
|
||||
@@ -242,7 +255,14 @@ pub async fn create_tcp_connection(
|
||||
}
|
||||
log::info!("wake up macos");
|
||||
}
|
||||
Connection::start(addr, stream, id, Arc::downgrade(&server)).await;
|
||||
Connection::start(
|
||||
addr,
|
||||
stream,
|
||||
id,
|
||||
Arc::downgrade(&server),
|
||||
control_permissions,
|
||||
)
|
||||
.await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -251,8 +271,9 @@ pub async fn accept_connection(
|
||||
socket: Stream,
|
||||
peer_addr: SocketAddr,
|
||||
secure: bool,
|
||||
control_permissions: Option<ControlPermissions>,
|
||||
) {
|
||||
if let Err(err) = accept_connection_(server, socket, secure).await {
|
||||
if let Err(err) = accept_connection_(server, socket, secure, control_permissions).await {
|
||||
log::warn!("Failed to accept connection from {}: {}", peer_addr, err);
|
||||
}
|
||||
}
|
||||
@@ -264,9 +285,18 @@ pub async fn create_relay_connection(
|
||||
peer_addr: SocketAddr,
|
||||
secure: bool,
|
||||
ipv4: bool,
|
||||
control_permissions: Option<ControlPermissions>,
|
||||
) {
|
||||
if let Err(err) =
|
||||
create_relay_connection_(server, relay_server, uuid.clone(), peer_addr, secure, ipv4).await
|
||||
if let Err(err) = create_relay_connection_(
|
||||
server,
|
||||
relay_server,
|
||||
uuid.clone(),
|
||||
peer_addr,
|
||||
secure,
|
||||
ipv4,
|
||||
control_permissions,
|
||||
)
|
||||
.await
|
||||
{
|
||||
log::error!(
|
||||
"Failed to create relay connection for {} with uuid {}: {}",
|
||||
@@ -284,6 +314,7 @@ async fn create_relay_connection_(
|
||||
peer_addr: SocketAddr,
|
||||
secure: bool,
|
||||
ipv4: bool,
|
||||
control_permissions: Option<ControlPermissions>,
|
||||
) -> ResultType<()> {
|
||||
let mut stream = socket_client::connect_tcp(
|
||||
socket_client::ipv4_to_ipv6(crate::check_port(relay_server, RELAY_PORT), ipv4),
|
||||
@@ -298,7 +329,7 @@ async fn create_relay_connection_(
|
||||
..Default::default()
|
||||
});
|
||||
stream.send(&msg_out).await?;
|
||||
create_tcp_connection(server, stream, peer_addr, secure).await?;
|
||||
create_tcp_connection(server, stream, peer_addr, secure, control_permissions).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -71,6 +71,7 @@ lazy_static::lazy_static! {
|
||||
static ref SESSIONS: Arc::<Mutex<HashMap<SessionKey, Session>>> = Default::default();
|
||||
static ref ALIVE_CONNS: Arc::<Mutex<Vec<i32>>> = Default::default();
|
||||
pub static ref AUTHED_CONNS: Arc::<Mutex<Vec<AuthedConn>>> = Default::default();
|
||||
pub static ref CONTROL_PERMISSIONS_ARRAY: Arc::<Mutex<Vec<(i32, ControlPermissions)>>> = Default::default();
|
||||
static ref SWITCH_SIDES_UUID: Arc::<Mutex<HashMap<String, (Instant, uuid::Uuid)>>> = Default::default();
|
||||
static ref WAKELOCK_SENDER: Arc::<Mutex<std::sync::mpsc::Sender<(usize, usize)>>> = Arc::new(Mutex::new(start_wakelock_thread()));
|
||||
}
|
||||
@@ -226,6 +227,7 @@ pub struct Connection {
|
||||
restart: bool,
|
||||
recording: bool,
|
||||
block_input: bool,
|
||||
control_permissions: Option<ControlPermissions>,
|
||||
last_test_delay: Option<Instant>,
|
||||
network_delay: u32,
|
||||
lock_after_session_end: bool,
|
||||
@@ -349,8 +351,14 @@ impl Connection {
|
||||
stream: super::Stream,
|
||||
id: i32,
|
||||
server: super::ServerPtrWeak,
|
||||
control_permissions: Option<ControlPermissions>,
|
||||
) {
|
||||
// Android is not supported yet, so we always set control_permissions to None.
|
||||
#[cfg(target_os = "android")]
|
||||
let control_permissions = None;
|
||||
let _raii_id = raii::ConnectionID::new(id);
|
||||
let _raii_control_permissions_id =
|
||||
raii::ControlPermissionsID::new(id, &control_permissions);
|
||||
let hash = Hash {
|
||||
salt: Config::get_salt(),
|
||||
challenge: Config::get_auto_password(6),
|
||||
@@ -401,14 +409,15 @@ impl Connection {
|
||||
port_forward_address: "".to_owned(),
|
||||
tx_to_cm,
|
||||
authorized: false,
|
||||
keyboard: Connection::permission("enable-keyboard"),
|
||||
clipboard: Connection::permission("enable-clipboard"),
|
||||
audio: Connection::permission("enable-audio"),
|
||||
keyboard: Self::permission(keys::OPTION_ENABLE_KEYBOARD, &control_permissions),
|
||||
clipboard: Self::permission(keys::OPTION_ENABLE_CLIPBOARD, &control_permissions),
|
||||
audio: Self::permission(keys::OPTION_ENABLE_AUDIO, &control_permissions),
|
||||
// to-do: make sure is the option correct here
|
||||
file: Connection::permission(keys::OPTION_ENABLE_FILE_TRANSFER),
|
||||
restart: Connection::permission("enable-remote-restart"),
|
||||
recording: Connection::permission("enable-record-session"),
|
||||
block_input: Connection::permission("enable-block-input"),
|
||||
file: Self::permission(keys::OPTION_ENABLE_FILE_TRANSFER, &control_permissions),
|
||||
restart: Self::permission(keys::OPTION_ENABLE_REMOTE_RESTART, &control_permissions),
|
||||
recording: Self::permission(keys::OPTION_ENABLE_RECORD_SESSION, &control_permissions),
|
||||
block_input: Self::permission(keys::OPTION_ENABLE_BLOCK_INPUT, &control_permissions),
|
||||
control_permissions,
|
||||
last_test_delay: None,
|
||||
network_delay: 0,
|
||||
lock_after_session_end: false,
|
||||
@@ -885,7 +894,7 @@ impl Connection {
|
||||
match data {
|
||||
#[cfg(all(target_os = "windows", feature = "flutter"))]
|
||||
ipc::Data::PrinterData(data) => {
|
||||
if config::Config::get_bool_option(config::keys::OPTION_ENABLE_REMOTE_PRINTER) {
|
||||
if Self::permission(keys::OPTION_ENABLE_REMOTE_PRINTER, &conn.control_permissions) {
|
||||
conn.send_printer_request(data).await;
|
||||
} else {
|
||||
conn.send_remote_printing_disallowed().await;
|
||||
@@ -1942,7 +1951,8 @@ impl Connection {
|
||||
false
|
||||
}
|
||||
|
||||
pub fn permission(enable_prefix_option: &str) -> bool {
|
||||
#[inline]
|
||||
pub fn is_permission_enabled_locally(enable_prefix_option: &str) -> bool {
|
||||
#[cfg(feature = "flutter")]
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
{
|
||||
@@ -1959,6 +1969,37 @@ impl Connection {
|
||||
)
|
||||
}
|
||||
|
||||
fn permission(
|
||||
enable_prefix_option: &str,
|
||||
control_permissions: &Option<ControlPermissions>,
|
||||
) -> bool {
|
||||
use hbb_common::rendezvous_proto::control_permissions::Permission;
|
||||
if let Some(control_permissions) = control_permissions {
|
||||
let permission = match enable_prefix_option {
|
||||
keys::OPTION_ENABLE_KEYBOARD => Some(Permission::keyboard),
|
||||
keys::OPTION_ENABLE_REMOTE_PRINTER => Some(Permission::remote_printer),
|
||||
keys::OPTION_ENABLE_CLIPBOARD => Some(Permission::clipboard),
|
||||
keys::OPTION_ENABLE_FILE_TRANSFER => Some(Permission::file),
|
||||
keys::OPTION_ENABLE_AUDIO => Some(Permission::audio),
|
||||
keys::OPTION_ENABLE_CAMERA => Some(Permission::camera),
|
||||
keys::OPTION_ENABLE_TERMINAL => Some(Permission::terminal),
|
||||
keys::OPTION_ENABLE_TUNNEL => Some(Permission::tunnel),
|
||||
keys::OPTION_ENABLE_REMOTE_RESTART => Some(Permission::restart),
|
||||
keys::OPTION_ENABLE_RECORD_SESSION => Some(Permission::recording),
|
||||
keys::OPTION_ENABLE_BLOCK_INPUT => Some(Permission::block_input),
|
||||
_ => None,
|
||||
};
|
||||
if let Some(permission) = permission {
|
||||
if let Some(enabled) =
|
||||
crate::get_control_permission(control_permissions.permissions, permission)
|
||||
{
|
||||
return enabled;
|
||||
}
|
||||
}
|
||||
}
|
||||
Self::is_permission_enabled_locally(enable_prefix_option)
|
||||
}
|
||||
|
||||
fn update_codec_on_login(&self) {
|
||||
use scrap::codec::{Encoder, EncodingUpdate::*};
|
||||
if let Some(o) = self.lr.clone().option.as_ref() {
|
||||
@@ -2054,7 +2095,10 @@ impl Connection {
|
||||
}
|
||||
match lr.union {
|
||||
Some(login_request::Union::FileTransfer(ft)) => {
|
||||
if !Connection::permission(keys::OPTION_ENABLE_FILE_TRANSFER) {
|
||||
if !Self::permission(
|
||||
keys::OPTION_ENABLE_FILE_TRANSFER,
|
||||
&self.control_permissions,
|
||||
) {
|
||||
self.send_login_error("No permission of file transfer")
|
||||
.await;
|
||||
sleep(1.).await;
|
||||
@@ -2063,7 +2107,7 @@ impl Connection {
|
||||
self.file_transfer = Some((ft.dir, ft.show_hidden));
|
||||
}
|
||||
Some(login_request::Union::ViewCamera(_vc)) => {
|
||||
if !Connection::permission(keys::OPTION_ENABLE_CAMERA) {
|
||||
if !Self::permission(keys::OPTION_ENABLE_CAMERA, &self.control_permissions) {
|
||||
self.send_login_error("No permission of viewing camera")
|
||||
.await;
|
||||
sleep(1.).await;
|
||||
@@ -2072,7 +2116,7 @@ impl Connection {
|
||||
self.view_camera = true;
|
||||
}
|
||||
Some(login_request::Union::Terminal(terminal)) => {
|
||||
if !Connection::permission(keys::OPTION_ENABLE_TERMINAL) {
|
||||
if !Self::permission(keys::OPTION_ENABLE_TERMINAL, &self.control_permissions) {
|
||||
self.send_login_error("No permission of terminal").await;
|
||||
sleep(1.).await;
|
||||
return false;
|
||||
@@ -2120,7 +2164,7 @@ impl Connection {
|
||||
}
|
||||
}
|
||||
Some(login_request::Union::PortForward(mut pf)) => {
|
||||
if !Connection::permission("enable-tunnel") {
|
||||
if !Self::permission(keys::OPTION_ENABLE_TUNNEL, &self.control_permissions) {
|
||||
self.send_login_error("No permission of IP tunneling").await;
|
||||
sleep(1.).await;
|
||||
return false;
|
||||
@@ -5167,6 +5211,41 @@ impl Retina {
|
||||
}
|
||||
}
|
||||
|
||||
/// Get control permission state from CONTROL_PERMISSIONS_ARRAY.
|
||||
/// Returns: Some(false) if any disable, Some(true) if any enable (and no disable), None if not set.
|
||||
pub fn get_control_permission_state(
|
||||
permission: hbb_common::rendezvous_proto::control_permissions::Permission,
|
||||
disable_if_has_disabled: bool,
|
||||
) -> Option<bool> {
|
||||
let control_permissions = CONTROL_PERMISSIONS_ARRAY.lock().unwrap();
|
||||
let mut has_enable = false;
|
||||
let mut has_disable = false;
|
||||
for (_, cp) in control_permissions.iter() {
|
||||
match crate::get_control_permission(cp.permissions, permission) {
|
||||
Some(false) => has_disable = true,
|
||||
Some(true) => has_enable = true,
|
||||
None => {}
|
||||
}
|
||||
}
|
||||
if disable_if_has_disabled {
|
||||
if has_disable {
|
||||
Some(false)
|
||||
} else if has_enable {
|
||||
Some(true)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
if has_enable {
|
||||
Some(true)
|
||||
} else if has_disable {
|
||||
Some(false)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AuthedConn {
|
||||
pub conn_id: i32,
|
||||
pub conn_type: AuthConnType,
|
||||
@@ -5178,6 +5257,7 @@ pub struct AuthedConn {
|
||||
mod raii {
|
||||
// ALIVE_CONNS: all connections, including unauthorized connections
|
||||
// AUTHED_CONNS: all authorized connections
|
||||
// CONTROL_PERMISSIONS_ARRAY: all non-None control permissions
|
||||
|
||||
use super::*;
|
||||
pub struct ConnectionID(i32);
|
||||
@@ -5368,6 +5448,34 @@ mod raii {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ControlPermissionsID {
|
||||
id: i32,
|
||||
control_permissions: Option<ControlPermissions>,
|
||||
}
|
||||
|
||||
impl Drop for ControlPermissionsID {
|
||||
fn drop(&mut self) {
|
||||
if self.control_permissions.is_some() {
|
||||
let mut lock = CONTROL_PERMISSIONS_ARRAY.lock().unwrap();
|
||||
lock.retain(|(conn_id, _)| *conn_id != self.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
impl ControlPermissionsID {
|
||||
pub fn new(id: i32, control_permissions: &Option<ControlPermissions>) -> Self {
|
||||
if let Some(s) = control_permissions {
|
||||
CONTROL_PERMISSIONS_ARRAY
|
||||
.lock()
|
||||
.unwrap()
|
||||
.push((id, s.clone()));
|
||||
}
|
||||
Self {
|
||||
id,
|
||||
control_permissions: control_permissions.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mod test {
|
||||
|
||||
10
src/ui.rs
10
src/ui.rs
@@ -699,6 +699,15 @@ impl UI {
|
||||
fn get_builtin_option(&self, key: String) -> String {
|
||||
crate::ui_interface::get_builtin_option(&key)
|
||||
}
|
||||
|
||||
fn is_remote_modify_enabled_by_control_permissions(&self) -> String {
|
||||
match crate::ui_interface::is_remote_modify_enabled_by_control_permissions() {
|
||||
Some(true) => "true",
|
||||
Some(false) => "false",
|
||||
None => "",
|
||||
}
|
||||
.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
impl sciter::EventHandler for UI {
|
||||
@@ -801,6 +810,7 @@ impl sciter::EventHandler for UI {
|
||||
fn verify_login(String, String);
|
||||
fn is_option_fixed(String);
|
||||
fn get_builtin_option(String);
|
||||
fn is_remote_modify_enabled_by_control_permissions();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1396,7 +1396,16 @@ function self.onMouse(evt) {
|
||||
}
|
||||
|
||||
function check_if_overlay() {
|
||||
if (handler.get_option('allow-remote-config-modification') != 'Y') {
|
||||
var enabled;
|
||||
var is_enabled_by_control_permissions = handler.is_remote_modify_enabled_by_control_permissions();
|
||||
if (is_enabled_by_control_permissions == "true") {
|
||||
enabled = true;
|
||||
} else if (is_enabled_by_control_permissions == "false") {
|
||||
enabled = false;
|
||||
} else {
|
||||
enabled = handler.get_option('allow-remote-config-modification') == 'Y';
|
||||
}
|
||||
if (!enabled) {
|
||||
var time0 = getTime();
|
||||
handler.check_mouse_time();
|
||||
self.timer(120ms, function() {
|
||||
|
||||
@@ -772,7 +772,14 @@ impl<T: InvokeUiCM> IpcTaskRunner<T> {
|
||||
#[tokio::main(flavor = "current_thread")]
|
||||
pub async fn start_ipc<T: InvokeUiCM>(cm: ConnectionManager<T>) {
|
||||
#[cfg(target_os = "windows")]
|
||||
ContextSend::enable(crate::Connection::permission(OPTION_ENABLE_FILE_TRANSFER));
|
||||
{
|
||||
let enabled = crate::Connection::is_permission_enabled_locally(OPTION_ENABLE_FILE_TRANSFER);
|
||||
let mut lock = crate::ui_interface::IS_FILE_TRANSFER_ENABLED
|
||||
.lock()
|
||||
.unwrap();
|
||||
ContextSend::enable(enabled);
|
||||
*lock = Some(enabled);
|
||||
}
|
||||
match ipc::new_listener("_cm").await {
|
||||
Ok(mut incoming) => {
|
||||
while let Some(result) = incoming.next().await {
|
||||
|
||||
@@ -69,6 +69,7 @@ lazy_static::lazy_static! {
|
||||
static ref ASYNC_JOB_STATUS : Arc<Mutex<String>> = Default::default();
|
||||
static ref ASYNC_HTTP_STATUS : Arc<Mutex<HashMap<String, String>>> = Arc::new(Mutex::new(HashMap::new()));
|
||||
static ref TEMPORARY_PASSWD : Arc<Mutex<String>> = Arc::new(Mutex::new("".to_owned()));
|
||||
static ref IS_REMOTE_MODIFY_ENABLED_BY_CONTROL_PERMISSIONS : Arc<Mutex<Option<bool>>> = Arc::new(Mutex::new(None));
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
@@ -79,6 +80,11 @@ lazy_static::lazy_static! {
|
||||
static ref CHILDREN : Children = Default::default();
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
lazy_static::lazy_static! {
|
||||
pub static ref IS_FILE_TRANSFER_ENABLED: Arc<Mutex<Option<bool>>> = Arc::new(Mutex::new(None));
|
||||
}
|
||||
|
||||
const INIT_ASYNC_JOB_STATUS: &str = " ";
|
||||
|
||||
#[cfg(any(target_os = "android", target_os = "ios", feature = "flutter"))]
|
||||
@@ -1166,10 +1172,6 @@ async fn check_connect_status_(reconnect: bool, rx: mpsc::UnboundedReceiver<ipc:
|
||||
let mut video_conn_count = 0;
|
||||
#[cfg(not(feature = "flutter"))]
|
||||
let mut id = "".to_owned();
|
||||
#[cfg(target_os = "windows")]
|
||||
let mut access_mode = "".to_owned();
|
||||
#[cfg(target_os = "windows")]
|
||||
let mut enable_file_transfer = "".to_owned();
|
||||
let is_cm = crate::common::is_cm();
|
||||
|
||||
loop {
|
||||
@@ -1194,28 +1196,6 @@ async fn check_connect_status_(reconnect: bool, rx: mpsc::UnboundedReceiver<ipc:
|
||||
Ok(Some(ipc::Data::Options(Some(v)))) => {
|
||||
*OPTIONS.lock().unwrap() = v;
|
||||
*OPTION_SYNCED.lock().unwrap() = true;
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
let (ft, am) = {
|
||||
let lock = OPTIONS.lock().unwrap();
|
||||
(
|
||||
lock.get(OPTION_ENABLE_FILE_TRANSFER).map(|x| x.to_string()).unwrap_or_default(),
|
||||
lock.get(OPTION_ACCESS_MODE).map(|x| x.to_string()).unwrap_or_default(),
|
||||
)
|
||||
};
|
||||
if ft != enable_file_transfer || am != access_mode {
|
||||
let access_mode_enabled = match am.as_str() {
|
||||
"full" => Some(true),
|
||||
"view" => Some(false),
|
||||
_ => None,
|
||||
};
|
||||
let enabled = access_mode_enabled.unwrap_or(config::option2bool(OPTION_ENABLE_FILE_TRANSFER, &ft));
|
||||
clipboard::ContextSend::enable(enabled);
|
||||
enable_file_transfer = ft;
|
||||
access_mode = am;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(Some(ipc::Data::Config((name, Some(value))))) => {
|
||||
if name == "id" {
|
||||
@@ -1251,6 +1231,19 @@ async fn check_connect_status_(reconnect: bool, rx: mpsc::UnboundedReceiver<ipc:
|
||||
video_conn_count,
|
||||
};
|
||||
}
|
||||
Ok(Some(ipc::Data::ControlPermissionsRemoteModify(v))) => {
|
||||
*IS_REMOTE_MODIFY_ENABLED_BY_CONTROL_PERMISSIONS.lock().unwrap() = v;
|
||||
}
|
||||
#[cfg(target_os = "windows")]
|
||||
Ok(Some(ipc::Data::FileTransferEnabledState(v))) => {
|
||||
if let Some(enabled) = v {
|
||||
let mut lock = IS_FILE_TRANSFER_ENABLED.lock().unwrap();
|
||||
if *lock != v {
|
||||
clipboard::ContextSend::enable(enabled);
|
||||
*lock = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
@@ -1264,6 +1257,9 @@ async fn check_connect_status_(reconnect: bool, rx: mpsc::UnboundedReceiver<ipc:
|
||||
c.send(&ipc::Data::Config(("temporary-password".to_owned(), None))).await.ok();
|
||||
#[cfg(feature = "flutter")]
|
||||
c.send(&ipc::Data::VideoConnCount(None)).await.ok();
|
||||
c.send(&ipc::Data::ControlPermissionsRemoteModify(None)).await.ok();
|
||||
#[cfg(target_os = "windows")]
|
||||
c.send(&ipc::Data::FileTransferEnabledState(None)).await.ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1556,3 +1552,9 @@ pub fn clear_trusted_devices() {
|
||||
pub fn max_encrypt_len() -> usize {
|
||||
hbb_common::config::ENCRYPT_MAX_LEN
|
||||
}
|
||||
|
||||
pub fn is_remote_modify_enabled_by_control_permissions() -> Option<bool> {
|
||||
*IS_REMOTE_MODIFY_ENABLED_BY_CONTROL_PERMISSIONS
|
||||
.lock()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user