From 290db4b95bd1083d8d871eef902275f6daa7b784 Mon Sep 17 00:00:00 2001 From: hinto-janaiyo Date: Thu, 24 Nov 2022 20:28:13 -0500 Subject: [PATCH] update: sanity check p2pool/xmrig path from user before starting Define a strict list [&str; 4] of valid path endings for p2pool/xmrig. This prevents users (for some reason) inputting a path to some other (maybe very important) file which Gupax would have completely overridden with the update binary. Windows paths end with [.exe]. --- src/gupax.rs | 13 +++--- src/main.rs | 8 ++-- src/update.rs | 108 ++++++++++++++++++++++++++++++++++++++++---------- 3 files changed, 100 insertions(+), 29 deletions(-) diff --git a/src/gupax.rs b/src/gupax.rs index 3c99008..ddfd5c7 100644 --- a/src/gupax.rs +++ b/src/gupax.rs @@ -23,9 +23,12 @@ use egui::{ RichText, Vec2, }; -use crate::constants::*; -use crate::disk::{Gupax,Version}; -use crate::update::*; +use crate::{ + constants::*, + disk::{Gupax,Version}, + update::*, + ErrorState,ErrorFerris,ErrorButtons, +}; use std::{ thread, sync::{Arc,Mutex}, @@ -75,7 +78,7 @@ pub enum Ratio { //---------------------------------------------------------------------------------------------------- Gupax impl Gupax { - pub fn show(&mut self, og: &Arc>, state_ver: &Arc>, update: &Arc>, file_window: &Arc>, state_path: &Path, width: f32, height: f32, frame: &mut eframe::Frame, ctx: &egui::Context, ui: &mut egui::Ui) { + pub fn show(&mut self, og: &Arc>, state_path: &Path, update: &Arc>, file_window: &Arc>, error_state: &mut ErrorState, width: f32, height: f32, frame: &mut eframe::Frame, ctx: &egui::Context, ui: &mut egui::Ui) { // Update button + Progress bar ui.group(|ui| { // These are in unnecessary [ui.vertical()]'s @@ -88,7 +91,7 @@ impl Gupax { ui.vertical(|ui| { ui.set_enabled(!updating); if ui.add_sized([width, height], Button::new("Check for updates")).on_hover_text(GUPAX_UPDATE).clicked() { - Update::spawn_thread(og, update, state_ver, state_path); + Update::spawn_thread(og, &self, state_path, update, error_state); } }); ui.vertical(|ui| { diff --git a/src/main.rs b/src/main.rs index aba5b10..3f1a844 100644 --- a/src/main.rs +++ b/src/main.rs @@ -479,7 +479,7 @@ fn init_options(initial_window_size: Option) -> NativeOptions { options } -fn init_auto(app: &App) { +fn init_auto(app: &mut App) { // Return early if [--no-startup] was not passed if app.no_startup { info!("[--no-startup] flag passed, skipping init_auto()..."); @@ -493,7 +493,7 @@ fn init_auto(app: &App) { // [Auto-Update] if app.state.gupax.auto_update { - Update::spawn_thread(&app.og, &app.update, &app.state.version, &app.state_path); + Update::spawn_thread(&app.og, &app.state.gupax, &app.state_path, &app.update, &mut app.error_state); } else { info!("Skipping auto-update..."); } @@ -636,7 +636,7 @@ fn main() { init_logger(now); let mut app = App::new(); app.now = now; - init_auto(&app); + init_auto(&mut app); let initial_window_size = match app.state.gupax.simple { true => Some(Vec2::new(app.state.gupax.selected_width as f32, app.state.gupax.selected_height as f32)), false => Some(Vec2::new(APP_DEFAULT_WIDTH, APP_DEFAULT_HEIGHT)), @@ -1001,7 +1001,7 @@ impl eframe::App for App { Status::show(self, self.width, self.height, ctx, ui); } Tab::Gupax => { - Gupax::show(&mut self.state.gupax, &self.og, &self.state.version, &self.update, &self.file_window, &self.state_path, self.width, self.height, frame, ctx, ui); + Gupax::show(&mut self.state.gupax, &self.og, &self.state_path, &self.update, &self.file_window, &mut self.error_state, self.width, self.height, frame, ctx, ui); } Tab::P2pool => { P2pool::show(&mut self.state.p2pool, &mut self.node_vec, &self.og, self.p2pool, &self.ping, &self.regex, self.width, self.height, ctx, ui); diff --git a/src/update.rs b/src/update.rs index bcac491..033923b 100644 --- a/src/update.rs +++ b/src/update.rs @@ -27,10 +27,12 @@ use anyhow::{anyhow,Error}; use arti_client::{TorClient}; use arti_hyper::*; -use crate::constants::GUPAX_VERSION; -//use crate::{Name::*,State}; -use crate::disk::*; -use crate::update::Name::*; +use crate::{ + constants::GUPAX_VERSION, + disk::*, + update::Name::*, + ErrorState,ErrorFerris,ErrorButtons, +}; use hyper::{ Client,Body,Request, header::{HeaderValue,LOCATION}, @@ -81,18 +83,18 @@ const P2POOL_HASH: &str = "sha256sums.txt.asc"; const XMRIG_HASH: &str = "SHA256SUMS"; #[cfg(target_os = "windows")] -const GUPAX_EXTENSION: &'static str = "-windows-x64-standalone.zip"; +const GUPAX_EXTENSION: &str = "-windows-x64-standalone.zip"; #[cfg(target_os = "windows")] -const P2POOL_EXTENSION: &'static str = "-windows-x64.zip"; +const P2POOL_EXTENSION: &str = "-windows-x64.zip"; #[cfg(target_os = "windows")] -const XMRIG_EXTENSION: &'static str = "-msvc-win64.zip"; +const XMRIG_EXTENSION: &str = "-msvc-win64.zip"; #[cfg(target_os = "macos")] -const GUPAX_EXTENSION: &'static str = "-macos-x64-standalone.tar.gz"; +const GUPAX_EXTENSION: &str = "-macos-x64-standalone.tar.gz"; #[cfg(target_os = "macos")] -const P2POOL_EXTENSION: &'static str = "-macos-x64.tar.gz"; +const P2POOL_EXTENSION: &str = "-macos-x64.tar.gz"; #[cfg(target_os = "macos")] -const XMRIG_EXTENSION: &'static str = "-macos-x64.tar.gz"; +const XMRIG_EXTENSION: &str = "-macos-x64.tar.gz"; #[cfg(target_os = "linux")] const GUPAX_EXTENSION: &str = "-linux-x64-standalone.tar.gz"; @@ -102,22 +104,32 @@ const P2POOL_EXTENSION: &str = "-linux-x64.tar.gz"; const XMRIG_EXTENSION: &str = "-linux-static-x64.tar.gz"; #[cfg(target_os = "windows")] -const GUPAX_BINARY: &'static str = "Gupax.exe"; +const GUPAX_BINARY: &str = "Gupax.exe"; #[cfg(target_os = "macos")] -const GUPAX_BINARY: &'static str = "Gupax"; +const GUPAX_BINARY: &str = "Gupax"; #[cfg(target_os = "linux")] const GUPAX_BINARY: &str = "gupax"; #[cfg(target_os = "windows")] -const P2POOL_BINARY: &'static str = "p2pool.exe"; +const P2POOL_BINARY: &str = "p2pool.exe"; #[cfg(target_family = "unix")] const P2POOL_BINARY: &str = "p2pool"; #[cfg(target_os = "windows")] -const XMRIG_BINARY: &'static str = "xmrig.exe"; +const XMRIG_BINARY: &str = "xmrig.exe"; #[cfg(target_family = "unix")] const XMRIG_BINARY: &str = "xmrig"; +#[cfg(target_os = "windows")] +const ACCEPTABLE_XMRIG: [&str; 4] = ["XMRIG.exe", "XMRig.exe", "Xmrig.exe", "xmrig.exe"]; +#[cfg(target_family = "unix")] +const ACCEPTABLE_XMRIG: [&str; 4] = ["XMRIG", "XMRig", "Xmrig", "xmrig"]; + +#[cfg(target_os = "windows")] +const ACCEPTABLE_P2POOL: [&str; 4] = ["P2POOL.exe", "P2Pool.exe", "P2pool.exe", "p2pool.exe"]; +#[cfg(target_family = "unix")] +const ACCEPTABLE_P2POOL: [&str; 4] = ["P2POOL", "P2Pool", "P2pool", "p2pool"]; + // Some fake Curl/Wget user-agents because GitHub API requires one and a Tor browser // user-agent might be fingerprintable without all the associated headers. const FAKE_USER_AGENT: [&str; 50] = [ @@ -253,14 +265,70 @@ impl Update { // actually contains the code. This is so that everytime // an update needs to happen (Gupax tab, auto-update), the // code only needs to be edited once, here. - pub fn spawn_thread(og: &Arc>, update: &Arc>, state_ver: &Arc>, state_path: &Path) { - update.lock().unwrap().path_p2pool = og.lock().unwrap().gupax.absolute_p2pool_path.display().to_string(); - update.lock().unwrap().path_xmrig = og.lock().unwrap().gupax.absolute_xmrig_path.display().to_string(); - update.lock().unwrap().tor = og.lock().unwrap().gupax.update_via_tor; + pub fn spawn_thread(og: &Arc>, gupax: &crate::disk::Gupax, state_path: &Path, update: &Arc>, error_state: &mut ErrorState) { + // Check P2Pool path for safety + // Attempt relative to absolute path + let p2pool_path = match into_absolute_path(gupax.p2pool_path.clone()) { + Ok(p) => p, + Err(e) => { error_state.set("Provided P2Pool path could not be turned into an absolute path", ErrorFerris::Error, ErrorButtons::Okay); return; }, + }; + // Attempt to get basename + let file = match p2pool_path.file_name() { + Some(p) => { + // Attempt to turn into str + match p.to_str() { + Some(p) => p, + None => { error_state.set("Provided P2Pool path could not be turned into a UTF-8 string (are you using non-English characters?)", ErrorFerris::Error, ErrorButtons::Okay); return; }, + } + }, + None => { error_state.set("Provided P2Pool path could not be found", ErrorFerris::Error, ErrorButtons::Okay); return; }, + }; + // If it doesn't look like [P2Pool], its probably a bad move + // to overwrite it with an update, so set an error. + // Doesnt seem like you can [match] on array indexes + // so that explains the ridiculous if/else. + if file == ACCEPTABLE_P2POOL[0] || file == ACCEPTABLE_P2POOL[1] || file == ACCEPTABLE_P2POOL[2] || file == ACCEPTABLE_P2POOL[3] { + info!("Update | Using P2Pool path: [{}]", p2pool_path.display()); + } else { + warn!("Update | Aborting update, incorrect P2Pool path: [{}]", file); + let text = format!("Provided P2Pool path seems incorrect. Not starting update for safety.\nTry one of these: {:?}", ACCEPTABLE_P2POOL); + error_state.set(text, ErrorFerris::Error, ErrorButtons::Okay); + return; + } + + // Check XMRig path for safety + let xmrig_path = match into_absolute_path(gupax.xmrig_path.clone()) { + Ok(p) => p, + Err(e) => { error_state.set("Provided XMRig path could not be turned into an absolute path", ErrorFerris::Error, ErrorButtons::Okay); return; }, + }; + let file = match xmrig_path.file_name() { + Some(p) => { + // Attempt to turn into str + match p.to_str() { + Some(p) => p, + None => { error_state.set("Provided XMRig path could not be turned into a UTF-8 string (are you using non-English characters?)", ErrorFerris::Error, ErrorButtons::Okay); return; }, + } + }, + None => { error_state.set("Provided XMRig path could not be found", ErrorFerris::Error, ErrorButtons::Okay); return; }, + }; + if file == ACCEPTABLE_XMRIG[0] || file == ACCEPTABLE_XMRIG[1] || file == ACCEPTABLE_XMRIG[2] || file == ACCEPTABLE_XMRIG[3] { + info!("Update | Using XMRig path: [{}]", xmrig_path.display()); + } else { + warn!("Update | Aborting update, incorrect XMRig path: [{}]", file); + let text = format!("Provided XMRig path seems incorrect. Not starting update for safety.\nTry one of these: {:?}", ACCEPTABLE_XMRIG); + error_state.set(text, ErrorFerris::Error, ErrorButtons::Okay); + return; + } + + update.lock().unwrap().path_p2pool = p2pool_path.display().to_string(); + update.lock().unwrap().path_xmrig = xmrig_path.display().to_string(); + update.lock().unwrap().tor = gupax.update_via_tor; + + // Clone before thread spawn let og = Arc::clone(og); - let state_ver = Arc::clone(state_ver); - let update = Arc::clone(update); + let state_ver = Arc::clone(&og.lock().unwrap().version); let state_path = state_path.to_path_buf(); + let update = Arc::clone(update); std::thread::spawn(move|| { info!("Spawning update thread..."); match Update::start(update.clone(), og.clone(), state_ver.clone()) {