From 95a5c3f565eb407429c87f5b617f4de1ca2d2c4d Mon Sep 17 00:00:00 2001 From: teesh3rt Date: Sun, 30 Mar 2025 12:30:59 +0300 Subject: [PATCH] feat: add getting admin to cross, knock out a todo --- shell/src/ash.rs | 10 +++-- utils/src/cross/mod.rs | 3 +- utils/src/cross/user/mod.rs | 11 +++++ utils/src/cross/user/unix.rs | 37 ++++++++++++++++ utils/src/cross/user/win.rs | 83 ++++++++++++++++++++++++++++++++++++ 5 files changed, 139 insertions(+), 5 deletions(-) create mode 100644 utils/src/cross/user/mod.rs create mode 100644 utils/src/cross/user/unix.rs create mode 100644 utils/src/cross/user/win.rs diff --git a/shell/src/ash.rs b/shell/src/ash.rs index 73ed7b4..ba8ffa5 100644 --- a/shell/src/ash.rs +++ b/shell/src/ash.rs @@ -10,10 +10,12 @@ impl Command for Ash { fn execute(&self) { loop { let path = env::current_dir(); - print!( - "{} $ ", // TODO: display "#" if root - path.expect("unknown").display() - ); + let userchar = if boxutils::cross::user::is_admin() { + '#' + } else { + '$' + }; + print!("{} {} ", path.expect("unknown").display(), userchar); let _ = io::stdout().flush(); let mut input = String::new(); io::stdin().read_line(&mut input).unwrap(); diff --git a/utils/src/cross/mod.rs b/utils/src/cross/mod.rs index 57f1fee..9f9c237 100644 --- a/utils/src/cross/mod.rs +++ b/utils/src/cross/mod.rs @@ -1 +1,2 @@ -pub mod fs; \ No newline at end of file +pub mod fs; +pub mod user; \ No newline at end of file diff --git a/utils/src/cross/user/mod.rs b/utils/src/cross/user/mod.rs new file mode 100644 index 0000000..cb895bc --- /dev/null +++ b/utils/src/cross/user/mod.rs @@ -0,0 +1,11 @@ +#[cfg(windows)] +mod win; + +#[cfg(windows)] +pub use win::*; + +#[cfg(unix)] +mod unix; + +#[cfg(unix)] +pub use unix::*; \ No newline at end of file diff --git a/utils/src/cross/user/unix.rs b/utils/src/cross/user/unix.rs new file mode 100644 index 0000000..a76164e --- /dev/null +++ b/utils/src/cross/user/unix.rs @@ -0,0 +1,37 @@ +#![cfg(unix)] + +use std::fs; +use std::process::Command; + +/// A safe wrapper for checking if the user is root. +struct UserPrivileges; + +impl UserPrivileges { + fn is_root() -> bool { + if let Ok(status) = fs::read_to_string("/proc/self/status") { + for line in status.lines() { + if line.starts_with("Uid:") { + let uid: u32 = line + .split_whitespace() + .nth(1) + .unwrap_or("1") + .parse() + .unwrap_or(1); + return uid == 0; + } + } + } + + // Fallback: Run `id -u` + Command::new("id") + .arg("-u") + .output() + .map(|o| o.stdout == b"0\n") + .unwrap_or(false) + } +} + +/// Safe function to check if the user has root privileges. +pub fn is_admin() -> bool { + UserPrivileges::is_root() +} diff --git a/utils/src/cross/user/win.rs b/utils/src/cross/user/win.rs new file mode 100644 index 0000000..c9919f1 --- /dev/null +++ b/utils/src/cross/user/win.rs @@ -0,0 +1,83 @@ +#![allow(dead_code)] +#![cfg(windows)] + +use std::ptr; +use std::ffi::c_void; +use std::mem::{size_of, zeroed}; +use std::os::raw::c_ulong; + +#[link(name = "advapi32")] +unsafe extern "system" { + fn OpenProcessToken(process: *mut c_void, access: c_ulong, token: *mut *mut c_void) -> i32; + fn GetTokenInformation( + token_handle: *mut c_void, + token_info_class: c_ulong, + token_info: *mut c_void, + token_info_length: c_ulong, + return_length: *mut c_ulong, + ) -> i32; + fn GetCurrentProcess() -> *mut c_void; + fn CloseHandle(handle: *mut c_void) -> i32; +} + +const TOKEN_QUERY: c_ulong = 0x0008; +const TOKEN_ELEVATION: c_ulong = 20; + +#[repr(C)] +struct TokenElevation { + token_is_elevated: c_ulong, +} + +/// A safe wrapper around a Windows access token handle. +struct AccessToken { + handle: *mut c_void, +} + +impl AccessToken { + /// Opens the access token for the current process. + fn open() -> Option { + let process = unsafe { GetCurrentProcess() }; + let mut token_handle: *mut c_void = ptr::null_mut(); + + let success = unsafe { OpenProcessToken(process, TOKEN_QUERY, &mut token_handle) }; + if success == 0 { + return None; + } + + Some(Self { handle: token_handle }) + } + + /// Checks if the token is elevated (i.e., running as an administrator). + fn is_elevated(&self) -> bool { + let mut elevation: TokenElevation = unsafe { zeroed() }; + let mut return_length: c_ulong = 0; + + let success = unsafe { + GetTokenInformation( + self.handle, + TOKEN_ELEVATION, + &mut elevation as *mut _ as *mut c_void, + size_of::() as c_ulong, + &mut return_length, + ) + }; + + success != 0 && elevation.token_is_elevated != 0 + } +} + +impl Drop for AccessToken { + /// Ensures the token handle is always closed properly. + fn drop(&mut self) { + unsafe { CloseHandle(self.handle) }; + } +} + +/// Safe function to check if the user is an administrator. +pub fn is_admin() -> bool { + if let Some(token) = AccessToken::open() { + token.is_elevated() + } else { + false + } +} \ No newline at end of file