Some functions (like ExitWindowsEx) need a privilege (SE_SHUTDOWN_NAME) to be enabled to work properly.
With the help of JWSCL this task is made very easy.

JWSCL provides several ways to enable and disable privileges.

  1. Use the methods of TJwSecurityToken
  2. Use the function JWEnablePrivilege
  3. Use the interface IJwPrivilegeScope

1. Use the methods of TJwSecurityToken

You can use TJwSecurityToken to enable, disable or test a privilege. However there are some tool functions that do it for you already in only a single call. They are called

2.Use the function JwEnablePrivilege and friends

A very convienient way to enable and disable a privilege is to use the function JwEnablePrivilege.

  1. type
  2.   TJwPrivilegeSetType =
  3.    (pst_Enable , pst_EnableIfAvail ,pst_Disable);
  4.  
  5. function JwEnablePrivilege (const Index : string;
  6.   const Query : JwPrivilegeSetType): boolean;

There are two ways to enabe a privilege and one way to disable it.

  1. Enable a privilege or die if the privilege does not exist
    1. try
    2.   JwEnablePrivilege(SE_DEBUG_NAME, pst_Enable);
    3. except
    4.   on E: EJwsclPrivilegeException do
    5.    //do error stuff here
    6. end;

    You should check for the exception EJwsclPrivilegeException because if the flag pst_Enable is used, the function raises the exception when the privilege does not exist.

  2. Enable a privilege only if it exists
    1. JwEnablePrivilege(SE_DEBUG_NAME, pst_EnableIfAvail);

    The code above may or may not enable the privilege depending on its availability. This is sometimes useful if you do not really need a privilege, but it might come handy if available. For example you could use SE_DEBUG_NAME privilege in a call to OpenProcess to open a foreign process. In the worst case that happens without the process is that OpenProcess will fail on processes that were not executed by the same user. However in each case you have to check the result of OpenProcess.

  3. Disable a privilege
    Disabling a privilege is not much work. It even won’t throw an exception if the privilege does not exist.

    1. JwEnablePrivilege(SE_DEBUG_NAME, pst_Disable);

To find out whether a special privilege is available use JwIsPrivilegeSet.
The following code illustrates how to use JwIsPrivilegeSet.

  1. uses JwsclToken;…      
  2.  
  3.   if JwIsPrivilegeSet(SE_DEBUG_NAME, pqt_Available) then
  4.   begin
  5.     if JwIsPrivilegeSet(SE_DEBUG_NAME, pqt_Enabled) then
  6.       JwEnablePrivilege(SE_DEBUG_NAME, pst_Disable)
  7.     else
  8.       JwEnablePrivilege(SE_DEBUG_NAME, pst_Enable);
  9.   end;

With this helper function JwEnablePrivilege won’t throw the exception EJwsclPrivilegeException if the privilege is not available.
A handy function is JwGetPrivilegesText, which returns a string of available privileges and their status. You also can define which privileges are shown.

JwGetPrivilegesText comes in two versions. The first version does not have any parameters and just returns a string with privilege names and their status. Each privilege is separated by a line break.

  1. WriteLn(JwGetPrivilegesText);

The output may look like depending on your status. The following privileges are from a standard user in Vista:

SeShutdownPrivilege [disabled]
SeChangeNotifyPrivilege [enabled]
SeUndockPrivilege [disabled]
SeIncreaseWorkingSetPrivilege [disabled]
SeTimeZonePrivilege [disabled]

The second version of JwGetPrivilegesText receives a list of privileges you want to be displayed:

  1. WriteLn(JwGetPrivilegesText([
  2.   SE_CHANGE_NOTIFY_NAME,
  3.   SE_DEBUG_NAME,
  4.   SE_SHUTDOWN_NAME,
  5.   SE_CHANGE_NOTIFY_NAME]));

The output may look like this:

SeChangeNotifyPrivilege [enabled]
SeDebugPrivilege [not available]
SeShutdownPrivilege [disabled]
SeChangeNotifyPrivilege [enabled]

Multipe threads and privileges:

You should always use a thread token when you work with several threads. Enabling and disabling privileges on a process token is very problematic. The reason is that you enable or disable a privilege for all threads. If a single thread enables a privilege and another one disables it, the first thread will fail to call a function that depends on that privilege.
It is possible to introduce lock mechanisms like semaphores. But this is not necessary because each thread can (and should) have its own token: An impersonated token or in other words : a thread token.

To use a thread token properly you have to add this code to your main thread function.

  1. procedure TMyThread.Execute;
  2. var Token : TJwSecurityToken;
  3. begin
  4.   Token := TJwSecurityToken.CreateTokenEffective(MAXIMUM_ALLOWED);
  5.   try
  6.     //check for error result
  7.     //you should proceed very carefully if the call fails
  8.     Token.ImpersonateLoggedOnUser;
  9.   except
  10.    on E1 :  EJwsclAccessTypeException do
  11.      //will be raised if the token is an impersonation token and does not have access type TOKEN_QUERY and TOKEN_IMPERSONATE)
  12.    on E2 : EJwsclAccessTypeException do
  13.      //will be raised if the token is a primary token and does not have access type TOKEN_QUERY and TOKEN_DUPLICATE)
  14.    on E3 : EJwsclSecurityException do
  15.      //will be raised if a winapi function failed
  16.   end;      
  17.  
  18.  try
  19.    //do your thread stuff here
  20.   finally
  21.     Token.Free;
  22.     Token := nil;
  23.   end;
  24. end;

