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

问题描述 投票:21回答:7

我正在尝试编写将与非特权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。当我在同一个可执行文件中放置两个new NamedPipeServerStream时,我会收到相同的错误...那么,如果您有单独的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或两个具有相同设置的实例都没关系,它仍然会引发异常。

c# .net-3.5 named-pipes
7个回答
31
投票

有两件事可能导致秒或后续实例化同一管道上的NamedPipeServerStream失败:

  • maxNumberOfServerInstances ctor参数在创建管道服务器的第一个实例时必须已设置为大于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标识会更好(但很难实施),这将确保只有特定的服务流程才能创建管道的实例。

如果要确保仅对本地登录的用户限制管道访问,即为了防止跨网络的远程访问,也可以在管道安全ACL中添加拒绝网络用户的ACE。


16
投票

我知道了。

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,现在可以正常使用。


我遇到了另一个障碍,但我解决了,但想将其发布,以防其他人通过Google找到。通过提供自己的Pipe安全对象,它将删除默认对象,因此,如果需要,您需要重新添加System组,以便在编写服务时可以与管道进行通信。我将上面的代码更新为我以前获得高级服务和非高级winforms应用程序以进行相互交流的方式(创建者所有者可能是不必要的)


3
投票

适用于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));

2
投票

“ Creator Owner”可以替换为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);

1
投票

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


0
投票

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

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);

0
投票

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

© www.soinside.com 2019 - 2024. All rights reserved.