【问题标题】:Console app to start remote service fails for some users某些用户启动远程服务的控制台应用程序失败
【发布时间】:2017-06-27 13:18:59
【问题描述】:

我知道这个问题已经被问到了多种变体,但我目前正处于我的智慧和谷歌搜索能力的尽头。我编写了一个控制台应用程序来重新启动远程机器上的服务。

我们域中的其他用户也需要能够为此目的使用此应用,即使他们的凭据不足以重新启动该服务。不过,他们确实拥有对该机器的正常用户访问(读取)权限。因此,我正在使用模拟。不幸的是,该应用程序仅对某些用户有效,而对其他人无效。

所有这些用户都属于同一个 AD 组,都拥有他们正在操作的计算机的本地管理员权限,并激活了 UAC。

代码如下:

public static extern bool LogonUser(String lpszUsername, String lpszDomain, String lpszPassword,
        int dwLogonType, int dwLogonProvider, out SafeTokenHandle phToken);

    [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern int DuplicateToken(IntPtr hToken, int impersonationLevel,
        ref IntPtr hNewToken);

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

    [PermissionSetAttribute(SecurityAction.Demand, Name = "FullTrust")]
    static void Main(string[] args)
    {
        SafeTokenHandle safeTokenHandle;
        try
        {
            const int LOGON32_PROVIDER_DEFAULT = 0;
            const int LOGON32_LOGON_INTERACTIVE = 2;
            const int LOGON32_LOGON_SERVICE = 5;

            // Call LogonUser to obtain a handle to an access token.
            bool returnValue = LogonUser(user, domain, password,
                LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT,
                out safeTokenHandle);

            // Use the token handle returned by LogonUser.
            using (WindowsIdentity newId = new WindowsIdentity(safeTokenHandle.DangerousGetHandle()))
            {
                //Duplicate token to set Impersonation Level to Delegate (3)
                IntPtr tokenDuplicate = IntPtr.Zero;
                DuplicateToken(newId.Token, 3, ref tokenDuplicate);
                using (WindowsIdentity newId2 = new WindowsIdentity(tokenDuplicate))
                {
                    using (WindowsImpersonationContext impersonatedUser = newId2.Impersonate())
                    {
                        var token = newId2.ImpersonationLevel;

                        Console.WriteLine("Running as: " + WindowsIdentity.GetCurrent().Name);
                        Console.WriteLine("Impersonation level: " + token.ToString());
                        var sc = new ServiceController
                        {
                            MachineName = remote,
                            ServiceName = myService
                        };
                        if (sc.Status.Equals(ServiceControllerStatus.Running))
                        {
                            sc.Stop();
                            while (!sc.Status.Equals(ServiceControllerStatus.Stopped))
                            {
                                sc.Refresh();
                                System.Threading.Thread.Sleep(100);
                            }
                        }
                        sc.Refresh();
                        if (sc.Status.Equals(ServiceControllerStatus.Stopped))
                        {
                            sc.Start();
                            while (!sc.Status.Equals(ServiceControllerStatus.Running))
                            {
                                sc.Refresh();
                                System.Threading.Thread.Sleep(100);
                            }
                        }
                    }
                }
            }
    }

}

public sealed class SafeTokenHandle : SafeHandleZeroOrMinusOneIsInvalid
{
    private SafeTokenHandle()
        : base(true)
    {
    }

    [DllImport("kernel32.dll")]
    [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
    [SuppressUnmanagedCodeSecurity]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern bool CloseHandle(IntPtr handle);

    protected override bool ReleaseHandle()
    {
        return CloseHandle(handle);
    }
}

我使用了 DuplicateToken,因为没有它,ImpersonationLevel 为“None”,所以我认为这可能是原因。唉,这并没有改变任何事情。

在模拟上下文中使用的用户凭据在远程计算机上具有本地管理员权限,并且可以重新启动服务。在使用此应用程序的六个用户中,其中 4 个用户的服务正常重启,而另外 2 个用户则失败并显示

无法在 [remote] 上启动服务控制管理器。你可能有 权限不足...

运行应用程序时的控制台输出适用于所有用户:

运行身份:[正确的用户]
模拟级别:委托

以管理员身份运行应用程序也不会改变任何内容。 它是一个普通的独立控制台应用程序,而不是 WCF 服务或任何依赖 IIS 的东西。

所有用户都在域内运行 Windows 7 Professional,直接连接到网络,而不是通过 VPN。

知道这可能是什么原因吗?我错过了什么吗?失败的用户会不会遗漏一些东西?框架版本?可再发行产品?有什么事吗?

【问题讨论】:

    标签: c# service impersonation delegation servicecontroller


    【解决方案1】:

    得到它的工作,虽然不是我真正喜欢的方式(因为它没有告诉我为什么这两个用户的其他代码失败)。

    => 使用 PowerShell 的强大功能:

    using System.Management.Automation;
    
    var command = new PSCommand();
    
    command.AddScript("$pass = convertto-securestring \"thePassword" -asplaintext -force");
    command.AddScript("$mycred = new-object -typename System.Management.Automation.PSCredential -argumentlist \"DOMAIN\\user\",$pass");
    
    command.AddScript("$obj = (gwmi -computername \"srv-inet\" -class Win32_Service -credential $mycred | Where-Object { $_.Name -match \"Name of the service\" })");
    command.AddScript("$obj.StopService()");
    
    using (var ps = PowerShell.Create())
    {
        ps.Commands = command;
        ps.Invoke();
    
        if (ps.HadErrors)
        {
            Console.WriteLine(ps.InvocationStateInfo.Reason);
        }
        while (ps.InvocationStateInfo.State != PSInvocationState.Completed)
            System.Threading.Thread.Sleep(10);
        //Script completed but service not yet stopped.
        //Wait 5 seconds, show countdown to user
        for (var i = 10; i > 5; i--)
        {
            Console.WriteLine(i.ToString());
            Thread.Sleep(1000);
        }
    
        ps.Commands.Clear();
        command = new PSCommand();
        command.AddScript("$pass = convertto-securestring \"thePassword\" -asplaintext -force");
        command.AddScript("$mycred = new-object -typename System.Management.Automation.PSCredential -argumentlist \"DOMAIN\\user\",$pass");
    
        command.AddScript("$obj = (gwmi -computername \"srv-inet\" -class Win32_Service -credential $mycred | Where-Object { $_.Name -match \"Name of the service\" })");
        command.AddScript("$obj.StartService()");
        ps.Commands = command;
        ps.Invoke();
    
        if (ps.HadErrors)
        {
            Console.WriteLine(ps.InvocationStateInfo.Reason);
        }
        while (ps.InvocationStateInfo.State != PSInvocationState.Completed)
            System.Threading.Thread.Sleep(10);
    
        //Script completed but service not yet fully started.
        //Wait 5 seconds, show countdown to user
        for (var i = 5; i > 0; i--)
        {
            Console.WriteLine(i.ToString());
            Thread.Sleep(1000);
        }
    }
    

    如您所见,这没有优化;我正在重复自己的服务开始。

    但它有效,因此我很高兴。

    仍然:如果有人知道是什么原因可能导致某些用户使用 ServiceController 的初始代码失败,请分享您的知识!

    附录: 甚至 PowerShell 方法一开始也不起作用。我不得不在其中一台出现故障的计算机上下载 Windows Management Framework 4.0。这似乎强化了两台机器上确实缺少一些组件的情况。唉,我不知道,哪个。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-09-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-10-30
      • 2017-05-21
      相关资源
      最近更新 更多