以低完整性级别打开命名管道

时间:2010-07-19 15:17:04

标签: c#

我正在开发一个由两个模块组成的应用程序。这些模块在以下环境中通过命名管道进行通信:

  • Windows 7家庭高级版x64
  • Visual Studio 2008
  • C#/ .Net 3.5

服务器以管理员权限(高完整性级别)运行。客户端以低完整性级别运行。为了使客户端可以连接到服务器,我需要以低完整性级别创建管道。我设法只在服务器以中等完整性级别运行时才这样做。

我测试了以下设置:

  1. 服务器:高,客户:低=>拒绝访问
  2. server:high,client:medium =>拒绝访问
  3. 服务器:高,客户端:高=>行
  4. 服务器:中,客户:低=>行
  5. server:medium,client:medium =>行
  6. 服务器:低,客户端:低=>行
  7. 安装程序#4显示创建的命名管道的完整性级别与进程的完整性级别不同,这很好。但是,我感兴趣的设置是第一个。

    我有一个样本,可以很容易地进行测试。如果连接成功,客户端将写入“已连接”,服务器将写入“已接收连接”。如果连接失败,客户端将写入“失败”,服务器将保持“等待”状态。

    以下是我执行客户端程序的方法(对于服务器,只需将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 个答案:

答案 0 :(得分:4)

适用于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);

答案 1 :(得分:3)

您在管道上设置强制完整性标签的代码可以成功实现此目的。但是,由于您的安全描述符未定义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;
    }

这适用于我,其余代码保持不变。

答案 2 :(得分:1)

我在去年12月发布的答案确实有效,尽管有人沉迷于自己的匿名驱逐投票。(至少,它在Vista SP2上有效,我认为Vista与Windows 7之间没有任何差异哪会影响这个问题)。

这是一种不同的方法,它也可以在管道工厂类中使用的SDDL字符串中指定DACL:

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

我的版本设置管道访问权限,允许任何经过身份验证的用户成为管道客户端。您可以向管道工厂类添加其他功能,以指定允许的客户端SID列表等。