Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Specify a security descriptor when using global Mutex #110416

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
using System.IO;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Security.AccessControl;
using System.Security.Principal;
using System.Text;
using System.Threading;
using Microsoft.Win32;
Expand All @@ -22,6 +24,13 @@ internal static void EnterMutexWithoutGlobal(string mutexName, ref Mutex mutex)
{
Mutex tmpMutex = new Mutex(false, mutexName, out _);

// Specify a SID in case the mutex has not yet been created; this prevents it from using the SID from the current thread.
// This SID (AuthenticatedUserSid) is the same one used by .NET Framework.
MutexSecurity sec = new MutexSecurity();
SecurityIdentifier authenticatedUserSid = new SecurityIdentifier(WellKnownSidType.AuthenticatedUserSid, null);
sec.AddAccessRule(new MutexAccessRule(authenticatedUserSid, MutexRights.Synchronize | MutexRights.Modify, AccessControlType.Allow));
tmpMutex.SetAccessControl(sec);

SafeWaitForMutex(tmpMutex, ref mutex);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ System.Diagnostics.EventLog</PackageDescription>
ReferenceOutputAssembly="false"
OutputItemType="TfmRuntimeSpecificPackageFile"
PrivateAssets="true" />
<ProjectReference Include="$(LibrariesProjectRoot)System.Threading.AccessControl\src\System.Threading.AccessControl.csproj" />
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This new dependency will also need to go into the ASP.NET shared framework:

System.Diagnostics.EventLog;

Looks like it was already present in WindowsDesktop:

System.Threading.AccessControl;

</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>$(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent);$(NetCoreAppMinimum)-windows;$(NetCoreAppMinimum);netstandard2.0;$(NetFrameworkMinimum)</TargetFrameworks>
Expand Down Expand Up @@ -139,6 +139,7 @@ System.Diagnostics.PerformanceCounter</PackageDescription>

<ItemGroup Condition="'$(TargetFrameworkIdentifier)' == '.NETCoreApp'">
<ProjectReference Include="$(LibrariesProjectRoot)System.Configuration.ConfigurationManager\src\System.Configuration.ConfigurationManager.csproj" />
<ProjectReference Include="$(LibrariesProjectRoot)System.Threading.AccessControl\src\System.Threading.AccessControl.csproj" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Security.AccessControl;
using System.Security.Principal;
using System.Threading;
using Xunit;

namespace System.Diagnostics.Tests
{
public static class MutexTests
{
[ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotInAppContainer))] // Can't create global objects in appcontainer
public static void VerifySecurityIdentifier()
{
string mutexName = $"{Guid.NewGuid():N}";

Mutex mutex = null;

// We can't test with the same global mutex used by performance counters, since the mutex was likely already
// initialized elsewhere and perhaps with with different ACLs, so we use a Guid to create a new mutex and
// then simulate the behavior by calling into EnterMutex() like the performance monitor code does.
#pragma warning disable CS0436 // Type conflicts with imported type
NetFrameworkUtils.EnterMutex(mutexName, ref mutex);
Comment on lines +20 to +24
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this is just testing the utility code without invoking the product at all? Could we instead do something to ensure the product code creates the Mutex, then open the Mutex created by the product and examine the ACL? I think the test should be able to access it since it's running as the same user.

#pragma warning restore CS0436

try
{
// This is the SID that is added by EnterMutex().
SecurityIdentifier authenticatedUserSid = new(WellKnownSidType.AuthenticatedUserSid, null);

MutexSecurity security = mutex.GetAccessControl();
AuthorizationRuleCollection rules = security.GetAccessRules(true, true, typeof(SecurityIdentifier));
Assert.Equal(1, rules.Count);
MutexAccessRule accessRule = (MutexAccessRule)rules[0];
SecurityIdentifier sid = (SecurityIdentifier)accessRule.IdentityReference;
Assert.Equal(authenticatedUserSid, sid);
}
finally
{
if (mutex != null)
{
mutex.ReleaseMutex();
mutex.Close();
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,20 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<IncludeRemoteExecutor>true</IncludeRemoteExecutor>
<TargetFrameworks>$(NetCoreAppCurrent)-windows;$(NetFrameworkMinimum)</TargetFrameworks>
</PropertyGroup>
<ItemGroup>
<Compile Include="PerformanceCounterTests.cs" />
<Compile Include="PerformanceCounterCategoryTests.cs" />
<Compile Include="ValidationTests.cs" Condition="'$(TargetFrameworkIdentifier)' == '.NETCoreApp'" />
<Compile Include="CounterSampleTests.cs" />
<Compile Include="CounterSampleCalculatorTests.cs" />
<Compile Include="CounterCreationDataTests.cs" />
<Compile Include="CounterCreationDataCollectionTests.cs" />
<Compile Include="InstanceDataTests.cs" />
<Compile Include="CounterCreationDataTests.cs" />
<Compile Include="CounterSampleCalculatorTests.cs" />
<Compile Include="CounterSampleTests.cs" />
<Compile Include="PerformanceCounterCategoryTests.cs" />
<Compile Include="PerformanceCounterTests.cs" />
<Compile Include="PerformanceDataTests.cs" />
<Compile Include="Helpers.cs" />
<Compile Include="InstanceDataTests.cs" />
<Compile Include="MutexTests.cs" Condition="'$(TargetFrameworkIdentifier)' == '.NETCoreApp'" />
<Compile Include="ValidationTests.cs" Condition="'$(TargetFrameworkIdentifier)' == '.NETCoreApp'" />
<Compile Include="$(CommonPath)System\Diagnostics\NetFrameworkUtils.cs" Link="Common\System\Diagnostics\NetFrameworkUtils.cs" Condition="'$(TargetFrameworkIdentifier)' == '.NETCoreApp'" />
<Compile Include="$(CommonTestPath)System\ShouldNotBeInvokedException.cs" Link="Common\System\ShouldNotBeInvokedException.cs" />
<Compile Include="PerformanceDataTests.cs" />
</ItemGroup>
<ItemGroup>
<Content Include="provider.man" CopyToOutputDirectory="Always" />
Expand All @@ -24,4 +26,4 @@
<!-- Some internal types are needed, so we reference the implementation assembly, rather than the reference assembly. -->
<ProjectReference Include="..\src\System.Diagnostics.PerformanceCounter.csproj" SkipUseReferenceAssembly="true" />
</ItemGroup>
</Project>
</Project>
Loading