Открытие именованного канала с низким уровнем целостности
Я работаю над приложением, которое состоит из двух модулей. Эти модули взаимодействуют через именованные каналы в следующей среде:
- Windows 7 Home Premium x64
- Visual Studio 2008
- C# /.Net 3.5
Сервер работает с правами администратора (высокий уровень целостности). Клиент работает с низким уровнем целостности. Чтобы клиент мог подключиться к серверу, мне нужно создать канал с низким уровнем целостности. Мне удается это делать только тогда, когда сервер работает на среднем уровне целостности.
Я протестировал следующие настройки:
- сервер: высокий, клиент: низкий => доступ запрещен
- сервер: высокий, клиент: средний => доступ запрещен
- сервер: высокий, клиент: высокий => ОК
- сервер: средний, клиент: низкий => ОК
- сервер: средний, клиент: средний => ОК
- сервер: низкий, клиент: низкий => ОК
Настройка #4 показывает, что именованный канал создается с уровнем целостности, отличным от уровня процесса, что хорошо. Тем не менее, интересующая меня настройка является первой.
У меня есть образец, который позволяет легко проверить. Если соединение установлено успешно, клиенты пишут "Подключено", а сервер - "Получено соединение". Если соединение не установлено, клиент пишет "Failed", а сервер остается в состоянии "Waiting".
Вот как я выполняю клиентскую программу (для сервера просто замените NamePipeClient на NamedPipeServer):
- средний уровень целостности:
- откройте командную строку
icacls NamedPipeClient.exe /setintegritylevel Medium
NamedPipeClient.exe
- низкий уровень целостности:
- откройте командную строку
icacls NamedPipeClient.exe /setintegritylevel Низкий
NamedPipeClient.exe
- высокий уровень целостности:
- откройте командную строку в режиме администратора
icacls NamedPipeClient.exe /setintegritylevel High
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();
}
}
}
3 ответа
У меня работает на 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);
Ваш код для установки обязательной метки целостности на канале достигает этого успешно. Однако, поскольку ваш дескриптор безопасности не определяет DACL, ваш канал создается со стандартным каналом.
Именно DACL вызывает сбой клиента с низким уровнем целостности при попытке подключиться к каналу, созданному вашим сервером с высоким уровнем целостности.
Вам нужно исправить DACL на сервере, прежде чем открывать слушатель. Вместо того чтобы пытаться создать полный дескриптор с использованием кода P/Invoke перед созданием канала, что довольно трудно понять правильно, я бы предложил использовать классы System.IO.Pipes, чтобы сделать это в управляемом коде как отдельный шаг после труба создана, вот так:
// 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;
}
Это работает для меня, с остальной частью вашего кода без изменений.
Ответ, который я опубликовал в декабре, действительно работает, несмотря на анонимное опровержение, которое кто-то потворствовал. (По крайней мере, это происходит в Vista SP2, и я не думаю, что есть какие-то различия между Vista и Windows 7, которые могли бы повлиять Эта проблема).
Вот другой подход, который также работает, указав DACL в строке SDDL, используемой внутри класса фабрики каналов:
Измените строку в методе 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();
}
Моя версия устанавливает доступ к каналу, чтобы позволить любому авторизованному пользователю быть клиентом канала. Вы можете добавить дополнительные функции к классу фабрики каналов, чтобы указать список разрешенных клиентских идентификаторов безопасности или тому подобное.