.net 如何在Windows服务中模拟时调用net.pipe(命名管道)WCF服务
从C#Windows服务使用Windows模拟通过net.pipe调用WCF服务时遇到问题 背景 该服务从队列读取数据并创建子应用程序域,每个子应用程序域针对从队列中提取的项目运行特定模块。我们将Windows服务称为“JobQueueAgent”,每个模块称为“Job”。我将继续使用这些术语。作业可以配置为作为指定用户运行。我们在作业的应用程序域中使用模拟来实现这一点。 以下是服务中的逻辑和凭据流: JobQueueAgent(Windows服务-主要用户)>>创建作业域>> 作业域(应用程序域)>>模拟子用户>> 使用模拟在线程上运行作业>> 作业(模块-子用户)>>作业逻辑 “主用户”和“子用户”都是具有“作为服务登录”权限的域帐户 该服务在运行Windows server 2012 R2的虚拟服务器上运行 以下是我正在使用的C#模拟代码:.net 如何在Windows服务中模拟时调用net.pipe(命名管道)WCF服务,.net,windows,wcf,named-pipes,impersonation,.net,Windows,Wcf,Named Pipes,Impersonation,从C#Windows服务使用Windows模拟通过net.pipe调用WCF服务时遇到问题 背景 该服务从队列读取数据并创建子应用程序域,每个子应用程序域针对从队列中提取的项目运行特定模块。我们将Windows服务称为“JobQueueAgent”,每个模块称为“Job”。我将继续使用这些术语。作业可以配置为作为指定用户运行。我们在作业的应用程序域中使用模拟来实现这一点。 以下是服务中的逻辑和凭据流: JobQueueAgent(Windows服务-主要用户)>>创建作业域>> 作业域(应用程序
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;
}
}
问题
对服务器上运行的另一个Windows服务进行net.pipe WCF服务调用需要某些作业。在模拟下运行时,net.pipe调用失败
以下是我在这种情况下得到的例外情况:
未处理的异常:System.ComponentModel.Win32Exception:无法访问
否认
服务器堆栈跟踪:在
System.ServiceModel.Channel.AppContainerInfo.GetCurrentProcessToken()
在
System.ServiceModel.Channels.AppContainerInfo.RunningInAppContainer()
在
System.ServiceModel.Channel.AppContainerInfo.get\u IsRunningAppContainer()
位于System.ServiceModel.Channels.PipeSharedMemory.BuildPipeName(字符串
管道(GUID)
net.pipe在未在模拟下运行时成功。将模拟用户添加到Administrators组时,net.pipe调用也会成功。这意味着在模拟过程中,用户需要一些特权才能进行调用。我们无法确定用户在模拟时进行net.pipe调用所需的策略、权限或访问权限。将用户设置为管理员是不可接受的
这是一个已知的问题吗?用户是否有获得成功所需的特定权利?我是否可以更改代码来解决此问题?似乎表明由于NetworkService的原因,这在ASP.NET应用程序中不起作用。不确定,但这不适用于此处。看起来,如果没有提升的(管理员)权限,这是不可能的 尽管如此,这里描述的双工合同可能持有密钥
啊,问题是: 服务器堆栈跟踪:在 System.ServiceModel.Channel.AppContainerInfo.GetCurrentProcessToken() 当您尝试打开管道时,系统将检查您是否在应用程序容器中。这涉及到查询流程令牌,而您正在模拟的用户没有执行此操作的权限 这对我来说似乎是一个错误。您可以尝试与Microsoft展开付费支持案例,但不能保证他们愿意发布修补程序,也不能保证他们能够尽快解决问题以满足您的需要 因此,我看到了两种可行的解决办法:
- 在模拟之前,将进程访问令牌上的ACL更改为授予
对新登录令牌的访问权。我相信登录令牌将包含一个登录SID,因此这将是最安全的选择,但授予用户帐户访问权限应该不会太危险。据我所知,token\u QUERY
访问权限未显示任何特别敏感的信息TOKEN\u查询
- 您可以在子用户的上下文中启动子进程,而不是使用模拟。效率较低,也不太方便,但这将是解决问题的简单方法
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.KernelObject, 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();
}
}
}
使用系统;
使用系统组件模型;
Net系统;
使用System.Runtime.InteropServices;
使用System.Security.AccessControl;
使用System.Security.Authentication;
使用System.Security.Permissions;
使用System.Security.Principal;
内部密封类ImpersonatedIdentity:IDisposable
{
[权限集(SecurityAction.Demand,Name=“FullTrust”)]
公共模拟身份(网络凭据)
{
if(credential==null)抛出新的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\u令牌))
{
抛出新的AuthenticationException(“模拟失败”),新的Win32Exception(Marshal.GetLastWin32Error());
}
_threadIdentity=新的WindowsIdentity(_令牌);
tokenSecurity.AddAccessRule(新的AccessRule(_-threadIdentity.User,TokenRights.TOKEN_查询,InheritanceFlags.None,PropagationFlags.None,AccessControlType.Allow));
tokenSecurity.ApplyChanges();
_上下文=_threadIdentity.Impersonate();
}
~ImpersonatedIdentity()
{
处置();
}
公共空间处置()
{
if(_processIdentity!=null)
{
_processIdentity.Dispose();
_processIdentity=null;
}
if(_token!=IntPtr.Zero)