Skip to main content

elevate_ti/security/
token.rs

1//! Process and thread security token manipulation.
2
3use std::{mem, ptr};
4
5use windows::{
6    Win32::{
7        Foundation::{
8            CloseHandle, ERROR_SUCCESS, GetLastError, HANDLE, LUID, SetLastError, WIN32_ERROR,
9        },
10        Security::{
11            AdjustTokenPrivileges, DuplicateTokenEx, GetTokenInformation, ImpersonateLoggedOnUser,
12            LUID_AND_ATTRIBUTES, LookupPrivilegeValueW, RevertToSelf, SE_PRIVILEGE_ENABLED,
13            SecurityImpersonation, SetTokenInformation, TOKEN_ACCESS_MASK, TOKEN_ADJUST_PRIVILEGES,
14            TOKEN_ALL_ACCESS, TOKEN_GROUPS, TOKEN_INFORMATION_CLASS, TOKEN_PRIVILEGES, TOKEN_QUERY,
15            TOKEN_USER, TokenGroups, TokenImpersonation, TokenPrimary, TokenSessionId, TokenUser,
16        },
17        System::{
18            SystemServices::MAXIMUM_ALLOWED,
19            Threading::{
20                GetCurrentProcess, OpenProcess, OpenProcessToken, PROCESS_QUERY_INFORMATION,
21            },
22        },
23    },
24    core::PCWSTR,
25};
26
27use super::{HandleGuard, Sid, privilege::Privilege, win32};
28use crate::error::ElevateError;
29
30/// Target token type when duplicating an existing security token.
31#[derive(Debug, Clone, Copy, PartialEq, Eq)]
32pub enum TokenType {
33    /// Primary token for launching new process instances.
34    Primary,
35    /// Impersonation token for thread-level security context borrowing.
36    Impersonation,
37}
38
39/// Strongly-typed RAII guard holding an open process or thread token handle.
40pub struct Token(HANDLE);
41
42impl Drop for Token {
43    fn drop(&mut self) {
44        if !self.0.is_invalid() {
45            unsafe {
46                let _ = CloseHandle(self.0);
47            }
48        }
49    }
50}
51
52impl Token {
53    /// Open the token belonging to the current application process.
54    pub fn current() -> Result<Self, ElevateError> {
55        let mut handle = HANDLE::default();
56        win32!(OpenProcessToken(
57            GetCurrentProcess(),
58            TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
59            &raw mut handle
60        ))?;
61        Ok(Self(handle))
62    }
63
64    /// Open the security token belonging to an arbitrary running process by PID.
65    pub fn from_pid(pid: u32) -> Result<Self, ElevateError> {
66        let proc = HandleGuard(win32!(OpenProcess(PROCESS_QUERY_INFORMATION, false, pid))?);
67        let mut token = HANDLE::default();
68        win32!(OpenProcessToken(
69            proc.0,
70            TOKEN_ACCESS_MASK(MAXIMUM_ALLOWED),
71            &raw mut token
72        ))?;
73        Ok(Self(token))
74    }
75
76    /// Borrow the underlying raw Win32 `HANDLE`.
77    pub const fn as_raw(&self) -> HANDLE {
78        self.0
79    }
80
81    /// Duplicate this token as either a Primary or Impersonation token.
82    pub fn duplicate(&self, token_type: TokenType) -> Result<Self, ElevateError> {
83        let access = match token_type {
84            TokenType::Primary => TOKEN_ALL_ACCESS,
85            TokenType::Impersonation => TOKEN_ACCESS_MASK(MAXIMUM_ALLOWED),
86        };
87        let raw_type = match token_type {
88            TokenType::Primary => TokenPrimary,
89            TokenType::Impersonation => TokenImpersonation,
90        };
91
92        let mut dup = HANDLE::default();
93        win32!(DuplicateTokenEx(
94            self.0,
95            access,
96            None,
97            SecurityImpersonation,
98            raw_type,
99            &raw mut dup
100        ))?;
101        Ok(Self(dup))
102    }
103
104    /// Enable the specified administrative privileges on this token.
105    pub fn enable_privileges(&self, privileges: &[Privilege]) -> Result<(), ElevateError> {
106        for &privilege in privileges {
107            let mut luid = LUID::default();
108            win32!(LookupPrivilegeValueW(
109                PCWSTR::null(),
110                privilege.as_pcwstr(),
111                &raw mut luid
112            ))?;
113
114            let privs = TOKEN_PRIVILEGES {
115                PrivilegeCount: 1,
116                Privileges: [LUID_AND_ATTRIBUTES {
117                    Luid: luid,
118                    Attributes: SE_PRIVILEGE_ENABLED,
119                }],
120            };
121
122            unsafe { SetLastError(WIN32_ERROR(0)) };
123            win32!(AdjustTokenPrivileges(
124                self.0,
125                false,
126                Some(ptr::from_ref(&privs)),
127                mem::size_of::<TOKEN_PRIVILEGES>() as u32,
128                None,
129                None,
130            ))?;
131
132            if unsafe { GetLastError() } != ERROR_SUCCESS {
133                return Err(ElevateError::PrivilegeNotAssigned(privilege.as_str()));
134            }
135        }
136        Ok(())
137    }
138
139    /// Query the Windows Session ID currently associated with this token.
140    pub fn session_id(&self) -> Result<u32, ElevateError> {
141        let mut session_id = 0u32;
142        let mut len = 0u32;
143        win32!(GetTokenInformation(
144            self.0,
145            TokenSessionId,
146            Some(ptr::from_mut(&mut session_id).cast()),
147            mem::size_of::<u32>() as u32,
148            &raw mut len
149        ))?;
150        Ok(session_id)
151    }
152
153    /// Explicitly assign a Windows Session ID to this token.
154    pub fn set_session_id(&self, session_id: u32) -> Result<(), ElevateError> {
155        win32!(SetTokenInformation(
156            self.0,
157            TokenSessionId,
158            ptr::from_ref(&session_id).cast(),
159            mem::size_of::<u32>() as u32
160        ))?;
161        Ok(())
162    }
163
164    /// Impersonate this token on the current thread, returning an RAII revert guard.
165    pub fn impersonate(&self) -> Result<ImpersonationGuard, ElevateError> {
166        win32!(ImpersonateLoggedOnUser(self.0))?;
167        Ok(ImpersonationGuard)
168    }
169
170    /// Retrieve the owner user SID string of this token.
171    pub fn user_sid(&self) -> Result<String, ElevateError> {
172        let buf = self.query_info_buffer(TokenUser)?;
173        // SAFETY: buf is pointer-aligned via Vec<usize> and populated by GetTokenInformation.
174        let user = unsafe { &*buf.as_ptr().cast::<TOKEN_USER>() };
175        Sid::string_from_raw(user.User.Sid)
176    }
177
178    /// Check whether this token holds membership in the specified string SID.
179    pub fn has_sid(&self, target_sid: PCWSTR) -> Result<bool, ElevateError> {
180        let target = Sid::parse(target_sid)?;
181        let buf = self.query_info_buffer(TokenGroups)?;
182        // SAFETY: buf is pointer-aligned via Vec<usize> and populated by GetTokenInformation.
183        let groups = unsafe { &*buf.as_ptr().cast::<TOKEN_GROUPS>() };
184
185        let slice = unsafe {
186            std::slice::from_raw_parts(groups.Groups.as_ptr(), groups.GroupCount as usize)
187        };
188        Ok(slice.iter().any(|g| Sid::equal(g.Sid, target.as_raw())))
189    }
190
191    /// Query raw token information into a pointer-aligned buffer.
192    fn query_info_buffer(
193        &self,
194        class: TOKEN_INFORMATION_CLASS,
195    ) -> Result<Vec<usize>, ElevateError> {
196        let mut len = 0u32;
197        let _ = unsafe { GetTokenInformation(self.0, class, None, 0, &raw mut len) };
198        if len == 0 {
199            return Err(ElevateError::InvalidTokenBuffer);
200        }
201
202        let usize_size = mem::size_of::<usize>();
203        let count = ((len as usize).saturating_add(usize_size - 1)) / usize_size;
204        let mut buf: Vec<usize> = vec![0; count];
205
206        win32!(GetTokenInformation(
207            self.0,
208            class,
209            Some(buf.as_mut_ptr().cast()),
210            len,
211            &raw mut len
212        ))?;
213        Ok(buf)
214    }
215}
216
217/// RAII guard representing active thread impersonation.
218pub struct ImpersonationGuard;
219
220impl Drop for ImpersonationGuard {
221    fn drop(&mut self) {
222        unsafe {
223            let _ = RevertToSelf();
224        }
225    }
226}