RAII wrappers over the Win32 access-token surface — OpenProcessToken,
DuplicateTokenEx, ImpersonateLoggedOnUser, SetThreadToken,
AdjustTokenPrivileges. The single hard invariant: dropping an
ImpersonationGuard calls RevertToSelf — always, including on unwind.
Hand-written impersonation code (popular Kerberos clients-style) forgets this on early returns;
here it is structurally impossible.
0.2 tested companion crate. The token, privilege, duplication, SID, and
scoped impersonation paths are implemented on top of win32-min; APIs may
still evolve before 1.0. See the central
win32-min ecosystem map
for compatibility and maturity information.
Handle-owning wrappers around the token API described in the
Access Tokens section of the Windows security model (see
processthreadsapi.h and the TOKEN_INFORMATION_CLASS enumeration).
Correctly reads ERROR_NOT_ALL_ASSIGNED from GetLastError on nominal
AdjustTokenPrivileges success — the one thing every hand-rolled
"enable SeDebugPrivilege" snippet on the internet gets wrong.
use windows_token::{Token, Privilege};
fn main() -> windows_token::Result<()> {
let tok = Token::open_current_process()?;
// Enable SeChangeNotifyPrivilege (normal users always hold this).
let _prev = tok.enable_privilege(Privilege::SeChangeNotify)?;
// Impersonation with guaranteed revert:
{
let _g = tok.impersonate_on_thread()?;
// ...do work under the impersonated identity...
} // <- RevertToSelf on drop, including on panic
println!("user = {}", tok.user_sid()?);
println!("il = {:?}", tok.integrity_level()?);
Ok(())
}Inspect the current identity and integrity level, then prove that duplicated self-impersonation is reverted by the RAII guard:
cargo run --example current_identityThis workflow does not require elevation, enable a privilege, or impersonate a
different account. See the ecosystem's complete
RESEARCH-WORKFLOWS.md
for permission and scope boundaries.
- Working:
Token::open_current_process/Token::open_process(pid, access)aroundOpenProcessToken.Token::duplicate(ty, level)-> ownedDuplicateTokenExhandle.Token::enable_privilege(Privilege::…)viaLookupPrivilegeValueW+AdjustTokenPrivilegeswith correctERROR_NOT_ALL_ASSIGNEDhandling.Token::impersonate_on_thread()/set_on_current_thread()returning anImpersonationGuard(Drop = RevertToSelf).Token::user_sid()/integrity_level()viaGetTokenInformation(TokenUser | TokenIntegrityLevel).
- Stubbed / next milestone:
OpenThreadTokenis not yet wired (onlyOpenProcessToken).- No
LookupAccountSidW— SIDs render asS-1-...strings only. - No
CreateProcessAsUserW/ restricted-token helpers. - No integration tests against a real DC; only smoke tests on
GetCurrentProcess().
win32-min >= 0.1.2, < 0.2with onlysecurity-tokenenabled.thiserror2 for the public error taxonomy.- No async runtime, serialization framework, logging facade, or generated Windows bindings.
win32-min— verified, dependency-free Win32 ABI foundation used by this crate.windows-lsa— LSA ticket cache access; typical pattern isimpersonate_on_threadthen read the target LUID's cache.windows-sspi-shim— SSPI Negotiate ergonomics; the impersonated identity flows straight intoSspiClient::for_spn.windows-scm— local Service Control Manager wrapper, for the SYSTEM-side of the same workflows.
Together these cover identity, authentication, and local administration workflows for Windows security research and defensive tooling.
MIT © 2026 zevs