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

Implement Environment.IsPrivilegedProcess #77355

Merged
merged 8 commits into from
Nov 3, 2022
Merged
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 @@ -12,7 +12,7 @@ internal static partial class Advapi32
[LibraryImport(Interop.Libraries.Advapi32, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static unsafe partial bool GetTokenInformation(
SafeAccessTokenHandle TokenHandle,
SafeTokenHandle TokenHandle,
TOKEN_INFORMATION_CLASS TokenInformationClass,
void* TokenInformation,
uint TokenInformationLength,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;

internal static partial class Interop
{
internal static partial class Advapi32
{
[Flags]
internal enum TOKEN_ACCESS_LEVELS : uint
{
AssignPrimary = 0x00000001,
Duplicate = 0x00000002,
Impersonate = 0x00000004,
Query = 0x00000008,
QuerySource = 0x00000010,
AdjustPrivileges = 0x00000020,
AdjustGroups = 0x00000040,
AdjustDefault = 0x00000080,
AdjustSessionId = 0x00000100,

Read = 0x00020000 | Query,

Write = 0x00020000 | AdjustPrivileges | AdjustGroups | AdjustDefault,

AllAccess = 0x000F0000 |
AssignPrimary |
Duplicate |
Impersonate |
Query |
QuerySource |
AdjustPrivileges |
AdjustGroups |
AdjustDefault |
AdjustSessionId,

MaximumAllowed = 0x02000000
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ public static unsafe bool IsProcessElevated()
return(userId == 0);
}

SafeAccessTokenHandle token;
if (!Interop.Advapi32.OpenProcessToken(Interop.Kernel32.GetCurrentProcess(), TokenAccessLevels.Read, out token))
SafeTokenHandle token;
if (!Interop.Advapi32.OpenProcessToken(Interop.Kernel32.GetCurrentProcess(), (int)TokenAccessLevels.Read, out token))
{
throw new Win32Exception(Marshal.GetLastWin32Error(), "Open process token failed");
}
Expand Down
6 changes: 4 additions & 2 deletions src/libraries/Common/tests/TestUtilities/TestUtilities.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,8 @@
Link="Common\Interop\Windows\Kernel32\Interop.SECURITY_ATTRIBUTES.cs" />
<Compile Include="$(CommonPath)Interop\Windows\NtDll\Interop.RtlGetVersion.cs"
Link="Common\Interop\Windows\NtDll\Interop.RtlGetVersion.cs" />
<Compile Include="$(CommonPath)Interop\Windows\Advapi32\Interop.OpenProcessToken_SafeAccessTokenHandle.cs"
Link="Common\Interop\Windows\Advapi32\Interop.OpenProcessToken_SafeAccessTokenHandle.cs" />
<Compile Include="$(CommonPath)Interop\Windows\Advapi32\Interop.OpenProcessToken.cs"
Link="Common\Interop\Windows\Advapi32\Interop.OpenProcessToken.cs" />
<Compile Include="$(CommonPath)Interop\Windows\Interop.Libraries.cs"
Link="Common\Interop\Windows\Interop.Libraries.cs" />
<Compile Include="$(CommonPath)Interop\Windows\Kernel32\Interop.CloseHandle.cs"
Expand All @@ -79,6 +79,8 @@
<Compile Include="$(CommonPath)Interop\Windows\Interop.BOOL.cs"
Link="Common\Interop\Windows\Interop.BOOL.cs" />

<Compile Include="$(CommonPath)Microsoft\Win32\SafeHandles\SafeTokenHandle.cs"
Link="Common\Microsoft\Win32\SafeHandles\SafeTokenHandle.cs"/>
<Compile Include="$(CommonPath)System\IO\PathInternal.Windows.cs"
Link="Common\System\IO\PathInternal.Windows.cs" />
</ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1414,9 +1414,15 @@
<Compile Include="$(CommonPath)Interop\Windows\Advapi32\Interop.EventWriteTransfer.cs">
<Link>Common\Interop\Windows\Advapi32\Interop.EventWriteTransfer.cs</Link>
</Compile>
<Compile Include="$(CommonPath)Interop\Windows\Advapi32\Interop.GetTokenInformation_void.cs">
<Link>Common\Interop\Windows\Advapi32\Interop.GetTokenInformation_void.cs</Link>
</Compile>
<Compile Include="$(CommonPath)Interop\Windows\Advapi32\Interop.LookupAccountNameW.cs">
<Link>Common\Interop\Windows\Advapi32\Interop.LookupAccountNameW.cs</Link>
</Compile>
<Compile Include="$(CommonPath)Interop\Windows\Advapi32\Interop.OpenProcessToken.cs">
<Link>Common\Interop\Windows\Advapi32\Interop.OpenProcessToken.cs</Link>
</Compile>
<Compile Include="$(CommonPath)Interop\Windows\Advapi32\Interop.RegCloseKey.cs">
<Link>Common\Interop\Windows\Advapi32\Interop.RegCloseKey.cs</Link>
</Compile>
Expand Down Expand Up @@ -1450,6 +1456,15 @@
<Compile Include="$(CommonPath)Interop\Windows\Advapi32\Interop.RegSetValueEx.cs">
<Link>Common\Interop\Windows\Advapi32\Interop.RegSetValueEx.cs</Link>
</Compile>
<Compile Include="$(CommonPath)Interop\Windows\Advapi32\Interop.TOKEN_ACCESS_LEVELS.cs">
<Link>Common\Interop\Windows\Advapi32\Interop.TOKEN_ACCESS_LEVELS.cs</Link>
</Compile>
<Compile Include="$(CommonPath)Interop\Windows\Advapi32\Interop.TOKEN_ELEVATION.cs">
<Link>Common\Interop\Windows\Advapi32\Interop.TOKEN_ELEVATION.cs</Link>
</Compile>
<Compile Include="$(CommonPath)Interop\Windows\Advapi32\Interop.TOKEN_INFORMATION_CLASS.cs">
<Link>Common\Interop\Windows\Advapi32\Interop.TOKEN_INFORMATION_CLASS.cs</Link>
</Compile>
<Compile Include="$(CommonPath)Interop\Windows\BCrypt\Interop.BCryptGenRandom.cs">
<Link>Common\Interop\Windows\BCrypt\Interop.BCryptGenRandom.cs</Link>
</Compile>
Expand Down Expand Up @@ -1882,6 +1897,9 @@
<Compile Include="$(CommonPath)Interop\Windows\Kernel32\Interop.GetModuleHandle.cs">
<Link>Common\Interop\Windows\Kernel32\Interop.GetModuleHandle.cs</Link>
</Compile>
<Compile Include="$(CommonPath)Microsoft\Win32\SafeHandles\SafeTokenHandle.cs">
<Link>Common\Microsoft\Win32\SafeHandles\SafeTokenHandle.cs</Link>
</Compile>
<Compile Include="$(CommonPath)System\IO\FileSystem.Attributes.Windows.cs">
<Link>Common\System\IO\FileSystem.Attributes.Windows.cs</Link>
</Compile>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ private static OperatingSystem GetOSVersion()
return new OperatingSystem(PlatformID.Other, new Version(1, 0, 0, 0));
}

private static bool IsPrivilegedProcessCore() => false;

private static int GetProcessId() => 42;

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ public static string MachineName

public static string UserName => Interop.Sys.GetUserNameFromPasswd(Interop.Sys.GetEUid());

private static bool IsPrivilegedProcessCore() => Interop.Sys.GetEUid() == 0;

[MethodImplAttribute(MethodImplOptions.NoInlining)] // Avoid inlining PInvoke frame into the hot path
private static int GetProcessId() => Interop.Sys.GetPid();

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Microsoft.Win32.SafeHandles;
using System.Diagnostics;
using System.IO;
using System.Runtime.CompilerServices;
Expand Down Expand Up @@ -83,6 +84,34 @@ private static string ExpandEnvironmentVariablesCore(string name)
return builder.ToString();
}

private static unsafe bool IsPrivilegedProcessCore()
{
SafeTokenHandle? token = null;
try
{
if (Interop.Advapi32.OpenProcessToken(Interop.Kernel32.GetCurrentProcess(), (int)Interop.Advapi32.TOKEN_ACCESS_LEVELS.Read, out token))
{
Interop.Advapi32.TOKEN_ELEVATION elevation = default;

if (Interop.Advapi32.GetTokenInformation(
token,
Interop.Advapi32.TOKEN_INFORMATION_CLASS.TokenElevation,
&elevation,
(uint)sizeof(Interop.Advapi32.TOKEN_ELEVATION),
out _))
{
return elevation.TokenIsElevated != Interop.BOOL.FALSE;
}
}

throw Win32Marshal.GetExceptionForLastWin32Error();
}
finally
{
token?.Dispose();
}
}

private static bool Is64BitOperatingSystemWhen32BitProcess =>
Interop.Kernel32.IsWow64Process(Interop.Kernel32.GetCurrentProcess(), out bool isWow64) && isWow64;

Expand Down
18 changes: 18 additions & 0 deletions src/libraries/System.Private.CoreLib/src/System/Environment.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,24 @@ public static partial class Environment
/// </summary>
internal static bool IsSingleProcessor => ProcessorCount == 1;

private static volatile sbyte s_privilegedProcess;

/// <summary>
/// Gets whether the current process is authorized to perform security-relevant functions.
/// </summary>
public static bool IsPrivilegedProcess
{
get
{
sbyte privilegedProcess = s_privilegedProcess;
if (privilegedProcess == 0)
{
s_privilegedProcess = privilegedProcess = IsPrivilegedProcessCore() ? (sbyte)1 : (sbyte)-1;
}
return privilegedProcess > 0;
}
}

// Unconditionally return false since .NET Core does not support object finalization during shutdown.
public static bool HasShutdownStarted => false;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
<Compile Include="System\IO\Path.IsPathFullyQualified.cs" />
<Compile Include="System\BitConverterArray.cs" />
<Compile Include="System\BitConverterBase.cs" />
<Compile Include="System\Environment.IsPrivilegedProcess.cs" />
<Compile Include="System\Environment.UserDomainName.cs" />
<Compile Include="System\Environment.UserName.cs" />
<Compile Include="System\Diagnostics\Stopwatch.cs" />
Expand Down Expand Up @@ -82,7 +83,7 @@

<Compile Include="$(CommonPath)Interop\Unix\System.Native\Interop.GetHostName.cs" Condition="'$(TargetPlatformIdentifier)' == 'Unix' or '$(TargetPlatformIdentifier)' == 'Browser'" Link="Common\Interop\Unix\System.Native\Interop.GetHostName.cs" />
<Compile Include="$(CommonPath)Interop\Unix\Interop.Libraries.cs" Link="Common\Interop\Unix\Interop.Libraries.cs" />
<Compile Include="$(CommonTestPath)System\Diagnostics\DebuggerAttributes.cs" Link="Common\System\Diagnostics\DebuggerAttributes.cs" />
<Compile Include="$(CommonTestPath)System\Diagnostics\DebuggerAttributes.cs" Link="Common\System\Diagnostics\DebuggerAttributes.cs" />
<Compile Include="$(CommonTestPath)System\IO\PathFeatures.cs" Link="Common\System\IO\PathFeatures.cs" />
<Compile Include="$(CommonTestPath)System\ShouldNotBeInvokedException.cs" Link="Common\System\ShouldNotBeInvokedException.cs" />
<Compile Include="$(CommonTestPath)System\Security\Cryptography\ByteUtils.cs" Link="Common\System\Security\Cryptography\ByteUtils.cs" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Runtime.InteropServices;
using Xunit;

namespace System.Tests
{
public class Environment_IsPrivilegedProcess
{
[Fact]
danmoseley marked this conversation as resolved.
Show resolved Hide resolved
[SkipOnPlatform(TestPlatforms.Browser, "Browser doesn't support geteuid")]
public void TestIsPrivilegedProcess()
{
Assert.Equal(AdminHelpers.IsProcessElevated(), Environment.IsPrivilegedProcess);
}
}
}
1 change: 1 addition & 0 deletions src/libraries/System.Runtime/ref/System.Runtime.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2384,6 +2384,7 @@ public static partial class Environment
public static bool HasShutdownStarted { get { throw null; } }
public static bool Is64BitOperatingSystem { get { throw null; } }
public static bool Is64BitProcess { get { throw null; } }
public static bool IsPrivilegedProcess { get { throw null; } }
public static string MachineName { get { throw null; } }
public static string NewLine { get { throw null; } }
public static System.OperatingSystem OSVersion { get { throw null; } }
Expand Down