NamedPipeClientStream 在 Connect 上抛出 UnauthorizedAccessException
NamedPipeClientStream throws UnauthorizedAccessException on Connect
在将 "write" 管道连接到 运行 服务时,我遇到了与其他人相同的问题:UnauthorizedAccessException。我尝试了所有解决方案,但没有任何方法可以成功连接。
该场景在系统托盘中有一个低完整性的 C#/WPF 应用程序 运行,它使用命名管道从 Windows 服务获取通知,并可以告诉该服务取消某些操作或等待获取更多数据(这就是为什么它需要一个写入服务的管道)。从服务的管道读取工作正常,我正在使用两个管道对象(一个从服务到客户端,另一个从客户端到服务)。
该服务在域用户的帐户下运行,但管道无法连接,无论它在什么下运行,包括本地系统。
服务器管道是这样创建的:
PipeSecurity ps = new PipeSecurity();
// Production service runs under current user credentials.
ps.AddAccessRule(new PipeAccessRule(WindowsIdentity.GetCurrent().User, PipeAccessRights.ReadWrite | PipeAccessRights.CreateNewInstance, AccessControlType.Allow));
// Test service runs under local system credentials.
ps.AddAccessRule(new PipeAccessRule(new SecurityIdentifier(WellKnownSidType.LocalSystemSid, null), PipeAccessRights.ReadWrite | PipeAccessRights.CreateNewInstance, AccessControlType.Allow));
// Add world just for the hell of it, still won't work.
ps.AddAccessRule(new PipeAccessRule(new SecurityIdentifier(WellKnownSidType.WorldSid, null), PipeAccessRights.FullControl, AccessControlType.Allow));
this.readPipe = new NamedPipeServerStream(clientPipeName, PipeDirection.In);
this.writePipe = new NamedPipeServerStream(serverPipeName, PipeDirection.Out, 1, PipeTransmissionMode.Byte, PipeOptions.None, 1024, 1024, ps);
客户端管道是这样创建的:
this.readPipe = new NamedPipeClientStream(".", serverPipeName, PipeDirection.In);
this.writePipe = new NamedPipeClientStream(".", clientPipeName, PipeDirection.Out);
// This doesn't make a difference.
//this.writePipe = new NamedPipeClientStream(".", clientPipeName, PipeAccessRights.FullControl, PipeOptions.None, TokenImpersonationLevel.None, HandleInheritability.None);
不想让这个打开,因为这个问题已经解决并且下面的代码正在生产中。
- 服务器管道必须有精心构造的 ACL 列表,以允许来自低完整性客户端管道的连接,只要用户经过身份验证或管理员。请参阅 CreateNativePipeSecurity()。
客户端管道照常创建
/// <summary>
/// Creates the client and server pipes.
/// </summary>
private void CreatePipes()
{
string serverPipeName = string.Format("{0}server", this.pipeName);
string clientPipeName = string.Format("{0}client", this.pipeName);
if (this.readPipe != null)
{
this.readPipe.Dispose();
}
if (this.writePipe != null)
{
this.writePipe.Dispose();
}
if (this.server)
{
// Create a write pipe for sending notifications to client.
this.writePipe = new NamedPipeServerStream(clientPipeName, PipeDirection.Out);
// Create a read pipe for receiving notifications from the client.
// Creating a pipe to high integrity process from low integrity process requires native access list creation (.NET bug).
NativeMethods.SECURITY_ATTRIBUTES securityAttributes = this.CreateNativePipeSecurity();
IntPtr securityAttributesPtr = Marshal.AllocHGlobal(Marshal.SizeOf(securityAttributes));
Marshal.StructureToPtr(securityAttributes, securityAttributesPtr, false);
string nativePipeName = string.Format(@"\.\pipe\{0}", serverPipeName);
SafePipeHandle nativePipe = NativeMethods.CreateNamedPipe(
nativePipeName,
NativeMethods.PipeOpenMode.PIPE_ACCESS_INBOUND,
0,
NativeMethods.PipeInstances.PIPE_UNLIMITED_INSTANCES,
0,
0,
NativeMethods.PipeWait.NMPWAIT_WAIT_FOREVER,
securityAttributesPtr);
int error = Marshal.GetLastWin32Error();
Marshal.FreeHGlobal(securityAttributesPtr);
if (nativePipe.IsInvalid)
{
throw new Win32Exception(error);
}
this.readPipe = new NamedPipeServerStream(PipeDirection.In, false, false, nativePipe);
}
else
{
// Create a read pipe for receiving notifications from server.
this.readPipe = new NamedPipeClientStream(".", clientPipeName, PipeDirection.In);
// Create a write pipe for sending notifications to the server.
this.writePipe = new NamedPipeClientStream(".", serverPipeName, PipeDirection.Out);
}
}
/// <summary>
/// Generate security attributes to allow low integrity process to connect to high integrity service.
/// </summary>
/// <returns>A structure filled with proper attributes.</returns>
private NativeMethods.SECURITY_ATTRIBUTES CreateNativePipeSecurity()
{
// Define the SDDL for the security descriptor.
string sddl = "D:" + // Discretionary ACL
"(A;OICI;GRGW;;;AU)" + // Allow read/write to authenticated users
"(A;OICI;GA;;;BA)"; // Allow full control to administrators
IntPtr securityDescriptor = IntPtr.Zero;
if (NativeMethods.ConvertStringSecurityDescriptorToSecurityDescriptor(
sddl, 1, out securityDescriptor, IntPtr.Zero) == 0)
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
NativeMethods.SECURITY_ATTRIBUTES sa = new NativeMethods.SECURITY_ATTRIBUTES();
sa.nLength = Marshal.SizeOf(sa);
sa.lpSecurityDescriptor = securityDescriptor;
sa.bInheritHandle = 0;
return sa;
}
在将 "write" 管道连接到 运行 服务时,我遇到了与其他人相同的问题:UnauthorizedAccessException。我尝试了所有解决方案,但没有任何方法可以成功连接。
该场景在系统托盘中有一个低完整性的 C#/WPF 应用程序 运行,它使用命名管道从 Windows 服务获取通知,并可以告诉该服务取消某些操作或等待获取更多数据(这就是为什么它需要一个写入服务的管道)。从服务的管道读取工作正常,我正在使用两个管道对象(一个从服务到客户端,另一个从客户端到服务)。
该服务在域用户的帐户下运行,但管道无法连接,无论它在什么下运行,包括本地系统。
服务器管道是这样创建的:
PipeSecurity ps = new PipeSecurity();
// Production service runs under current user credentials.
ps.AddAccessRule(new PipeAccessRule(WindowsIdentity.GetCurrent().User, PipeAccessRights.ReadWrite | PipeAccessRights.CreateNewInstance, AccessControlType.Allow));
// Test service runs under local system credentials.
ps.AddAccessRule(new PipeAccessRule(new SecurityIdentifier(WellKnownSidType.LocalSystemSid, null), PipeAccessRights.ReadWrite | PipeAccessRights.CreateNewInstance, AccessControlType.Allow));
// Add world just for the hell of it, still won't work.
ps.AddAccessRule(new PipeAccessRule(new SecurityIdentifier(WellKnownSidType.WorldSid, null), PipeAccessRights.FullControl, AccessControlType.Allow));
this.readPipe = new NamedPipeServerStream(clientPipeName, PipeDirection.In);
this.writePipe = new NamedPipeServerStream(serverPipeName, PipeDirection.Out, 1, PipeTransmissionMode.Byte, PipeOptions.None, 1024, 1024, ps);
客户端管道是这样创建的:
this.readPipe = new NamedPipeClientStream(".", serverPipeName, PipeDirection.In);
this.writePipe = new NamedPipeClientStream(".", clientPipeName, PipeDirection.Out);
// This doesn't make a difference.
//this.writePipe = new NamedPipeClientStream(".", clientPipeName, PipeAccessRights.FullControl, PipeOptions.None, TokenImpersonationLevel.None, HandleInheritability.None);
不想让这个打开,因为这个问题已经解决并且下面的代码正在生产中。
- 服务器管道必须有精心构造的 ACL 列表,以允许来自低完整性客户端管道的连接,只要用户经过身份验证或管理员。请参阅 CreateNativePipeSecurity()。
客户端管道照常创建
/// <summary> /// Creates the client and server pipes. /// </summary> private void CreatePipes() { string serverPipeName = string.Format("{0}server", this.pipeName); string clientPipeName = string.Format("{0}client", this.pipeName); if (this.readPipe != null) { this.readPipe.Dispose(); } if (this.writePipe != null) { this.writePipe.Dispose(); } if (this.server) { // Create a write pipe for sending notifications to client. this.writePipe = new NamedPipeServerStream(clientPipeName, PipeDirection.Out); // Create a read pipe for receiving notifications from the client. // Creating a pipe to high integrity process from low integrity process requires native access list creation (.NET bug). NativeMethods.SECURITY_ATTRIBUTES securityAttributes = this.CreateNativePipeSecurity(); IntPtr securityAttributesPtr = Marshal.AllocHGlobal(Marshal.SizeOf(securityAttributes)); Marshal.StructureToPtr(securityAttributes, securityAttributesPtr, false); string nativePipeName = string.Format(@"\.\pipe\{0}", serverPipeName); SafePipeHandle nativePipe = NativeMethods.CreateNamedPipe( nativePipeName, NativeMethods.PipeOpenMode.PIPE_ACCESS_INBOUND, 0, NativeMethods.PipeInstances.PIPE_UNLIMITED_INSTANCES, 0, 0, NativeMethods.PipeWait.NMPWAIT_WAIT_FOREVER, securityAttributesPtr); int error = Marshal.GetLastWin32Error(); Marshal.FreeHGlobal(securityAttributesPtr); if (nativePipe.IsInvalid) { throw new Win32Exception(error); } this.readPipe = new NamedPipeServerStream(PipeDirection.In, false, false, nativePipe); } else { // Create a read pipe for receiving notifications from server. this.readPipe = new NamedPipeClientStream(".", clientPipeName, PipeDirection.In); // Create a write pipe for sending notifications to the server. this.writePipe = new NamedPipeClientStream(".", serverPipeName, PipeDirection.Out); } } /// <summary> /// Generate security attributes to allow low integrity process to connect to high integrity service. /// </summary> /// <returns>A structure filled with proper attributes.</returns> private NativeMethods.SECURITY_ATTRIBUTES CreateNativePipeSecurity() { // Define the SDDL for the security descriptor. string sddl = "D:" + // Discretionary ACL "(A;OICI;GRGW;;;AU)" + // Allow read/write to authenticated users "(A;OICI;GA;;;BA)"; // Allow full control to administrators IntPtr securityDescriptor = IntPtr.Zero; if (NativeMethods.ConvertStringSecurityDescriptorToSecurityDescriptor( sddl, 1, out securityDescriptor, IntPtr.Zero) == 0) { throw new Win32Exception(Marshal.GetLastWin32Error()); } NativeMethods.SECURITY_ATTRIBUTES sa = new NativeMethods.SECURITY_ATTRIBUTES(); sa.nLength = Marshal.SizeOf(sa); sa.lpSecurityDescriptor = securityDescriptor; sa.bInheritHandle = 0; return sa; }