繁体   English   中英

在Windows服务中以特定凭据启动进程

[英]Starting processes under specific credentials from a Windows service

我已经花了几天时间解决这个问题,即使在线上有很多不同的示例,这也是一个棘手的问题,我无法让它们在我的情况下工作。

我有一个在本地系统帐户下运行的Windows服务。 它具有一个WCF端点,用于监听API请求。 通过API告知服务后,该服务应在系统会话(0)中使用“ Worker”帐户凭据启动一个新进程。 流程是一个工作人员,负责检查队列中的工作并执行。 如果找不到工作,它将休眠一会儿并再次检查。 如果确实找到了工作,它将在相同的会话中以相同的凭据启动一个新流程,然后开始工作。 完成工作后,它将关闭。

“工作者”是域帐户,并且是计算机上本地管理员组的成员,该组对可执行文件具有执行权限。 该计算机与该帐户位于同一域中。

问题在于,当服务尝试启动进程时,它将从CreateProcessAsUser方法获取ERROR_ACCESS_DENIED (5)错误代码。

我尝试在具有相同凭据的Windows 7计算机上运行相同的代码,并且运行良好,但是在Windows Server 2008上运行时会得到该错误代码。

该代码太大,无法在此处显示,因此我将其放在其他位置...

ProcessHelperhttp : //pastie.org/private/y7idu3nw4xv1fxzeizbn9g

该服务调用StartAsUserFromService方法以启动该过程,该方法在建立会话后在内部调用CreateProcessAsUser 该过程调用StartAsUserFromApplication方法以启动其后继方法,该方法内部调用CreateProcessWithLogonW

ImpersonationContexthttp : //pastie.org/private/xppc7wnoidajmpq8h8sg

服务需要获取用户令牌才能像他们一样启动进程。 该过程不需要它来启动其后继者。 据我所知,模拟在Server 2008上是成功的,但是它没有某些权限,因此我无法弄清楚哪个。

编辑:

我在Windows 7计算机上尝试了本地管理员帐户和域帐户,它们都可以正常工作。 但是它们都不在Server 2008计算机上工作。 必须在某处缺少许可,但是我不知道在哪里。 错误消息没有帮助。

我还尝试在可执行文件的兼容性选项卡中打勾“以管理员身份运行”框,但这没什么区别。

编辑:

我使用流程监视器来查看服务中发生的情况,这就是错误的出处...

Date & Time:    12/02/2014 11:44:03
Event Class:    File System
Operation:  CreateFile
Result: ACCESS DENIED
Path:   D:\..\executable.exe
TID:    6244
Duration:   0.0000450
Desired Access: Read Data/List Directory, Execute/Traverse, Read Attributes, Synchronize
Disposition:    Open
Options:    Synchronous IO Non-Alert, Non-Directory File
Attributes: n/a
ShareMode:  Read, Delete
AllocationSize: n/a
Impersonating:  Domain\Worker

Date & Time:    12/02/2014 11:44:03
Event Class:    File System
Operation:  CreateFile
Result: ACCESS DENIED
Path:   D:\..\executable.exe
TID:    6244
Duration:   0.0000480
Desired Access: Execute/Traverse, Synchronize
Disposition:    Open
Options:    Synchronous IO Non-Alert, Non-Directory File
Attributes: n/a
ShareMode:  Read, Delete
AllocationSize: n/a
Impersonating:  Domain\Worker

一些技巧 :
如何模仿
C#中的模拟代码
模拟库(班级和班级)
WindowsIdentity.Impersonate方法

尝试使用此示例(在某处找到它):

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

[assembly:SecurityPermissionAttribute(SecurityAction.RequestMinimum,UnmanagedCode=true)]
[assembly:PermissionSetAttribute(SecurityAction.RequestMinimum, Name ="FullTrust")] 
public class ImpersonationDemo 
{

    [StructLayout(LayoutKind.Sequential)]
    public struct SECURITY_ATTRIBUTES
    {
        public int Length;
        public IntPtr lpSecurityDescriptor;
        public bool bInheritHandle;
    }

    public enum SECURITY_IMPERSONATION_LEVEL
    {
        SecurityAnonymous,
        SecurityIdentification,
        SecurityImpersonation,
        SecurityDelegation
    }

    public enum TOKEN_TYPE
    {
        TokenPrimary = 1,
        TokenImpersonation
    } 

    [DllImport("advapi32.dll", CharSet = CharSet.Auto, 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)]
    private unsafe static extern int FormatMessage(int dwFlags, ref IntPtr lpSource, int dwMessageId, int dwLanguageId, ref String lpBuffer, int nSize, IntPtr *Arguments);

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

