plugin_framewor, support plugin signature verification
Signed-off-by: fufesou <shuanglongchen@yeah.net>
This commit is contained in:
parent
d417149949
commit
522f6f3309
@ -1,12 +1,17 @@
|
|||||||
use super::*;
|
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 hbb_common::{lazy_static, log, message_proto::PluginRequest};
|
||||||
use serde_derive::Deserialize;
|
use serde_derive::{Deserialize, Serialize};
|
||||||
use serde_json;
|
use serde_json;
|
||||||
use std::{
|
use std::{
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
ffi::{c_char, c_void},
|
ffi::{c_char, c_void},
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
|
thread,
|
||||||
|
time::Duration,
|
||||||
};
|
};
|
||||||
|
|
||||||
const MSG_TO_RUSTDESK_TARGET: &str = "rustdesk";
|
const MSG_TO_RUSTDESK_TARGET: &str = "rustdesk";
|
||||||
@ -73,6 +78,18 @@ pub(super) struct MsgToExtSupport {
|
|||||||
pub data: Vec<u8>,
|
pub data: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
struct PluginSignReq {
|
||||||
|
plugin_id: String,
|
||||||
|
version: String,
|
||||||
|
msg: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
struct PluginSignResp {
|
||||||
|
signed_msg: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
macro_rules! cb_msg_field {
|
macro_rules! cb_msg_field {
|
||||||
($field: ident) => {
|
($field: ident) => {
|
||||||
let $field = match cstr_to_string($field) {
|
let $field = match cstr_to_string($field) {
|
||||||
@ -118,12 +135,12 @@ pub(super) extern "C" fn cb_msg(
|
|||||||
content: *const c_void,
|
content: *const c_void,
|
||||||
len: usize,
|
len: usize,
|
||||||
) -> PluginReturn {
|
) -> PluginReturn {
|
||||||
cb_msg_field!(peer);
|
|
||||||
cb_msg_field!(target);
|
cb_msg_field!(target);
|
||||||
cb_msg_field!(id);
|
cb_msg_field!(id);
|
||||||
|
|
||||||
match &target as _ {
|
match &target as _ {
|
||||||
MSG_TO_PEER_TARGET => {
|
MSG_TO_PEER_TARGET => {
|
||||||
|
cb_msg_field!(peer);
|
||||||
if let Some(session) = SESSIONS.write().unwrap().get_mut(&peer) {
|
if let Some(session) = SESSIONS.write().unwrap().get_mut(&peer) {
|
||||||
let content_slice =
|
let content_slice =
|
||||||
unsafe { std::slice::from_raw_parts(content as *const u8, len) };
|
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 => {
|
MSG_TO_UI_TARGET => {
|
||||||
|
cb_msg_field!(peer);
|
||||||
let content_slice = unsafe { std::slice::from_raw_parts(content as *const u8, len) };
|
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 channel = u16::from_le_bytes([content_slice[0], content_slice[1]]);
|
||||||
let content = std::string::String::from_utf8(content_slice[2..].to_vec())
|
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()
|
PluginReturn::success()
|
||||||
}
|
}
|
||||||
MSG_TO_CONFIG_TARGET => {
|
MSG_TO_CONFIG_TARGET => {
|
||||||
|
cb_msg_field!(peer);
|
||||||
let s = early_return_value!(
|
let s = early_return_value!(
|
||||||
std::str::from_utf8(unsafe { std::slice::from_raw_parts(content as _, len) }),
|
std::str::from_utf8(unsafe { std::slice::from_raw_parts(content as _, len) }),
|
||||||
ERR_CALLBACK_INVALID_MSG,
|
ERR_CALLBACK_INVALID_MSG,
|
||||||
@ -194,6 +213,7 @@ pub(super) extern "C" fn cb_msg(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
MSG_TO_EXT_SUPPORT_TARGET => {
|
MSG_TO_EXT_SUPPORT_TARGET => {
|
||||||
|
cb_msg_field!(peer);
|
||||||
let s = early_return_value!(
|
let s = early_return_value!(
|
||||||
std::str::from_utf8(unsafe { std::slice::from_raw_parts(content as _, len) }),
|
std::str::from_utf8(unsafe { std::slice::from_raw_parts(content as _, len) }),
|
||||||
ERR_CALLBACK_INVALID_MSG,
|
ERR_CALLBACK_INVALID_MSG,
|
||||||
@ -207,43 +227,7 @@ pub(super) extern "C" fn cb_msg(
|
|||||||
);
|
);
|
||||||
super::callback_ext::ext_support_callback(&id, &peer, &msg)
|
super::callback_ext::ext_support_callback(&id, &peer, &msg)
|
||||||
}
|
}
|
||||||
MSG_TO_RUSTDESK_TARGET => {
|
MSG_TO_RUSTDESK_TARGET => handle_msg_to_rustdesk(id, content, len),
|
||||||
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::<MsgToRustDesk>(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::<SignatureVerification>(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(
|
_ => PluginReturn::new(
|
||||||
errno::ERR_CALLBACK_TARGET,
|
errno::ERR_CALLBACK_TARGET,
|
||||||
&format!("Unknown target '{}'", target),
|
&format!("Unknown target '{}'", target),
|
||||||
@ -258,6 +242,118 @@ fn is_peer_channel(channel: u16) -> bool {
|
|||||||
|| channel & MSG_TO_UI_FLUTTER_CHANNEL_FORWARD != 0
|
|| 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::<MsgToRustDesk>(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::<SignatureVerification>(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::<PluginSignResp>() {
|
||||||
|
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) {
|
fn push_event_to_ui(channel: u16, peer: &str, content: &str) {
|
||||||
let mut m = HashMap::new();
|
let mut m = HashMap::new();
|
||||||
m.insert("name", MSG_TO_UI_TYPE_PLUGIN_EVENT);
|
m.insert("name", MSG_TO_UI_TYPE_PLUGIN_EVENT);
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use hbb_common::{libc, log, ResultType};
|
use hbb_common::{bail, libc, log, ResultType};
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::{
|
use std::{
|
||||||
@ -133,6 +133,10 @@ fn get_plugin_dir(id: &str) -> ResultType<PathBuf> {
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn cstr_to_string(cstr: *const c_char) -> ResultType<String> {
|
fn cstr_to_string(cstr: *const c_char) -> ResultType<String> {
|
||||||
|
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 {
|
Ok(String::from_utf8(unsafe {
|
||||||
CStr::from_ptr(cstr).to_bytes().to_vec()
|
CStr::from_ptr(cstr).to_bytes().to_vec()
|
||||||
})?)
|
})?)
|
||||||
|
@ -17,6 +17,7 @@ use std::{
|
|||||||
sync::{Arc, RwLock},
|
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";
|
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_UI: &[u8; 10] = b"handle_ui\0";
|
||||||
const METHOD_HANDLE_PEER: &[u8; 12] = b"handle_peer\0";
|
const METHOD_HANDLE_PEER: &[u8; 12] = b"handle_peer\0";
|
||||||
@ -252,7 +253,7 @@ make_plugin!(
|
|||||||
clear: PluginFuncClear,
|
clear: PluginFuncClear,
|
||||||
desc: PluginFuncDesc,
|
desc: PluginFuncDesc,
|
||||||
call: PluginFuncCall,
|
call: PluginFuncCall,
|
||||||
server_with_out_data: PluginFuncCallWithOutData
|
call_with_out_data: PluginFuncCallWithOutData
|
||||||
);
|
);
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
@ -290,6 +291,7 @@ pub(super) fn load_plugins() -> ResultType<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn load_plugin_dir(dir: &PathBuf) {
|
fn load_plugin_dir(dir: &PathBuf) {
|
||||||
|
log::debug!("Begin load plugin dir: {}", dir.display());
|
||||||
if let Ok(rd) = std::fs::read_dir(dir) {
|
if let Ok(rd) = std::fs::read_dir(dir) {
|
||||||
for entry in rd {
|
for entry in rd {
|
||||||
match entry {
|
match entry {
|
||||||
@ -343,6 +345,8 @@ pub fn reload_plugin(id: &str) -> ResultType<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn load_plugin_path(path: &str) -> ResultType<()> {
|
fn load_plugin_path(path: &str) -> ResultType<()> {
|
||||||
|
log::info!("Begin load plugin {}", path);
|
||||||
|
|
||||||
let plugin = Plugin::new(path)?;
|
let plugin = Plugin::new(path)?;
|
||||||
let desc = plugin.desc()?;
|
let desc = plugin.desc()?;
|
||||||
|
|
||||||
@ -383,7 +387,7 @@ fn load_plugin_path(path: &str) -> ResultType<()> {
|
|||||||
PLUGIN_INFO.write().unwrap().insert(id.clone(), plugin_info);
|
PLUGIN_INFO.write().unwrap().insert(id.clone(), plugin_info);
|
||||||
PLUGINS.write().unwrap().insert(id.clone(), plugin);
|
PLUGINS.write().unwrap().insert(id.clone(), plugin);
|
||||||
|
|
||||||
log::info!("Plugin {} loaded", id);
|
log::info!("Plugin {} loaded, {}", id, path);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -399,17 +403,15 @@ pub fn load_plugin(id: &str) -> ResultType<()> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn handle_event(method: &[u8], id: &str, peer: &str, event: &[u8]) -> ResultType<()> {
|
fn handle_event(method: &[u8], id: &str, peer: &str, event: &[u8]) -> ResultType<()> {
|
||||||
let mut peer: String = peer.to_owned();
|
let mut peer: String = peer.to_owned();
|
||||||
peer.push('\0');
|
peer.push('\0');
|
||||||
match PLUGINS.read().unwrap().get(id) {
|
plugin_call(id, method, &peer, event)
|
||||||
Some(plugin) => {
|
}
|
||||||
let mut ret = (plugin.call)(
|
|
||||||
method.as_ptr() as _,
|
pub fn plugin_call(id: &str, method: &[u8], peer: &str, event: &[u8]) -> ResultType<()> {
|
||||||
peer.as_ptr() as _,
|
let mut ret = plugin_call_get_return(id, method, peer, event)?;
|
||||||
event.as_ptr() as _,
|
|
||||||
event.len(),
|
|
||||||
);
|
|
||||||
if ret.is_success() {
|
if ret.is_success() {
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
@ -422,7 +424,22 @@ fn handle_event(method: &[u8], id: &str, peer: &str, event: &[u8]) -> ResultType
|
|||||||
msg
|
msg
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn plugin_call_get_return(
|
||||||
|
id: &str,
|
||||||
|
method: &[u8],
|
||||||
|
peer: &str,
|
||||||
|
event: &[u8],
|
||||||
|
) -> ResultType<PluginReturn> {
|
||||||
|
match PLUGINS.read().unwrap().get(id) {
|
||||||
|
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),
|
None => bail!("Plugin {} not found", id),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -497,7 +514,7 @@ pub fn handle_client_event(id: &str, peer: &str, event: &[u8]) -> Message {
|
|||||||
Some(plugin) => {
|
Some(plugin) => {
|
||||||
let mut out = std::ptr::null_mut();
|
let mut out = std::ptr::null_mut();
|
||||||
let mut out_len: usize = 0;
|
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 _,
|
METHOD_HANDLE_PEER.as_ptr() as _,
|
||||||
peer.as_ptr() as _,
|
peer.as_ptr() as _,
|
||||||
event.as_ptr() as _,
|
event.as_ptr() as _,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user