在设置创建第二个PipeSecurity实例时,命名管道服务器会抛出UnauthorizedAccessException

时间:2010-08-13 15:06:59

标签: c# .net-3.5 named-pipes

我正在尝试编写一个(提升权限)服务,该服务将与非特权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或两个具有相同设置的实例,它仍会抛出异常并不重要。

7 个答案:

答案 0 :(得分:28)

有两件事可以导致第二次或后续的实例化 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服务中时,那么服务进程的Logon SID标识就更好了(但更难以实现) - 这将确保只有特定的服务进程才能创建管道实例。

如果要确保管道访问仅限于本地登录的用户,即阻止通过网络进行远程访问,您还可以将网络用户的拒绝ACE添加到管道安全ACL中。

答案 1 :(得分:15)

我明白了。

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应用程序以便彼此交谈(创建者所有者可能是不必要的)

答案 2 :(得分: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));

答案 3 :(得分:2)

“创建者所有者”可以替换为System.Security.Principal.WindowsIdentity.GetCurrent()。为任何用户命名并删除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);

答案 4 :(得分: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);

答案 5 :(得分:0)

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

答案 6 :(得分:-1)

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