소스 검색

Implemented PipeSecurity. GetAccessControl, SetAccessControl, and ACL-containing constructor overrides now work on pipes.

On another note, after writing a test I discovered Asynchronous pipes appear to be completely broken on Win32.
The class does not correctly handle overlapped I/O. This patch doesn't fix that.

One other fix in this patch, the DllImports for Win32 pipes did not have SetLastError set.
So, they were unable to return a meaningful error in the event of failure. This is now fixed, and
UnauthorizedAccessException correctly throws for pipe connects denied by ACL (necessary for PipeSecurity test).
James Bellinger 13 년 전
부모
커밋
490a86b6ec

+ 1 - 0
mcs/class/System.Core/System.Core_test.dll.sources

@@ -3,6 +3,7 @@ System/TimeZoneInfo.AdjustmentRuleTest.cs
 System/TimeZoneInfo.TransitionTimeTest.cs
 System.Collections.Generic/HashSetTest.cs
 System.IO.MemoryMappedFiles/MemoryMappedFileTest.cs
+System.IO.Pipes/PipeSecurityTest.cs
 System.Linq/EnumerableFixture.cs
 System.Linq/EnumerableTest.cs
 System.Linq/EnumerableMoreTest.cs

+ 2 - 5
mcs/class/System.Core/System.IO.Pipes/AnonymousPipeServerStream.cs

