我已经花了几天时间来解决这个问题,尽管网上有很多不同的例子,这是一个棘手的问题,我不能让他们在我的场景中工作。
我有一个在本地系统帐户下运行的Windows服务。它有一个监听API请求的WCF端点。当通过API告知时,该服务应该在系统会话(0)和“工作者”帐户凭证中启动新进程。该进程是一个检查队列中的工作并执行此操作的工作程序。如果找不到工作,它会睡一会儿并再次检查。如果确实找到了工作,它会在同一会话中启动一个新进程并使用相同的凭据并完成工作。完成工作后,它就会关闭。
“Worker”是域帐户,是计算机上本地管理员组的成员,对可执行文件具有执行权限。该计算机与帐户位于同一域中。
问题是,当服务尝试启动该过程时,它会从ERROR_ACCESS_DENIED (5)
方法获取CreateProcessAsUser
错误代码。
我尝试在具有相同凭据的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
答案 0 :(得分:1)
一些提示:
How to Impersonate
Impersonation code in C#
Impersonation Libraries (Class & Com Class)
WindowsIdentity.Impersonate Method
尝试使用此示例(在某处找到):
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); } } }
答案 1 :(得分:0)
我设法让这些代码从这段代码开始:
ProcessHelper :http://pastie.org/private/dlkytj8rbigs8ixwtg
TokenImpersonationContext :http://pastie.org/private/nu3pvpghoea6pwwlvjuq
服务调用StartAsUserFromService
方法,进程调用StartAsUserFromApplication
方法启动其后继方法。
我在LogonType.Batch
调用中使用LogonUser
,因为该进程需要与另一个WCF服务进行通信并需要进行身份验证。可以使用LogonType.Network
和LogonType.NetworkClearText
,但在Net.Tcp端口共享服务中使用Worker用户帐户导致权限问题。
这个答案很有帮助:Using Process.Start() to start a process as a different user from within a Windows Service