diff --git a/Cargo.lock b/Cargo.lock index 18d64893e2..56026dbf19 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1863,16 +1863,6 @@ dependencies = [ "once_cell", ] -[[package]] -name = "is-root" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04a4202a60e86f1c9702706bb42270dadd333f2db7810157563c86f17af3c873" -dependencies = [ - "users", - "winapi", -] - [[package]] name = "is-terminal" version = "0.4.8" @@ -2739,7 +2729,6 @@ dependencies = [ "htmlescape", "indexmap 2.0.0", "indicatif", - "is-root", "is-terminal", "itertools", "libc", @@ -5508,16 +5497,6 @@ dependencies = [ "percent-encoding", ] -[[package]] -name = "users" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa4227e95324a443c9fcb06e03d4d85e91aabe9a5a02aa818688b6918b6af486" -dependencies = [ - "libc", - "log", -] - [[package]] name = "utf-8" version = "0.7.6" diff --git a/crates/nu-command/Cargo.toml b/crates/nu-command/Cargo.toml index b66227d9cf..e4eab6328a 100644 --- a/crates/nu-command/Cargo.toml +++ b/crates/nu-command/Cargo.toml @@ -50,7 +50,6 @@ fs_extra = "1.3" htmlescape = "0.3" indexmap = "2.0" indicatif = "0.17" -is-root = "0.1" is-terminal = "0.4.8" itertools = "0.10" log = "0.4" @@ -110,7 +109,13 @@ optional = true version = "3.0" [target.'cfg(windows)'.dependencies.windows] -features = ["Win32_Foundation", "Win32_Storage_FileSystem", "Win32_System_SystemServices"] +features = [ + "Win32_Foundation", + "Win32_Storage_FileSystem", + "Win32_System_SystemServices", + "Win32_Security", + "Win32_System_Threading" +] version = "0.48" [features] diff --git a/crates/nu-command/src/experimental/is_admin.rs b/crates/nu-command/src/experimental/is_admin.rs index 2fe3797651..8907389657 100644 --- a/crates/nu-command/src/experimental/is_admin.rs +++ b/crates/nu-command/src/experimental/is_admin.rs @@ -1,4 +1,3 @@ -use is_root::is_root; use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ @@ -48,3 +47,62 @@ impl Command for IsAdmin { ] } } + +/// Returns `true` if user is root; `false` otherwise +fn is_root() -> bool { + is_root_impl() +} + +#[cfg(unix)] +fn is_root_impl() -> bool { + nix::unistd::Uid::current().is_root() +} + +#[cfg(windows)] +fn is_root_impl() -> bool { + use windows::Win32::{ + Foundation::{CloseHandle, HANDLE}, + Security::{GetTokenInformation, TokenElevation, TOKEN_ELEVATION, TOKEN_QUERY}, + System::Threading::{GetCurrentProcess, OpenProcessToken}, + }; + + let mut handle = HANDLE::default(); + let mut elevated = false; + + // Checks whether the access token associated with the current process has elevated privileges. + // SAFETY: `elevated` only touched by safe code. + // `handle` lives long enough, initialized, mutated as out param, used, closed with validity check. + // `elevation` only read on success and passed with correct `size`. + unsafe { + // Opens the access token associated with the current process. + if OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &mut handle).as_bool() { + let mut elevation = TOKEN_ELEVATION::default(); + let mut size = std::mem::size_of::() as u32; + + // Retrieves elevation token information about the access token associated with the current process. + // Call available since XP + // https://learn.microsoft.com/en-us/windows/win32/api/securitybaseapi/nf-securitybaseapi-gettokeninformation + if GetTokenInformation( + handle, + TokenElevation, + Some(&mut elevation as *mut TOKEN_ELEVATION as *mut _), + size, + &mut size, + ) + .as_bool() + { + // Whether the token has elevated privileges. + // Safe to read as `GetTokenInformation` will not write outside `elevation` and it succeeded + // See: https://learn.microsoft.com/en-us/windows/win32/api/securitybaseapi/nf-securitybaseapi-gettokeninformation#parameters + elevated = elevation.TokenIsElevated != 0; + } + } + + if !handle.is_invalid() { + // Closes the object handle. + CloseHandle(handle); + } + } + + elevated +}