Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to start or stop IIS and also a Windows Service in remote machine using C#

Tags:

c#

Getting Exception with this code... eventhough I've administrator privileges in the remote machine

class Program
    {
        static void Main(string[] args)
        {
            var sc = new System.ServiceProcess.ServiceController("W3SVC", "10.201.58.114");
            sc.Start();
            sc.WaitForStatus(System.ServiceProcess.ServiceControllerStatus.Running);            
            sc.Stop();
            sc.WaitForStatus(System.ServiceProcess.ServiceControllerStatus.Stopped);
        }
    }

Exception:

An unhandled exception of type 'System.InvalidOperationException' occurred in System.ServiceProcess.dll

Additional information: Cannot open Service Control Manager on computer '10.201.58.114'. This operation might require other privileges.

like image 447
venkat Avatar asked Jan 21 '16 09:01

venkat


1 Answers

Are the machines in the same domain? If not Administrator on machine1 is not equivalent Administrator on machine2, so that could be your problem.

One possibility is that you need to grant access for this user - on the remote machine - to stop and start the service like so:

SUBINACL /SERVICE \\<MACHINE>\W3SVC /GRANT=<MACHINE>\<USER>=TO

There's an example of this in the comments of the second code block below (as I needed this even with the in-code identity impersonation).

If this doesn't solve the problem, you could try impersonating the remote user. I managed to get this working using the following code.

First, create a new class WrapperImpersonationContext.cs:

using System;
using System.Runtime.InteropServices;
using System.Security.Principal;
using System.Security.Permissions;
using System.ComponentModel;

//Version from http://michiel.vanotegem.nl/2006/07/windowsimpersonationcontext-made-easy/

public class WrapperImpersonationContext
{
    [DllImport("advapi32.dll", SetLastError = true)]
    public static extern bool LogonUser(String lpszUsername, String lpszDomain,
        String lpszPassword, int dwLogonType, int dwLogonProvider, ref IntPtr phToken);

    [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
    public extern static bool CloseHandle(IntPtr handle);

    private const int LOGON32_PROVIDER_DEFAULT = 0;
    private const int LOGON32_LOGON_INTERACTIVE = 2;

    private string m_Domain;
    private string m_Password;
    private string m_Username;
    private IntPtr m_Token;

    private WindowsImpersonationContext m_Context = null;


    protected bool IsInContext
    {
        get { return m_Context != null; }
    }

    public WrapperImpersonationContext(string domain, string username, string password)
    {
        m_Domain = domain;
        m_Username = username;
        m_Password = password;
    }

    [PermissionSetAttribute(SecurityAction.Demand, Name = "FullTrust")]
    public void Enter()
    {
        if (this.IsInContext) return;
        m_Token = new IntPtr(0);
        try
        {
            m_Token = IntPtr.Zero;
            bool logonSuccessfull = LogonUser(
                m_Username,
                m_Domain,
                m_Password,
                LOGON32_LOGON_INTERACTIVE,
                LOGON32_PROVIDER_DEFAULT,
                ref m_Token);
            if (logonSuccessfull == false)
            {
                int error = Marshal.GetLastWin32Error();
                throw new Win32Exception(error);
            }
            WindowsIdentity identity = new WindowsIdentity(m_Token);
            m_Context = identity.Impersonate();
        }
        catch (Exception exception)
        {
            // Catch exceptions here
        }
    }


    [PermissionSetAttribute(SecurityAction.Demand, Name = "FullTrust")]
    public void Leave()
    {
        if (this.IsInContext == false) return;
        m_Context.Undo();

        if (m_Token != IntPtr.Zero) CloseHandle(m_Token);
        m_Context = null;
    }
}

Then you should be able to run the following. Note that you'll need to change the machine-name, user-name and password to match your setup. Also pay attention to the comments as there's important security settings information that I discovered along the way:

//Code for Program.cs demonstrating the identity impersonation for a ServiceController.

using System;
using System.Security.Principal;
using System.ServiceProcess;

namespace RemoteConnectionTest
{
    class MainClass
    {
        public static void Main (string[] args)
        {

            try {

                //Based on the code from http://michiel.vanotegem.nl/2006/07/windowsimpersonationcontext-made-easy/

                Console.WriteLine("Current user: " + WindowsIdentity.GetCurrent().Name);
                //Also worked with the IP address of GARNET (which was the machine name).
                WrapperImpersonationContext context = new WrapperImpersonationContext("GARNET", "TestAdmin1", "password123");
                context.Enter();
                // Execute code under other uses context
                Console.WriteLine("Current user: " + WindowsIdentity.GetCurrent().Name);

                // Code to execute.

                //Try running the following command on the remote server first to ensure
                //the user has the appropriate access (obviously substitute the
                //username and machine name).
                // runas /user:TestAdmin "sc \\GARNET stop W3SVC"

                //Also, make sure the user on the remote server has access for
                //services granted as described here: http://stackoverflow.com/a/5084563/201648
                //Otherwise you may see an error along the lines of:
                //Cannot open W3SVC service on computer '<SERVER>'. ---> System.ComponentModel.Win32Exception: Access is denied
                //For my configuration I had to run the command:
                // SUBINACL /SERVICE \\GARNET\W3SVC /GRANT=GARNET\TestAdmin=TO
                //It's entirely possible that running this command will allow your existing code to work without using impersonation.

                //You may need to install SUBINACL https://www.microsoft.com/en-au/download/details.aspx?id=23510
                //By default SUBINACL will install to C:\Program Files (x86)\Windows Resource Kits\Tools
                //so CD to that directory and then run the SUBINACL command above.

                //Also worked with the IP address of GARNET (which was the machine name).
                var sc = new ServiceController("W3SVC", "GARNET");
                sc.Start();

                sc.WaitForStatus(ServiceControllerStatus.Running);            
                sc.Stop();
                sc.WaitForStatus(ServiceControllerStatus.Stopped);

                //END - code to execute.
                context.Leave();
                Console.WriteLine("Your code ran successfully. Current user: " + WindowsIdentity.GetCurrent().Name);

            } catch (Exception ex) {
                Console.WriteLine("An exception occured - details as follows: {0}", ex.Message);
                Console.WriteLine("The full stack trace is: {0}", ex);
            }

            Console.WriteLine ("Press any key to exit...");
            Console.ReadLine();

        }
    }

}

Ensure that the remote machine can be assessed using the provided credentials before you try to do this in code e.g. by connecting as this user through remote desktop.

like image 66
Aaron Newton Avatar answered Nov 11 '22 06:11

Aaron Newton