Открытие именованного канала с низким уровнем целостности

Я работаю над приложением, которое состоит из двух модулей. Эти модули обмениваются данными по именованным каналам в следующей среде:

  • Windows 7 Home Premium x64
  • Visual Studio 2008
  • С#/.Net 3.5

Сервер работает с правами администратора (высокий уровень целостности). Клиент работает с низким уровнем целостности. Чтобы клиент мог подключиться к серверу, мне нужно создать канал с низким уровнем целостности. Мне удается это сделать только тогда, когда сервер работает на уровне средней целостности.

Я тестировал следующие настройки:

  • сервер: высокий, клиент: низкий = > доступ отказался
  • сервер: высокий, клиент: средний = > доступ отказался
  • сервер: высокий, клиент: высокий = > ОК
  • сервер: средний, клиент: низкий = > ОК
  • сервер: средний, клиент: средний = > ОК
  • сервер: низкий, клиент: низкий = > ОК

Настройка №4 показывает, что именованный канал создается с другим уровнем целостности, чем тот, который является хорошим. Тем не менее, установка, которую меня интересует, является первой.

У меня есть образец, который позволяет легко протестировать. Если соединение выполнено успешно, клиенты записывают "Подключено", а сервер записывает "Полученное соединение". Если соединение не удалось, клиент пишет "Failed", и сервер остается на "Ожидание".

Вот как я запускаю клиентскую программу (для сервера просто замените NamePipeClient на NamedPipeServer):

  • средний уровень целостности:
    • откройте командную строку
    • icacls NamedPipeClient.exe/setintegritylevel Medium

    • NamedPipeClient.exe

  • низкий уровень целостности:
    • откройте командную строку
    • icacls NamedPipeClient.exe/setintegritylevel Низкий

    • NamedPipeClient.exe

  • высокий уровень целостности:
    • откройте командную строку в режиме администратора
    • icacls NamedPipeClient.exe/setintegritylevel Высокий

    • NamedPipeClient.exe

Любая помощь будет принята с благодарностью!

Код сервера

Program.cs

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");
        }
    }
}

LowIntegrityPipeFactory.cs

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;
        }

    }
}

Клиентский код

Program.cs

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();
        }
    }
}

Ответы

Ответ 1

Ваш код для установки обязательной метки целостности на трубе достигает этого успешно. Однако, поскольку ваш дескриптор безопасности не определяет DACL, ваш канал создается по умолчанию.

Именно DACL вызывает отказ клиента с низкой целостностью при попытке подключиться к каналу, созданному вашим сервером высокой целостности.

Вам нужно исправить DACL на сервере, прежде чем открывать прослушиватель. Вместо того, чтобы пытаться создать полный дескриптор, используя код P/Invoke, прежде чем создавать канал, что довольно сложно получить, я бы предложил использовать классы System.IO.Pipes, чтобы сделать это в управляемом коде как отдельный шаг после pipe создается следующим образом:

    // Fix up the DACL on the pipe before opening the listener instance
    // This won't disturb the SACL containing the mandatory integrity label
    NamedPipeServerStream handleForSecurity = null;
    try
    {
        handleForSecurity = new NamedPipeServerStream("NamedPipe/Test", PipeDirection.InOut, -1, PipeTransmissionMode.Byte, PipeOptions.None, 0, 0, null, System.IO.HandleInheritability.None, PipeAccessRights.ChangePermissions);
        PipeSecurity ps = handleForSecurity.GetAccessControl();
        PipeAccessRule aceClients = new PipeAccessRule(
            new SecurityIdentifier(WellKnownSidType.AuthenticatedUserSid, null), // or some other group defining the allowed clients
            PipeAccessRights.ReadWrite, 
            AccessControlType.Allow);
        PipeAccessRule aceOwner = new PipeAccessRule(
            WindowsIdentity.GetCurrent().Owner,
            PipeAccessRights.FullControl,
            AccessControlType.Allow);
        ps.AddAccessRule(aceClients);
        ps.AddAccessRule(aceOwner);
        handleForSecurity.SetAccessControl(ps);
    }
    finally
    {
        if (null != handleForSecurity) handleForSecurity.Close();
        handleForSecurity = null;
    }

Это работает для меня, при этом остальная часть вашего кода не изменяется.

Ответ 2

Работает для меня в Windows 7 SP1

public static class NativeMethods
{
    public const string LOW_INTEGRITY_SSL_SACL = "S:(ML;;NW;;;LW)";

    public static int ERROR_SUCCESS = 0x0;

    public const int LABEL_SECURITY_INFORMATION = 0x00000010;

    public enum SE_OBJECT_TYPE
    {
        SE_UNKNOWN_OBJECT_TYPE = 0,
        SE_FILE_OBJECT,
        SE_SERVICE,
        SE_PRINTER,
        SE_REGISTRY_KEY,
        SE_LMSHARE,
        SE_KERNEL_OBJECT,
        SE_WINDOW_OBJECT,
        SE_DS_OBJECT,
        SE_DS_OBJECT_ALL,
        SE_PROVIDER_DEFINED_OBJECT,
        SE_WMIGUID_OBJECT,
        SE_REGISTRY_WOW64_32KEY
    }



