diff --git a/src/platform/linux.rs b/src/platform/linux.rs index 1e39b1f07..5ca8052f3 100644 --- a/src/platform/linux.rs +++ b/src/platform/linux.rs @@ -660,17 +660,17 @@ pub fn check_super_user_permission() -> ResultType { Ok(status.success() && status.code() == Some(0)) } -pub fn elevate(arg: Vec<&str>) -> ResultType> { +pub fn elevate(args: Vec<&str>) -> ResultType> { let cmd = std::env::current_exe()?; match cmd.to_str() { Some(cmd) => { - let mut args = vec![cmd]; - args.append(&mut arg.clone()); + let mut args_with_exe = vec![cmd]; + args_with_exe.append(&mut args.clone()); // -E required for opensuse if is_opensuse() { - args.insert(0, "-E"); + args_with_exe.insert(0, "-E"); } - let task = Command::new("pkexec").args(args).spawn()?; + let task = Command::new("pkexec").args(args_with_exe).spawn()?; Ok(Some(task)) } None => { diff --git a/src/platform/macos.mm b/src/platform/macos.mm index a252a9a8f..d40a56014 100644 --- a/src/platform/macos.mm +++ b/src/platform/macos.mm @@ -42,31 +42,45 @@ extern "C" bool InputMonitoringAuthStatus(bool prompt) { #endif } +extern "C" bool Elevate(char* process, char** args) { + AuthorizationRef authRef; + OSStatus status; + + status = AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment, + kAuthorizationFlagDefaults, &authRef); + if (status != errAuthorizationSuccess) { + printf("Failed to create AuthorizationRef\n"); + return false; + } + + AuthorizationItem authItem = {kAuthorizationRightExecute, 0, NULL, 0}; + AuthorizationRights authRights = {1, &authItem}; + AuthorizationFlags flags = kAuthorizationFlagDefaults | + kAuthorizationFlagInteractionAllowed | + kAuthorizationFlagPreAuthorize | + kAuthorizationFlagExtendRights; + status = AuthorizationCopyRights(authRef, &authRights, kAuthorizationEmptyEnvironment, flags, NULL); + if (status != errAuthorizationSuccess) { + printf("Failed to authorize\n"); + return false; + } + + if (process != NULL) { + FILE *pipe = NULL; + status = AuthorizationExecuteWithPrivileges(authRef, process, kAuthorizationFlagDefaults, args, &pipe); + if (status != errAuthorizationSuccess) { + printf("Failed to run as root\n"); + AuthorizationFree(authRef, kAuthorizationFlagDefaults); + return false; + } + } + + AuthorizationFree(authRef, kAuthorizationFlagDefaults); + return true; +} + extern "C" bool MacCheckAdminAuthorization() { - AuthorizationRef authRef; - OSStatus status; - - status = AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment, - kAuthorizationFlagDefaults, &authRef); - if (status != errAuthorizationSuccess) { - printf("Failed to create AuthorizationRef\n"); - return false; - } - - AuthorizationItem authItem = {kAuthorizationRightExecute, 0, NULL, 0}; - AuthorizationRights authRights = {1, &authItem}; - AuthorizationFlags flags = kAuthorizationFlagDefaults | - kAuthorizationFlagInteractionAllowed | - kAuthorizationFlagPreAuthorize | - kAuthorizationFlagExtendRights; - status = AuthorizationCopyRights(authRef, &authRights, kAuthorizationEmptyEnvironment, flags, NULL); - if (status != errAuthorizationSuccess) { - printf("Failed to authorize\n"); - return false; - } - - AuthorizationFree(authRef, kAuthorizationFlagDefaults); - return true; + return Elevate(NULL, NULL); } extern "C" float BackingScaleFactor() { diff --git a/src/platform/macos.rs b/src/platform/macos.rs index 954d858bd..9a2c8f918 100644 --- a/src/platform/macos.rs +++ b/src/platform/macos.rs @@ -17,11 +17,15 @@ use core_graphics::{ display::{kCGNullWindowID, kCGWindowListOptionOnScreenOnly, CGWindowListCopyWindowInfo}, window::{kCGWindowName, kCGWindowOwnerPID}, }; -use hbb_common::{allow_err, anyhow::anyhow, bail, log, message_proto::Resolution}; +use hbb_common::{allow_err, anyhow::anyhow, bail, libc, log, message_proto::Resolution}; use include_dir::{include_dir, Dir}; use objc::{class, msg_send, sel, sel_impl}; use scrap::{libc::c_void, quartz::ffi::*}; -use std::path::PathBuf; +use std::{ + ffi::{c_char, CString}, + mem::size_of, + path::PathBuf, +}; static PRIVILEGES_SCRIPTS_DIR: Dir = include_dir!("$CARGO_MANIFEST_DIR/src/platform/privileges_scripts"); @@ -35,6 +39,7 @@ extern "C" { fn AXIsProcessTrustedWithOptions(options: CFDictionaryRef) -> BOOL; fn InputMonitoringAuthStatus(_: BOOL) -> BOOL; fn MacCheckAdminAuthorization() -> BOOL; + fn Elevate(process: *const c_char, args: *const *const c_char) -> BOOL; fn MacGetModeNum(display: u32, numModes: *mut u32) -> BOOL; fn MacGetModes( display: u32, @@ -671,3 +676,30 @@ pub fn change_resolution(name: &str, width: usize, height: usize) -> ResultType< pub fn check_super_user_permission() -> ResultType { unsafe { Ok(MacCheckAdminAuthorization() == YES) } } + +pub fn elevate(args: Vec<&str>) -> ResultType { + let cmd = std::env::current_exe()?; + match cmd.to_str() { + Some(cmd) => { + let cmd = CString::new(cmd)?; + let mut cstring_args = Vec::new(); + for arg in args.iter() { + cstring_args.push(CString::new(*arg)?); + } + unsafe { + let args_ptr: *mut *const c_char = + libc::malloc((cstring_args.len() + 1) * size_of::<*const c_char>()) as _; + for i in 0..cstring_args.len() { + *args_ptr.add(i) = cstring_args[i].as_ptr() as _; + } + *args_ptr.add(cstring_args.len()) = std::ptr::null() as _; + let r = Elevate(cmd.as_ptr() as _, args_ptr as _); + libc::free(args_ptr as _); + Ok(r == YES) + } + } + None => { + bail!("Failed to get current exe str"); + } + } +} diff --git a/src/plugin/manager.rs b/src/plugin/manager.rs index 8ea72169e..0c60f982f 100644 --- a/src/plugin/manager.rs +++ b/src/plugin/manager.rs @@ -20,11 +20,11 @@ const MSG_TO_UI_PLUGIN_MANAGER_UNINSTALL: &str = "plugin_uninstall"; const IPC_PLUGIN_POSTFIX: &str = "_plugin"; #[cfg(target_os = "windows")] -const PLUGIN_PLATFORM: &str = "Windows"; +const PLUGIN_PLATFORM: &str = "windows"; #[cfg(target_os = "linux")] -const PLUGIN_PLATFORM: &str = "Linux"; +const PLUGIN_PLATFORM: &str = "linux"; #[cfg(target_os = "macos")] -const PLUGIN_PLATFORM: &str = "MacOS"; +const PLUGIN_PLATFORM: &str = "macos"; lazy_static::lazy_static! { static ref PLUGIN_INFO: Arc>> = Arc::new(Mutex::new(HashMap::new())); @@ -67,38 +67,82 @@ fn get_source_plugins() -> HashMap { let mut plugins = HashMap::new(); for source in get_plugin_source_list().into_iter() { let url = format!("{}/meta.toml", source.url); - match reqwest::blocking::get(&url) { - Ok(resp) => { - if !resp.status().is_success() { - log::error!( - "Failed to get plugin list from '{}', status code: {}", - url, - resp.status() + // match reqwest::blocking::get(&url) { + // Ok(resp) => { + // if !resp.status().is_success() { + // log::error!( + // "Failed to get plugin list from '{}', status code: {}", + // url, + // resp.status() + // ); + // } + // if let Ok(text) = resp.text() { + // match toml::from_str::(&text) { + // Ok(manager_meta) => { + // for meta in manager_meta.plugins.iter() { + // if !meta.platforms.to_uppercase().contains(&PLUGIN_PLATFORM.to_uppercase()) { + // continue; + // } + // plugins.insert( + // meta.id.clone(), + // PluginInfo { + // source: source.clone(), + // meta: meta.clone(), + // installed_version: "".to_string(), + // invalid_reason: "".to_string(), + // }, + // ); + // } + // } + // Err(e) => log::error!("Failed to parse plugin list from '{}', {}", url, e), + // } + // } + // } + // Err(e) => log::error!("Failed to get plugin list from '{}', {}", url, e), + // } + + let text = r#" +version = "v0.1.0" +description = "" + +[[plugins]] +id = "RustDesk.c.privacy-mode" +name = "Privacy Mode" +version = "v0.1.0" +description = "This plugin can enable private mode to prevent others from seeing your operations." +platforms = "Windows,Linux,MacOS" +author = "RustDesk" +home = "" +license = "MIT" +source = "" + +[plugins.publish_info] +published = "2023-05-07 14:00:00" +last_released = "2023-05-07 14:00:00" + "# + .to_string(); + match toml::from_str::(&text) { + Ok(manager_meta) => { + for meta in manager_meta.plugins.iter() { + if !meta + .platforms + .to_uppercase() + .contains(&PLUGIN_PLATFORM.to_uppercase()) + { + continue; + } + plugins.insert( + meta.id.clone(), + PluginInfo { + source: source.clone(), + meta: meta.clone(), + installed_version: "".to_string(), + invalid_reason: "".to_string(), + }, ); } - if let Ok(text) = resp.text() { - match toml::from_str::(&text) { - Ok(manager_meta) => { - for meta in manager_meta.plugins.iter() { - if !meta.platforms.contains(PLUGIN_PLATFORM) { - continue; - } - plugins.insert( - meta.id.clone(), - PluginInfo { - source: source.clone(), - meta: meta.clone(), - installed_version: "".to_string(), - invalid_reason: "".to_string(), - }, - ); - } - } - Err(e) => log::error!("Failed to parse plugin list from '{}', {}", url, e), - } - } } - Err(e) => log::error!("Failed to get plugin list from '{}', {}", url, e), + Err(e) => log::error!("Failed to parse plugin list from '{}', {}", url, e), } } plugins @@ -154,6 +198,71 @@ pub fn load_plugin_list() { *plugin_info_lock = plugins; } +#[cfg(target_os = "windows")] +fn elevate_install( + plugin_id: &str, + plugin_url: &str, + same_plugin_exists: bool, +) -> ResultType { + // to-do: Support args with space in quotes. 'arg 1' and "arg 2" + let args = if same_plugin_exists { + format!("--plugin-install {}", plugin_id) + } else { + format!("--plugin-install {} {}", plugin_id, plugin_url) + }; + Ok(crate::platform::elevate(&args)?) +} + +#[cfg(target_os = "linux")] +fn elevate_install( + plugin_id: &str, + plugin_url: &str, + same_plugin_exists: bool, +) -> ResultType { + let mut args = vec!["--plugin-install", plugin_id]; + if !same_plugin_exists { + args.push(&plugin_url); + } + allowed_install = match crate::platform::elevate(args) { + Ok(Some(mut child)) => match child.wait() { + Ok(status) => { + if status.success() { + true + } else { + log::error!( + "Failed to wait install process, process status: {:?}", + status + ); + false + } + } + Err(e) => { + log::error!("Failed to wait install process, error: {}", e); + false + } + }, + Ok(None) => false, + Err(e) => { + log::error!("Failed to run install process, error: {}", e); + false + } + }; + Ok(allowed_install) +} + +#[cfg(target_os = "macos")] +fn elevate_install( + plugin_id: &str, + plugin_url: &str, + same_plugin_exists: bool, +) -> ResultType { + let mut args = vec!["--plugin-install", plugin_id]; + if !same_plugin_exists { + args.push(&plugin_url); + } + Ok(crate::platform::elevate(args)?) +} + pub fn install_plugin(id: &str) -> ResultType<()> { match PLUGIN_INFO.lock().unwrap().get(id) { Some(plugin) => { @@ -163,7 +272,6 @@ pub fn install_plugin(id: &str) -> ResultType<()> { same_plugin_exists = true; } } - let plugin_url = format!( "{}/plugins/{}/{}/{}_{}.zip", plugin.source.url, @@ -172,50 +280,7 @@ pub fn install_plugin(id: &str) -> ResultType<()> { plugin.meta.id, plugin.meta.version ); - - let allowed_install; - #[cfg(windows)] - { - // to-do: Support args with space in quotes. 'arg 1' and "arg 2" - let args = if same_plugin_exists { - format!("--plugin-install {}", id) - } else { - format!("--plugin-install {} {}", id, plugin_url) - }; - allowed_install = crate::platform::elevate(&args)?; - } - #[cfg(target_os = "linux")] - { - let mut args = vec!["--plugin-install", id]; - if !same_plugin_exists { - args.push(&plugin_url); - } - allowed_install = match crate::platform::elevate(args) { - Ok(Some(mut child)) => match child.wait() { - Ok(status) => { - if status.success() { - true - } else { - log::error!( - "Failed to wait install process, process status: {:?}", - status - ); - false - } - } - Err(e) => { - log::error!("Failed to wait install process, error: {}", e); - false - } - }, - Ok(None) => false, - Err(e) => { - log::error!("Failed to run install process, error: {}", e); - false - } - }; - } - + let allowed_install = elevate_install(id, &plugin_url, same_plugin_exists)?; if allowed_install && same_plugin_exists { super::ipc::load_plugin(id)?; super::plugins::load_plugin(id)?;