@@ -66,16 +66,13 @@ namespace System.IO.Pipes
 		public AnonymousPipeServerStream (PipeDirection direction, HandleInheritability inheritability, int bufferSize, PipeSecurity pipeSecurity)
 			: base (direction, bufferSize)
 		{
-			if (pipeSecurity != null)
-				throw ThrowACLException ();
-
 			if (direction == PipeDirection.InOut)
 				throw new NotSupportedException ("Anonymous pipe direction can only be either in or out.");
 
 			if (IsWindows)
-				impl = new Win32AnonymousPipeServer (this,direction, inheritability, bufferSize);
+				impl = new Win32AnonymousPipeServer (this, direction, inheritability, bufferSize, pipeSecurity);
 			else
-				impl = new UnixAnonymousPipeServer (this,direction, inheritability, bufferSize);
+				impl = new UnixAnonymousPipeServer (this, direction, inheritability, bufferSize);
 
 			InitializeHandle (impl.Handle, false, false);
 			IsConnected = true;

+ 5 - 4
mcs/class/System.Core/System.IO.Pipes/NamedPipeServerStream.cs

@@ -88,15 +88,16 @@ namespace System.IO.Pipes
 		public NamedPipeServerStream (string pipeName, PipeDirection direction, int maxNumberOfServerInstances, PipeTransmissionMode transmissionMode, PipeOptions options, int inBufferSize, int outBufferSize, PipeSecurity pipeSecurity, HandleInheritability inheritability, PipeAccessRights additionalAccessRights)
 			: base (direction, transmissionMode, outBufferSize)
 		{
-			if (pipeSecurity != null)
-				throw ThrowACLException ();
 			var rights = ToAccessRights (direction) | additionalAccessRights;
 			// FIXME: reject some rights declarations (for ACL).
 
 			if (IsWindows)
-				impl = new Win32NamedPipeServer (this, pipeName, maxNumberOfServerInstances, transmissionMode, rights, options, inBufferSize, outBufferSize, inheritability);
+				impl = new Win32NamedPipeServer (this, pipeName, maxNumberOfServerInstances, transmissionMode,
+								 rights, options, inBufferSize, outBufferSize,
+								 pipeSecurity, inheritability);
 			else
-				impl = new UnixNamedPipeServer (this, pipeName, maxNumberOfServerInstances, transmissionMode, rights, options, inBufferSize, outBufferSize, inheritability);
+				impl = new UnixNamedPipeServer (this, pipeName, maxNumberOfServerInstances, transmissionMode,
+								rights, options, inBufferSize, outBufferSize, inheritability);
 
 			InitializeHandle (impl.Handle, false, (options & PipeOptions.Asynchronous) != PipeOptions.None);
 		}

+ 29 - 6
mcs/class/System.Core/System.IO.Pipes/PipeSecurity.cs

@@ -43,9 +43,13 @@ namespace System.IO.Pipes
 		public PipeSecurity ()
 			: base (false, ResourceType.FileObject)
 		{
-
 		}
-
+		
+		internal PipeSecurity (SafeHandle handle, AccessControlSections includeSections)
+			: base (false, ResourceType.FileObject, handle, includeSections)
+		{
+		}
+		
 		public override Type AccessRightType {
 			get { return typeof (PipeAccessRights); }
 		}
@@ -87,15 +91,34 @@ namespace System.IO.Pipes
 		[SecurityPermission (SecurityAction.Assert, UnmanagedCode = true)]
 		protected internal void Persist (SafeHandle handle)
 		{
-			throw new NotImplementedException ();
+			WriteLock();
+			try {
+				Persist (handle, AccessControlSectionsModified, null);
+			} finally {
+				WriteUnlock ();
+			}
 		}
 
 		[SecurityPermission (SecurityAction.Assert, UnmanagedCode = true)]
 		protected internal void Persist (string name)
 		{
-			throw new NotImplementedException ();
-		}
-
+			WriteLock();
+			try {
+				Persist (name, AccessControlSectionsModified, null);
+			} finally {
+				WriteUnlock ();
+			}
+		}
+		
+		AccessControlSections AccessControlSectionsModified {
+			get {
+				return (AccessRulesModified ? AccessControlSections.Access : 0) |
+				       (AuditRulesModified  ? AccessControlSections.Audit  : 0) |
+				       (OwnerModified       ? AccessControlSections.Owner  : 0) |
+				       (GroupModified       ? AccessControlSections.Group  : 0);
+			}
+		}
+		
 		public bool RemoveAccessRule (PipeAccessRule rule)
 		{
 			return RemoveAccessRule ((AccessRule)rule);

+ 12 - 6
mcs/class/System.Core/System.IO.Pipes/PipeStream.cs

@@ -132,7 +132,9 @@ namespace System.IO.Pipes
 				if (!IsConnected)
 					throw new InvalidOperationException ("Pipe is not connected");
 				if (stream == null)
-					stream = new FileStream (handle.DangerousGetHandle (), CanRead ? (CanWrite ? FileAccess.ReadWrite : FileAccess.Read) : FileAccess.Write, true, buffer_size, IsAsync);
+					stream = new FileStream (handle.DangerousGetHandle (),
+								 CanRead ? (CanWrite ? FileAccess.ReadWrite : FileAccess.Read)
+								 	 : FileAccess.Write, true, buffer_size, IsAsync);
 				return stream;
 			}
 			set { stream = value; }
@@ -193,7 +195,7 @@ namespace System.IO.Pipes
 		protected internal void CheckWriteOperations ()
 		{
 			if (!IsConnected)
-				throw new InvalidOperationException ("Pipe us not connected");
+				throw new InvalidOperationException ("Pipe is not connected");
 			if (!CanWrite)
 				throw new NotSupportedException ("The pipe stream does not support write operations");
 		}
@@ -232,16 +234,20 @@ namespace System.IO.Pipes
 			throw new NotSupportedException ();
 		}
 
-		[MonoNotSupported ("ACL is not supported in Mono")]
 		public PipeSecurity GetAccessControl ()
 		{
-			throw ThrowACLException ();
+			return new PipeSecurity (SafePipeHandle,
+						 AccessControlSections.Owner |
+						 AccessControlSections.Group |
+						 AccessControlSections.Access);
 		}
 
-		[MonoNotSupported ("ACL is not supported in Mono")]
 		public void SetAccessControl (PipeSecurity pipeSecurity)
 		{
-			throw ThrowACLException ();
+			if (pipeSecurity == null)
+				throw new ArgumentNullException ("pipeSecurity");
+				
+			pipeSecurity.Persist (SafePipeHandle);
 		}
 
 		// pipe I/O