    [DllImport("advapi32.dll", EntryPoint = "ConvertStringSecurityDescriptorToSecurityDescriptorW")]
    [return: MarshalAs(UnmanagedType.Bool)]
    public static extern Boolean ConvertStringSecurityDescriptorToSecurityDescriptor(
        [MarshalAs(UnmanagedType.LPWStr)] String strSecurityDescriptor,
        UInt32 sDRevision,
        ref IntPtr securityDescriptor,
        ref UInt32 securityDescriptorSize);

    [DllImport("kernel32.dll", EntryPoint = "LocalFree")]
    public static extern UInt32 LocalFree(IntPtr hMem);

    [DllImport("Advapi32.dll", EntryPoint = "SetSecurityInfo")]
    public static extern int SetSecurityInfo(SafeHandle hFileMappingObject,
                                                SE_OBJECT_TYPE objectType,
                                                Int32 securityInfo,
                                                IntPtr psidOwner,
                                                IntPtr psidGroup,
                                                IntPtr pDacl,
                                                IntPtr pSacl);
    [DllImport("advapi32.dll", EntryPoint = "GetSecurityDescriptorSacl")]
    [return: MarshalAs(UnmanagedType.Bool)]
    public static extern Boolean GetSecurityDescriptorSacl(
        IntPtr pSecurityDescriptor,
        out IntPtr lpbSaclPresent,
        out IntPtr pSacl,
        out IntPtr lpbSaclDefaulted);
}

public class InterProcessSecurity
{

    public static void SetLowIntegrityLevel(SafeHandle hObject)
    {
        IntPtr pSD = IntPtr.Zero;
        IntPtr pSacl;
        IntPtr lpbSaclPresent;
        IntPtr lpbSaclDefaulted;
        uint securityDescriptorSize = 0;

        if (NativeMethods.ConvertStringSecurityDescriptorToSecurityDescriptor(NativeMethods.LOW_INTEGRITY_SSL_SACL, 1, ref pSD, ref securityDescriptorSize))
        {
            if (NativeMethods.GetSecurityDescriptorSacl(pSD, out lpbSaclPresent, out pSacl, out lpbSaclDefaulted))
            {
                var err = NativeMethods.SetSecurityInfo(hObject,
                                              NativeMethods.SE_OBJECT_TYPE.SE_KERNEL_OBJECT,
                                              NativeMethods.LABEL_SECURITY_INFORMATION,
                                              IntPtr.Zero,
                                              IntPtr.Zero,
                                              IntPtr.Zero,
                                              pSacl);
                if (err != NativeMethods.ERROR_SUCCESS)
                {
                    throw new Win32Exception(err);
                }
            }
            NativeMethods.LocalFree(pSD);
        }
    }
}

Настройка серверной части

   InterProcessSecurity.SetLowIntegrityLevel(pipeServer.SafePipeHandle);

Ответ 3

Ответ, который я опубликовал в декабре, действительно работает, несмотря на анонимное голосование, по которому кто-то побаливался. (По крайней мере, это касается Vista SP2, и я не думаю, что между Vista и Windows 7 существуют различия. что повлияет на эту проблему).

Здесь также используется другой подход, указывающий DACL внутри строки SDDL, используемой внутри класса factory:

Измените строку в методе CreateLowIntegrityNamedPipe (string pipeName), который вызывает ConvertStringSecurityDescriptorToSecurityDescriptor, таким образом:

bool result = ConvertStringSecurityDescriptorToSecurityDescriptor(
     CreateSddlForPipeSecurity(), 1, out securityDescriptorPtr, 
     out securityDescriptorSize);

и предоставить дополнительный частный статический метод, например:

    private static string CreateSddlForPipeSecurity()
    {
        const string LOW_INTEGRITY_LABEL_SACL = "S:(ML;;NW;;;LW)";
        const string EVERYONE_CLIENT_ACE = "(A;;0x12019b;;;WD)";
        const string CALLER_ACE_TEMPLATE = "(A;;0x12019f;;;{0})";

        StringBuilder sb = new StringBuilder();
        sb.Append(LOW_INTEGRITY_LABEL_SACL);
        sb.Append("D:");
        sb.Append(EVERYONE_CLIENT_ACE);
        sb.AppendFormat(CALLER_ACE_TEMPLATE, WindowsIdentity.GetCurrent().Owner.Value);
        return sb.ToString();
    }

Моя версия устанавливает доступ к каналу, чтобы позволить любому аутентифицированному пользователю быть клиентом канала. Вы можете добавить дополнительные функции в класс pipe factory, чтобы указать список разрешенных идентификаторов SID клиента или таких, как.