我有一个问题调用一个WCF服务通过net.pipe与Windows模拟从C#Windows服务。
背景
该服务从队列中读取并创build子应用程序域,每个域都运行一个特定的模块。 我们将Windows服务称为“JobQueueAgent”,每个模块称为“作业”。 我将继续使用这些条款。 作业可以configuration为以指定的用户身份运行。 我们在作业的应用程序域内使用模拟来实现这一点。 以下是服务中的逻辑和凭证stream程:
JobQueueAgent(Windows服务 – 主用户)>>创build作业域>>作业域(应用程序域)>>模拟子用户>>在模拟线程上运行作业>>作业(模块 – 子用户)>>作业逻辑
“主要用户”和“子用户”都是具有“作为服务login”权限的域帐户。
该服务在运行Windows Server 2012 R2的虚拟服务器上运行。
以下是我正在使用的C#模拟代码:
namespace JobQueue.WindowsServices { using System; using System.ComponentModel; using System.Net; using System.Runtime.InteropServices; using System.Security.Authentication; using System.Security.Permissions; using System.Security.Principal; internal sealed class ImpersonatedIdentity : IDisposable { [PermissionSetAttribute(SecurityAction.Demand, Name = "FullTrust")] public ImpersonatedIdentity(NetworkCredential credential) { if (credential == null) throw new ArgumentNullException("credential"); if (LogonUser(credential.UserName, credential.Domain, credential.Password, 5, 0, out _handle)) { _context = WindowsIdentity.Impersonate(_handle); } else { throw new AuthenticationException("Impersonation failed.", newWin32Exception(Marshal.GetLastWin32Error())); } } ~ImpersonatedIdentity() { Dispose(); } public void Dispose() { if (_handle != IntPtr.Zero) { CloseHandle(_handle); _handle = IntPtr.Zero; } if (_context != null) { _context.Undo(); _context.Dispose(); _context = null; } GC.SuppressFinalize(this); } [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool LogonUser(string userName, string domain, string password, int logonType,int logonProvider, out IntPtr handle); [DllImport("kernel32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool CloseHandle(IntPtr handle); private IntPtr _handle = IntPtr.Zero; private WindowsImpersonationContext _context; } }
问题
一些工作需要net.pipe WCF服务调用服务器上运行的另一个Windows服务。 在模拟下运行时,net.pipe调用失败。
这里是我在这种情况下得到的例外:
未处理的exception:System.ComponentModel.Win32Exception:访问被拒绝
服务器堆栈跟踪:位于System.ServiceModel.Channels.AppContainerInfo.GetCurrentProcessToken()位于System.ServiceModel.Channels.AppContainerInfo.RunningInAppContainer()位于System.ServiceModel.Channels.AppContainerInfo.get_IsRunningInAppContainer()位于System.ServiceModel.Channels.PipeSharedMemory.BuildPipeName (String pipeGuid)
当不在模拟下运行时,net.pipe成功。 当模拟用户添加到pipe理员组时,net.pipe调用也会成功。 这意味着用户需要在模拟下拨打电话时有一些特权。 我们还没有能够确定用户需要什么样的政策,特权或访问来模拟net.pipe调用。 使用户成为pipe理员是不可接受的。
这是一个已知的问题? 用户需要成功的特殊权利吗? 有没有我能解决这个问题的代码更改? 在使用impersonate = true的网站中使用WCF的net.pipe似乎表明,由于NetworkService的原因,这在ASP.NET应用程序中不起作用。 不确定,但不应该在这里适用。
在Microsoft支持的帮助下,我能够通过修改线程标识的访问权限(在另一个答案中由Harry Johnston提出的建议)来解决这个问题。 这是我现在使用的模拟代码:
using System; using System.ComponentModel; using System.Net; using System.Runtime.InteropServices; using System.Security.AccessControl; using System.Security.Authentication; using System.Security.Permissions; using System.Security.Principal; internal sealed class ImpersonatedIdentity : IDisposable { [PermissionSet(SecurityAction.Demand, Name = "FullTrust")] public ImpersonatedIdentity(NetworkCredential credential) { if (credential == null) throw new ArgumentNullException(nameof(credential)); _processIdentity = WindowsIdentity.GetCurrent(); var tokenSecurity = new TokenSecurity(new SafeTokenHandleRef(_processIdentity.Token), AccessControlSections.Access); if (!LogonUser(credential.UserName, credential.Domain, credential.Password, 5, 0, out _token)) { throw new AuthenticationException("Impersonation failed.", new Win32Exception(Marshal.GetLastWin32Error())); } _threadIdentity = new WindowsIdentity(_token); tokenSecurity.AddAccessRule(new AccessRule<TokenRights>(_threadIdentity.User, TokenRights.TOKEN_QUERY, InheritanceFlags.None, PropagationFlags.None, AccessControlType.Allow)); tokenSecurity.ApplyChanges(); _context = _threadIdentity.Impersonate(); } ~ImpersonatedIdentity() { Dispose(); } public void Dispose() { if (_processIdentity != null) { _processIdentity.Dispose(); _processIdentity = null; } if (_token != IntPtr.Zero) { CloseHandle(_token); _token = IntPtr.Zero; } if (_context != null) { _context.Undo(); _context.Dispose(); _context = null; } GC.SuppressFinalize(this); } [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool LogonUser(string userName, string domain, string password, int logonType, int logonProvider, out IntPtr handle); [DllImport("kernel32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool CloseHandle(IntPtr handle); private WindowsIdentity _processIdentity; private WindowsIdentity _threadIdentity; private IntPtr _token = IntPtr.Zero; private WindowsImpersonationContext _context; [Flags] private enum TokenRights { TOKEN_QUERY = 8 } private class TokenSecurity : ObjectSecurity<TokenRights> { public TokenSecurity(SafeHandle safeHandle, AccessControlSections includeSections) : base(false, ResourceType.coreelObject, safeHandle, includeSections) { _safeHandle = safeHandle; } public void ApplyChanges() { Persist(_safeHandle); } private readonly SafeHandle _safeHandle; } private class SafeTokenHandleRef : SafeHandle { public SafeTokenHandleRef(IntPtr handle) : base(IntPtr.Zero, false) { SetHandle(handle); } public override bool IsInvalid { get { return handle == IntPtr.Zero || handle == new IntPtr(-1); } } protected override bool ReleaseHandle() { throw new NotImplementedException(); } } }
啊,这是问题:
服务器堆栈跟踪:在System.ServiceModel.Channels.AppContainerInfo.GetCurrentProcessToken()
当您尝试打开管道时,系统正在检查您是否在应用程序容器中。 这涉及查询您正在模拟的用户没有权限执行的流程令牌。
这对我来说似乎是一个错误。 您可以尝试向Microsoft开放付费支持案例,但不能保证他们愿意发布修补程序,或者他们将能够尽快解决问题,以满足您的需求。
所以我看到两个似是而非的解决方法:
在模拟之前,更改进程访问令牌上的ACL以授予对新登录令牌的TOKEN_QUERY
访问权限。 我相信登录标记将包含一个登录SID,所以这将是最安全的选择,但它不应该是太危险,而是授予访问该用户帐户。 据我所知, TOKEN_QUERY
访问权限不会显示任何特别敏感的信息。
您可以在子用户的上下文中启动子进程,而不是使用模拟。 效率较低,不太方便,但这将是一个解决问题的简单方法。
看起来像没有提升(管理员)权限是不可能的。
https://social.msdn.microsoft.com/Forums/vstudio/en-US/6a26497f-0346-4929-ad42-ff4459be60e4/error-while-attempting-to-call-netpipe-wcf-service-while-impersonating?论坛= WCF#6a26497f-0346-4929-AD42-ff4459be60e4
尽管这里描述的双重合约可能是关键。
非管理员用户的客户端不能使用net.pipe与服务进行通信