+ 83 - 44
mcs/class/System.Core/System.IO.Pipes/PipeWin32.cs

@@ -42,6 +42,22 @@ using Microsoft.Win32.SafeHandles;
 
 namespace System.IO.Pipes
 {
+	static class Win32PipeError
+	{
+		public static Exception GetException ()
+		{
+			return GetException (Marshal.GetLastWin32Error ());
+		}
+		
+		public static Exception GetException (int errorCode)
+		{
+			switch (errorCode) {
+			case 5: return new UnauthorizedAccessException ();
+			default: return new Win32Exception (errorCode);
+			}
+		}
+	}
+	
 	abstract class Win32AnonymousPipe : IPipe
 	{
 		protected Win32AnonymousPipe ()
@@ -78,12 +94,21 @@ namespace System.IO.Pipes
 	{
 		// AnonymousPipeServerStream owner;
 
-		public Win32AnonymousPipeServer (AnonymousPipeServerStream owner, PipeDirection direction, HandleInheritability inheritability, int bufferSize)
+		public unsafe Win32AnonymousPipeServer (AnonymousPipeServerStream owner, PipeDirection direction,
+							HandleInheritability inheritability, int bufferSize,
+							PipeSecurity pipeSecurity)
 		{
 			IntPtr r, w;
-			SecurityAttributesHack att = new SecurityAttributesHack (inheritability == HandleInheritability.Inheritable);
-			if (!Win32Marshal.CreatePipe (out r, out w, ref att, bufferSize))
-				throw new Win32Exception (Marshal.GetLastWin32Error ());
+			
+			byte[] securityDescriptor = null;
+			if (pipeSecurity != null)
+				securityDescriptor = pipeSecurity.GetSecurityDescriptorBinaryForm ();
+				
+			fixed (byte* securityDescriptorPtr = securityDescriptor) {
+				SecurityAttributes att = new SecurityAttributes (inheritability, (IntPtr)securityDescriptorPtr);
+				if (!Win32Marshal.CreatePipe (out r, out w, ref att, bufferSize))
+					throw Win32PipeError.GetException ();
+			}
 
 			var rh = new SafePipeHandle (r, true);
 			var wh = new SafePipeHandle (w, true);
@@ -132,10 +157,9 @@ namespace System.IO.Pipes
 				int s, c, m, t;
 				byte [] un = new byte [200];
 				while (true) {
-					if (!Win32Marshal.GetNamedPipeHandleState (Handle, out s, out c, out m, out t, un, un.Length)) {
-						var xxx = Marshal.GetLastWin32Error ();
-						throw new Win32Exception (xxx);
-					}
+					if (!Win32Marshal.GetNamedPipeHandleState (Handle, out s, out c, out m, out t, un, un.Length))
+						throw Win32PipeError.GetException ();
+
 					if (un [un.Length - 1] == 0)
 						break;
 					un = new byte [un.Length * 10];
@@ -167,16 +191,18 @@ namespace System.IO.Pipes
 		}
 
 		// .ctor without handle - create new
-		public Win32NamedPipeClient (NamedPipeClientStream owner, string serverName, string pipeName, PipeAccessRights desiredAccessRights, PipeOptions options, HandleInheritability inheritability)
+		public Win32NamedPipeClient (NamedPipeClientStream owner, string serverName, string pipeName,
+					     PipeAccessRights desiredAccessRights, PipeOptions options,
+					     HandleInheritability inheritability)
 		{
 			name = String.Format ("\\\\{0}\\pipe\\{1}", serverName, pipeName);
-			var att = new SecurityAttributesHack (inheritability == HandleInheritability.Inheritable);
+			var att = new SecurityAttributes (inheritability, IntPtr.Zero);
 			is_async = (options & PipeOptions.Asynchronous) != PipeOptions.None;
 
 			opener = delegate {
 				var ret = Win32Marshal.CreateFile (name, desiredAccessRights, 0, ref att, 3, 0, IntPtr.Zero);
 				if (ret == new IntPtr (-1L))
-					throw new Win32Exception (Marshal.GetLastWin32Error ());
+					throw Win32PipeError.GetException ();
 
 				return new SafePipeHandle (ret, true);
 			};
@@ -210,7 +236,7 @@ namespace System.IO.Pipes
 				throw new InvalidOperationException ("The named pipe is already connected");
 
 			if (!Win32Marshal.WaitNamedPipe (name, timeout))
-				throw new Win32Exception (Marshal.GetLastWin32Error ());
+				throw Win32PipeError.GetException ();
 			Connect ();
 		}
 
@@ -219,7 +245,7 @@ namespace System.IO.Pipes
 				int s, c, m, t;
 				byte [] un = null;
 				if (!Win32Marshal.GetNamedPipeHandleState (Handle, out s, out c, out m, out t, un, 0))
-					throw new Win32Exception (Marshal.GetLastWin32Error ());
+					throw Win32PipeError.GetException ();
 				return c;
 			}
 		}
@@ -237,17 +263,16 @@ namespace System.IO.Pipes
 		}
 
 		// .ctor without handle - create new
-		public Win32NamedPipeServer (NamedPipeServerStream owner, string pipeName, int maxNumberOfServerInstances, PipeTransmissionMode transmissionMode, PipeAccessRights rights, PipeOptions options, int inBufferSize, int outBufferSize, HandleInheritability inheritability)
+		public unsafe Win32NamedPipeServer (NamedPipeServerStream owner, string pipeName, int maxNumberOfServerInstances,
+						    PipeTransmissionMode transmissionMode, PipeAccessRights rights,
+						    PipeOptions options, int inBufferSize, int outBufferSize,
+						    PipeSecurity pipeSecurity, HandleInheritability inheritability)
 		{
 			string name = String.Format ("\\\\.\\pipe\\{0}", pipeName);
 
-			uint openMode = 0;
-			if ((rights & PipeAccessRights.ReadData) != 0)
-				openMode |= 1;
-			if ((rights & PipeAccessRights.WriteData) != 0)
-				openMode |= 2;
-			if ((options & PipeOptions.WriteThrough) != 0)
-				openMode |= 0x80000000;
+			uint openMode;
+			openMode = (uint)rights | (uint)options; // Enum values match Win32 flags exactly.
+			
 			int pipeMode = 0;
 			if ((owner.TransmissionMode & PipeTransmissionMode.Message) != 0)
 				pipeMode |= 4;
@@ -256,12 +281,19 @@ namespace System.IO.Pipes
 			if ((options & PipeOptions.Asynchronous) != 0)
 				pipeMode |= 1;
 
-			// FIXME: is nDefaultTimeout = 0 ok?
-			var att = new SecurityAttributesHack (inheritability == HandleInheritability.Inheritable);
-			var ret = Win32Marshal.CreateNamedPipe (name, openMode, pipeMode, maxNumberOfServerInstances, outBufferSize, inBufferSize, 0, ref att, IntPtr.Zero);
-			if (ret == new IntPtr (-1L))
-				throw new Win32Exception (Marshal.GetLastWin32Error ());
-			handle = new SafePipeHandle (ret, true);
+			byte[] securityDescriptor = null;
+			if (pipeSecurity != null)
+				securityDescriptor = pipeSecurity.GetSecurityDescriptorBinaryForm ();
+			
+			fixed (byte* securityDescriptorPtr = securityDescriptor) {
+				// FIXME: is nDefaultTimeout = 0 ok?
+				var att = new SecurityAttributes (inheritability, (IntPtr)securityDescriptorPtr);
+				var ret = Win32Marshal.CreateNamedPipe (name, openMode, pipeMode, maxNumberOfServerInstances,
+									outBufferSize, inBufferSize, 0, ref att, IntPtr.Zero);
+				if (ret == new IntPtr (-1L))
+					throw Win32PipeError.GetException ();
+				handle = new SafePipeHandle (ret, true);
+			}
 		}
 
 		SafePipeHandle handle;
@@ -278,22 +310,22 @@ namespace System.IO.Pipes
 		public void WaitForConnection ()
 		{
 			if (!Win32Marshal.ConnectNamedPipe (Handle, IntPtr.Zero))
-				throw new Win32Exception (Marshal.GetLastWin32Error ());
+				throw Win32PipeError.GetException ();
 		}
 	}
 
 	[StructLayout (LayoutKind.Sequential)]
