1

組み込みの NETWORK SERVICE アカウントで動作するように構成されたアプリケーション プールを使用して、ローカル IIS 7 に Web アプリケーションを展開しています。この Web アプリケーションから、Windows サービスのステータス (開始、停止など) を確認する必要があります。私はそれを得るためにそのようなステートメントを使用しました:

public string GetServiceStatus(string machine, string service)
{
    var service = new ServiceController(machine, service);
    service.Refresh();
    return service.Status;
}

これmachineは、イントラネット内のホストの IP アドレス (192.168.0.7 とします) であり、Windows サービスが実行されています (組み込みの NETWORK SERVICE アカウントでも実行されています)。

残念ながら、コードは例外を与えます:

service.Status threw an exception of type 'System.InvalidOperationException' 
Cannot open MyService service on computer '192.168.0.7'. Access is denied.

問題はどこだ ?

4

2 に答える 2

1

問題は、NETWORK SERVICE が Windows サービスを制御するための十分な権限を持っていないことです。制御できるようにするには、別のユーザー コンテキストに切り替える必要がありました。しかし、私はアプリケーション全体でそれをやりたくありませんでした。代わりに、特定の ID の下でコード実行の任意の部分を探していました。

私は、Malcolm Frexner によって示されているものを含む、なりすましに関する多くのリソースを確認しました。私は Windows 7 (64 ビット) と Windows Server 2008 R2 (64 ビット) を使用しているため、動作していないことがわかりました。私はそのような一般的な解決策になりました:

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<TResult>(Func<TResult> 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<TResult>(Func<TResult> 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();
                }
            }            
        }
    }
}

さらに、サービスがインストールされているマシンに新しいユーザーを作成する必要がありました。ユーザーは、Windows サービスを制御する権限を持っている必要があります。そのためには、管理者グループに追加できます。

これで、サービスを開始/停止し、そのような方法で現在のステータスを取得できます:

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私のオペレーティングシステムで動作しているパラメータを取る単なる関数です。Web からの他の同様のソリューションでは、値 2 または 9 でdwLogonTypewin 32 API 関数に渡されたパラメーターを使用しましたLogonUserAが、私のシステムでは値 8 が正しいです。

ところで:Impersonate偽装を設定し、実際の作業を行うラムダを渡すラッパー関数です。このスタイルのコードを記述するコンピューター サイエンス用語は、高階プログラミングです。

于 2013-01-18T10:34:48.323 に答える
0

偽装を使用できるかどうか試してください

http://support.microsoft.com/kb/306158/en-us

于 2013-01-15T21:31:48.187 に答える