Skip to main content

token_privilege/
lib.rs

1//! Safe Rust wrapper for Windows process token privilege and elevation detection.
2//!
3//! All unsafe Win32 FFI calls are contained within the crate. Consumers use a fully
4//! safe public API and can maintain `#![forbid(unsafe_code)]` in their own crates.
5//!
6//! On non-Windows platforms, all functions return
7//! <code>Err([TokenPrivilegeError::UnsupportedPlatform])</code>.
8//!
9//! # Examples
10//!
11//! ```rust,no_run
12//! use token_privilege::{is_elevated, is_privilege_enabled, privileges};
13//!
14//! fn main() -> Result<(), Box<dyn std::error::Error>> {
15//!     if is_elevated()? {
16//!         println!("Running as Administrator");
17//!     }
18//!
19//!     if is_privilege_enabled(privileges::SE_DEBUG)? {
20//!         println!("SeDebugPrivilege is enabled");
21//!     }
22//!
23//!     Ok(())
24//! }
25//! ```
26
27mod error;
28
29#[cfg(target_os = "windows")]
30mod elevation;
31#[cfg(target_os = "windows")]
32mod ffi;
33#[cfg(target_os = "windows")]
34mod privilege;
35
36pub use error::TokenPrivilegeError;
37
38/// Represents the status of a single Windows privilege.
39///
40/// Returned by [`enumerate_privileges`] to describe each privilege on the
41/// current process token, including its name and current state.
42///
43/// # Examples
44///
45/// ```rust,no_run
46/// use token_privilege::enumerate_privileges;
47///
48/// let privileges = enumerate_privileges()?;
49/// for priv_info in &privileges {
50///     println!("{}: enabled={}", priv_info.name, priv_info.enabled);
51/// }
52/// # Ok::<(), Box<dyn std::error::Error>>(())
53/// ```
54#[derive(Debug, Clone, PartialEq, Eq)]
55#[non_exhaustive]
56pub struct PrivilegeInfo {
57    /// The privilege name (e.g., `"SeDebugPrivilege"`).
58    pub name: String,
59    /// Whether the privilege is currently enabled.
60    pub enabled: bool,
61    /// Whether the privilege is enabled by default.
62    pub enabled_by_default: bool,
63    /// Whether the privilege has been removed from the token.
64    pub removed: bool,
65}
66
67/// Well-known Windows privilege name constants.
68///
69/// Use these with [`is_privilege_enabled`] and [`has_privilege`] to avoid
70/// hard-coding privilege name strings.
71pub mod privileges {
72    /// Debug programs (`SeDebugPrivilege`).
73    pub const SE_DEBUG: &str = "SeDebugPrivilege";
74    /// Back up files and directories (`SeBackupPrivilege`).
75    pub const SE_BACKUP: &str = "SeBackupPrivilege";
76    /// Restore files and directories (`SeRestorePrivilege`).
77    pub const SE_RESTORE: &str = "SeRestorePrivilege";
78    /// Shut down the system (`SeShutdownPrivilege`).
79    pub const SE_SHUTDOWN: &str = "SeShutdownPrivilege";
80    /// Manage auditing and security log (`SeSecurityPrivilege`).
81    pub const SE_SECURITY: &str = "SeSecurityPrivilege";
82    /// Take ownership of files or other objects (`SeTakeOwnershipPrivilege`).
83    pub const SE_TAKE_OWNERSHIP: &str = "SeTakeOwnershipPrivilege";
84    /// Load and unload device drivers (`SeLoadDriverPrivilege`).
85    pub const SE_LOAD_DRIVER: &str = "SeLoadDriverPrivilege";
86    /// Profile system performance (`SeSystemProfilePrivilege`).
87    pub const SE_SYSTEM_PROFILE: &str = "SeSystemProfilePrivilege";
88    /// Change the system time (`SeSystemtimePrivilege`).
89    pub const SE_SYSTEMTIME: &str = "SeSystemtimePrivilege";
90    /// Bypass traverse checking (`SeChangeNotifyPrivilege`).
91    pub const SE_CHANGE_NOTIFY: &str = "SeChangeNotifyPrivilege";
92    /// Impersonate a client after authentication (`SeImpersonatePrivilege`).
93    pub const SE_IMPERSONATE: &str = "SeImpersonatePrivilege";
94    /// Create global objects (`SeCreateGlobalPrivilege`).
95    pub const SE_CREATE_GLOBAL: &str = "SeCreateGlobalPrivilege";
96    /// Adjust memory quotas for a process (`SeIncreaseQuotaPrivilege`).
97    pub const SE_INCREASE_QUOTA: &str = "SeIncreaseQuotaPrivilege";
98    /// Remove computer from docking station (`SeUndockPrivilege`).
99    pub const SE_UNDOCK: &str = "SeUndockPrivilege";
100    /// Perform volume maintenance tasks (`SeManageVolumePrivilege`).
101    pub const SE_MANAGE_VOLUME: &str = "SeManageVolumePrivilege";
102    /// Replace a process-level token (`SeAssignPrimaryTokenPrivilege`).
103    pub const SE_ASSIGN_PRIMARY_TOKEN: &str = "SeAssignPrimaryTokenPrivilege";
104    /// Increase scheduling priority (`SeIncreaseBasePriorityPrivilege`).
105    pub const SE_INCREASE_BASE_PRIORITY: &str = "SeIncreaseBasePriorityPrivilege";
106    /// Create a pagefile (`SeCreatePagefilePrivilege`).
107    pub const SE_CREATE_PAGEFILE: &str = "SeCreatePagefilePrivilege";
108    /// Act as part of the operating system (`SeTcbPrivilege`).
109    pub const SE_TCB: &str = "SeTcbPrivilege";
110    /// Force shutdown from a remote system (`SeRemoteShutdownPrivilege`).
111    pub const SE_REMOTE_SHUTDOWN: &str = "SeRemoteShutdownPrivilege";
112}
113
114// ─── Windows implementation (delegates to domain modules) ───
115
116/// Check if the current process is running with elevated (Administrator) privileges.
117///
118/// Returns `true` if the process token's `TokenIsElevated` field is nonzero,
119/// indicating the process is running elevated (typically via UAC).
120///
121/// # Errors
122///
123/// Returns [`TokenPrivilegeError::UnsupportedPlatform`] on non-Windows.
124/// Returns an error if the process token cannot be opened or queried.
125#[cfg(target_os = "windows")]
126pub fn is_elevated() -> Result<bool, TokenPrivilegeError> {
127    elevation::is_elevated()
128}
129
130/// Check if a specific named privilege is present and enabled on the current process token.
131///
132/// # Arguments
133///
134/// * `privilege_name` — The Windows privilege name (e.g., `"SeDebugPrivilege"`).
135///   Use constants from the [`privileges`] module.
136///
137/// # Errors
138///
139/// Returns [`TokenPrivilegeError::UnsupportedPlatform`] on non-Windows.
140/// Returns an error if the privilege name is invalid or the check fails.
141#[cfg(target_os = "windows")]
142pub fn is_privilege_enabled(privilege_name: &str) -> Result<bool, TokenPrivilegeError> {
143    privilege::is_privilege_enabled(privilege_name)
144}
145
146/// Check if a specific named privilege is present on the current process token,
147/// regardless of whether it is currently enabled.
148///
149/// # Errors
150///
151/// Returns [`TokenPrivilegeError::UnsupportedPlatform`] on non-Windows.
152/// Returns an error if the privilege name is invalid.
153#[cfg(target_os = "windows")]
154pub fn has_privilege(privilege_name: &str) -> Result<bool, TokenPrivilegeError> {
155    privilege::has_privilege(privilege_name)
156}
157
158/// Enumerate all privileges on the current process token.
159///
160/// Returns a list of [`PrivilegeInfo`] describing each privilege, its name,
161/// and its current status.
162///
163/// # Errors
164///
165/// Returns [`TokenPrivilegeError::UnsupportedPlatform`] on non-Windows.
166/// Returns an error if the process token cannot be opened or enumerated.
167#[cfg(target_os = "windows")]
168pub fn enumerate_privileges() -> Result<Vec<PrivilegeInfo>, TokenPrivilegeError> {
169    privilege::enumerate_privileges()
170}
171
172// ─── Non-Windows stubs ───
173
174/// Check if the current process is running with elevated (Administrator) privileges.
175///
176/// # Platform Support
177///
178/// This is a non-Windows stub that always returns an error. On Windows, this
179/// queries the process token for UAC elevation status.
180///
181/// # Errors
182///
183/// Always returns [`TokenPrivilegeError::UnsupportedPlatform`] on non-Windows.
184#[cfg(not(target_os = "windows"))]
185pub const fn is_elevated() -> Result<bool, TokenPrivilegeError> {
186    Err(TokenPrivilegeError::UnsupportedPlatform)
187}
188
189/// Check if a specific named privilege is present and enabled on the current process token.
190///
191/// # Platform Support
192///
193/// This is a non-Windows stub that always returns an error. On Windows, this
194/// looks up the privilege by name and checks if it is enabled.
195///
196/// # Errors
197///
198/// Always returns [`TokenPrivilegeError::UnsupportedPlatform`] on non-Windows.
199#[cfg(not(target_os = "windows"))]
200pub const fn is_privilege_enabled(_privilege_name: &str) -> Result<bool, TokenPrivilegeError> {
201    Err(TokenPrivilegeError::UnsupportedPlatform)
202}
203
204/// Check if a specific named privilege is present on the current process token,
205/// regardless of whether it is currently enabled.
206///
207/// # Platform Support
208///
209/// This is a non-Windows stub that always returns an error. On Windows, this
210/// checks whether the named privilege exists on the process token, regardless
211/// of its enabled state.
212///
213/// # Errors
214///
215/// Always returns [`TokenPrivilegeError::UnsupportedPlatform`] on non-Windows.
216#[cfg(not(target_os = "windows"))]
217pub const fn has_privilege(_privilege_name: &str) -> Result<bool, TokenPrivilegeError> {
218    Err(TokenPrivilegeError::UnsupportedPlatform)
219}
220
221/// Enumerate all privileges on the current process token.
222///
223/// # Platform Support
224///
225/// This is a non-Windows stub that always returns an error. On Windows, this
226/// returns a [`Vec<PrivilegeInfo>`] with each privilege's name and status.
227///
228/// # Errors
229///
230/// Always returns [`TokenPrivilegeError::UnsupportedPlatform`] on non-Windows.
231#[cfg(not(target_os = "windows"))]
232pub const fn enumerate_privileges() -> Result<Vec<PrivilegeInfo>, TokenPrivilegeError> {
233    Err(TokenPrivilegeError::UnsupportedPlatform)
234}
235
236#[cfg(not(target_os = "windows"))]
237#[cfg(test)]
238mod stub_tests {
239    use super::*;
240
241    #[test]
242    fn is_elevated_returns_unsupported() {
243        assert!(matches!(
244            is_elevated(),
245            Err(TokenPrivilegeError::UnsupportedPlatform)
246        ));
247    }
248
249    #[test]
250    fn is_privilege_enabled_returns_unsupported() {
251        assert!(matches!(
252            is_privilege_enabled("SeDebugPrivilege"),
253            Err(TokenPrivilegeError::UnsupportedPlatform)
254        ));
255    }
256
257    #[test]
258    fn has_privilege_returns_unsupported() {
259        assert!(matches!(
260            has_privilege("SeDebugPrivilege"),
261            Err(TokenPrivilegeError::UnsupportedPlatform)
262        ));
263    }
264
265    #[test]
266    fn enumerate_privileges_returns_unsupported() {
267        assert!(matches!(
268            enumerate_privileges(),
269            Err(TokenPrivilegeError::UnsupportedPlatform)
270        ));
271    }
272}