-	struct SecurityAttributesHack
+	struct SecurityAttributes
 	{
 		public readonly int Length;
 		public readonly IntPtr SecurityDescriptor;
 		public readonly bool Inheritable;
 
-		public SecurityAttributesHack (bool inheritable)
+		public SecurityAttributes (HandleInheritability inheritability, IntPtr securityDescriptor)
 		{
-			Length = 0;
-			SecurityDescriptor = IntPtr.Zero;
-			Inheritable = inheritable;
+			Length = Marshal.SizeOf (typeof (SecurityAttributes));
+			SecurityDescriptor = securityDescriptor;
+			Inheritable = inheritability == HandleInheritability.Inheritable;
 		}
 	}
 
@@ -314,32 +346,39 @@ namespace System.IO.Pipes
 		}
 
 		// http://msdn.microsoft.com/en-us/library/aa365152%28VS.85%29.aspx
-		[DllImport ("kernel32")]
-		internal static extern bool CreatePipe (out IntPtr readHandle, out IntPtr writeHandle, ref SecurityAttributesHack pipeAtts, int size);
+		[DllImport ("kernel32", SetLastError=true)]
+		internal static extern bool CreatePipe (out IntPtr readHandle, out IntPtr writeHandle,
+							ref SecurityAttributes pipeAtts, int size);
 
 		// http://msdn.microsoft.com/en-us/library/aa365150%28VS.85%29.aspx
