Add UAC elevation support with manifest

- 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 <noreply@anthropic.com>
This commit is contained in:
2025-12-28 16:12:03 -07:00
parent c57429f26a
commit 43f15b0b1a
5 changed files with 123 additions and 3 deletions

View File

@@ -79,6 +79,7 @@ windows-service = "0.7"
[build-dependencies]
prost-build = "0.13"
winres = "0.1"
[profile.release]
lto = true

View File

@@ -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(())
}

View File

@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity
version="1.0.0.0"
processorArchitecture="*"
name="GuruConnect.Agent"
type="win32"
/>
<description>GuruConnect Remote Desktop Agent</description>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
<security>
<requestedPrivileges>
<!-- Request highest available privileges (admin if possible, user otherwise) -->
<requestedExecutionLevel level="highestAvailable" uiAccess="false"/>
</requestedPrivileges>
</security>
</trustInfo>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- Windows 10 and Windows 11 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
<!-- Windows 8.1 -->
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
<!-- Windows 8 -->
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
<!-- Windows 7 -->
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
</application>
</compatibility>
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/pm</dpiAware>
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2, PerMonitor</dpiAwareness>
</windowsSettings>
</application>
</assembly>

View File

@@ -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<String> {
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::<TOKEN_ELEVATION>() 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();