C# 如果设置了PipeSecurity,则命名管道服务器在创建第二个实例时引发UnauthorizedAccessException

C# 如果设置了PipeSecurity,则命名管道服务器在创建第二个实例时引发UnauthorizedAccessException,c#,.net-3.5,named-pipes,C#,.net 3.5,Named Pipes,我正在尝试编写一个(提升权限)服务,该服务将与非特权winforms应用程序进行通信。我能够有两个控制台应用程序(一个不是)来回对话没有问题,但我在做一个服务和一个winforms应用程序时遇到了问题 管道的第一个实例运行良好。但是,在我的客户机连接之后,我尝试创建一个新实例,以便在第二个客户机连接时准备就绪,但是NamedPipeServerStream的构造函数抛出一个异常 System.UnauthorizedAccessException was unhandled Message=

我正在尝试编写一个(提升权限)服务,该服务将与非特权winforms应用程序进行通信。我能够有两个控制台应用程序(一个不是)来回对话没有问题,但我在做一个服务和一个winforms应用程序时遇到了问题

管道的第一个实例运行良好。但是,在我的客户机连接之后,我尝试创建一个新实例,以便在第二个客户机连接时准备就绪,但是NamedPipeServerStream的构造函数抛出一个异常

System.UnauthorizedAccessException was unhandled
  Message=Access to the path is denied.
  Source=System.Core
  StackTrace:
       at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
       at System.IO.Pipes.NamedPipeServerStream.Create(String fullPipeName, PipeDirection direction, Int32 maxNumberOfServerInstances, PipeTransmissionMode transmissionMode, PipeOptions options, Int32 inBufferSize, Int32 outBufferSize, PipeAccessRights rights, SECURITY_ATTRIBUTES secAttrs)
       at System.IO.Pipes.NamedPipeServerStream..ctor(String pipeName, PipeDirection direction, Int32 maxNumberOfServerInstances, PipeTransmissionMode transmissionMode, PipeOptions options, Int32 inBufferSize, Int32 outBufferSize, PipeSecurity pipeSecurity, HandleInheritability inheritability, PipeAccessRights additionalAccessRights)
       at System.IO.Pipes.NamedPipeServerStream..ctor(String pipeName, PipeDirection direction, Int32 maxNumberOfServerInstances, PipeTransmissionMode transmissionMode, PipeOptions options, Int32 inBufferSize, Int32 outBufferSize, PipeSecurity pipeSecurity)
       at PipeServer.Server.Client..ctor(String pipeName, List`1 container) in E:\Visual Studio 2010\Projects\Sandbox Service\PipeServer.cs:line 27
       at PipeServer.Server.ListenForClients() in E:\Visual Studio 2010\Projects\Sandbox Service\PipeServer.cs:line 148
       at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
       at System.Threading.ThreadHelper.ThreadStart()
  InnerException: 
示例已删除请参见底部的示例以了解更简单的测试用例

第一次迭代运行良好。当客户端连接并且第二次调用
client=new client
时,它会调用
Pipe=new NamedPipeServerStream
,并引发异常

有人能看出我犯了什么错误吗


还有一点信息,出于好奇,我回到了我的控制台应用程序。要测试多个实例,我只需多次运行exe。当我在同一个可执行文件中放入两个新名称PipeServerStream时,我得到了相同的错误。。。那么,如果您有单独的exe作为指向同一命名管道地址的服务器,但禁止在同一个exe中执行,那么为什么可以这样做呢

static void Main()
{
    PipeAccessRule pr = new PipeAccessRule("Users", PipeAccessRights.ReadWrite, System.Security.AccessControl.AccessControlType.Allow);
    PipeSecurity ps = new PipeSecurity();
    ps.AddAccessRule(pr);
    using (NamedPipeServerStream pipeServer =
        new NamedPipeServerStream("testpipe",PipeDirection.InOut,10, 
                                    PipeTransmissionMode.Message, 
                                    PipeOptions.WriteThrough,4028,4028,ps))
    using (NamedPipeServerStream pipeServer2 = //v-- Throws the execption, but if you comment this out and run the EXE twice it works fine. creating a new instance of ps and pr does not change anything.
        new NamedPipeServerStream("testpipe", PipeDirection.InOut, 10, 
                                    PipeTransmissionMode.Message,
                                    PipeOptions.WriteThrough, 4028, 4028, ps))
    {

更多信息:

如果我没有设置
PipeSecurity
它不会抛出异常,但是当我设置安全性时,它会抛出异常。不管我是否传递两个实例相同的
PipeSecurity
,或者两个具有相同设置的实例,它仍然会抛出异常。

我找到了它

static void Main()
{
    PipeSecurity ps = new PipeSecurity();
    ps.AddAccessRule(new PipeAccessRule("Users", PipeAccessRights.ReadWrite | PipeAccessRights.CreateNewInstance, AccessControlType.Allow));
    ps.AddAccessRule(new PipeAccessRule("CREATOR OWNER", PipeAccessRights.FullControl, AccessControlType.Allow));
    ps.AddAccessRule(new PipeAccessRule("SYSTEM", PipeAccessRights.FullControl, AccessControlType.Allow));
    ps.AddAccessRule(pa);
    using (NamedPipeServerStream pipeServer =
        new NamedPipeServerStream("testpipe",PipeDirection.InOut,10, 
                                    PipeTransmissionMode.Message, PipeOptions.WriteThrough, 1024,1024,ps))
    using (NamedPipeServerStream pipeServer2 =
        new NamedPipeServerStream("testpipe", PipeDirection.InOut, 10,
                                    PipeTransmissionMode.Message, PipeOptions.WriteThrough,1024,1024,ps))
    {
通过添加权限
PipeAccessRights.CreateNewInstance
它现在可以正常工作



我遇到了另一个障碍,但我解决了它,但我想发布它,以防其他人通过谷歌发现这一点。通过提供您自己的管道安全对象,它将删除默认对象,因此,如果您需要它,您需要重新添加系统组,以便在您编写服务时它可以与管道通信。我将上述代码更新为我过去用来获得提升服务和非提升winforms应用程序的代码,以便彼此对话(可能不需要创建者-所有者)

有两种情况会导致第二次或后续应用程序的实例化 同一管道上的NamedPipeServerStream将失败:

  • 创建管道服务器的第一个实例时,maxNumberOfServerInstances参数必须设置为大于1。否则,第二次调用将失败,除非第一个实例已完全关闭
  • 调用ctor的进程必须具有由PipeAccessRights.CreateNewInstance表示的访问权限。这是一项强大的权利,管道服务器应该谨慎地加以保护,因为它允许其所有者充当管道服务器
服务流程应设置管道安全性,因此:

PipeSecurity ps = new PipeSecurity(); 
    ps.AddAccessRule(new PipeAccessRule(myPipeUsersGroup, PipeAccessRights.ReadWrite, AccessControlType.Allow)); 
    ps.AddAccessRule(new PipeAccessRule(myPipeServerIdentity, PipeAccessRights.FullControl, AccessControlType.Allow)); 
其中:

  • myPipeUsersGroup
    是一个组的占位符,该组包含将连接到管道的所有潜在客户身份。根据您的需求/用例,这可能是特定的客户端标识、自定义组或内置组,如“用户”或“管理员”
  • myPipeServerIdentity
    是服务标识的占位符。例如,可以将其设置为
    WindowsIdentity.GetCurrent().Owner
    。当管道服务器托管在Windows服务中时,更好的(但更难实现)是服务进程的登录SID标识—这将确保只有特定的服务进程才能创建管道实例
如果要确保管道访问仅限于本地登录的用户,即防止通过网络进行远程访问,还可以将网络用户的拒绝ACE添加到管道安全ACL中。

“创建者所有者”可以替换为System.security.Principal.WindowsIdentity.GetCurrent().Name并删除任何用户的PipeAccessRights.CreateNewInstance

static void Main()
{
    PipeSecurity ps = new PipeSecurity();
    ps.AddAccessRule(new PipeAccessRule("Users", PipeAccessRights.ReadWrite, AccessControlType.Allow));
    ps.AddAccessRule(new PipeAccessRule(System.Security.Principal.WindowsIdentity.GetCurrent().Name, PipeAccessRights.FullControl, AccessControlType.Allow));
    ps.AddAccessRule(new PipeAccessRule("SYSTEM", PipeAccessRights.FullControl, AccessControlType.Allow));
    ps.AddAccessRule(pa);

适用于本地化版本Windows的解决方案:

pipeSecurity.AddAccessRule(new PipeAccessRule(new SecurityIdentifier(WellKnownSidType.BuiltinUsersSid, null),
  accessRights, PipeAccessRights.ReadWrite | PipeAccessRights.CreateNewInstance, AccessControlType.Allow));
pipeSecurity.AddAccessRule(new PipeAccessRule(new SecurityIdentifier(WellKnownSidType.CreatorOwnerSid, null),
  PipeAccessRights.FullControl, AccessControlType.Allow));
pipeSecurity.AddAccessRule(new PipeAccessRule(new SecurityIdentifier(WellKnownSidType.LocalSystemSid, null),
  PipeAccessRights.FullControl, AccessControlType.Allow));

我知道这个问题有很多答案,但我通过在我的namedpipe代码所在的项目的app.manifest文件中将requestedExecutionLevel设置为requireAdministrator来修复了一个类似的问题。
这为我修复了UnauthorizedAccessException

这将使每个人都可以访问服务器进行读写操作, 它也可以在所有语言的操作系统上工作,因为用户名不是硬编码的

public NamedPipeServerInstance(string pipeName, int maxNumberOfServerInstances)
{
    var ps = new PipeSecurity();
    var sid = new SecurityIdentifier(WellKnownSidType.AuthenticatedUserSid, null);
    var everyone = sid.Translate(typeof(NTAccount));
    ps.SetAccessRule(new PipeAccessRule(everyone,
        PipeAccessRights.ReadWrite, AccessControlType.Allow));

    server = new NamedPipeServerStream(pipeName,
        PipeDirection.InOut,
        maxNumberOfServerInstances,
        PipeTransmissionMode.Message,
        PipeOptions.Asynchronous,
        4028, 4028, ps);

    var asyncResult = server.BeginWaitForConnection(OnConnected, null);
}

client = new NamedPipeClientStream(".", pipeName, PipeDirection.Out, PipeOptions.Asynchronous);

对我来说,服务器端作为管理员用户运行,客户端作为普通用户运行。因此,解决方案是使用相同的身份重新打开它们。

不需要创建者所有者。向用户授予PipeAccessRights.CreateNewInstance权限是一个坏主意:它允许在框中运行的任何代码创建此管道实例的服务器端:不太可能是您想要的,可能是一个安全漏洞。只有管道服务器才应该拥有此权限(它使服务器能够创建多个管道实例以同时与多个客户端通信)。@Chris Dickson那么您建议用户帐户的安全设置是什么?事实上,它应该是ps.AddAccessRule(新的管道访问规则(WindowsIdentity.GetCurrent().User,PipeAccessRights.FullControl,AccessControlType.Allow))-1对于错误信息和不良实践的长期存在。WellKnownSidType.CreatorOwner不正确-它对命名管道的使用没有影响。不应向WellKnownSidType.BuiltinUsersSid提供CreateNewInstance,除非您主动希望有权访问该框的任何人接管管道服务。但是,您是对的,应该使用WellKnownSidType的枚举,而不是其他响应者建议的字符串常量。您好,您能告诉我您为什么建议