[英]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上运行时会得到该错误代码。
该代码太大,无法在此处显示,因此我将其放在其他位置...
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#中的模拟代码
模拟库(班级和班级)
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); } } }
我设法使流程从以下代码开始:
ProcessHelper : http : //pastie.org/private/dlkytj8rbigs8ixwtg
TokenImpersonationContext : http : //pastie.org/private/nu3pvpghoea6pwwlvjuq
该服务调用StartAsUserFromService
方法,并且该过程调用StartAsUserFromApplication
方法以启动其后继程序。
我在LogonUser
调用中使用LogonType.Batch
,因为该进程需要与另一个WCF服务进行对话并需要进行身份验证。 可以使用LogonType.Network
和LogonType.NetworkClearText
,但是在使用Worker用户帐户的Net.Tcp端口共享服务中引起了权限问题。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.