Vanara/Security/UAC.cs

122 lines
4.7 KiB
C#

using Microsoft.Win32;
using System.ComponentModel;
using System.Diagnostics;
using System.Security.Principal;
using static Vanara.PInvoke.AdvApi32;
namespace Vanara.Security;
/// <summary>
/// Provides information about the state of User Access Control for the system.
/// </summary>
public static partial class UAC
{
private static bool? enabled;
/// <summary>
/// Determines whether the provided process can be elevated. Effectively, this checks that UAC is available and that the process is running under an
/// account that belongs to the Administrators group.
/// </summary>
/// <param name="process">The process. If this value is <c>null</c>, then the current process is used.</param>
/// <returns><c>true</c> if this process can be elevated; otherwise, <c>false</c>.</returns>
public static bool CanElevate(Process? process = null) => IsEnabled() && IsRunningAsAdmin(process);
/// <summary>Determines whether the specified process is elevated.</summary>
/// <param name="process">The process. If this value is <c>null</c>, then the current process is used.</param>
/// <returns><c>true</c> if the specified process is elevated; otherwise, <c>false</c>.</returns>
public static bool IsElevated(Process? process = null)
{
try
{
// Open the access token of the current process with TOKEN_QUERY.
using var hObject = SafeHTOKEN.FromProcess(process ?? Process.GetCurrentProcess(), TokenAccess.TOKEN_QUERY | TokenAccess.TOKEN_DUPLICATE);
return hObject.IsElevated;
}
catch { }
return false;
}
/// <summary>Determines whether UAC is enabled on this system.</summary>
/// <returns><c>true</c> if UAC is enabled; otherwise, <c>false</c>.</returns>
public static bool IsEnabled()
{
if (!enabled.HasValue)
{
if (Environment.OSVersion.Version.Major < 6)
enabled = true;
else
enabled = Registry.GetValue(
@"HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System", "EnableLUA", 0)?.Equals(1);
}
return enabled.GetValueOrDefault();
}
/// <summary>
/// The function checks whether the primary access token of the process belongs to user account that is a member of the local Administrators group,
/// even if it currently is not elevated.
/// </summary>
/// <param name="proc">The process to check.</param>
/// <returns>
/// Returns true if the primary access token of the process belongs to user account that is a member of the local Administrators group. Returns false
/// if the token does not.
/// </returns>
public static bool IsRunningAsAdmin(Process? proc = null)
{
SafeHTOKEN? hObjectToCheck = null;
// Open the access token of the current process for query and duplicate.
using (var hObject = SafeHTOKEN.FromProcess(proc, TokenAccess.TOKEN_QUERY | TokenAccess.TOKEN_DUPLICATE))
{
// Determine whether system is running Windows Vista or later operating systems (major version >= 6) because they support linked tokens, but
// previous versions (major version < 6) do not.
if (Environment.OSVersion.Version.Major >= 6)
{
// Marshal the TOKEN_ELEVATION_TYPE enum from native to .NET.
var elevType = hObject.GetInfo<TOKEN_ELEVATION_TYPE>(TOKEN_INFORMATION_CLASS.TokenElevationType);
// If limited, get the linked elevated token for further check.
if (elevType == TOKEN_ELEVATION_TYPE.TokenElevationTypeLimited)
{
// Marshal the linked token value from native to .NET.
hObjectToCheck = new SafeHTOKEN(hObject.GetInfo<TOKEN_LINKED_TOKEN>(TOKEN_INFORMATION_CLASS.TokenLinkedToken).LinkedToken.DangerousGetHandle());
}
}
// CheckTokenMembership requires an impersonation token. If we just got a linked token, it already is an impersonation token. If we did not get a
// linked token, duplicate the original into an impersonation token for CheckTokenMembership.
if (hObjectToCheck is null)
{
if (!DuplicateToken(hObject, SECURITY_IMPERSONATION_LEVEL.SecurityIdentification, out hObjectToCheck))
throw new Win32Exception();
}
}
if (hObjectToCheck is null || hObjectToCheck.IsInvalid) return false;
// Check if the token to be checked contains admin SID.
using (hObjectToCheck)
using (var id = new WindowsIdentity(hObjectToCheck.DangerousGetHandle()))
return id.IsAdmin();
}
/*/// <summary>Runs the current application elevated if it isn't already. <note>This will close the current running instance.</note></summary>
public static void RunCurrentApplicationElevated()
{
if (!WindowsIdentity.GetCurrent().IsAdmin())
{
// Launch itself as administrator
var proc = new ProcessStartInfo(System.Windows.Forms.Application.ExecutablePath)
{
UseShellExecute = true,
WorkingDirectory = Environment.CurrentDirectory,
Verb = "runas"
};
try
{
Process.Start(proc);
System.Windows.Forms.Application.Exit();
}
catch { }
}
}*/
}