从Windows服务的特定凭据启动进程
我已经花了几天时间来解决这个问题,尽管网上有很多不同的例子,这是一个棘手的问题,我不能让他们在我的场景中工作。
我有一个在本地系统帐户下运行的Windows服务。 它有一个WCF端点侦听API请求。 当通过API告知时,该服务应该在系统会话(0)和“工作者”帐户凭证中启动新进程。 该进程是一个检查队列中的工作并执行此操作的工作程序。 如果找不到工作,它会睡一会儿并再次检查。 如果确实找到了工作,它会在同一会话中启动一个新进程并使用相同的凭据并完成工作。 完成工作后关闭。
“Worker”是域帐户,是计算机上本地管理员组的成员,对可执行文件具有执行权限。 该计算机与该帐户位于同一域中。
问题是,当服务尝试启动进程时,它会从CreateProcessAsUser
方法获取ERROR_ACCESS_DENIED (5)
错误代码。
我尝试在具有相同凭据的Windows 7计算机上运行相同的代码并且工作正常,但在Windows Server 2008上运行时会获得该错误代码。
代码太大了,不能在这里显示,所以我把它放在其他地方……
ProcessHelper : http : //pastie.org/private/y7idu3nw4xv1fxzeizbn9g
该服务调用StartAsUserFromService
方法来启动进程,该进程在建立会话后在内部调用CreateProcessAsUser
。 该进程调用StartAsUserFromApplication
方法来启动其后继,后者在内部调用CreateProcessWithLogonW
。
ImpersonationContext : http : //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#中的模拟代码
模拟图书馆(Class&Com Class)
WindowsIdentity.Impersonate方法
尝试使用此示例(在某处找到):
使用系统; 使用System.Runtime.InteropServices; 使用System.Security.Principal; 使用System.Security.Permissions; [组件:SecurityPermissionAttribute(SecurityAction.RequestMinimum,UnmanagedCode =真)] [assembly:PermissionSetAttribute(SecurityAction.RequestMinimum,Name =“FullTrust”)] 公共类ImpersonationDemo { [StructLayout(LayoutKind.Sequential)] public struct SECURITY_ATTRIBUTES { public int Length; public IntPtr lpSecurityDescriptor; public bool bInheritHandle; } 公共枚举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, IntPtr phNewToken); // GetErrorMessage格式并返回错误消息 //对应于输入的errorCode。 public unsafe静态字符串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) { 抛出新的exception(“无法格式化错误代码的消息”+ errorCode +“。”); } return lpMsgBuf; } //测试工具 //如果将此代码合并到DLL中,请务必要求FullTrust。 [PermissionSetAttribute(SecurityAction.Demand,Name =“FullTrust”)] public static void Main(string [] args) { IntPtr tokenHandle = new IntPtr(0); IntPtr dupeTokenHandle = new IntPtr(0); 尝试 { string UserName,MachineName; //使用获取指定用户,计算机和密码的用户令牌 //非托管的LogonUser方法。 Console.Write(“输入要登录的计算机的名称:”); MachineName = Console.ReadLine(); Console.Write(“输入您要模拟的{0}上的用户的登录名:”,MachineName); UserName = Console.ReadLine(); Console.Write(“输入{0}的密码:”,UserName); const int LOGON32_PROVIDER_DEFAULT = 3; //此参数使LogonUser创建主令牌。 const int LOGON32_LOGON_INTERACTIVE = 8; tokenHandle = IntPtr.Zero; dupeTokenHandle = IntPtr.Zero; //调用LogonUser以获取访问令牌的句柄。 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失败,错误代码:{0}”,ret); Console.WriteLine(“\ n错误:[{0}] {1} \ n”,ret,GetErrorMessage(ret)); 返回; } Console.WriteLine(“LogonUser成功了吗?”+(returnValue?“是”:“否”)); Console.WriteLine(“Windows NT令牌的值:”+ tokenHandle); //检查身份 Console.WriteLine(“冒充之前:”+ 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。”); 返回; } //传递给以下构造函数的标记必须 //是主要令牌,以便将其用于模拟。 WindowsIdentity newId =新的WindowsIdentity(dupeTokenHandle); WindowsImpersonationContext impersonatedUser = newId.Impersonate(); //检查身份 Console.WriteLine(“冒充后:”+ WindowsIdentity.GetCurrent()。Name); //停止冒充用户 impersonatedUser.Undo(); //检查身份 Console.WriteLine(“撤消后:”+ WindowsIdentity.GetCurrent()。Name); //释放令牌 if(tokenHandle!= IntPtr.Zero) CloseHandle的(tokenHandle); if(dupeTokenHandle!= IntPtr.Zero) CloseHandle的(dupeTokenHandle); } catch(Exception ex) { Console.WriteLine(“exception发生。”+ ex.Message); } } }
我设法让这些代码从这个代码开始:
ProcessHelper : http : //pastie.org/private/dlkytj8rbigs8ixwtg
TokenImpersonationContext : http : //pastie.org/private/nu3pvpghoea6pwwlvjuq
该服务调用StartAsUserFromService
方法,该进程调用StartAsUserFromApplication
方法以启动其后继方法。
我在LogonUser
调用中使用LogonType.Batch
,因为进程需要与另一个WCF服务通信并需要进行身份validation。 可以使用LogonType.Network
和LogonType.NetworkClearText
,但在Net.Tcp端口共享服务中使用Worker用户帐户导致权限问题。
这个答案很有用: 使用Process.Start()以Windows服务中的不同用户身份启动进程