liblitho 0.2.0

cli tool to flash/clone the images to storage devices
Documentation
//! Windows privilege elevation via UAC (`ShellExecuteW` runas).

use crate::platform::elevated_cli_args;
use crate::platform::traits::{ElevationCapability, PrivilegeOps};
use std::ffi::OsStr;
use std::mem;
use std::os::windows::ffi::OsStrExt;
use std::ptr;
use winapi::um::handleapi::CloseHandle;
use winapi::um::processthreadsapi::{GetCurrentProcess, OpenProcessToken};
use winapi::um::securitybaseapi::GetTokenInformation;
use winapi::um::shellapi::ShellExecuteW;
use winapi::um::winnt::{TokenElevation, TOKEN_ELEVATION, TOKEN_QUERY};
use winapi::um::winuser::SW_SHOW;

use super::WindowsPlatform;

impl PrivilegeOps for WindowsPlatform {
    fn elevation_method() -> &'static str {
        "uac"
    }

    fn is_elevated() -> bool {
        // SAFETY: process token is queried then closed; no use-after-close.
        unsafe {
            let mut token = ptr::null_mut();
            if OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &mut token) == 0 {
                return false;
            }

            let mut elevation = TOKEN_ELEVATION { TokenIsElevated: 0 };
            let mut size = 0;
            let ok = GetTokenInformation(
                token,
                TokenElevation,
                &mut elevation as *mut _ as *mut _,
                mem::size_of::<TOKEN_ELEVATION>() as u32,
                &mut size,
            );
            CloseHandle(token);

            ok != 0 && elevation.TokenIsElevated != 0
        }
    }

    fn elevation_capability() -> ElevationCapability {
        if Self::is_elevated() {
            ElevationCapability {
                ready: true,
                status_label: "elevated",
            }
        } else {
            ElevationCapability {
                ready: true,
                status_label: "UAC available",
            }
        }
    }

    fn validate_elevation_prerequisites() -> Result<(), String> {
        Ok(())
    }

    fn elevation_dialog_lines() -> [&'static str; 3] {
        [
            "Flash and clone operations require administrator access.",
            "You will be prompted by User Account Control (UAC).",
            "This window will close and a new elevated session will start.",
        ]
    }

    fn relaunch_elevated(mode: &str, device: &str, image: &str) -> Result<(), String> {
        if Self::is_elevated() {
            return Err("Already running with administrator privileges.".into());
        }

        let exe =
            std::env::current_exe().map_err(|e| format!("Could not resolve executable: {e}"))?;
        let parameters = windows_command_line(&elevated_cli_args(mode, device, image));

        // SAFETY: wide strings are NUL-terminated; return value checked against ShellExecute threshold.
        let status = unsafe {
            ShellExecuteW(
                ptr::null_mut(),
                wide("runas").as_ptr(),
                wide(&exe.to_string_lossy()).as_ptr(),
                wide(&parameters).as_ptr(),
                ptr::null(),
                SW_SHOW,
            )
        };

        if (status as isize) <= 32 {
            return Err(format!(
                "UAC elevation was denied or failed (ShellExecute code {}). \
                 Try running litho-tui from an elevated terminal.",
                status as isize
            ));
        }

        Ok(())
    }
}

fn windows_command_line(args: &[String]) -> String {
    args.iter()
        .map(|arg| {
            if arg.chars().any(|c| c.is_whitespace() || c == '"') {
                format!("\"{}\"", arg.replace('"', "\\\""))
            } else {
                arg.clone()
            }
        })
        .collect::<Vec<_>>()
        .join(" ")
}

fn wide(value: &str) -> Vec<u16> {
    OsStr::new(value).encode_wide().chain(Some(0)).collect()
}