How to get the user’s token in a service
Posted by: Christian Wimmer in: JEDI Windows Security Code Lib
Sometimes it is necessary to retrieve a user’s token or act as a user who is logged on. By default a service uses the SYSTEM token and this leads to a security problem. If a service solves tasks send by another low privileges process (client), the client can do things it shouldn’t do. For this reason we have two possible solutions:
- The service retrieves the process token and impersonate it.
- The service retrieves the token from the session where the process runs.
TJwSecurityToken implements different constructors that allows to retrieve a token from…
- … the current process or thread
Use CreateTokenByProcess, CreateTokenByThread or CreateTokenEffective to retrieve the appropriate token.
- … a process
Use CreateTokenByProcess or CreateTokenByProcessID to get the token from a process handle or a process ID. You cannot use the latter constructor for processes in another session unless you activate the debug privilege.
- … a session
This constructor is for compatibility reasons in WindowsNT, 2000 only and returns the process token of a given process (default: explorer.exe) which has to be in the same session. This process is possible because services and the first logged on user reside both in session 0 in preVista Windows versions. It can also be used to retrieve a process token from a known process in the same session.
This constructor uses the official WTSQueryUserToken API and returns the token of a session ID (logged on user). It can only be called within the SYSTEM account and is not available in pre XP Windows versions.
This constructor enhances CreateWTSQueryUserToken and thus makes it possible to call it from Windows 2000 and newer. It can also be used to retrieve a token from another Terminal Client (Terminal Server must be running to do so)
It is straight forward to get a token from a process, thread or session.
var Token : TJwSecurityToken;
Enable debug privilege (if available) and open any process you want using maximum possible access.
Token := TJwSecurityToken.CreateTokenByProcess(AnyProcessHandle, MAXIMUM_ALLOWED);
Get the token from a process known by name in the same session.
Token := TJwSecurityToken.CreateCompatibilityQueryUserToken(MAXIMUM_ALLOWED, ‘delphi32.exe’);
Get the user’s token from the physical session ID.
Token := TJwSecurityToken.CreateWTSQueryUserTokenEx(nil, WtsGetActiveConsoleSessionID);
Edit: First parameter is a TJwTerminalServer Object. Not a handle!
The rest :
- check for exceptions
- impersonate the token (sets the thread token)
- solve the task
- revert to self (remove the thread token) and
- free the token object.
on E: EJwsclWinCallFailedException do
on E2: EJwsclProcessNotFound do //for CreateCompatibilityQueryUserToken only
//do stuff on user’s behalf
Hint: Do not impersonate the user and call CreateProcess to create a process in the user’s context. It does not work. Instead use CreateProcessAsUser.