Search code examples
delphidelphi-7

Impersonate Administrator to restart Windows service


I have a Delphi application that restart certain Windows service.

If the user who execute this application has Administrative rights (member of Administrators group), the service restart would be successful.

For normal user, the service restart would fail.

It will also success if this normal user use "right-click" Run As Administrator. But, the normal user must type in the Administrator username and password.

I want to solve this for the normal user.

My idea is to do "impersonate user" within the code, obviously impersonating the local Administrator account.

But, it still doesn't work.

Here is my code:

function GetCurrUserName: string;
var
  Size              : DWORD;
begin
  Size := MAX_COMPUTERNAME_LENGTH + 1;
  SetLength(Result, Size);
  if GetUserName(PChar(Result), Size) then
    SetLength(Result, Size)
  else
    Result := '';
end;

function ConnectAs(const lpszUsername, lpszPassword: string): Boolean;
var
  hToken       : THandle;
begin
  Result := LogonUser(PChar(lpszUsername), nil, PChar(lpszPassword), LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, hToken);
  if Result then
    Result := ImpersonateLoggedOnUser(hToken)
  else
    RaiseLastOSError;
end;

And I call it like this:

try
  ConnectAs('Administrator','password');
except
  on E: Exception do
    Writeln(E.ClassName, ': ', E.Message);
end;

// Restart the Windows service

// Done, back to the user
RevertToSelf;

The service restart will fail. If user run it as Administrator (right-click RunAs), it works.

The impersonation itself is working fine. I could check it with getting the current username. But, why it still doesn't work. It looks like the user impersonate without Administrator privilege.

Is there any other kind of impersonation?


Solution

  • This is to be expected. Under UAC users in the administrators group receive a filtered token unless they pass through the UAC elevation process. You are impersonating the other user, but you are doing so without UAC elevation and so have a filtered, reduced privilege token.

    In order to execute code with elevated rights, under UAC, you must pass through the UAC elevation process. If you could avoid doing so then UAC would be pointless.

    As far as I can tell you have the following principle options:

    1. Start another process to do the work, using the runas shell verb to force elevation, or indeed some other mechanism to arrange elevation. This will take your through the UAC dialog which you want to avoid.
    2. As an alternative, install two services. The first service is the service that you currently install, the one that you wish to restart. The second service is a small and simple service whose sole task is to perform that restart. Because that second service runs in session 0 where there is no UAC, you can easily arrange that it has sufficient rights to restart the first service. When you need to restart service 1 from your desktop app, send a request to service 2 so that service 2 can perform the restart of service 1.

    As an aside, I do note that you don't check for errors in your call to ImpersonateLoggedOnUser.