From d417149949b4c2f3d5ab57d46b80220bd7cf3bd2 Mon Sep 17 00:00:00 2001 From: fufesou Date: Mon, 15 May 2023 09:57:13 +0800 Subject: [PATCH 1/5] plugin_framework, mid commit Signed-off-by: fufesou --- src/plugin/callback_msg.rs | 52 ++++++++++++++++++++++++++++++++++++++ src/plugin/errno.rs | 2 ++ src/plugin/plugins.rs | 22 +++++++++------- 3 files changed, 67 insertions(+), 9 deletions(-) diff --git a/src/plugin/callback_msg.rs b/src/plugin/callback_msg.rs index 6967e6853..1884240ee 100644 --- a/src/plugin/callback_msg.rs +++ b/src/plugin/callback_msg.rs @@ -9,11 +9,14 @@ use std::{ sync::Arc, }; +const MSG_TO_RUSTDESK_TARGET: &str = "rustdesk"; const MSG_TO_PEER_TARGET: &str = "peer"; const MSG_TO_UI_TARGET: &str = "ui"; const MSG_TO_CONFIG_TARGET: &str = "config"; const MSG_TO_EXT_SUPPORT_TARGET: &str = "ext-support"; +const MSG_TO_RUSTDESK_SIGNATURE_VERIFICATION: &str = "signature_verification"; + #[allow(dead_code)] const MSG_TO_UI_FLUTTER_CHANNEL_MAIN: u16 = 0x01 << 0; #[allow(dead_code)] @@ -37,6 +40,18 @@ lazy_static::lazy_static! { }; } +#[derive(Deserialize)] +pub struct MsgToRustDesk { + pub r#type: String, + pub data: Vec, +} + +#[derive(Deserialize)] +pub struct SignatureVerification { + pub version: String, + pub data: Vec, +} + #[derive(Debug, Deserialize)] struct ConfigToUi { channel: u16, @@ -192,6 +207,43 @@ pub(super) extern "C" fn cb_msg( ); super::callback_ext::ext_support_callback(&id, &peer, &msg) } + MSG_TO_RUSTDESK_TARGET => { + let s = early_return_value!( + std::str::from_utf8(unsafe { std::slice::from_raw_parts(content as _, len) }), + ERR_CALLBACK_INVALID_MSG, + "parse msg string" + ); + let msg_to_rustdesk = early_return_value!( + serde_json::from_str::(s), + ERR_CALLBACK_INVALID_MSG, + "parse msg '{}'", + s + ); + match &msg_to_rustdesk.r#type as &str { + MSG_TO_RUSTDESK_SIGNATURE_VERIFICATION => { + // let signature_data = early_return_value!( + // std::str::from_utf8(&msg_to_rustdesk.data), + // ERR_CALLBACK_INVALID_MSG, + // "parse signature data string" + // ); + // let signature_data = early_return_value!( + // serde_json::from_str::(signature_data), + // ERR_CALLBACK_INVALID_MSG, + // "parse signature data '{}'", + // s + // ); + // to-do: Request server to sign the data. + PluginReturn::success() + } + t => PluginReturn::new( + errno::ERR_CALLBACK_TARGET_TYPE, + &format!( + "Unknown target type '{}' for target {}", + t, MSG_TO_RUSTDESK_TARGET + ), + ), + } + } _ => PluginReturn::new( errno::ERR_CALLBACK_TARGET, &format!("Unknown target '{}'", target), diff --git a/src/plugin/errno.rs b/src/plugin/errno.rs index 410df5132..6b1e3612d 100644 --- a/src/plugin/errno.rs +++ b/src/plugin/errno.rs @@ -13,6 +13,8 @@ pub const ERR_PLUGIN_LOAD: i32 = 10001; pub const ERR_PLUGIN_MSG_INIT: i32 = 10101; pub const ERR_PLUGIN_MSG_INIT_INVALID: i32 = 10102; pub const ERR_PLUGIN_MSG_GET_LOCAL_PEER_ID: i32 = 10103; +pub const ERR_PLUGIN_SIGNATURE_NOT_VERIFIED: i32 = 10104; +pub const ERR_PLUGIN_SIGNATURE_VERIFICATION_FAILED: i32 = 10105; // invalid pub const ERR_CALL_UNIMPLEMENTED: i32 = 10201; pub const ERR_CALL_INVALID_METHOD: i32 = 10202; diff --git a/src/plugin/plugins.rs b/src/plugin/plugins.rs index 2207e4757..61287b1b0 100644 --- a/src/plugin/plugins.rs +++ b/src/plugin/plugins.rs @@ -17,6 +17,7 @@ use std::{ sync::{Arc, RwLock}, }; +pub const METHOD_HANDLE_SIGNATURE_VERIFICATION: &[u8; 30] = b"handle_signature_verification\0"; const METHOD_HANDLE_UI: &[u8; 10] = b"handle_ui\0"; const METHOD_HANDLE_PEER: &[u8; 12] = b"handle_peer\0"; pub const METHOD_HANDLE_LISTEN_EVENT: &[u8; 20] = b"handle_listen_event\0"; @@ -93,19 +94,21 @@ type CallbackNative = extern "C" fn( raw: *const c_void, raw_len: usize, ) -> super::native::NativeReturnValue; -/// The main function of the plugin on the client(self) side. +/// The main function of the plugin. /// /// method: The method. "handle_ui" or "handle_peer" /// peer: The peer id. /// args: The arguments. /// len: The length of the arguments. -type PluginFuncClientCall = extern "C" fn( +type PluginFuncCall = extern "C" fn( method: *const c_char, peer: *const c_char, args: *const c_void, len: usize, ) -> PluginReturn; -/// The main function of the plugin on the server(remote) side. +/// The main function of the plugin. +/// This function is called mainly for handling messages from the peer, +/// and then send messages back to the peer. /// /// method: The method. "handle_ui" or "handle_peer" /// peer: The peer id. @@ -114,7 +117,7 @@ type PluginFuncClientCall = extern "C" fn( /// out: The output. /// The plugin allocate memory with `libc::malloc` and return the pointer. /// out_len: The length of the output. -type PluginFuncServerCall = extern "C" fn( +type PluginFuncCallWithOutData = extern "C" fn( method: *const c_char, peer: *const c_char, args: *const c_void, @@ -138,6 +141,7 @@ struct Callbacks { } #[derive(Serialize)] +#[repr(C)] struct InitInfo { is_server: bool, } @@ -247,8 +251,8 @@ make_plugin!( reset: PluginFuncReset, clear: PluginFuncClear, desc: PluginFuncDesc, - client_call: PluginFuncClientCall, - server_call: PluginFuncServerCall + call: PluginFuncCall, + server_with_out_data: PluginFuncCallWithOutData ); #[derive(Serialize)] @@ -400,7 +404,7 @@ fn handle_event(method: &[u8], id: &str, peer: &str, event: &[u8]) -> ResultType peer.push('\0'); match PLUGINS.read().unwrap().get(id) { Some(plugin) => { - let mut ret = (plugin.client_call)( + let mut ret = (plugin.call)( method.as_ptr() as _, peer.as_ptr() as _, event.as_ptr() as _, @@ -455,7 +459,7 @@ fn _handle_listen_event(event: String, peer: String) { for id in plugins { match PLUGINS.read().unwrap().get(&id) { Some(plugin) => { - let mut ret = (plugin.client_call)( + let mut ret = (plugin.call)( METHOD_HANDLE_LISTEN_EVENT.as_ptr() as _, peer.as_ptr() as _, evt_bytes.as_ptr() as _, @@ -493,7 +497,7 @@ pub fn handle_client_event(id: &str, peer: &str, event: &[u8]) -> Message { Some(plugin) => { let mut out = std::ptr::null_mut(); let mut out_len: usize = 0; - let mut ret = (plugin.server_call)( + let mut ret = (plugin.server_with_out_data)( METHOD_HANDLE_PEER.as_ptr() as _, peer.as_ptr() as _, event.as_ptr() as _, From 522f6f3309847d670c205d56b9c4799749acf22b Mon Sep 17 00:00:00 2001 From: fufesou Date: Tue, 16 May 2023 09:48:33 +0800 Subject: [PATCH 2/5] plugin_framewor, support plugin signature verification Signed-off-by: fufesou --- src/plugin/callback_msg.rs | 176 ++++++++++++++++++++++++++++--------- src/plugin/mod.rs | 6 +- src/plugin/plugins.rs | 63 ++++++++----- 3 files changed, 181 insertions(+), 64 deletions(-) diff --git a/src/plugin/callback_msg.rs b/src/plugin/callback_msg.rs index 1884240ee..6da76bedc 100644 --- a/src/plugin/callback_msg.rs +++ b/src/plugin/callback_msg.rs @@ -1,12 +1,17 @@ use super::*; -use crate::flutter::{self, APP_TYPE_CM, APP_TYPE_MAIN, SESSIONS}; +use crate::{ + flutter::{self, APP_TYPE_CM, APP_TYPE_MAIN, SESSIONS}, + ui_interface::get_api_server, +}; use hbb_common::{lazy_static, log, message_proto::PluginRequest}; -use serde_derive::Deserialize; +use serde_derive::{Deserialize, Serialize}; use serde_json; use std::{ collections::HashMap, ffi::{c_char, c_void}, sync::Arc, + thread, + time::Duration, }; const MSG_TO_RUSTDESK_TARGET: &str = "rustdesk"; @@ -73,6 +78,18 @@ pub(super) struct MsgToExtSupport { pub data: Vec, } +#[derive(Debug, Serialize)] +struct PluginSignReq { + plugin_id: String, + version: String, + msg: Vec, +} + +#[derive(Debug, Deserialize)] +struct PluginSignResp { + signed_msg: Vec, +} + macro_rules! cb_msg_field { ($field: ident) => { let $field = match cstr_to_string($field) { @@ -118,12 +135,12 @@ pub(super) extern "C" fn cb_msg( content: *const c_void, len: usize, ) -> PluginReturn { - cb_msg_field!(peer); cb_msg_field!(target); cb_msg_field!(id); match &target as _ { MSG_TO_PEER_TARGET => { + cb_msg_field!(peer); if let Some(session) = SESSIONS.write().unwrap().get_mut(&peer) { let content_slice = unsafe { std::slice::from_raw_parts(content as *const u8, len) }; @@ -143,6 +160,7 @@ pub(super) extern "C" fn cb_msg( } } MSG_TO_UI_TARGET => { + cb_msg_field!(peer); let content_slice = unsafe { std::slice::from_raw_parts(content as *const u8, len) }; let channel = u16::from_le_bytes([content_slice[0], content_slice[1]]); let content = std::string::String::from_utf8(content_slice[2..].to_vec()) @@ -151,6 +169,7 @@ pub(super) extern "C" fn cb_msg( PluginReturn::success() } MSG_TO_CONFIG_TARGET => { + cb_msg_field!(peer); let s = early_return_value!( std::str::from_utf8(unsafe { std::slice::from_raw_parts(content as _, len) }), ERR_CALLBACK_INVALID_MSG, @@ -194,6 +213,7 @@ pub(super) extern "C" fn cb_msg( } } MSG_TO_EXT_SUPPORT_TARGET => { + cb_msg_field!(peer); let s = early_return_value!( std::str::from_utf8(unsafe { std::slice::from_raw_parts(content as _, len) }), ERR_CALLBACK_INVALID_MSG, @@ -207,43 +227,7 @@ pub(super) extern "C" fn cb_msg( ); super::callback_ext::ext_support_callback(&id, &peer, &msg) } - MSG_TO_RUSTDESK_TARGET => { - let s = early_return_value!( - std::str::from_utf8(unsafe { std::slice::from_raw_parts(content as _, len) }), - ERR_CALLBACK_INVALID_MSG, - "parse msg string" - ); - let msg_to_rustdesk = early_return_value!( - serde_json::from_str::(s), - ERR_CALLBACK_INVALID_MSG, - "parse msg '{}'", - s - ); - match &msg_to_rustdesk.r#type as &str { - MSG_TO_RUSTDESK_SIGNATURE_VERIFICATION => { - // let signature_data = early_return_value!( - // std::str::from_utf8(&msg_to_rustdesk.data), - // ERR_CALLBACK_INVALID_MSG, - // "parse signature data string" - // ); - // let signature_data = early_return_value!( - // serde_json::from_str::(signature_data), - // ERR_CALLBACK_INVALID_MSG, - // "parse signature data '{}'", - // s - // ); - // to-do: Request server to sign the data. - PluginReturn::success() - } - t => PluginReturn::new( - errno::ERR_CALLBACK_TARGET_TYPE, - &format!( - "Unknown target type '{}' for target {}", - t, MSG_TO_RUSTDESK_TARGET - ), - ), - } - } + MSG_TO_RUSTDESK_TARGET => handle_msg_to_rustdesk(id, content, len), _ => PluginReturn::new( errno::ERR_CALLBACK_TARGET, &format!("Unknown target '{}'", target), @@ -258,6 +242,118 @@ fn is_peer_channel(channel: u16) -> bool { || channel & MSG_TO_UI_FLUTTER_CHANNEL_FORWARD != 0 } +fn handle_msg_to_rustdesk(id: String, content: *const c_void, len: usize) -> PluginReturn { + let s = early_return_value!( + std::str::from_utf8(unsafe { std::slice::from_raw_parts(content as _, len) }), + ERR_CALLBACK_INVALID_MSG, + "parse msg string" + ); + let msg_to_rustdesk = early_return_value!( + serde_json::from_str::(s), + ERR_CALLBACK_INVALID_MSG, + "parse msg '{}'", + s + ); + match &msg_to_rustdesk.r#type as &str { + MSG_TO_RUSTDESK_SIGNATURE_VERIFICATION => request_plugin_sign(id, msg_to_rustdesk), + t => PluginReturn::new( + errno::ERR_CALLBACK_TARGET_TYPE, + &format!( + "Unknown target type '{}' for target {}", + t, MSG_TO_RUSTDESK_TARGET + ), + ), + } +} + +fn request_plugin_sign(id: String, msg_to_rustdesk: MsgToRustDesk) -> PluginReturn { + let signature_data = early_return_value!( + std::str::from_utf8(&msg_to_rustdesk.data), + ERR_CALLBACK_INVALID_MSG, + "parse signature data string" + ); + let signature_data = early_return_value!( + serde_json::from_str::(signature_data), + ERR_CALLBACK_INVALID_MSG, + "parse signature data '{}'", + signature_data + ); + // to-do: Request server to sign the data. + thread::spawn(move || { + let sign_url = format!("{}/lic/web/api/plugin-sign", get_api_server()); + let client = reqwest::blocking::Client::new(); + let req = PluginSignReq { + plugin_id: id.clone(), + version: signature_data.version, + msg: signature_data.data, + }; + match client + .post(sign_url) + .json(&req) + .timeout(Duration::from_secs(10)) + .send() + { + Ok(response) => match response.json::() { + Ok(sign_resp) => { + match super::plugins::plugin_call( + &id, + super::plugins::METHOD_HANDLE_SIGNATURE_VERIFICATION, + "", + &sign_resp.signed_msg, + ) { + Ok(..) => { + match super::plugins::plugin_call_get_return( + &id, + super::plugins::METHOD_HANDLE_STATUS, + "", + &[], + ) { + Ok(mut ret) => { + assert!(!ret.msg.is_null()); + let msg = cstr_to_string(ret.msg).unwrap_or_default(); + free_c_ptr(ret.msg as _); + if ret.code == super::errno::ERR_SUCCESS { + log::info!("Plugin '{}' status: '{}'", id, msg); + } else { + log::error!( + "Failed to handle plugin event, id: {}, method: {}, code: {}, msg: {}", + id, + std::string::String::from_utf8(super::plugins::METHOD_HANDLE_STATUS.to_vec()).unwrap_or_default(), + ret.code, + msg + ); + } + } + Err(e) => { + log::error!( + "Failed to call status for plugin '{}': {}", + &id, + e + ); + } + } + } + Err(e) => { + log::error!( + "Failed to call signature verification for plugin '{}': {}", + &id, + e + ); + } + } + } + Err(e) => { + log::error!("Failed to decode response for plugin '{}': {}", &id, e); + } + }, + Err(e) => { + log::error!("Failed to request sign for plugin '{}', {}", &id, e); + } + } + }); + PluginReturn::success() +} + fn push_event_to_ui(channel: u16, peer: &str, content: &str) { let mut m = HashMap::new(); m.insert("name", MSG_TO_UI_TYPE_PLUGIN_EVENT); diff --git a/src/plugin/mod.rs b/src/plugin/mod.rs index 19fc11979..908e80c95 100644 --- a/src/plugin/mod.rs +++ b/src/plugin/mod.rs @@ -1,4 +1,4 @@ -use hbb_common::{libc, log, ResultType}; +use hbb_common::{bail, libc, log, ResultType}; #[cfg(target_os = "windows")] use std::env; use std::{ @@ -133,6 +133,10 @@ fn get_plugin_dir(id: &str) -> ResultType { #[inline] fn cstr_to_string(cstr: *const c_char) -> ResultType { + assert!(!cstr.is_null(), "cstr must be a valid pointer"); + if cstr.is_null() { + bail!("failed to convert string, the pointer is null"); + } Ok(String::from_utf8(unsafe { CStr::from_ptr(cstr).to_bytes().to_vec() })?) diff --git a/src/plugin/plugins.rs b/src/plugin/plugins.rs index 61287b1b0..0f7c37d2a 100644 --- a/src/plugin/plugins.rs +++ b/src/plugin/plugins.rs @@ -17,6 +17,7 @@ use std::{ sync::{Arc, RwLock}, }; +pub const METHOD_HANDLE_STATUS: &[u8; 14] = b"handle_status\0"; pub const METHOD_HANDLE_SIGNATURE_VERIFICATION: &[u8; 30] = b"handle_signature_verification\0"; const METHOD_HANDLE_UI: &[u8; 10] = b"handle_ui\0"; const METHOD_HANDLE_PEER: &[u8; 12] = b"handle_peer\0"; @@ -252,7 +253,7 @@ make_plugin!( clear: PluginFuncClear, desc: PluginFuncDesc, call: PluginFuncCall, - server_with_out_data: PluginFuncCallWithOutData + call_with_out_data: PluginFuncCallWithOutData ); #[derive(Serialize)] @@ -290,6 +291,7 @@ pub(super) fn load_plugins() -> ResultType<()> { } fn load_plugin_dir(dir: &PathBuf) { + log::debug!("Begin load plugin dir: {}", dir.display()); if let Ok(rd) = std::fs::read_dir(dir) { for entry in rd { match entry { @@ -343,6 +345,8 @@ pub fn reload_plugin(id: &str) -> ResultType<()> { } fn load_plugin_path(path: &str) -> ResultType<()> { + log::info!("Begin load plugin {}", path); + let plugin = Plugin::new(path)?; let desc = plugin.desc()?; @@ -383,7 +387,7 @@ fn load_plugin_path(path: &str) -> ResultType<()> { PLUGIN_INFO.write().unwrap().insert(id.clone(), plugin_info); PLUGINS.write().unwrap().insert(id.clone(), plugin); - log::info!("Plugin {} loaded", id); + log::info!("Plugin {} loaded, {}", id, path); Ok(()) } @@ -399,30 +403,43 @@ pub fn load_plugin(id: &str) -> ResultType<()> { Ok(()) } +#[inline] fn handle_event(method: &[u8], id: &str, peer: &str, event: &[u8]) -> ResultType<()> { let mut peer: String = peer.to_owned(); peer.push('\0'); + plugin_call(id, method, &peer, event) +} + +pub fn plugin_call(id: &str, method: &[u8], peer: &str, event: &[u8]) -> ResultType<()> { + let mut ret = plugin_call_get_return(id, method, peer, event)?; + if ret.is_success() { + Ok(()) + } else { + let (code, msg) = ret.get_code_msg(); + bail!( + "Failed to handle plugin event, id: {}, method: {}, code: {}, msg: {}", + id, + std::string::String::from_utf8(method.to_vec()).unwrap_or_default(), + code, + msg + ); + } +} + +#[inline] +pub fn plugin_call_get_return( + id: &str, + method: &[u8], + peer: &str, + event: &[u8], +) -> ResultType { match PLUGINS.read().unwrap().get(id) { - Some(plugin) => { - let mut ret = (plugin.call)( - method.as_ptr() as _, - peer.as_ptr() as _, - event.as_ptr() as _, - event.len(), - ); - if ret.is_success() { - Ok(()) - } else { - let (code, msg) = ret.get_code_msg(); - bail!( - "Failed to handle plugin event, id: {}, method: {}, code: {}, msg: {}", - id, - std::string::String::from_utf8(method.to_vec()).unwrap_or_default(), - code, - msg - ); - } - } + Some(plugin) => Ok((plugin.call)( + method.as_ptr() as _, + peer.as_ptr() as _, + event.as_ptr() as _, + event.len(), + )), None => bail!("Plugin {} not found", id), } } @@ -497,7 +514,7 @@ pub fn handle_client_event(id: &str, peer: &str, event: &[u8]) -> Message { Some(plugin) => { let mut out = std::ptr::null_mut(); let mut out_len: usize = 0; - let mut ret = (plugin.server_with_out_data)( + let mut ret = (plugin.call_with_out_data)( METHOD_HANDLE_PEER.as_ptr() as _, peer.as_ptr() as _, event.as_ptr() as _, From 97e96827573047d4d49edf8ce4d753657745b74f Mon Sep 17 00:00:00 2001 From: fufesou Date: Tue, 16 May 2023 09:56:16 +0800 Subject: [PATCH 3/5] plugin_framework, remove unused mut Signed-off-by: fufesou --- src/plugin/callback_msg.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugin/callback_msg.rs b/src/plugin/callback_msg.rs index 6da76bedc..e510369a6 100644 --- a/src/plugin/callback_msg.rs +++ b/src/plugin/callback_msg.rs @@ -308,7 +308,7 @@ fn request_plugin_sign(id: String, msg_to_rustdesk: MsgToRustDesk) -> PluginRetu "", &[], ) { - Ok(mut ret) => { + Ok(ret) => { assert!(!ret.msg.is_null()); let msg = cstr_to_string(ret.msg).unwrap_or_default(); free_c_ptr(ret.msg as _); From cc46fd59fcd25447fa719c76063d28647b854e25 Mon Sep 17 00:00:00 2001 From: fufesou Date: Tue, 16 May 2023 13:47:11 +0800 Subject: [PATCH 4/5] plugin_framework, add peer id for plugin signature Signed-off-by: fufesou --- src/plugin/callback_msg.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/plugin/callback_msg.rs b/src/plugin/callback_msg.rs index e510369a6..985267c69 100644 --- a/src/plugin/callback_msg.rs +++ b/src/plugin/callback_msg.rs @@ -80,6 +80,7 @@ pub(super) struct MsgToExtSupport { #[derive(Debug, Serialize)] struct PluginSignReq { + peer_id: String, plugin_id: String, version: String, msg: Vec, @@ -282,7 +283,10 @@ fn request_plugin_sign(id: String, msg_to_rustdesk: MsgToRustDesk) -> PluginRetu thread::spawn(move || { let sign_url = format!("{}/lic/web/api/plugin-sign", get_api_server()); let client = reqwest::blocking::Client::new(); + // to-do: Add peer id and plugin_id in the message to sign. + // So that the server can verify the ids. let req = PluginSignReq { + peer_id: crate::ui_interface::get_id(), plugin_id: id.clone(), version: signature_data.version, msg: signature_data.data, From 2a6bcf80d7a8ef6e4b4df26e2d8fc32d749284f8 Mon Sep 17 00:00:00 2001 From: fufesou Date: Tue, 16 May 2023 13:50:05 +0800 Subject: [PATCH 5/5] remove unused comment Signed-off-by: fufesou --- src/plugin/callback_msg.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/plugin/callback_msg.rs b/src/plugin/callback_msg.rs index 985267c69..9a82adfec 100644 --- a/src/plugin/callback_msg.rs +++ b/src/plugin/callback_msg.rs @@ -283,8 +283,6 @@ fn request_plugin_sign(id: String, msg_to_rustdesk: MsgToRustDesk) -> PluginRetu thread::spawn(move || { let sign_url = format!("{}/lic/web/api/plugin-sign", get_api_server()); let client = reqwest::blocking::Client::new(); - // to-do: Add peer id and plugin_id in the message to sign. - // So that the server can verify the ids. let req = PluginSignReq { peer_id: crate::ui_interface::get_id(), plugin_id: id.clone(),