ImpersonateLoggedOnUser has a lot of possible exception handlers. This is because there are several ways how the call can fail. You should make sure that your main thread code is not executed without an assigned thread token.

Additionally you should also never call TerminateThread or ExitThread because in this case the finally Block would not be executed (memory leak).

3. Use the interface IJwPrivilegeScope

It is always a good thing to disable a privilege after it was used. The only way to do it safe is to use a try finally catch. If something happens the privilege is disabled at least.

  1. try
  2.   wEnablePrivilege ( SE_SHUTDOWN_NAME , pst_Enable );
  3. except
  4.   on E : EJwsclPrivilegeException do
  5.   // error handling
  6. end;      
  7.  
  8. try
  9.   //do your stuff here
  10. finally
  11.   JwEnablePrivilege ( SE_SHUTDOWN_NAME , pst_Disable);
  12. end;

This codes needs a lot of work to write if several other privileges are necessary. Fortunately there is a way to accomplish this task much more convenient. We use COM and the unit JwsclPrivileges which implements the interface IJwPrivilegeScope.
IJwPrivilegeScope allows to enable several privileges at once and also disable them as soon as the internal reference counter drops to zero. A huge advantage is that Delphi helps a lot with the reference counting. It automatically increases or decreases the reference counter for several actions like passing the interface to another function. Find out more about scopes and Delphi’s reference counting for interfaces here.
The automatic privilege mangagment can be used in the following way:

  1. procedure YourClass.FooMethod;
  2. var Privs : IJwPrivilegeScope;
  3. begin
  4.   try
  5.     Privs := JwGetPrivilegeScope([SE_SHUTDOWN_NAME, SE_TCB_NAME, SE_SECURITY_NAME]);
  6.   except
  7.     on E : EJwsclPrivilegeException do
  8.       //do things on error and exit
  9.   end;
  10.   //do things on success that needs privileges enabled
  11. end; //here the privileges are automatically disabled      
  12.  
  13. begin
  14.   FooMethod;
  15.   //SE_SHUTDOWN_NAME, SE_TCB_NAME, SE_SECURITY_NAME are disabled.

The interface Privs will run out of scope as soon as the method FooMethod exits. In this last step the activated privileges are disabled automatically.
If you combine this mechanism with the thread token shown in “procedure TYourThread.Execute;” you can easily play with privileges without disturbing other thread tokens. However you need a thread token only if you run several threads. In a single thread application the effort isn’t usually necessary for the discussed task (but there may be exceptions).

  1. var Text : String ;      
  2.  
  3. procedure YourClass.FooMethod;
  4. var PrivScope : IJwPrivilegeScope ;
  5. begin
  6.   // Privilege is only active in this procedure
  7.   PrivScope := JwGetPrivilegeScope ([ SE_SHUTDOWN_NAME ],
  8.           pst_EnableIfAvail );
  9.   Text := JwGetPrivilegesText ([ SE_SHUTDOWN_NAME ]); // enabled
  10.   writeln(Text);
  11. end;      
  12.  
  13. var ImpToken : TJwSecurityToken;
  14. begin
  15.   // create thread token from the process token
  16.   ImpToken := TJwSecurityToken . CreateTokenByProcess (0,
  17.     TOKEN_ADJUST_PRIVILEGES or TOKEN_QUERY or TOKEN_READ or
  18.     TOKEN_IMPERSONATE or TOKEN_DUPLICATE );      
  19.  
  20.   try
  21.     ImpToken . ImpersonateLoggedOnUser ;
  22.   except
  23.     //exception handling left out
  24.   end;      
  25.  
  26.   try
  27.     FooMethod;
  28.   finally
  29.     ImpToken.Free;
  30.   end;
  31. end


Some hints to remember:

  1. You cannot add privileges that were not granted to the token. There are two ways to do so with a SYSTEM account (like a service)
    1. Use another process token that contains the necessary privilege
    2. Create your own token by using LsaLogonUser. It allows to add groups and privileges.

    Be warned that using these mechanisms incorrectly may create a security hole.

  2. You can remove privileges by recreating the token using CreateRestrictedToken. The new token is then called restricted token. Maybe you already know the word from Vista and the twin token.
  3. Using a (restricted) thread token on code that is not trustworthy is very risky because the code can always return to the process token. This is done by calling RevertToSelf. In this case you must execute the code in a seperate process. Create the process with CreateProcessAsUser and pass the (restricted) token to the hToken parameter. If you fear the inter-process communication you can also use an out-of process COM DLL.
  4. Always use an exception handler if the method could raise an exception. If an exception is raised within a thread, the thread will immediately stop working and leave resource leaks.
  5. Do not force a user to have a special privilege. Many privileges aren’t needed anyway. For example, the SE_DEBUG_NAME privilege - despite its name - isn’t needed for debugging applications. In fact you can debug an application that was started under your user’s account. However you need the debug privilege only for foreign processes. This includes system processes of course. Raymond answers the question why the debug privilege grants administrator accesss.

Tell me how you liked this blog entry by adding a comment.

Send post as PDF to www.pdf24.org
convert this post to pdf.