-		[DllImport ("kernel32")]
-		internal static extern IntPtr CreateNamedPipe (string name, uint openMode, int pipeMode, int maxInstances, int outBufferSize, int inBufferSize, int defaultTimeout, ref SecurityAttributesHack securityAttributes, IntPtr atts);
+		[DllImport ("kernel32", SetLastError=true)]
+		internal static extern IntPtr CreateNamedPipe (string name, uint openMode, int pipeMode, int maxInstances,
+							       int outBufferSize, int inBufferSize, int defaultTimeout,
+							       ref SecurityAttributes securityAttributes, IntPtr atts);
 
 		// http://msdn.microsoft.com/en-us/library/aa365146%28VS.85%29.aspx
-		[DllImport ("kernel32")]
+		[DllImport ("kernel32", SetLastError=true)]
 		internal static extern bool ConnectNamedPipe (SafePipeHandle handle, IntPtr overlapped);
 
 		// http://msdn.microsoft.com/en-us/library/aa365166%28VS.85%29.aspx
-		[DllImport ("kernel32")]
+		[DllImport ("kernel32", SetLastError=true)]
 		internal static extern bool DisconnectNamedPipe (SafePipeHandle handle);
 
 		// http://msdn.microsoft.com/en-us/library/aa365443%28VS.85%29.aspx
-		[DllImport ("kernel32")]
-		internal static extern bool GetNamedPipeHandleState (SafePipeHandle handle, out int state, out int curInstances, out int maxCollectionCount, out int collectDateTimeout, byte [] userName, int maxUserNameSize);
+		[DllImport ("kernel32", SetLastError=true)]
+		internal static extern bool GetNamedPipeHandleState (SafePipeHandle handle,
+								     out int state, out int curInstances,
+								     out int maxCollectionCount, out int collectDateTimeout,
+								     byte [] userName, int maxUserNameSize);
 
 		// http://msdn.microsoft.com/en-us/library/aa365800%28VS.85%29.aspx
-		[DllImport ("kernel32")]
+		[DllImport ("kernel32", SetLastError=true)]
 		internal static extern bool WaitNamedPipe (string name, int timeout);
 
 		// http://msdn.microsoft.com/en-us/library/aa363858%28VS.85%29.aspx
