From: Carlos Sanchez Lopez <1175054+carlossanlop@users.noreply.github.com> Date: Mon, 2 Dec 2019 23:54:46 +0000 (-0800) Subject: Add NamedPipeServerStream method that takes an ACL (#317) X-Git-Tag: submit/tizen/20210909.063632~10842 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=034c5a695a312417741811450e25f3bd2f9bc6b6;p=platform%2Fupstream%2Fdotnet%2Fruntime.git Add NamedPipeServerStream method that takes an ACL (#317) Add NamedPipeServerStream method that takes an ACL Approved API proposal: dotnet/corefx#41657 We don't currently have a way to create a pipe with a given ACL in .NET Core. We can modify the ACL, but it would be more secure to have the proper ACL on the pipe from the start. This PR adds a new static class and method that can create an NamedPipeServerStream taking a PipeSecurity object, reusing code that can already perform this task. --- diff --git a/src/libraries/System.IO.Pipes.AccessControl/ref/System.IO.Pipes.AccessControl.cs b/src/libraries/System.IO.Pipes.AccessControl/ref/System.IO.Pipes.AccessControl.cs index ffce891..6f29979 100644 --- a/src/libraries/System.IO.Pipes.AccessControl/ref/System.IO.Pipes.AccessControl.cs +++ b/src/libraries/System.IO.Pipes.AccessControl/ref/System.IO.Pipes.AccessControl.cs @@ -71,4 +71,9 @@ namespace System.IO.Pipes { public static System.IO.Pipes.AnonymousPipeServerStream Create(System.IO.Pipes.PipeDirection direction, System.IO.HandleInheritability inheritability, int bufferSize, System.IO.Pipes.PipeSecurity pipeSecurity) { throw null; } } + + public static class NamedPipeServerStreamAcl + { + public static System.IO.Pipes.NamedPipeServerStream Create(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, System.IO.HandleInheritability inheritability = System.IO.HandleInheritability.None, System.IO.Pipes.PipeAccessRights additionalAccessRights = default) { throw null; } + } } diff --git a/src/libraries/System.IO.Pipes.AccessControl/tests/AnonymousPipeTests/AnonymousPipeServerStreamAclTests.cs b/src/libraries/System.IO.Pipes.AccessControl/tests/AnonymousPipeTests/AnonymousPipeServerStreamAclTests.cs index 4443a75..d92a23b 100644 --- a/src/libraries/System.IO.Pipes.AccessControl/tests/AnonymousPipeTests/AnonymousPipeServerStreamAclTests.cs +++ b/src/libraries/System.IO.Pipes.AccessControl/tests/AnonymousPipeTests/AnonymousPipeServerStreamAclTests.cs @@ -8,10 +8,6 @@ namespace System.IO.Pipes.Tests { public class AnonymousPipeServerStreamAclTests : PipeServerStreamAclTestBase { - private const PipeDirection DefaultPipeDirection = PipeDirection.In; - private const HandleInheritability DefaultInheritability = HandleInheritability.None; - private const int DefaultBufferSize = 1; - [Fact] public void Create_NullSecurity() { @@ -28,10 +24,7 @@ namespace System.IO.Pipes.Tests } [Theory] - [InlineData((PipeDirection)(int.MinValue))] - [InlineData((PipeDirection)0)] - [InlineData((PipeDirection)4)] - [InlineData((PipeDirection)(int.MaxValue))] + [MemberData(nameof(Create_InvalidPipeDirection_MemberData))] public void Create_InvalidPipeDirection(PipeDirection direction) { Assert.Throws(() => @@ -41,10 +34,7 @@ namespace System.IO.Pipes.Tests } [Theory] - [InlineData((HandleInheritability)(int.MinValue))] - [InlineData((HandleInheritability)(-1))] - [InlineData((HandleInheritability)2)] - [InlineData((HandleInheritability)(int.MaxValue))] + [MemberData(nameof(Create_InvalidInheritability_MemberData))] public void Create_InvalidInheritability(HandleInheritability inheritability) { Assert.Throws(() => @@ -54,8 +44,7 @@ namespace System.IO.Pipes.Tests } [Theory] - [InlineData(int.MinValue)] - [InlineData(-1)] + [MemberData(nameof(Create_InvalidBufferSize_MemberData))] public void Create_InvalidBufferSize(int bufferSize) { Assert.Throws(() => @@ -66,7 +55,7 @@ namespace System.IO.Pipes.Tests public static IEnumerable Create_ValidParameters_MemberData() => from direction in new[] { PipeDirection.In, PipeDirection.Out } - from inheritability in Enum.GetValues(typeof(HandleInheritability)).Cast() + from inheritability in new[] { HandleInheritability.None, HandleInheritability.Inheritable } from bufferSize in new[] { 0, 1 } select new object[] { direction, inheritability, bufferSize }; @@ -78,7 +67,7 @@ namespace System.IO.Pipes.Tests } public static IEnumerable Create_CombineRightsAndAccessControl_MemberData() => - from rights in Enum.GetValues(typeof(PipeAccessRights)).Cast() + from rights in s_combinedPipeAccessRights from accessControl in new[] { AccessControlType.Allow, AccessControlType.Deny } select new object[] { rights, accessControl }; @@ -87,22 +76,13 @@ namespace System.IO.Pipes.Tests [MemberData(nameof(Create_CombineRightsAndAccessControl_MemberData))] public void Create_CombineRightsAndAccessControl(PipeAccessRights rights, AccessControlType accessControl) { - // These are the two cases that create a valid pipe when using Allow - if ((rights == PipeAccessRights.FullControl || rights == PipeAccessRights.ReadWrite) && - accessControl == AccessControlType.Allow) + // These are the only two rights that allow creating a pipe when using Allow + if (accessControl == AccessControlType.Allow && + (rights == PipeAccessRights.FullControl || rights == PipeAccessRights.ReadWrite)) { VerifyValidSecurity(rights, accessControl); } - // When creating the PipeAccessRule for the PipeSecurity, the PipeAccessRule constructor calls AccessMaskFromRights, which explicilty removes the Synchronize bit from rights when AccessControlType is Deny - // and rights is not FullControl, so using Synchronize with Deny is not allowed - else if (rights == PipeAccessRights.Synchronize && accessControl == AccessControlType.Deny) - { - Assert.Throws("accessMask", () => - { - PipeSecurity security = GetPipeSecurity(WellKnownSidType.BuiltinUsersSid, PipeAccessRights.Synchronize, AccessControlType.Deny); - }); - } - // Any other case is not authorized + // Any other combination is not authorized else { PipeSecurity security = GetPipeSecurity(WellKnownSidType.BuiltinUsersSid, rights, accessControl); @@ -113,11 +93,12 @@ namespace System.IO.Pipes.Tests } } - [Theory] - [InlineData(PipeAccessRights.ReadWrite | PipeAccessRights.Synchronize, AccessControlType.Allow)] - public void Create_ValidBitwiseRightsSecurity(PipeAccessRights rights, AccessControlType accessControl) + [Fact] + public void Create_ValidBitwiseRightsSecurity() { - VerifyValidSecurity(rights, accessControl); + // Synchronize gets removed from the bitwise combination, + // but ReadWrite (an allowed right) should remain untouched + VerifyValidSecurity(PipeAccessRights.ReadWrite | PipeAccessRights.Synchronize, AccessControlType.Allow); } private void VerifyValidSecurity(PipeAccessRights rights, AccessControlType accessControl) diff --git a/src/libraries/System.IO.Pipes.AccessControl/tests/NamedPipeTests/NamedPipeServerStreamAclTests.cs b/src/libraries/System.IO.Pipes.AccessControl/tests/NamedPipeTests/NamedPipeServerStreamAclTests.cs new file mode 100644 index 0000000..893bda0 --- /dev/null +++ b/src/libraries/System.IO.Pipes.AccessControl/tests/NamedPipeTests/NamedPipeServerStreamAclTests.cs @@ -0,0 +1,316 @@ +using System.Collections.Generic; +using System.Linq; +using System.Security.AccessControl; +using System.Security.Cryptography; +using System.Security.Principal; +using Xunit; + +namespace System.IO.Pipes.Tests +{ + public class NamedPipeServerStreamAclTests : PipeServerStreamAclTestBase + { + private const int DefaultNumberOfServerInstances = 1; + private const PipeTransmissionMode DefaultPipeTransmissionMode = PipeTransmissionMode.Byte; + private const PipeOptions DefaultPipeOptions = PipeOptions.None; + + [Fact] + public void Create_NullSecurity() + { + CreateNamedPipe(GetRandomName(), expectedSecurity: null).Dispose(); + CreateNamedPipe(GetRandomName(), expectedSecurity: null, options: PipeOptions.WriteThrough).Dispose(); + CreateNamedPipe(GetRandomName(), expectedSecurity: null, options: PipeOptions.Asynchronous).Dispose(); + } + + [Theory] + [InlineData((PipeOptions)(-1))] + [InlineData((PipeOptions)1)] + [InlineData((PipeOptions)int.MaxValue)] + public void Create_InvalidOptions(PipeOptions options) + { + Assert.Throws("options", () => + { + CreateAndVerifyNamedPipe(GetRandomName(), GetBasicPipeSecurity(), options: options).Dispose(); + }); + } + + [Theory] + [InlineData(PipeOptions.None)] + [InlineData(PipeOptions.Asynchronous)] + [InlineData(PipeOptions.WriteThrough)] + [InlineData(PipeOptions.Asynchronous | PipeOptions.WriteThrough)] + public void Create_ValidOptions(PipeOptions options) + { + CreateAndVerifyNamedPipe(GetRandomName(), GetBasicPipeSecurity(), options: options).Dispose(); + } + + // Creating a pipe with CurrentUserOnly should be allowed only when the passed pipeSecurity is null. + [Fact] + public void Create_NullSecurity_PipeOptionsCurrentUserOnly() + { + using NamedPipeServerStream pipe = CreateNamedPipe(GetRandomName(), null, options: PipeOptions.CurrentUserOnly); + PipeSecurity actualSecurity = pipe.GetAccessControl(); + PipeSecurity expectedSecurity = GetPipeSecurityForCurrentUserOnly(); + VerifyPipeSecurity(expectedSecurity, actualSecurity); + } + + // We do not allow using PipeOptions.CurrentUserOnly and passing a PipeSecurity object at the same time, + // because the Create method will force the usage of a custom PipeSecurity instance assigned to the + // current user with full control allowed + [Fact] + public void Create_ValidSecurity_PipeOptionsCurrentUserOnly() + { + Assert.Throws("pipeSecurity", () => + { + CreateNamedPipe(GetRandomName(), GetBasicPipeSecurity(), options: PipeOptions.CurrentUserOnly); + }); + } + + [Fact] + public void Create_InvalidName() + { + Assert.Throws(() => + { + CreateNamedPipe(pipeName: "", GetBasicPipeSecurity()); + }); + + Assert.Throws("pipeName", () => + { + CreateNamedPipe(pipeName: null, GetBasicPipeSecurity()); + }); + + Assert.Throws("pipeName", () => + { + CreateNamedPipe(pipeName: "anonymous", GetBasicPipeSecurity()); + }); + } + + [Theory] + [MemberData(nameof(Create_InvalidPipeDirection_MemberData))] + public void Create_InvalidPipeDirection(PipeDirection direction) + { + Assert.Throws(() => + { + CreateAndVerifyNamedPipe(GetRandomName(), GetBasicPipeSecurity(), direction: direction).Dispose(); + }); + } + + [Theory] + [InlineData(int.MinValue)] + [InlineData(0)] + [InlineData(255)] + [InlineData(int.MaxValue)] + public void Create_InvalidMaxNumberOfServerInstances(int maxNumberOfServerInstances) + { + Assert.Throws("maxNumberOfServerInstances", () => + { + CreateAndVerifyNamedPipe(GetRandomName(), GetBasicPipeSecurity(), maxNumberOfServerInstances: maxNumberOfServerInstances).Dispose(); + }); + } + + [Theory] + [InlineData(-1)] // We interpret -1 as MaxAllowedServerInstances. + [InlineData(1)] + [InlineData(2)] + [InlineData(254)] + public void Create_ValidMaxNumberOfServerInstances(int instances) + { + CreateAndVerifyNamedPipe(GetRandomName(), GetBasicPipeSecurity(), maxNumberOfServerInstances: instances).Dispose(); + } + + [Theory] + [InlineData((PipeTransmissionMode)(-1))] + [InlineData((PipeTransmissionMode)2)] + public void Create_InvalidTransmissionMode(PipeTransmissionMode transmissionMode) + { + Assert.Throws("transmissionMode", () => + { + CreateAndVerifyNamedPipe(GetRandomName(), GetBasicPipeSecurity(), transmissionMode: transmissionMode).Dispose(); + }); + } + + [Theory] + [MemberData(nameof(Create_InvalidBufferSize_MemberData))] + public void Create_InvalidInBufferSize(int inBufferSize) + { + Assert.Throws(() => + { + CreateAndVerifyNamedPipe(GetRandomName(), GetBasicPipeSecurity(), inBufferSize: inBufferSize).Dispose(); + }); + } + + [Theory] + [MemberData(nameof(Create_InvalidBufferSize_MemberData))] + public void Create_InvalidOutBufferSize(int outBufferSize) + { + Assert.Throws(() => + { + CreateAndVerifyNamedPipe(GetRandomName(), GetBasicPipeSecurity(), outBufferSize: outBufferSize).Dispose(); + }); + } + + [Theory] + [MemberData(nameof(Create_InvalidInheritability_MemberData))] + public void Create_InvalidInheritability(HandleInheritability inheritability) + { + Assert.Throws(() => + { + CreateAndVerifyNamedPipe(GetRandomName(), GetBasicPipeSecurity(), inheritability: inheritability).Dispose(); + }); + } + + [Theory] + [InlineData(PipeAccessRights.Read)] + [InlineData(PipeAccessRights.ReadExtendedAttributes)] + [InlineData(PipeAccessRights.ReadAttributes)] + [InlineData(PipeAccessRights.ReadPermissions)] + [InlineData(PipeAccessRights.Write)] + [InlineData(PipeAccessRights.WriteExtendedAttributes)] + [InlineData(PipeAccessRights.WriteAttributes)] + public void Create_InvalidAdditionalAccessRights(PipeAccessRights additionalAccessRights) + { + // GetBasicPipeSecurity returns an object created with PipeAccessRights.ReadWrite as default. This enum is formed by: + // - PipeAccessRights.Read: This enum is formed by: + // - PipeAccessRights.ReadData | PipeAccessRights.ReadExtendedAttributes | PipeAccessRights.ReadAttributes | PipeAccessRights.ReadPermissions + // - PipeAccessRights.Write: This enum is formed by: + // - PipeAccessRights.WriteData | PipeAccessRights.WriteExtendedAttributes | PipeAccessRights.WriteAttributes + + // additionalAccessRights gets bitwise merged with the 'dwOpenMode' parameter we pass to CreateNamedPipeW. + // This parameter can acquire any of the values described here: https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-createnamedpipea + // It's particularly important to mention that two of the accepted values collide with the value of two PipeAccessRights enum values: + // - ReadData (0x1): Same value as PIPE_ACCESS_INBOUND + // - WriteData (0x2): Same value as PIPE_ACCESS_OUTBOUND + + // Any other value will throw with the message 'The parameter is incorrect.' + Assert.Throws(() => + { + Create_AdditionalAccessRights(additionalAccessRights).Dispose(); + }); + } + + [Theory] + [InlineData(PipeAccessRights.CreateNewInstance)] + [InlineData(PipeAccessRights.Delete)] + public void Create_WindowsNotAcceptedAdditionalAccessRights(PipeAccessRights additionalAccessRights) + { + // Exception message: "The parameter is incorrect." + // Neither CreateNewInstance (0x4) nor Delete (0x10000) collide with any of the dwOpenMode values that get into the bitwise combination: + // PipeOptions, PipeDirection, Interop.Kernel32.FileOperations.FILE_FLAG_FIRST_PIPE_INSTANCE + // But Windows does not accept them anyway + Assert.Throws(() => + { + Create_AdditionalAccessRights(additionalAccessRights).Dispose(); + }); + } + + [Fact] + public void Create_NotEnoughPrivilegesAdditionalAccessRights() + { + // Exception message: "A required privilege is not held by the client" + Assert.Throws(() => + { + Create_AdditionalAccessRights(PipeAccessRights.AccessSystemSecurity).Dispose(); + }); + } + + [Theory] + [InlineData(PipeAccessRights.ReadData)] + [InlineData(PipeAccessRights.WriteData)] + [InlineData(PipeAccessRights.ChangePermissions)] + [InlineData(PipeAccessRights.TakeOwnership)] + public void Create_ValidAdditionalAccessRights(PipeAccessRights additionalAccessRights) + { + using var pipe = Create_AdditionalAccessRights(additionalAccessRights); + + // This contains the rights added to BasicPipeSecurity plus the one we are testing + PipeSecurity expectedPipeSecurity = GetPipeSecurity(WellKnownSidType.BuiltinUsersSid, additionalAccessRights | PipeAccessRights.ReadWrite, AccessControlType.Allow); + + // additional should be applied to the pipe, so actual should be identical to expected + PipeSecurity actualPipeSecurity = pipe.GetAccessControl(); + + VerifyPipeSecurity(expectedPipeSecurity, actualPipeSecurity); + } + + private NamedPipeServerStream Create_AdditionalAccessRights(PipeAccessRights additionalAccessRights) + { + // GetBasicPipeSecurity returns an object created with PipeAccessRights.ReadWrite as default + PipeSecurity initialPipeSecurity = GetBasicPipeSecurity(); + return CreateNamedPipe(GetRandomName(), initialPipeSecurity, additionalAccessRights: additionalAccessRights); + } + + public static IEnumerable Create_ValidParameters_MemberData() => + from options in new[] { PipeOptions.None, PipeOptions.Asynchronous, PipeOptions.WriteThrough } + from direction in new[] { PipeDirection.In, PipeDirection.Out, PipeDirection.InOut } + from transmissionMode in new[] { PipeTransmissionMode.Byte, PipeTransmissionMode.Message } + from inheritability in new[] { HandleInheritability.None, HandleInheritability.Inheritable } + from inBufferSize in new[] { 0, 1 } + from outBufferSize in new[] { 0, 1 } + from maxNumberOfServerInstances in new[] { -1, 1, 254 } + from rights in s_combinedPipeAccessRights + from controlType in new[] { AccessControlType.Allow, AccessControlType.Deny } + select new object[] { options, direction, transmissionMode, inheritability, inBufferSize, outBufferSize, maxNumberOfServerInstances, rights, controlType }; + + [Theory] + [MemberData(nameof(Create_ValidParameters_MemberData))] + public void Create_ValidParameters(PipeOptions options, PipeDirection direction, PipeTransmissionMode transmissionMode, HandleInheritability inheritability, int inBufferSize, int outBufferSize, int maxNumberOfServerInstances, PipeAccessRights rights, AccessControlType controlType) + { + if (controlType != AccessControlType.Deny && (rights & ~PipeAccessRights.Synchronize) != 0) + { + PipeSecurity security = GetPipeSecurity(WellKnownSidType.BuiltinUsersSid, rights, controlType); + CreateAndVerifyNamedPipe(GetRandomName(), security, direction, maxNumberOfServerInstances, transmissionMode, options, inBufferSize, outBufferSize, inheritability, 0).Dispose(); + } + } + + private NamedPipeServerStream CreateAndVerifyNamedPipe( + string pipeName, + PipeSecurity expectedSecurity, + PipeDirection direction = DefaultPipeDirection, + int maxNumberOfServerInstances = DefaultNumberOfServerInstances, + PipeTransmissionMode transmissionMode = DefaultPipeTransmissionMode, + PipeOptions options = DefaultPipeOptions, + int inBufferSize = DefaultBufferSize, + int outBufferSize = DefaultBufferSize, + HandleInheritability inheritability = DefaultInheritability, + PipeAccessRights additionalAccessRights = 0) + { + NamedPipeServerStream pipe = CreateNamedPipe(pipeName, expectedSecurity, direction, maxNumberOfServerInstances, transmissionMode, options, inBufferSize, outBufferSize, inheritability, additionalAccessRights); + + if (expectedSecurity != null) + { + PipeSecurity actualSecurity = pipe.GetAccessControl(); + VerifyPipeSecurity(expectedSecurity, actualSecurity); + } + return pipe; + } + + private NamedPipeServerStream CreateNamedPipe( + string pipeName, + PipeSecurity expectedSecurity, + PipeDirection direction = DefaultPipeDirection, + int maxNumberOfServerInstances = DefaultNumberOfServerInstances, + PipeTransmissionMode transmissionMode = DefaultPipeTransmissionMode, + PipeOptions options = DefaultPipeOptions, + int inBufferSize = DefaultBufferSize, + int outBufferSize = DefaultBufferSize, + HandleInheritability inheritability = DefaultInheritability, + PipeAccessRights additionalAccessRights = 0) + { + NamedPipeServerStream pipe = NamedPipeServerStreamAcl.Create(pipeName, direction, maxNumberOfServerInstances, transmissionMode, options, inBufferSize, outBufferSize, expectedSecurity, inheritability, additionalAccessRights); + Assert.NotNull(pipe); + return pipe; + } + + // This is the code we use in the Create method called by the NamedPipeServerStream constructor + private PipeSecurity GetPipeSecurityForCurrentUserOnly() + { + PipeSecurity security = new PipeSecurity(); + + using WindowsIdentity currentIdentity = WindowsIdentity.GetCurrent(); + SecurityIdentifier identifier = currentIdentity.Owner; + PipeAccessRule rule = new PipeAccessRule(identifier, PipeAccessRights.FullControl, AccessControlType.Allow); + security.AddAccessRule(rule); + security.SetOwner(identifier); + + return security; + } + } +} diff --git a/src/libraries/System.IO.Pipes.AccessControl/tests/PipeServerStreamAclTestBase.cs b/src/libraries/System.IO.Pipes.AccessControl/tests/PipeServerStreamAclTestBase.cs index 679710e..b1d1b13 100644 --- a/src/libraries/System.IO.Pipes.AccessControl/tests/PipeServerStreamAclTestBase.cs +++ b/src/libraries/System.IO.Pipes.AccessControl/tests/PipeServerStreamAclTestBase.cs @@ -8,11 +8,50 @@ namespace System.IO.Pipes.Tests { public class PipeServerStreamAclTestBase { + protected const PipeDirection DefaultPipeDirection = PipeDirection.In; + protected const HandleInheritability DefaultInheritability = HandleInheritability.None; + protected const int DefaultBufferSize = 1; + + // As it is documented in the source definition of the PipeAccessRights enum, we do not have a 0 value on purpose (can't grant nor deny "nothing"). + // So ReadWrite will be used in these unit tests the sole minimum additional granted right, considering that AnonymousPipeServerStreams can only + // get created with either ReadWrite or FullControl. + protected const PipeAccessRights DefaultAccessRight = PipeAccessRights.ReadWrite; + + // PipeAccessRights.Synchronize is not included in this arary because it is handled in a special way inside the PipeAccessRuleInstance constructor when creating the access mask: If Deny is specified, Synchronize gets removed from the rights. + // So this right's behavior is verified separately in the System.IO.Pipes.Tests.PipeTest_AclExtensions.PipeSecurity_VerifySynchronizeMasks unit test. + protected static readonly PipeAccessRights[] s_mostRights = new[] + { + PipeAccessRights.ReadData, + PipeAccessRights.WriteData, + PipeAccessRights.CreateNewInstance, + PipeAccessRights.ReadExtendedAttributes, + PipeAccessRights.WriteExtendedAttributes, + PipeAccessRights.ReadAttributes, + PipeAccessRights.WriteAttributes, + PipeAccessRights.Write, + PipeAccessRights.Delete, + PipeAccessRights.ReadPermissions, + PipeAccessRights.Read, + PipeAccessRights.ReadWrite, + PipeAccessRights.ChangePermissions, + PipeAccessRights.TakeOwnership, + PipeAccessRights.FullControl, + PipeAccessRights.AccessSystemSecurity + }; + + protected static readonly PipeAccessRights[] s_bitWisePipeAccessRights = new[] + { + PipeAccessRights.ChangePermissions | PipeAccessRights.ReadPermissions, + PipeAccessRights.ReadExtendedAttributes | PipeAccessRights.WriteExtendedAttributes + }; + + protected static IEnumerable s_combinedPipeAccessRights = s_mostRights.Concat(s_bitWisePipeAccessRights); + protected PipeSecurity GetBasicPipeSecurity() { return GetPipeSecurity( WellKnownSidType.BuiltinUsersSid, - PipeAccessRights.FullControl, + DefaultAccessRight, AccessControlType.Allow); } @@ -55,5 +94,38 @@ namespace System.IO.Pipes.Tests expectedRule.InheritanceFlags == actualRule.InheritanceFlags && expectedRule.PropagationFlags == actualRule.PropagationFlags; } + + protected string GetRandomName() + { + return Guid.NewGuid().ToString("N"); + } + + public static IEnumerable Create_MostAccessRights_MemberData() => + from rights in s_mostRights + select new object[] { rights }; + + public static IEnumerable Create_InvalidPipeDirection_MemberData() => + from direction in new[] + { + (PipeDirection)(int.MinValue), + (PipeDirection)0, + (PipeDirection)4, + (PipeDirection)(int.MaxValue) + } + select new object[] { direction }; + + public static IEnumerable Create_InvalidInheritability_MemberData() => + from inheritability in new[] + { + (HandleInheritability)int.MinValue, + (HandleInheritability)(-1), + (HandleInheritability)2, + (HandleInheritability)int.MaxValue + } + select new object[] { inheritability }; + + public static IEnumerable Create_InvalidBufferSize_MemberData() => + from bufferSize in new[] { int.MinValue, -1 } + select new object[] { bufferSize }; } } diff --git a/src/libraries/System.IO.Pipes.AccessControl/tests/PipeTest.AclExtensions.cs b/src/libraries/System.IO.Pipes.AccessControl/tests/PipeTest.AclExtensions.cs index 011207c..3b99f30 100644 --- a/src/libraries/System.IO.Pipes.AccessControl/tests/PipeTest.AclExtensions.cs +++ b/src/libraries/System.IO.Pipes.AccessControl/tests/PipeTest.AclExtensions.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Security.AccessControl; +using System.Security.Principal; using Xunit; namespace System.IO.Pipes.Tests @@ -81,5 +83,20 @@ namespace System.IO.Pipes.Tests pair.writeablePipe.SetAccessControl(security); } } + + // This test matches NetFX behavior + [Fact] + public void PipeSecurity_VerifySynchronizeMasks() + { + var si = new SecurityIdentifier(WellKnownSidType.BuiltinUsersSid, null); + + // This is a valid mask that should not throw + new PipeAccessRule(si, PipeAccessRights.Synchronize, AccessControlType.Allow); + + Assert.Throws("accessMask", () => + { + new PipeAccessRule(si, PipeAccessRights.Synchronize, AccessControlType.Deny); + }); + } } } diff --git a/src/libraries/System.IO.Pipes.AccessControl/tests/System.IO.Pipes.AccessControl.Tests.csproj b/src/libraries/System.IO.Pipes.AccessControl/tests/System.IO.Pipes.AccessControl.Tests.csproj index 7bd84f2..30fdc15 100644 --- a/src/libraries/System.IO.Pipes.AccessControl/tests/System.IO.Pipes.AccessControl.Tests.csproj +++ b/src/libraries/System.IO.Pipes.AccessControl/tests/System.IO.Pipes.AccessControl.Tests.csproj @@ -8,6 +8,7 @@ + diff --git a/src/libraries/System.IO.Pipes/src/MatchingRefApiCompatBaseline.txt b/src/libraries/System.IO.Pipes/src/MatchingRefApiCompatBaseline.txt index 4bf6540..45d7379 100644 --- a/src/libraries/System.IO.Pipes/src/MatchingRefApiCompatBaseline.txt +++ b/src/libraries/System.IO.Pipes/src/MatchingRefApiCompatBaseline.txt @@ -1,5 +1,6 @@ # Exposed public in System.IO.Pipes.AccessControl but implemented in System.IO.Pipes TypesMustExist : Type 'System.IO.Pipes.AnonymousPipeServerStreamAcl' does not exist in the reference but it does exist in the implementation. +TypesMustExist : Type 'System.IO.Pipes.NamedPipeServerStreamAcl' does not exist in the reference but it does exist in the implementation. TypesMustExist : Type 'System.IO.Pipes.PipeAccessRights' does not exist in the reference but it does exist in the implementation. TypesMustExist : Type 'System.IO.Pipes.PipeAccessRule' does not exist in the reference but it does exist in the implementation. TypesMustExist : Type 'System.IO.Pipes.PipeAuditRule' does not exist in the reference but it does exist in the implementation. diff --git a/src/libraries/System.IO.Pipes/src/Resources/Strings.resx b/src/libraries/System.IO.Pipes/src/Resources/Strings.resx index b22a3ea..c8193e4 100644 --- a/src/libraries/System.IO.Pipes/src/Resources/Strings.resx +++ b/src/libraries/System.IO.Pipes/src/Resources/Strings.resx @@ -1,4 +1,5 @@ - + +