Named Pipe Server throws UnauthorizedAccessException when creating a second instance if PipeSecurity is set
Asked Answered
I

8

38

I am trying to write a (elevated privilege) service that will talk to a non privileged winforms application. I was able to have two console applications (one elevated one not) talk back and forth no problem but I am having a problem doing a service and a winforms app.

The first instance of the pipe works perfectly. However after my client connects and I try to create a new instance so it will be ready if a 2nd client connects, but the constructor for NamedPipeServerStream throws a 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: 

Example removed see example at the bottom for simpler test case

The first iteration works fine. it is when the client connects and client = new Client gets called a second time, which in turn calls Pipe = new NamedPipeServerStream and that will throw the exception.

Can anyone see what mistake I am making?


A little more information, Out of curiousity I went back to my console app. To test multiple instances I just ran the exe multiple times. When i put two new NamedPipeServerStream in the same executable I get the same error... So why is it ok if you have separate exe's acting as the server pointing to the same named pipe address but it is forbidden to do it inside the same 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))
    {

More Information:

If I don't set PipeSecurity it does not throw the exception, but when I set the security it will. It does not matter if i pass both instances the same PipeSecurity or two instances with the same settings it still throws the exception.

Imhoff answered 13/8, 2010 at 15:6 Comment(0)
E
41

There are two things which can cause the instantiation of a second or subsequent NamedPipeServerStream on the same pipe to fail:

  • the maxNumberOfServerInstances ctor argument must have been set to more than 1 when the first instance of the pipe server was created. If not, the second call will fail unless the first instance has already been closed completely.
  • the process calling the ctor must have the access right represented by PipeAccessRights.CreateNewInstance. This is a powerful right which the pipe server should guard jealously, as it allows its possessor the ability to act as a pipe server.

The service process should set the pipe security thus:

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

where:

  • myPipeUsersGroup is a placeholder for a group which contains all the prospective client identities which will connect to the pipe. Depending on your requirements/use case this might be a specific client identity, a custom group, or a built in group such as "Users" or "Administrators".
  • myPipeServerIdentity is a placeholder for the service identity. This might be set, for example, to WindowsIdentity.GetCurrent().Owner. When the pipe server is hosted in a Windows service, then even better (but a good deal harder to implement) would be the Logon SID identity of the service process - this would ensure that only the specific service process could create instances of the pipe.

If you want to ensure that pipe access is restricted to just users logged on locally i.e. to prevent remote access across a network, you can also add a deny ACE for Network Users into the pipe security ACL.

Emileemilee answered 6/12, 2010 at 22:21 Comment(4)
Hello, can you let me know why did you suggested to use WindowsIdentity.GetCurrent().Owner and not WindowsIdentity.GetCurrent().User? What are main differences and what should be used when named pipe server is hosted in WinForm application, WPF application, WinService? I read something about Owner, User in WindowsIdentity but I would like to check right usage of it with you. ThanksPlatinous
If you want the Logon Sid identity of the service process, that's because you (presumably) are running as the service process. Therefore you can just specify ps.AddAccessRule(new PipeAccessRule(System.Security.Principal.WindowsIdentity.GetCurrent().Name, PipeAccessRights.FullControl, AccesscontrolType.Allow));Orgeat
@erict: that isn't the same thing as the Logon SID of the service process, which is unique that that process, rather than to the security principal.Emileemilee
@ChrisDickson Just thank you kind stranger ;) I've got this error from a bug which caused my server pipe to be created twice. Knowing (thanks to you) there is a limit on instances and this exception is thrown when limit exceeded - i found and fixed the bug in 10 seconds.Superiority
I
23

I figured it out.

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

By adding the rights PipeAccessRights.CreateNewInstance it now works fine.


I hit another snag but i solved it, but wanted to post it in case other people found this through Google. by providing your own Pipe security object it removes the default one, so if you need it you need to re-add the System group so it can talk to the pipe if you are writing a service. I updated my above code to what I used to get a elevated service and a non elevated winforms app to talk to each other (Creator owner is likely unnecessary)

Imhoff answered 13/8, 2010 at 15:49 Comment(4)
Creator Owner is unnecessary. And granting PipeAccessRights.CreateNewInstance rights to Users is a bad idea: it enables any code running on the box to create the server side of an instance of this pipe: unlikely to be what you want and probably a security hole. It is only the pipe server which should have this right (it is what enables a server to create several pipe instances to talk to multiple clients concurrently).Emileemilee
@Chris Dickson What would you recommend as the security settings for the user account then?Imhoff
It says PipeSecurity cannot be foundMarigraph
I think the error about PipeSecurity cannot be found might be specific to Unity. However I figured out my issue was entirely unrelated. The creator of the pipe needed to specify that it allows read and writeMarigraph
E
3

"Creator Owner" can be replaced by System.Security.Principal.WindowsIdentity.GetCurrent().Name and delete PipeAccessRights.CreateNewInstance for any users.

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);
Eulogist answered 16/6, 2011 at 11:11 Comment(1)
in fact it should be ps.AddAccessRule(new PipeAccessRule(WindowsIdentity.GetCurrent().User, PipeAccessRights.FullControl, AccessControlType.Allow));Fork
K
3

The solution that works on localized versions of 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));
Keyser answered 19/6, 2012 at 15:27 Comment(1)
-1 for misinformation and perpetuation of poor practice. WellKnownSidType.CreatorOwner is not correct - it has no effect on usage of named pipes. And WellKnownSidType.BuiltinUsersSid should not be given CreateNewInstance unless you actively want anyone with access to the box to take over your pipe service. You are right, however, that the enumerations of WellKnownSidType should be used instead of the string constants suggested by some other responders.Emileemilee
K
3

I know there are many answers to this question, but i fixed a similar problem by setting the level of requestedExecutionLevel to requireAdministrator in the app.manifest file of the project where my namedpipe code resides. And this fixed the UnauthorizedAccessException for me

Knowledgeable answered 4/3, 2015 at 7:44 Comment(0)
R
1

For me, the server side is running as an Administrator user, and the client side is running as a normal user. So the solution is to simply reopen them using the same identity.

Republicanize answered 28/11, 2019 at 6:44 Comment(0)
R
1

For those using newer versions of C# with Dotnet core, NamedPipeServerStream = new NamedPipeServerStream() no longer takes an eighth argument for a PipeSecurity object like Chris and Scott's answers use. Instead, you will need to use NamedPipeServerStream = NamedPipeServerStreamAcl.Create(), like so:

NamedPipeServerStream pipeServer = NamedPipeServerStreamAcl.Create("MTGAPipe01", PipeDirection.InOut, 10, PipeTransmissionMode.Message, PipeOptions.Asynchronous,default,default,ps)
Roband answered 5/11, 2023 at 0:11 Comment(1)
After 2 days haunted by nightmares and headaches, finally I saw your answer and it solved my problem! Thanks so much :)Moue
R
0

This will make server to be accessible for everyone for read and write, It will also work on the OS with all languages, as the username is not hard coded

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);
Reseau answered 5/12, 2018 at 19:13 Comment(1)
Everyone is not WellKnownSidType.AuthenticatedUserSid. You would have to use WellKnownSidType.WorldSid instead. See msdn.microsoft.com/en-us/library/…Stanfill

© 2022 - 2024 — McMap. All rights reserved.