-		[DllImport ("kernel32")]
-		internal static extern IntPtr CreateFile (string name, PipeAccessRights desiredAccess, FileShare fileShare, ref SecurityAttributesHack atts, int creationDisposition, int flags, IntPtr templateHandle);
+		[DllImport ("kernel32", SetLastError=true)]
+		internal static extern IntPtr CreateFile (string name, PipeAccessRights desiredAccess, FileShare fileShare,
+					      ref SecurityAttributes atts, int creationDisposition, int flags, IntPtr templateHandle);
 
 	}
 }

+ 202 - 0
mcs/class/System.Core/Test/System.IO.Pipes/PipeSecurityTest.cs

@@ -0,0 +1,202 @@
+// PipeSecurityTest.cs - NUnit Test Cases for PipeSecurity
+//
+// Authors:
+//	James Bellinger  <[email protected]>
+//
+// Copyright (C) 2012 James Bellinger
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.IO.Pipes;
+using System.Security.AccessControl;
+using System.Security.Principal;
+using System.Threading;
+using NUnit.Framework;
+
+namespace MonoTests.System.IO.Pipes
+{
+	[TestFixture]
+	public class PipeSecurityTest
+	{
+		[Test]
+		public void NamedPipeDefaultPermissionsWork ()
+		{
+			if (PlatformID.Win32NT != Environment.OSVersion.Platform) {
+				Assert.Ignore (); return;
+			}
+
+			string name = @"Local\MonoTestPipeNPNPW";
+			using (NamedPipeServerStream server = CreateNamedServer (false, name, null, 0)) {
+				PipeSecurity security = server.GetAccessControl ();
+
+				AuthorizationRuleCollection rules = security.GetAccessRules (true, false,
+				                                                             typeof (SecurityIdentifier));
+				Assert.AreNotEqual (0, rules.Count);
+			}
+		}
+
+		[Test]
+		public void NamedPipeSetAccessControlFailsWithoutChangePermissionRight ()
+		{
+			if (PlatformID.Win32NT != Environment.OSVersion.Platform) {
+				Assert.Ignore (); return;
+			}
+
+			string name = @"Local\MonoTestPipeNPSACFWCPR";
+			using (NamedPipeServerStream server = CreateNamedServer (false, name, null, 0)) {
+				bool unauthorized = false;
+				try {
+					AddDenyEveryone (server);
+				} catch (UnauthorizedAccessException) {
+					unauthorized = true;
+				}
+
+				Assert.IsTrue (unauthorized, "PipeAccessRights.ChangePermissions was not required");
+			}
+		}
+
+		[Test]
+		public void NamedPipePermissionsActuallyWorkSyncAllow ()
+		{
+			NamedPipePermissionsActuallyWorkSync (@"Local\MonoTestPipeNPPAWSA", false);
+		}
+
+		[Test]
+		public void NamedPipePermissionsActuallyWorkSyncDeny ()
+		{
+			NamedPipePermissionsActuallyWorkSync (@"Local\MonoTestPipeNPPAWSD", true);
+		}
+
+		void NamedPipePermissionsActuallyWorkSync (string name, bool addDenyEveryone)
+		{
+			if (PlatformID.Win32NT != Environment.OSVersion.Platform) {
+				Assert.Ignore (); return;
+			}
+
+			PipeSecurity security = new PipeSecurity ();
+			SecurityIdentifier worldSid = new SecurityIdentifier ("WD");
+			PipeAccessRule rule = new PipeAccessRule (worldSid,
+			                                          PipeAccessRights.FullControl,
+			                                          AccessControlType.Allow);
+			security.AddAccessRule (rule);
+
+			using (NamedPipeServerStream server = CreateNamedServer (false, name, security,
+										 PipeAccessRights.ChangePermissions)) {
+				security = server.GetAccessControl ();
+
+				AuthorizationRuleCollection rules;
+				rules = security.GetAccessRules (true, true, typeof (SecurityIdentifier));
+				Assert.AreEqual (1, rules.Count);
+
+				rule = (PipeAccessRule)rules [0];
+				Assert.AreEqual (AccessControlType.Allow, rule.AccessControlType);
+				Assert.AreEqual (worldSid, rule.IdentityReference);
+				Assert.AreEqual (PipeAccessRights.FullControl, rule.PipeAccessRights);
+
+				if (addDenyEveryone)
+					AddDenyEveryone (server);
+
+				bool unauthorized = false;
+				using (NamedPipeClientStream client = CreateNamedClient (false, name)) {
+					try {
+						client.Connect (1000);
+					} catch (UnauthorizedAccessException) {
+						unauthorized = true;
+					}
+				}
+
+				Assert.AreEqual (addDenyEveryone, unauthorized);
+			}
+		}
+
+
+		[Test]
+		[Category ("NotWorking")] // Async is completely broken on Mono Win32 pipes.
+		public void NamedPipePermissionsActuallyWorkAsync ()
+		{
+			if (PlatformID.Win32NT != Environment.OSVersion.Platform) {
+				Assert.Ignore (); return;
+			}
+
+			IAsyncResult waitForConnection;
+			string name = @"Local\MonoTestPipeNPPAWA";
+
+			using (NamedPipeServerStream server = CreateNamedServer (true, name, null,
+										 PipeAccessRights.ChangePermissions)) {
+				// Test connecting to make sure our later test throwing is due to permissions.
+				waitForConnection = server.BeginWaitForConnection (null, null);
+
+				using (NamedPipeClientStream client = CreateNamedClient (true, name)) {
+					client.Connect (1000);
+
+					if (!waitForConnection.AsyncWaitHandle.WaitOne (1000)) {
+						Assert.Fail ("No connection request received."); return;
+					}
+					server.EndWaitForConnection (waitForConnection);
+					server.Disconnect ();
+				}
+
+				// Let's add a Deny for Everyone.
+				AddDenyEveryone (server);
+
+				// This Connect call should fail.
+				waitForConnection = server.BeginWaitForConnection (null, null);
+
+				bool unauthorized = false;
+				using (NamedPipeClientStream client = CreateNamedClient (true, name)) {
+					try {
+						client.Connect (1000);
+					} catch (UnauthorizedAccessException) {
+						unauthorized = true;
+					}
+				}
+
+				Assert.IsTrue (unauthorized, "Client was allowed to connect despite Deny ACE.");
+			}
+		}
+
+		static void AddDenyEveryone (PipeStream stream)
+		{
+			PipeAccessRule rule; PipeSecurity security;
+			AuthorizationRuleCollection inRules, outRules;
+
+			// Let's add a Deny for Everyone.
+			security = stream.GetAccessControl ();
+
+			inRules = security.GetAccessRules (true, false, typeof (SecurityIdentifier));
+			Assert.AreNotEqual (0, inRules.Count);
+
+			rule = new PipeAccessRule (new SecurityIdentifier ("WD"),
+			                           PipeAccessRights.FullControl,
+			                           AccessControlType.Deny);
+			security.AddAccessRule (rule);
+			stream.SetAccessControl (security);
+
+			security = stream.GetAccessControl ();
+			outRules = security.GetAccessRules (true, false, typeof (SecurityIdentifier));
+			Assert.AreEqual (inRules.Count + 1, outRules.Count);
+		}
+
+		static NamedPipeClientStream CreateNamedClient (bool @async, string name)
+		{
+			return new NamedPipeClientStream (".", name,
+			                                  PipeDirection.InOut,
+			                                  @async ? PipeOptions.Asynchronous : PipeOptions.None);
+		}
+
+		static NamedPipeServerStream CreateNamedServer (bool @async, string name,
+		                                           PipeSecurity security,
+		                                           PipeAccessRights additionalRights)
+		{
+			return new NamedPipeServerStream (name,
+			                                  PipeDirection.InOut, 1,
+			                                  PipeTransmissionMode.Byte,
+			                                  @async ? PipeOptions.Asynchronous : PipeOptions.None,
+			                                  512, 512, security,
+			                                  HandleInheritability.None,
+			                                  additionalRights);
+		}
+	}
+}
+