How to check if the current user is an Administrator (even if UAC is on)
There may be a scenario where you want to determine from code if the current user is an Administrator.
One example of this which I have had to deal with is checking for software updates.
Say your application contacts a service to see if there is a newer version of the application available; if so, you can download and run the installer.
Imagine that the installer requires admin privileges; you don’t want to run the installer if the current user does not have administrative privileges.
So how can we check if the user is an admin or not?
In VB6, C++ etc
There is a Windows API function you can use very easily to see if the current user is an admin: IsUserAnAdmin.
BOOL IsUserAnAdmin(void);
Visual Basic 6 Declaration
Error: no lexer for alias 'vb' found
While you can still use this, it is actually deprecated, and the documentation recommends you call the CheckTokenMembership function instead (which IsUserAnAdmin
is a wrapper for).
C# .NET
using System;
using System.Security.Principal;
var identity = WindowsIdentity.GetCurrent();
if (identity == null) throw new InvalidOperationException("Couldn't get the current user identity");
var principal = new WindowsPrincipal(identity);
return principal.IsInRole(WindowsBuiltInRole.Administrator);
User Account Control (UAC)
A problem arises when you use any of the above code on a machine that has UAC enabled, and the process is not elevated.
While the user may be an administrator, when the process is not elevated yet, the user has a split token – which doesn’t have the administrator privileges.
A way around this is to use the GetTokenInformation API call to inspect the token to see if it’s a split token. In most cases this will mean that UAC is on and the current user is an administrator.
This is not 100% reliable (see References) but it’s probably the best we can do for now.
C#
This code is slightly easier in .NET, as there’s already a fair amount of code we don’t have to write to get the current process’s token.
First, we’ll need some code to support the GetTokenInformation API call:
[DllImport("advapi32.dll", SetLastError = true)]
static extern bool GetTokenInformation(IntPtr tokenHandle, TokenInformationClass tokenInformationClass, IntPtr tokenInformation, int tokenInformationLength, out int returnLength);
/// <summary>
/// Passed to <see cref="GetTokenInformation"/> to specify what
/// information about the token to return.
/// </summary>
enum TokenInformationClass
{
TokenUser = 1,
TokenGroups,
TokenPrivileges,
TokenOwner,
TokenPrimaryGroup,
TokenDefaultDacl,
TokenSource,
TokenType,
TokenImpersonationLevel,
TokenStatistics,
TokenRestrictedSids,
TokenSessionId,
TokenGroupsAndPrivileges,
TokenSessionReference,
TokenSandBoxInert,
TokenAuditPolicy,
TokenOrigin,
TokenElevationType,
TokenLinkedToken,
TokenElevation,
TokenHasRestrictions,
TokenAccessInformation,
TokenVirtualizationAllowed,
TokenVirtualizationEnabled,
TokenIntegrityLevel,
TokenUiAccess,
TokenMandatoryPolicy,
TokenLogonSid,
MaxTokenInfoClass
}
/// <summary>
/// The elevation type for a user token.
/// </summary>
enum TokenElevationType
{
TokenElevationTypeDefault = 1,
TokenElevationTypeFull,
TokenElevationTypeLimited
}
Then, the actual code to detect if the user is an Administrator (returning true if they are, otherwise false).
var identity = WindowsIdentity.GetCurrent();
if (identity == null) throw new InvalidOperationException("Couldn't get the current user identity");
var principal = new WindowsPrincipal(identity);
// Check if this user has the Administrator role. If they do, return immediately.
// If UAC is on, and the process is not elevated, then this will actually return false.
if (principal.IsInRole(WindowsBuiltInRole.Administrator)) return true;
// If we're not running in Vista onwards, we don't have to worry about checking for UAC.
if (Environment.OSVersion.Platform != PlatformID.Win32NT || Environment.OSVersion.Version.Major < 6)
{
// Operating system does not support UAC; skipping elevation check.
return false;
}
int tokenInfLength = Marshal.SizeOf(typeof(int));
IntPtr tokenInformation = Marshal.AllocHGlobal(tokenInfLength);
try
{
var token = identity.Token;
var result = GetTokenInformation(token, TokenInformationClass.TokenElevationType, tokenInformation, tokenInfLength, out tokenInfLength);
if (!result)
{
var exception = Marshal.GetExceptionForHR( Marshal.GetHRForLastWin32Error() );
throw new InvalidOperationException("Couldn't get token information", exception);
}
var elevationType = (TokenElevationType)Marshal.ReadInt32(tokenInformation);
switch (elevationType)
{
case TokenElevationType.TokenElevationTypeDefault:
// TokenElevationTypeDefault - User is not using a split token, so they cannot elevate.
return false;
case TokenElevationType.TokenElevationTypeFull:
// TokenElevationTypeFull - User has a split token, and the process is running elevated. Assuming they're an administrator.
return true;
case TokenElevationType.TokenElevationTypeLimited:
// TokenElevationTypeLimited - User has a split token, but the process is not running elevated. Assuming they're an administrator.
return true;
default:
// Unknown token elevation type.
return false;
}
}
finally
{
if (tokenInformation != IntPtr.Zero) Marshal.FreeHGlobal(tokenInformation);
}
Visual Basic 6 (VB6)
For Visual Basic 6, there’s some additional code, as we need to get the token for the current process, and use more calls to also get the operating system version.
Error: no lexer for alias 'vb' found