    [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError=true)]
    public extern static bool DuplicateToken(IntPtr ExistingTokenHandle,  int SECURITY_IMPERSONATION_LEVEL, ref IntPtr DuplicateTokenHandle);

    [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public extern static bool DuplicateTokenEx( IntPtr hExistingToken, uint dwDesiredAccess, ref SECURITY_ATTRIBUTES lpTokenAttributes,
        SECURITY_IMPERSONATION_LEVEL ImpersonationLevel,
        TOKEN_TYPE TokenType,
        out IntPtr phNewToken);

    // GetErrorMessage formats and returns an error message
    // corresponding to the input errorCode.
    public unsafe static string GetErrorMessage(int errorCode)
    {
        int FORMAT_MESSAGE_ALLOCATE_BUFFER = 0x00000100;
        int FORMAT_MESSAGE_IGNORE_INSERTS = 0x00000200;
        int FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000;

        int messageSize = 255;
        String lpMsgBuf = "";
        int dwFlags = FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS;

        IntPtr ptrlpSource = IntPtr.Zero;
        IntPtr prtArguments = IntPtr.Zero;

        int retVal = FormatMessage(dwFlags, ref ptrlpSource, errorCode, 0, ref lpMsgBuf, messageSize, &prtArguments);
        if (0 == retVal)
        {
            throw new Exception("Failed to format message for error code " + errorCode + ". ");
        }

    return lpMsgBuf;
    }

    // Test harness.
    // If you incorporate this code into a DLL, be sure to demand FullTrust.
    [PermissionSetAttribute(SecurityAction.Demand, Name = "FullTrust")]
    public static void Main(string[] args)
    {
        IntPtr tokenHandle = new IntPtr(0);
        IntPtr dupeTokenHandle = new IntPtr(0);
        try
        {
            string UserName, MachineName;

            // Get the user token for the specified user, machine, and password using the
            // unmanaged LogonUser method.

            Console.Write("Enter the name of a machine on which to log on: ");
            MachineName = Console.ReadLine();

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

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

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

            tokenHandle = IntPtr.Zero;
            dupeTokenHandle = IntPtr.Zero;

            // Call LogonUser to obtain a handle to an access token.
            bool returnValue = LogonUser(UserName, MachineName, "mm4geata", 
                LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, ref tokenHandle);

            Console.WriteLine("LogonUser called.");

            if (false == returnValue)
            {
                int ret = Marshal.GetLastWin32Error();
                Console.WriteLine("LogonUser failed with error code : {0}", ret);
                Console.WriteLine("\nError: [{0}] {1}\n", ret, GetErrorMessage(ret));

                return;
            }

            Console.WriteLine("Did LogonUser Succeed? " + (returnValue? "Yes" : "No"));
            Console.WriteLine("Value of Windows NT token: " + tokenHandle);

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

            //bool retVal = DuplicateToken(tokenHandle, SecurityImpersonation, ref dupeTokenHandle);

            SECURITY_ATTRIBUTES sa = new SECURITY_ATTRIBUTES();
            sa.bInheritHandle = true;
            sa.Length = Marshal.SizeOf(sa);
            sa.lpSecurityDescriptor = (IntPtr)0;

            bool retVal = DuplicateTokenEx(tokenHandle, 0x10000000, ref sa, SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation, TOKEN_TYPE.TokenImpersonation, out dupeTokenHandle); 


            if (false == retVal)
            {
                CloseHandle(tokenHandle);
                Console.WriteLine("Exception thrown in trying to duplicate token.");
                return;
            }


            // The token that is passed to the following constructor must
            // be a primary token in order to use it for impersonation.
            WindowsIdentity newId = new WindowsIdentity(dupeTokenHandle);
            WindowsImpersonationContext impersonatedUser = newId.Impersonate();

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

            // Stop impersonating the user.
            impersonatedUser.Undo();

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

            // Free the tokens.
            if (tokenHandle != IntPtr.Zero)
            CloseHandle(tokenHandle);
            if (dupeTokenHandle != IntPtr.Zero)
            CloseHandle(dupeTokenHandle);
        }
        catch(Exception ex)
        {
            Console.WriteLine("Exception occurred. " + ex.Message);
        }

    }
}

我设法使流程从以下代码开始:

ProcessHelperhttp//pastie.org/private/dlkytj8rbigs8ixwtg

TokenImpersonationContexthttp : //pastie.org/private/nu3pvpghoea6pwwlvjuq

该服务调用StartAsUserFromService方法,并且该过程调用StartAsUserFromApplication方法以启动其后继程序。

我在LogonUser调用中使用LogonType.Batch ,因为该进程需要与另一个WCF服务进行对话并需要进行身份验证。 可以使用LogonType.NetworkLogonType.NetworkClearText ,但是在使用Worker用户帐户的Net.Tcp端口共享服务中引起了权限问题。

该答案很有帮助: 使用Process.Start()以Windows用户身份以其他用户身份启动进程

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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