.net 如何在Windows服务中模拟时调用net.pipe(命名管道)WCF服务

.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服务-主要用户)>>创建作业域>> 作业域(应用程序

从C#Windows服务使用Windows模拟通过net.pipe调用WCF服务时遇到问题

背景

该服务从队列读取数据并创建子应用程序域,每个子应用程序域针对从队列中提取的项目运行特定模块。我们将Windows服务称为“JobQueueAgent”,每个模块称为“Job”。我将继续使用这些术语。作业可以配置为作为指定用户运行。我们在作业的应用程序域中使用模拟来实现这一点。 以下是服务中的逻辑和凭据流:

JobQueueAgent(Windows服务-主要用户)>>创建作业域>> 作业域(应用程序域)>>模拟子用户>> 使用模拟在线程上运行作业>> 作业(模块-子用户)>>作业逻辑

“主用户”和“子用户”都是具有“作为服务登录”权限的域帐户

该服务在运行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;
    }
}
问题

对服务器上运行的另一个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更改为授予
    token\u QUERY
    对新登录令牌的访问权。我相信登录令牌将包含一个登录SID,因此这将是最安全的选择,但授予用户帐户访问权限应该不会太危险。据我所知,
    TOKEN\u查询
    访问权限未显示任何特别敏感的信息

  • 您可以在子用户的上下文中启动子进程,而不是使用模拟。效率较低,也不太方便,但这将是解决问题的简单方法


在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.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)