From 43f15b0b1a60d922d29823cb8f64f33ede65c169 Mon Sep 17 00:00:00 2001 From: Mike Swanson Date: Sun, 28 Dec 2025 16:12:03 -0700 Subject: [PATCH] Add UAC elevation support with manifest MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Added guruconnect.manifest requesting highestAvailable privileges - Using winres to embed manifest in executable - Added is_elevated() function to detect admin status - Logs elevation status on startup - Manifest includes Windows 7-11 compatibility 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- Cargo.lock | 25 +++++++++++++++++++--- agent/Cargo.toml | 1 + agent/build.rs | 21 +++++++++++++++++++ agent/guruconnect.manifest | 36 +++++++++++++++++++++++++++++++ agent/src/main.rs | 43 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 123 insertions(+), 3 deletions(-) create mode 100644 agent/guruconnect.manifest diff --git a/Cargo.lock b/Cargo.lock index dd0ff0e..a4c9337 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1029,7 +1029,7 @@ dependencies = [ "thiserror 1.0.69", "tokio", "tokio-tungstenite", - "toml", + "toml 0.8.2", "tracing", "tracing-subscriber", "tray-icon", @@ -1037,6 +1037,7 @@ dependencies = [ "uuid", "windows", "windows-service", + "winres", "zstd", ] @@ -1061,7 +1062,7 @@ dependencies = [ "sqlx", "thiserror 1.0.69", "tokio", - "toml", + "toml 0.8.2", "tower", "tower-http", "tracing", @@ -2968,7 +2969,7 @@ dependencies = [ "cfg-expr", "heck 0.5.0", "pkg-config", - "toml", + "toml 0.8.2", "version-compare", ] @@ -3172,6 +3173,15 @@ dependencies = [ "tokio", ] +[[package]] +name = "toml" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" +dependencies = [ + "serde", +] + [[package]] name = "toml" version = "0.8.2" @@ -3969,6 +3979,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "winres" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b68db261ef59e9e52806f688020631e987592bd83619edccda9c47d42cde4f6c" +dependencies = [ + "toml 0.5.11", +] + [[package]] name = "wit-bindgen" version = "0.46.0" diff --git a/agent/Cargo.toml b/agent/Cargo.toml index a49d6bb..60c0f3f 100644 --- a/agent/Cargo.toml +++ b/agent/Cargo.toml @@ -79,6 +79,7 @@ windows-service = "0.7" [build-dependencies] prost-build = "0.13" +winres = "0.1" [profile.release] lto = true diff --git a/agent/build.rs b/agent/build.rs index d1606dc..f93ee9d 100644 --- a/agent/build.rs +++ b/agent/build.rs @@ -7,5 +7,26 @@ fn main() -> Result<()> { // Rerun if proto changes println!("cargo:rerun-if-changed=../proto/guruconnect.proto"); + // On Windows, embed the manifest for UAC elevation + #[cfg(target_os = "windows")] + { + println!("cargo:rerun-if-changed=guruconnect.manifest"); + + let mut res = winres::WindowsResource::new(); + res.set_manifest_file("guruconnect.manifest"); + res.set("ProductName", "GuruConnect Agent"); + res.set("FileDescription", "GuruConnect Remote Desktop Agent"); + res.set("LegalCopyright", "Copyright (c) AZ Computer Guru"); + res.set_icon("guruconnect.ico"); // Optional: add icon if available + + // Only compile if the manifest exists + if std::path::Path::new("guruconnect.manifest").exists() { + if let Err(e) = res.compile() { + // Don't fail the build if resource compilation fails + eprintln!("Warning: Failed to compile Windows resources: {}", e); + } + } + } + Ok(()) } diff --git a/agent/guruconnect.manifest b/agent/guruconnect.manifest new file mode 100644 index 0000000..aec389f --- /dev/null +++ b/agent/guruconnect.manifest @@ -0,0 +1,36 @@ + + + + GuruConnect Remote Desktop Agent + + + + + + + + + + + + + + + + + + + + + + + true/pm + PerMonitorV2, PerMonitor + + + diff --git a/agent/src/main.rs b/agent/src/main.rs index 4615d02..d27a00f 100644 --- a/agent/src/main.rs +++ b/agent/src/main.rs @@ -28,6 +28,10 @@ use tracing_subscriber::FmtSubscriber; use windows::Win32::UI::WindowsAndMessaging::{MessageBoxW, MB_OK, MB_ICONINFORMATION}; #[cfg(windows)] use windows::core::PCWSTR; +#[cfg(windows)] +use windows::Win32::Security::{GetTokenInformation, TokenElevation, TOKEN_ELEVATION, TOKEN_QUERY}; +#[cfg(windows)] +use windows::Win32::System::Threading::{GetCurrentProcess, OpenProcessToken}; /// Extract a 6-digit support code from the executable's filename. /// Looks for patterns like "GuruConnect-123456.exe" or "123456.exe" @@ -56,6 +60,38 @@ fn extract_code_from_filename() -> Option { None } +/// Check if the process is running with elevated privileges (Windows only) +#[cfg(windows)] +fn is_elevated() -> bool { + unsafe { + let mut token_handle = windows::Win32::Foundation::HANDLE::default(); + if OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &mut token_handle).is_err() { + return false; + } + + let mut elevation = TOKEN_ELEVATION::default(); + let mut size = std::mem::size_of::() as u32; + + let result = GetTokenInformation( + token_handle, + TokenElevation, + Some(&mut elevation as *mut _ as *mut _), + size, + &mut size, + ); + + let _ = windows::Win32::Foundation::CloseHandle(token_handle); + + result.is_ok() && elevation.TokenIsElevated != 0 + } +} + +#[cfg(not(windows))] +fn is_elevated() -> bool { + // On non-Windows, check if running as root + unsafe { libc::geteuid() == 0 } +} + /// Show a message box to the user (Windows only) #[cfg(windows)] fn show_message_box(title: &str, message: &str) { @@ -98,6 +134,13 @@ async fn main() -> Result<()> { info!("GuruConnect Agent v{}", env!("CARGO_PKG_VERSION")); + // Check and log elevation status + if is_elevated() { + info!("Running with elevated (administrator) privileges"); + } else { + info!("Running with standard user privileges"); + } + // Extract support code from executable filename // e.g., GuruConnect-123456.exe -> 123456 let support_code = extract_code_from_filename();