Opening a named pipe in low integrity level
<p>I'm working on an application which is made of two modules. These modules communicate through named pipes in the following environment:  </p>

<ul>
<li>Windows 7 Home Premium x64</li>
<li>Visual Studio 2008</li>
<li>C# / .Net 3.5</li>
</ul>

<p>The server runs with administrator rights (high integrity level). The client runs in low integrity level. So that the client can connect to the server, I need to create the pipe in low integrity level. I manage to do this only when the server runs in medium integrity level.</p>

<p>I tested the following setups :</p>

<ol>
<li>server : high, client : low => access refused</li>
<li>server : high, client : medium => access refused</li>
<li>server : high, client : high => OK</li>
<li>server : medium, client : low => OK</li>
<li>server : medium, client : medium => OK</li>
<li>server : low, client : low => OK</li>
</ol>

<p>Setup #4 shows that the named pipe gets created with different integrity level than the one of the process, which is good. However, the setup I am interested in is the first one.</p>

<p>I have a sample which makes it easy to test. If the connection is successful, the clients writes "Connected" and the server writes "Received connection". If the connection fails, the client writes "Failed" and the server stays on "Waiting".</p>

<p>Here is how I execute the client program (for the server, simply replace NamePipeClient with NamedPipeServer):</p>

<ul>
<li>medium integrity level:
<ul>
<li>open the command prompt</li>
<li>> icacls NamedPipeClient.exe /setintegritylevel Medium</li>
<li>> NamedPipeClient.exe</li>
</ul></li>
<li>low integrity level:
<ul>
<li>open the command prompt</li>
<li>> icacls NamedPipeClient.exe /setintegritylevel Low</li>
<li>> NamedPipeClient.exe</li>
</ul></li>
<li>high integrity level:
<ul>
<li>open the command prompt in admin mode</li>
<li>> icacls NamedPipeClient.exe /setintegritylevel High</li>
<li>> NamedPipeClient.exe</li>
</ul></li>
</ul>

<p>Any help will be greatly appreciated!</p>

<h2>Server code</h2>

<p>Program.cs</p>

<pre><code>using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Win32.SafeHandles;
using System.IO.Pipes;

namespace NamedPipeServer
{
    class Program
    {
        static void Main(string[] args)
        {
            SafePipeHandle handle = LowIntegrityPipeFactory.CreateLowIntegrityNamedPipe("NamedPipe/Test");
            NamedPipeServerStream pipeServer = new NamedPipeServerStream(PipeDirection.InOut, true, false, handle);
            pipeServer.BeginWaitForConnection(HandleConnection, pipeServer);

            Console.WriteLine("Waiting...");
            Console.ReadLine();
        }

        private static void HandleConnection(IAsyncResult ar)
        {
            Console.WriteLine("Received connection");
        }
    }
}
</code></pre>

<p>LowIntegrityPipeFactory.cs</p>

<pre><code>using System;
using Microsoft.Win32.SafeHandles;
using System.Runtime.InteropServices;
using System.IO.Pipes;
using System.ComponentModel;
using System.IO;
using System.Security.Principal;
using System.Security.AccessControl;

namespace NamedPipeServer
{
    static class LowIntegrityPipeFactory
    {
        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern SafePipeHandle CreateNamedPipe(string pipeName, int openMode,
            int pipeMode, int maxInstances, int outBufferSize, int inBufferSize, int defaultTimeout,
            SECURITY_ATTRIBUTES securityAttributes);

        [DllImport("Advapi32.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = false)]
        private static extern bool ConvertStringSecurityDescriptorToSecurityDescriptor(
            [In] string StringSecurityDescriptor,
            [In] uint StringSDRevision,
            [Out] out IntPtr SecurityDescriptor,
            [Out] out int SecurityDescriptorSize
        );

        [StructLayout(LayoutKind.Sequential)]
        private struct SECURITY_ATTRIBUTES
        {
            public int nLength;
            public IntPtr lpSecurityDescriptor;
            public int bInheritHandle;
        }

        private const string LOW_INTEGRITY_SSL_SACL = "S:(ML;;NW;;;LW)";

        public static SafePipeHandle CreateLowIntegrityNamedPipe(string pipeName)
        {
            // convert the security descriptor
            IntPtr securityDescriptorPtr = IntPtr.Zero;
            int securityDescriptorSize = 0;
            bool result = ConvertStringSecurityDescriptorToSecurityDescriptor(
                LOW_INTEGRITY_SSL_SACL, 1, out securityDescriptorPtr, out securityDescriptorSize);
            if (!result)
                throw new Win32Exception(Marshal.GetLastWin32Error());

            SECURITY_ATTRIBUTES securityAttributes = new SECURITY_ATTRIBUTES();
            securityAttributes.nLength = Marshal.SizeOf(securityAttributes);
            securityAttributes.bInheritHandle = 1;
            securityAttributes.lpSecurityDescriptor = securityDescriptorPtr;

            SafePipeHandle handle = CreateNamedPipe(@"\\.\pipe\" + pipeName,
                PipeDirection.InOut, 100, PipeTransmissionMode.Byte, PipeOptions.Asynchronous,
                0, 0, PipeAccessRights.ReadWrite, securityAttributes);
            if (handle.IsInvalid)
                throw new Win32Exception(Marshal.GetLastWin32Error());

            return handle;
        }

        private static SafePipeHandle CreateNamedPipe(string fullPipeName, PipeDirection direction,
            int maxNumberOfServerInstances, PipeTransmissionMode transmissionMode, PipeOptions options,
            int inBufferSize, int outBufferSize, PipeAccessRights rights, SECURITY_ATTRIBUTES secAttrs)
        {
            int openMode = (int)direction | (int)options;
            int pipeMode = 0;
            if (maxNumberOfServerInstances == -1)
                maxNumberOfServerInstances = 0xff;

            SafePipeHandle handle = CreateNamedPipe(fullPipeName, openMode, pipeMode,
                maxNumberOfServerInstances, outBufferSize, inBufferSize, 0, secAttrs);
            if (handle.IsInvalid)
                throw new Win32Exception(Marshal.GetLastWin32Error());
            return handle;
        }

    }
}
</code></pre>

<h2>Client code</h2>

<p>Program.cs</p>

<pre><code>using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO.Pipes;

namespace NamedPipeClient
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                var pipeClient = new NamedPipeClientStream(".", "NamedPipe/Test",
                    PipeDirection.InOut,
                    PipeOptions.None);
                pipeClient.Connect(100);
            }
            catch (Exception ex)
            {
                Console.WriteLine("Failed: " + ex);
                return;
            }

            Console.WriteLine("Connected");
            Console.ReadLine();
        }
    }
}
</code></pre>
