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

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

管道的第一个实例完美无缺。 然而,在我的客户端连接之后,我尝试创建一个新实例,以便在第二个客户端连接时准备就绪,但NamedPipeServerStream的构造函数会引发exception

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并且将抛出exception。

谁能看到我犯的是什么错误?


更多信息,出于好奇,我回到了我的控制台应用程序。 为了测试多个实例,我只是多次运行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它不会抛出exception,但是当我设置安全性时它会。 如果我将两个实例传递给相同的PipeSecurity或两个具有相同设置的实例,它仍会抛出exception并不重要。

有两件事可能导致同一管道上的第二个或后续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标识就更好了(但更难以实现) – 这将确保只有特定的服务进程才能创建管道实例。

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

我想到了。

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

可以用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); 

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