简体   繁体   中英

Start / stop windows services with C# ServiceController and Impersonation

I'm developing remote keyword library for robot framework. This library is running in c# .net.

The remote library is built using NRobot-Server . This is handling xml-rpc server and other stuff, so I can write only keywords for robot framework. FYI this xml-rpc server is multi-threaded.

Based on this answer and this demo I've managed to put something together. However I'm always getting Cannot open <<my service>> service on computer '192.168.0.105'

Cannot open <<my service>> service on computer '192.168.0.105'.
at System.ServiceProcess.ServiceController.GetServiceHandle(Int32 desiredAccess)
   at System.ServiceProcess.ServiceController.Stop()
   at RKL.KeywordLibrary.KeywordLibraryImpl.ControlService(String host, String username, String password, String name, String action, String domain) in C:\Dev\QueueServiceSystemTestRKL\src\RKL\KeywordLibrary\KeywordLibraryImpl.cs:line 115
   at RKL.KeywordLibrary.RklKeywordClass.ControlService(String h
ost, String username, String password, String name, String action) in C:\Dev\RKL\src\RKL\KeywordLibrary\RKLKeywordClass.cs:line 21

Since I should be able to control services remotely, the dev environment looks like this:

-------------------     --------------------                              
|      OS X       |     |      Win 10      |                              
|                 |     |                  |                             
| robot framework | --> |  remote keyword  |                                
|                 |     |   library (C#)   |                           
-------------------     --------------------                             
                                |
                                |
                                v 
                       --------------------- 
                       |  Win Server 2019  | 
                       |                   | 
                       |      service      | 
                       |                   | 
                       --------------------- 

Actual code looks like this (I'm using SimpleImpersonation nuget )

public void ControlService(string host, string username, string password, string name, string action)
{
    var credentials = new UserCredentials(username, password);
    Impersonation.RunAsUser(credentials, SimpleImpersonation.LogonType.Interactive, () =>
    {
        ServiceController sc = new ServiceController(name, host);
        TimeSpan timeout = new TimeSpan(0,0,30);
        switch (action)
        {
            case "start":
                sc.Start();
                sc.WaitForStatus(ServiceControllerStatus.Running, timeout);
                break;
            case "stop":
                sc.Stop();
                sc.WaitForStatus(ServiceControllerStatus.Stopped, timeout);
                break;
            default:
                string msg = String.Format("Unknown action: '{0}'", action);
                throw new Exception(msg);
        }
    });
}

Also to mention, I've created testuser on both windows machines and both users are Administrators.

Currently I'm trying to disable all windows security, however I'm dead in the water. Just trying random stuff.

Does anyone know where could be problem? Any help would be appreciated!

So I've found out about ServiceControllerPermission class and tried it out. And it worked

public void ControlService(string host, string username, string password, string name, string action)
{
    var credentials = new UserCredentials(username, password);
    Impersonation.RunAsUser(credentials, SimpleImpersonation.LogonType.Interactive, () =>
    {
        ServiceControllerPermission scp = new ServiceControllerPermission(ServiceControllerPermissionAccess.Control, host, name);
        scp.Assert();

        ServiceController sc = new ServiceController(name, host);
        TimeSpan timeout = new TimeSpan(0,0,30);
        switch (action)
        {
            case "start":
                sc.Start();
                sc.WaitForStatus(ServiceControllerStatus.Running, timeout);
                break;
            case "stop":
                sc.Stop();
                sc.WaitForStatus(ServiceControllerStatus.Stopped, timeout);
                break;
            default:
                string msg = String.Format("Unknown action: '{0}'", action);
                throw new Exception(msg);
        }
    });
}

I tried to use the solution above to start a windows-service, as well as other possible solutions i've found on the inte.net. In all cases I run into the same issue and can't find / understand the reason why. In the moment "sc.Start();" is called I get an exception > inner exception "access dinied".

Based on the code above I adapted it to the newer version via "WindowsIdentity.RunImpersonated". The rest should be the same. the used credentials are the local admin on a test-machine. Via mmc or cmd I can start the service with this user. but not with this code.

Any help is welcome, because I've rigth now no more ideas how to get it done.

thanks.

public void MainRun()
{
// Get the user token for the specified user, domain, and password using the   
// unmanaged LogonUser method.   
// The local machine name can be used for the domain name to impersonate a user on this machine.  
  
Console.Write("Enter the name of the domain on which to log on: ");
string domainName = Console.ReadLine();

Console.Write("Enter the login of a user on {0} that you wish to impersonate: ", domainName);
string userName = Console.ReadLine();

Console.Write("Enter the password for {0}: ", userName);

const int LOGON32_PROVIDER_DEFAULT = 0;
//This parameter causes LogonUser to create a primary token.   
const int LOGON32_LOGON_INTERACTIVE = 2;

// Call LogonUser to obtain a handle to an access token.   
SafeAccessTokenHandle safeAccessTokenHandle;
bool returnValue = LogonUser(userName, domainName, Console.ReadLine(),
    LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT,
    out safeAccessTokenHandle);

if (false == returnValue)
{
    int ret = Marshal.GetLastWin32Error();
    Console.WriteLine("LogonUser failed with error code : {0}", ret);
    throw new System.ComponentModel.Win32Exception(ret);
}

Console.WriteLine("Did LogonUser Succeed? " + (returnValue ? "Yes" : "No"));
// Check the identity.  
Console.WriteLine("Before impersonation: " + WindowsIdentity.GetCurrent().Name);

// Note: if you want to run as unimpersonated, pass  
//       'SafeAccessTokenHandle.InvalidHandle' instead of variable 'safeAccessTokenHandle'  
WindowsIdentity.RunImpersonated(
    safeAccessTokenHandle,
    // User action  
    () =>
    {
        ServiceControllerPermission scp = new ServiceControllerPermission(ServiceControllerPermissionAccess.Control, 
            Program.host, Program.serviceName);
        scp.Assert();

        ServiceController sc = new ServiceController(Program.serviceName, Program.host);
        TimeSpan timeout = new TimeSpan(0, 0, 30);

        sc.Start();
        sc.WaitForStatus(ServiceControllerStatus.Running, timeout);

        // Check the identity.  
        Console.WriteLine("During impersonation: " + WindowsIdentity.GetCurrent().Name);
    }
    );

// Check the identity again.  
Console.WriteLine("After impersonation: " + WindowsIdentity.GetCurrent().Name);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM