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}