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.
There are two things which can cause the instantiation of a second or subsequent NamedPipeServerStream on the same pipe to fail:
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.