"试图执行未经授权的操作"调用NamedPipeServerStream.SetAccessControl时

时间:2018-06-06 02:38:29

标签: c# .net named-pipes

我尝试使用PipeSecurity来保护NamedPipeServerStream。当我在下面的代码段中调用this.pipeServer.SetAccessControl(pipeSecurity)时,我得到以下异常:

Attempted to perform an unauthorized operation.
    at System.Security.AccessControl.Win32.SetSecurityInfo(ResourceType type, String name, SafeHandle handle, SecurityInfos securityInformation, SecurityIdentifier owner, SecurityIdentifier group, GenericAcl sacl, GenericAcl dacl)
    at System.Security.AccessControl.NativeObjectSecurity.Persist(String name, SafeHandle handle, AccessControlSections includeSections, Object exceptionContext)
    at System.Security.AccessControl.NativeObjectSecurity.Persist(SafeHandle handle, AccessControlSections includeSections, Object exceptionContext)
    at System.IO.Pipes.PipeSecurity.Persist(SafeHandle handle)

代码:

this.pipeServer =
    new NamedPipeServerStream(
        pipeName,
        PipeDirection.InOut,
        1,
        PipeTransmissionMode.Byte,
        PipeOptions.Asynchronous);

PipeSecurity pipeSecurity = new PipeSecurity();

WindowsIdentity identity = WindowsIdentity.GetCurrent();
WindowsPrincipal principal = new WindowsPrincipal(identity);

if (principal.IsInRole(WindowsBuiltInRole.Administrator))
{
    // Allow the Administrators group full access to the pipe.
    pipeSecurity.AddAccessRule(new PipeAccessRule(
        new SecurityIdentifier(WellKnownSidType.BuiltinAdministratorsSid, null).Translate(typeof(NTAccount)),
        PipeAccessRights.FullControl, AccessControlType.Allow));
} else {
    // Allow current user read and write access to the pipe.
    pipeSecurity.AddAccessRule(new PipeAccessRule(
        WindowsIdentity.GetCurrent().User,
        PipeAccessRights.ReadWrite, AccessControlType.Allow));
}

this.pipeServer.SetAccessControl(pipeSecurity);

我做错了什么想法?

使用System.IO.AccessControl nuget包在.NET Framework(目标net451)和.NET Standard 1.6中发生这种情况:

https://www.nuget.org/packages/System.IO.Pipes.AccessControl/

修改:

我能够使用#ifdef来使用适用于.NET Framework的the following constructor

  

public NamedPipeServerStream(string pipeName,System.IO.Pipes.PipeDirection direction,int maxNumberOfServerInstances,System.IO.Pipes.PipeTransmissionMode transmissionMode,System.IO.Pipes.PipeOptions options,int inBufferSize,int outBufferSize,System.IO.Pipes .PipeSecurity pipeSecurity)

但是,.NET Standard中不存在此构造函数。我尝试使用添加到.NET Core的this function

PipesAclExtensions.SetAccessControl(PipeStream, PipeSecurity)

但是从那之前就产生了同样的例外。

I created a gist to show this

4 个答案:

答案 0 :(得分:4)

最近,在将项目转换为.Net Core时遇到相同的问题。

我添加了一个nuget包来帮助进行过渡:https://www.nuget.org/packages/NamedPipeServerStream.NetFrameworkVersion/

  • 该软件包面向.Net Standard 2.0,并包含来自.Net Framework的原始构造函数(支持PipeSecurity,HandleInheritability和PipeAccessRights)。
  • 我从反编译的代码中还原了它,而未进行任何更改。
  • 完全支持异常,没有代码丢失。
  • 有个好名字。
  • 源代码也可用。
Install-Package NamedPipeServerStream.NetFrameworkVersion
using System.IO.Pipes;

var pipeSecurity = new PipeSecurity();
pipeSecurity.AddAccessRule(new PipeAccessRule(WindowsIdentity.GetCurrent().Owner, PipeAccessRights.ReadWrite, AccessControlType.Allow));

using var serverStream = NamedPipeServerStreamConstructors.New(pipeName, PipeDirection.InOut, 1, PipeTransmissionMode.Byte, PipeOptions.Asynchronous | PipeOptions.WriteThrough, 0, 0, pipeSecurity);

答案 1 :(得分:1)

我只是遇到了同样的问题,并试图对其进行跟踪。

TL; DR

当前状态(2019年2月)令人难过,但确实如此:它不适用于当今NET标准中提供的类。

门票参考

在这种情况下,有趣的还可能是与* nix相关的

可能的解决方法

一个人仍然可以使用本机API调用来按需要设置安全性,但这并不是出于胆小。基本上需要执行以下步骤:


