D. Jonker
D. Jonker

Reputation: 103

Impersonation alternating between success and failure each run

Good day,

I wrote a Windows Service Program to let myself check if certain services are running on a remote machine. The program triggers every minute and then it prompts me when the service status has changed.

I used this code to do the impersonation login

public class Network
{
    public class SoddingNetworkAuth : IDisposable
    {
        [DllImport("advapi32.dll", SetLastError = true)]
        private static extern bool LogonUser(string lpszUsername, string lpszDomain, string lpszPassword, int dwLogonType, int dwLogonProvider, out IntPtr phToken);
        [DllImport("kernel32", SetLastError = true)]
        private static extern bool CloseHandle(IntPtr hObject);
        private IntPtr userHandle = IntPtr.Zero;
        private WindowsImpersonationContext impersonationContext;
        public SoddingNetworkAuth(string user, string domain, string password)
        {
            if (!string.IsNullOrEmpty(user))
            {
                // Call LogonUser to get a token for the user  
                bool loggedOn = LogonUser(user, domain, password,
                                9 /*(int)LogonType.LOGON32_LOGON_NEW_CREDENTIALS*/,
                                3 /*(int)LogonProvider.LOGON32_PROVIDER_WINNT50*/,
                out userHandle);
                if (!loggedOn)
                    throw new Win32Exception(Marshal.GetLastWin32Error());
                // Begin impersonating the user  
                impersonationContext = WindowsIdentity.Impersonate(userHandle);
            }
        }
        public void Dispose()
        {
            if (userHandle != IntPtr.Zero)
                CloseHandle(userHandle);
            if (impersonationContext != null)
                impersonationContext.Undo();
        }
    }
}

And then I use the results from this accordingly.

The problem I am encountering is that on every second run the impersonation fails. So If I had 5 runs the results would be as follows:

result:Running
result:Cannot open Service Control Manager on computer [Computer Name]
result:Running
result:Cannot open Service Control Manager on computer [Computer Name]
result:Running.

I am using a domain Service Account login (which can be changed, in the event the login needed to change) to log into the machine which has the rights to access the service control manager.

using (new Impersonation.Network.SoddingNetworkAuth(userName, domain, configuration.password))
{
      var serviceController = new ServiceController(configuration.serviceName, configuration.IPaddress);
      if (serviceController.Status == ServiceControllerStatus.Running)
      {
        isRunning = true;
      }
}

Any Ideas as to why it fails on every second run?

Upvotes: 1

Views: 433

Answers (1)

D. Jonker
D. Jonker

Reputation: 103

For those who are interested, the rror was the manor in which is disposed of the data. See below the changes I made to the code to fix the problem.

try
    {
      if (!string.IsNullOrEmpty(user))
      {
        // Call LogonUser to get a token for the user  
        bool loggedOn = LogonUser(user, domain, password,
                        9 /*(int)LogonType.LOGON32_LOGON_NEW_CREDENTIALS*/,
                        3 /*(int)LogonProvider.LOGON32_PROVIDER_WINNT50*/,
        out userHandle);
        if (!loggedOn)
        {
          if (userHandle != IntPtr.Zero)
          {
            CloseHandle(userHandle);
          }
          throw new Win32Exception(Marshal.GetLastWin32Error());

        }
        // Begin impersonating the user  
        impersonationContext = WindowsIdentity.Impersonate(userHandle);
      }
      if (userHandle != IntPtr.Zero)
      {
        CloseHandle(userHandle);
      }
      disposed = false;
    }
    catch
    {
    }
  }
  public void Dispose()
  {
    try
    {
      //Dispose of unmanaged resources.
      Dispose(true);
      // Suppress finalization.
      GC.SuppressFinalize(this);
    }
    catch
    {
    }

  }
  protected virtual void Dispose(bool Disposing)
  {
    // Check to see if Dispose has already been called.
    try
    {
      if (disposed)
        return;

      if (Disposing)
      {
        if (userHandle != IntPtr.Zero)
        {
          CloseHandle(userHandle);
        }

        if (impersonationContext != null)
        {
          //impersonationContext.Undo();
          impersonationContext.Dispose();
        }
      }
      disposed = true;
    }
    catch
    {
    }

  }

The manor in which I called the data in the main class changed to be as follows:

IntPtr userHandle = IntPtr.Zero;
    bool loggedOn = LogonUser(
userName,
domain,
password,
9,
3,
out userHandle);
    if (!loggedOn)
      throw new Win32Exception(Marshal.GetLastWin32Error());

    WindowsImpersonationContext impersonationContext = WindowsIdentity.Impersonate(userHandle);

          using (var serviceController = new ServiceController(serviceName, IPaddress))
          {
            //code goes here
          }
          serviceController.Dispose();
          if (userHandle != IntPtr.Zero)
            CloseHandle(userHandle);

          if (impersonationContext != null)
            impersonationContext.Undo();

Upvotes: 0

Related Questions