调用 NamedPipeServerStream.SetAccessControl 时“尝试执行未经授权的操作”
Posted
技术标签:
【中文标题】调用 NamedPipeServerStream.SetAccessControl 时“尝试执行未经授权的操作”【英文标题】:"Attempted to perform an unauthorized operation" when calling NamedPipeServerStream.SetAccessControl 【发布时间】:2018-06-06 02:38:29 【问题描述】:我正在尝试使用PipeSecurity
来保护NamedPipeServerStream
。当我在下面的 sn-p 中调用 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 方向, 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.
【问题讨论】:
social.msdn.microsoft.com/Forums/vstudio/en-US/… 有帮助吗? Setting named pipe security in a Domain的可能重复 这不是重复的 - 他的目标是 .NET 标准 1.6 - 该版本没有您提供的链接的解决方案中建议的构造函数。 @mjwills 感谢这些链接,它为我修复了 .NET Framework 但正如 user3141326 所述,.NET Standard 中不存在该构造函数。请查看我的编辑。 【参考方案1】:我刚刚遇到了同样的问题,并试图追踪它。
TL;DR
当前状态(2019 年 2 月)令人难过,但却是事实:它只是不适用于当今 NET 标准中给出的类。
票证参考
issue 30170 “unauthorized operation” on NamedPipeServerStream.SetAccessControl issue 31190 System.IO.Pipes.AccessControl package does not work issue 24040 NamedPipeServerStream: Provide support for WRITE_DAC在这种情况下也很有趣的可能是与 *nix 相关的
issue 34400 mechanism for lower privileged user to connect to a privileged user's pipe可能的解决方法
仍然可以使用本机 API 调用来根据需要设置安全性,但这不适合胆小的人。基本上需要执行这些步骤:
设置security descriptor in binary representation 将其连接到SECURITY_ATTRIBUTES
structure
创建管道via Windows API CreateNamedPipe()
将生成的句柄包装成SafeHandle
并将其输入正确的NamedPipeServerStream CTOR 变体
PS:至少我们现在可以在代码中查找它撞墙的地方。想象一下 20 年前有这个问题......
【讨论】:
【参考方案2】:在 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 操作系统。
【讨论】:
可以确认此方法也适用于 .NET 6【参考方案3】:最近在将项目翻译成.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);
【讨论】:
【参考方案4】:我已经设法实现了 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 代码发现了这一点。奇怪的是,他们有所有的管道安全代码,但没有它的构造函数。因此,另一种方法实际上可能是反射。
【讨论】:
这很有效,谢谢。我只需要使用 sddl 来允许匿名用户。搞定了,谢谢。 非常感谢您的努力。这很好用,我更喜欢 NuGet 包方法。如果客户愿意,他们也可以很容易地扩展该方法以允许指定硬编码的参数,例如缓冲区大小和管道打开模式。干得好,先生! 0 我正在尝试运行此代码,但我偶然发现了这一行:pipeSecurity.GetSecurityDescriptorSddlForm()。 pipeSecurity是如何定义的? 查看问题中的代码sn-ps,看看如何创建pipeSecurity对象。以上是关于调用 NamedPipeServerStream.SetAccessControl 时“尝试执行未经授权的操作”的主要内容,如果未能解决你的问题,请参考以下文章
NamedPipeServerStream 和 WaitForConnection 方法
NamedPipeServerStream/NamedPipeClientStream 包装器
为啥我的 NamedPipeServerStream 不等待?
CallNamedPipe 和 NamedPipeServerStream,访问被拒绝?