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:

  1. The service retrieves the process token and impersonate it.
  2. The service retrieves the token from the session where the process runs.

TJwSecurityToken implements different constructors that allows to retrieve a token from…

  1. … the current process or thread
    Use CreateTokenByProcess, CreateTokenByThread or CreateTokenEffective to retrieve the appropriate token.
  2. … 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.
  3. … a session
    • CreateCompatibilityQueryUserToken
      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.
    • CreateWTSQueryUserToken
      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.
    • CreateWTSQueryUserTokenEx
      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;
begin
try

Enable debug privilege (if available) and open any process you want using maximum possible access.

JwEnablePrivilege(SE_DEBUG_NAME,pst_EnableIfAvail);
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.
 except
on E: EJwsclWinCallFailedException do
//cleanup
on E2: EJwsclProcessNotFound do //for CreateCompatibilityQueryUserToken only
//cleanup
end;
try
Token.ImpersonateLoggedOnUser;
//do stuff on user’s behalf
finally
Token.RevertToSelf;
Token.Free;
end;
end;

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.