PS:至少我们现在可以在碰壁的代码中查找它。试想一下20年前有这个问题...

答案 2 :(得分:1)

我设法提出了可能的解决方法JensG。我使用此示例进行构建: https://code.msdn.microsoft.com/CSNamedPipeServer-4c760c2c/sourcecode?fileId=21684&pathId=1498714400

    public static class NativeNamedPipeServer
    {
        public static SafePipeHandle CreateNamedPipeServer(string pipeName, string sddl)
        {
            return NativeMethod.CreateNamedPipe(
                @"\\.\pipe\" + pipeName, // The unique pipe name.
                PipeOpenMode.PIPE_ACCESS_DUPLEX | PipeOpenMode.ASYNCHRONOUS,
                PipeMode.PIPE_TYPE_BYTE,
                1, // Max server instances
                1024 * 16, // Output buffer size
                1024 * 16, // Input buffer size
                NMPWAIT_USE_DEFAULT_WAIT, // Time-out interval
                CreateNativePipeSecurity(sddl) // Pipe security attributes
            );
        }

        /// <summary>
        /// The CreateNativePipeSecurity function creates and initializes a new 
        /// SECURITY_ATTRIBUTES object to allow Authenticated Users read and 
        /// write access to a pipe, and to allow the Administrators group full 
        /// access to the pipe.
        /// </summary>
        /// <returns>
        /// A SECURITY_ATTRIBUTES object that allows Authenticated Users read and 
        /// write access to a pipe, and allows the Administrators group full 
        /// access to the pipe.
        /// </returns>
        /// <see cref="http://msdn.microsoft.com/en-us/library/aa365600(VS.85).aspx"/>
        private static SECURITY_ATTRIBUTES CreateNativePipeSecurity(string sddl)
        {
            if (!NativeMethod.ConvertStringSecurityDescriptorToSecurityDescriptor(
                sddl, 1, out var pSecurityDescriptor, IntPtr.Zero))
            {
                throw new Win32Exception();
            }

            SECURITY_ATTRIBUTES sa = new SECURITY_ATTRIBUTES();
            sa.nLength = Marshal.SizeOf(sa);
            sa.lpSecurityDescriptor = pSecurityDescriptor;
            sa.bInheritHandle = false;
            return sa;
        }


        #region Native API Signatures and Types

        /// <summary>
        /// Named Pipe Open Modes
        /// http://msdn.microsoft.com/en-us/library/aa365596.aspx
        /// </summary>
        [Flags]
        internal enum PipeOpenMode : uint
        {
            PIPE_ACCESS_INBOUND = 0x00000001, // Inbound pipe access.
            PIPE_ACCESS_OUTBOUND = 0x00000002, // Outbound pipe access.
            PIPE_ACCESS_DUPLEX = 0x00000003, // Duplex pipe access.

            // added from C# PipeOptions.cs
            WRITE_THROUGH = 0x80000000,
            ASYNCHRONOUS = 0x40000000,
            CURRENT_USER_ONLY = 0x20000000
        }

        /// <summary>
        /// Named Pipe Type, Read, and Wait Modes
        /// http://msdn.microsoft.com/en-us/library/aa365605.aspx
        /// </summary>
        [Flags]
        internal enum PipeMode : uint
        {
            // Type Mode
            PIPE_TYPE_BYTE = 0x00000000, // Byte pipe type.
            PIPE_TYPE_MESSAGE = 0x00000004, // Message pipe type.

            // Read Mode
            PIPE_READMODE_BYTE = 0x00000000, // Read mode of type Byte.
            PIPE_READMODE_MESSAGE = 0x00000002, // Read mode of type Message.

            // Wait Mode
            PIPE_WAIT = 0x00000000, // Pipe blocking mode.
            PIPE_NOWAIT = 0x00000001 // Pipe non-blocking mode.
        }

        /// <summary>
        /// Uses the default time-out specified in a call to the 
        /// CreateNamedPipe method.
        /// </summary>
        internal const uint NMPWAIT_USE_DEFAULT_WAIT = 0x00000000;


        /// <summary>
        /// The SECURITY_ATTRIBUTES structure contains the security descriptor for 
        /// an object and specifies whether the handle retrieved by specifying 
        /// this structure is inheritable. This structure provides security 
        /// settings for objects created by various functions, such as CreateFile, 
        /// CreateNamedPipe, CreateProcess, RegCreateKeyEx, or RegSaveKeyEx.
        /// </summary>
        [StructLayout(LayoutKind.Sequential)]
        internal class SECURITY_ATTRIBUTES
        {
            public int nLength;
            public SafeLocalMemHandle lpSecurityDescriptor;
            public bool bInheritHandle;
        }


        /// <summary>
        /// Represents a wrapper class for a local memory pointer. 
        /// </summary>
        [SuppressUnmanagedCodeSecurity,
         HostProtection(SecurityAction.LinkDemand, MayLeakOnAbort = true)]
        internal sealed class SafeLocalMemHandle : SafeHandleZeroOrMinusOneIsInvalid
        {
            public SafeLocalMemHandle() : base(true)
            {
            }

            public SafeLocalMemHandle(IntPtr preexistingHandle, bool ownsHandle)
                : base(ownsHandle)
            {
                base.SetHandle(preexistingHandle);
            }

            [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success),
             DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
            private static extern IntPtr LocalFree(IntPtr hMem);

            protected override bool ReleaseHandle()
            {
                return (LocalFree(base.handle) == IntPtr.Zero);
            }
        }


        /// <summary>
        /// The class exposes Windows APIs to be used in this code sample.
        /// </summary>
        [SuppressUnmanagedCodeSecurity]
        internal class NativeMethod
        {
            /// <summary>
            /// Creates an instance of a named pipe and returns a handle for 
            /// subsequent pipe operations.
            /// </summary>
            /// <param name="pipeName">Pipe name</param>
            /// <param name="openMode">Pipe open mode</param>
            /// <param name="pipeMode">Pipe-specific modes</param>
            /// <param name="maxInstances">Maximum number of instances</param>
            /// <param name="outBufferSize">Output buffer size</param>
            /// <param name="inBufferSize">Input buffer size</param>
            /// <param name="defaultTimeout">Time-out interval</param>
            /// <param name="securityAttributes">Security attributes</param>
            /// <returns>If the function succeeds, the return value is a handle 
            /// to the server end of a named pipe instance.</returns>
            [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
            public static extern SafePipeHandle CreateNamedPipe(string pipeName,
                PipeOpenMode openMode, PipeMode pipeMode, int maxInstances,
                int outBufferSize, int inBufferSize, uint defaultTimeout,
                SECURITY_ATTRIBUTES securityAttributes);


            /// <summary>
            /// The ConvertStringSecurityDescriptorToSecurityDescriptor function 
            /// converts a string-format security descriptor into a valid, 
            /// functional security descriptor.
            /// </summary>
            /// <param name="sddlSecurityDescriptor">
            /// A string containing the string-format security descriptor (SDDL) 
            /// to convert.
            /// </param>
            /// <param name="sddlRevision">
            /// The revision level of the sddlSecurityDescriptor string. 
            /// Currently this value must be 1.
            /// </param>
            /// <param name="pSecurityDescriptor">
            /// A pointer to a variable that receives a pointer to the converted 
            /// security descriptor.
            /// </param>
            /// <param name="securityDescriptorSize">
            /// A pointer to a variable that receives the size, in bytes, of the 
            /// converted security descriptor. This parameter can be IntPtr.Zero.
            /// </param>
            /// <returns>
            /// If the function succeeds, the return value is true.
            /// </returns>
            [return: MarshalAs(UnmanagedType.Bool)]
            [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
            public static extern bool ConvertStringSecurityDescriptorToSecurityDescriptor(
                string sddlSecurityDescriptor, int sddlRevision,
                out SafeLocalMemHandle pSecurityDescriptor,
                IntPtr securityDescriptorSize);
        }

        #endregion
    }

创建:

            var safePipeHandle = NativeNamedPipeServer.CreateNamedPipeServer(_pipeName, 
                pipeSecurity.GetSecurityDescriptorSddlForm(AccessControlSections.Access));
            var stream = new NamedPipeServerStream(PipeDirection.InOut, true, false, safePipeHandle);

最棘手的部分是使异步工作,因为原始源没有PipeOpenMode.ASYNCHRONOUS标志。通过检查.NET Core 3.0代码了解了这一点。奇怪的是,它们在其中拥有所有管道安全代码,但没有该代码的构造函数。因此,替代方法实际上可能是反思。

答案 3 :(得分:0)

在NET 5中,您可以通过定义如下内容来创建供所有用户帐户使用的 NamedPipeServerStream

PipeSecurity pipeSecurity = new PipeSecurity();
pipeSecurity.AddAccessRule(new PipeAccessRule(new SecurityIdentifier(WellKnownSidType.WorldSid, null), PipeAccessRights.ReadWrite, AccessControlType.Allow));
using (var pipe = NamedPipeServerStreamAcl.Create("MyAppPipeName", PipeDirection.InOut, 1, PipeTransmissionMode.Message, PipeOptions.None, 0, 0, pipeSecurity))
{
    pipe.WaitForConnection();
    // ...
}

请注意,这仅适用于Windows操作系统。