Remote controlling of windows service installed on intranet machine

后端 未结 2 545
醉话见心
醉话见心 2021-01-15 23:53

I have web application deployed on my local IIS 7, with application pool configured to work under built-in NETWORK SERVICE account. From this web application I need to check

2条回答
  •  梦毁少年i
    2021-01-16 00:07

    The problem is NETWORK SERVICE doesn't have sufficient rights for controlling windows services. I needed to switch to another user context to be able to control it. But I didn't want to do it for entire application. Instead I was searching for arbitrary piece of code execution under specific identity.

    I've checked a lot of resources for impersonation included that shown by Malcolm Frexner. Because I'm working with Windows 7 (64bit) and also with Windows Server 2008 R2 (64bit), that I've found were not working for me. I ended up with such generic solution:

    using System;
    using System.ComponentModel;
    using System.Runtime.InteropServices;
    using System.Security.Principal;
    
    namespace Thing.Namespace
    {
        public enum LogOnType
        {
            LogOn32LogOnInteractive = 2,
            LogOn32LogOnNetwork = 3,
            LogOn32LogOnBatch = 4,
            LogOn32LogOnService = 5,
            LogOn32LogOnUnlock = 7,
            LogOn32LogOnNetworkCleartext = 8,
            LogOn32LogOnNewCredentials = 9
        }
    
        public enum LogOnProvider
        {
            LogOn32ProviderDefault = 0,
            LogOn32ProviderWinnt35 = 1,
            LogOn32ProviderWinnt40 = 2,
            LogOn32ProviderWinnt50 = 3
        }
    
        public enum ImpersonationLevel
        {
            SecurityAnonymous = 0,
            SecurityIdentification = 1,
            SecurityImpersonation = 2,
            SecurityDelegation = 3
        }
    
        public static class IdentityBoss
        {
            private static WindowsImpersonationContext _impersonationContext;
            private static readonly object _locker = new object();
    
            private static class NativeMethods
            {
                [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
                public static extern int LogonUser(String lpszUserName,
                                                   String lpszDomain,
                                                   String lpszPassword,
                                                   int dwLogonType,
                                                   int dwLogonProvider,
                                                   ref IntPtr phToken);
    
                [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)]
                public static extern int DuplicateToken(IntPtr hToken,
                                                        int impersonationLevel,
                                                        ref IntPtr hNewToken);
    
                [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)]
                [return: MarshalAs(UnmanagedType.Bool)]
                public static extern bool RevertToSelf();
    
                [DllImport("kernel32.dll", SetLastError = true)]
                [return: MarshalAs(UnmanagedType.Bool)]
                public static extern bool CloseHandle(IntPtr handle);
            }  
    
            public static void Impersonate(Action action, string user, string domain, string password,
                                           LogOnType logOnType, LogOnProvider logOnProvider,
                                           ImpersonationLevel impersonationLevel)
            {
                try
                {
                    ImpersonateValidUser(user, domain, password, logOnType, logOnProvider, impersonationLevel);
                    action();
                }
                finally
                {
                    UndoImpersonation();
                }
            }
    
            public static void ImpersonateHappily(Action action, string user, string domain, string password)
            {
                Impersonate(action, user, domain, password, LogOnType.LogOn32LogOnNetworkCleartext,
                            LogOnProvider.LogOn32ProviderDefault, ImpersonationLevel.SecurityImpersonation);
            }
    
            public static TResult Impersonate(Func action, string user, string domain, string password,
                                                       LogOnType logOnType, LogOnProvider logOnProvider,
                                                       ImpersonationLevel impersonationLevel)
            {
                try
                {
                    ImpersonateValidUser(user, domain, password, logOnType, logOnProvider, impersonationLevel);
                    return action();
                }
                finally
                {
                    UndoImpersonation();
                }
            }
    
            public static TResult ImpersonateHappily(Func action, string user, string domain, string password)
            {
                return Impersonate(action, user, domain, password, LogOnType.LogOn32LogOnNetworkCleartext,
                                   LogOnProvider.LogOn32ProviderDefault, ImpersonationLevel.SecurityImpersonation);
            }
    
            private static void ImpersonateValidUser(String userName, String domain, String password, LogOnType logonType, LogOnProvider logonProvider, ImpersonationLevel impersonationLevel)
            {
                lock (_locker)
                {
                    var token = IntPtr.Zero;
                    var tokenDuplicate = IntPtr.Zero;
                    WindowsIdentity tempWindowsIdentity = null;
    
                    try
                    {
                        if (!NativeMethods.RevertToSelf())
                            throw new Win32Exception(Marshal.GetLastWin32Error());
                        if (NativeMethods.LogonUser(userName, domain, password, (int) logonType, (int) logonProvider,ref token) == 0)
                            throw new Win32Exception(Marshal.GetLastWin32Error());
                        if (NativeMethods.DuplicateToken(token, (int) impersonationLevel, ref tokenDuplicate) == 0)
                            throw new Win32Exception(Marshal.GetLastWin32Error());
    
                        tempWindowsIdentity = new WindowsIdentity(tokenDuplicate);
                        _impersonationContext = tempWindowsIdentity.Impersonate();
                    }
                    finally
                    {
                        if (token != IntPtr.Zero)
                            NativeMethods.CloseHandle(token);
                        if (tokenDuplicate != IntPtr.Zero)
                            NativeMethods.CloseHandle(tokenDuplicate);
                        if (tempWindowsIdentity != null)
                            tempWindowsIdentity.Dispose();
                    }
                }
            }
    
            private static void UndoImpersonation()
            {
                lock (_locker)
                {
                    if (_impersonationContext != null)
                    {
                        _impersonationContext.Undo();
                    }
                }            
            }
        }
    }
    

    In addition I needed to create new user on my machine where the service is installed. User has to have permissions for controlling windows services - for that purpose it can be added to Administrators group.

    Now I can start / stop my services and getting theirs current statuses in such way:

    private const string user = "MyUser";  
    private const string domain = ".";
    private const string password = "MyPa$$w0rd";
    
    public string StartService(string machine, string service)
    {
        IdentityBoss.ImpersonateHappily(
            () =>
                {
                    Controller.Instance.StartService(machine, service);
                }, user, domain, password
            );
    }
    
    public string GetServiceStatus(string machine, string service)
    {
        return IdentityBoss.ImpersonateHappily(
            () =>
                {
                    return Controller.Instance.GetServiceStatus(machine, service);
                }, user, domain, password
            );
    }
    

    ImpersonateHappily is just a function which takes parameters which are working with my operating system. Other similar solutions from the web used dwLogonType parameter passed to win 32 api function LogonUserA with values 2 or 9, while under my system value 8 is correct.

    BTW: Impersonate is a wrapper function which sets up the impersonation and then passes it a lambda which does the actual work. The fancy computer science term for this style of writing code is higher-order programming.

提交回复
热议问题