Detect if running as Administrator with or without elevated privileges?
Asked Answered
D

11

92

I have an application that needs to detect whether or not it is running with elevated privileges or not. I currently have code set up like this:

static bool IsAdministrator()
{
    WindowsIdentity identity = WindowsIdentity.GetCurrent();
    WindowsPrincipal principal = new WindowsPrincipal(identity);
    return principal.IsInRole (WindowsBuiltInRole.Administrator);
}

This works to detect if a user is an administrator or not, but doesn't work if running as an administrator without elevation. (For example in vshost.exe).

How can I determine whether or not elevation is [already in force or] possible?

Deangelis answered 2/8, 2009 at 23:49 Comment(0)
I
60

Try this out:

using Microsoft.Win32;
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Security.Principal;

public static class UacHelper
{
    private const string uacRegistryKey = "Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System";
    private const string uacRegistryValue = "EnableLUA";

    private static uint STANDARD_RIGHTS_READ = 0x00020000;
    private static uint TOKEN_QUERY = 0x0008;
    private static uint TOKEN_READ = (STANDARD_RIGHTS_READ | TOKEN_QUERY);

    [DllImport("advapi32.dll", SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    static extern bool OpenProcessToken(IntPtr ProcessHandle, UInt32 DesiredAccess, out IntPtr TokenHandle);

    [DllImport("advapi32.dll", SetLastError = true)]
    public static extern bool GetTokenInformation(IntPtr TokenHandle, TOKEN_INFORMATION_CLASS TokenInformationClass, IntPtr TokenInformation, uint TokenInformationLength, out uint ReturnLength);

    public enum TOKEN_INFORMATION_CLASS
    {
        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
    }

    public enum TOKEN_ELEVATION_TYPE
    {
        TokenElevationTypeDefault = 1,
        TokenElevationTypeFull,
        TokenElevationTypeLimited
    }

    public static bool IsUacEnabled
    {
        get
        {
            RegistryKey uacKey = Registry.LocalMachine.OpenSubKey(uacRegistryKey, false);
            bool result = uacKey.GetValue(uacRegistryValue).Equals(1);
            return result;
        }
    }

    public static bool IsProcessElevated
    {
        get
        {
            if (IsUacEnabled)
            {
                IntPtr tokenHandle;
                if (!OpenProcessToken(Process.GetCurrentProcess().Handle, TOKEN_READ, out tokenHandle))
                {
                    throw new ApplicationException("Could not get process token.  Win32 Error Code: " + Marshal.GetLastWin32Error());
                }

                TOKEN_ELEVATION_TYPE elevationResult = TOKEN_ELEVATION_TYPE.TokenElevationTypeDefault;

                int elevationResultSize = Marshal.SizeOf((int)elevationResult);
                uint returnedSize = 0;
                IntPtr elevationTypePtr = Marshal.AllocHGlobal(elevationResultSize);

                bool success = GetTokenInformation(tokenHandle, TOKEN_INFORMATION_CLASS.TokenElevationType, elevationTypePtr, (uint)elevationResultSize, out returnedSize);
                if (success)
                {
                    elevationResult = (TOKEN_ELEVATION_TYPE)Marshal.ReadInt32(elevationTypePtr);
                    bool isProcessAdmin = elevationResult == TOKEN_ELEVATION_TYPE.TokenElevationTypeFull;
                    return isProcessAdmin;
                }
                else
                {
                    throw new ApplicationException("Unable to determine the current elevation.");
                }
            }
            else
            {
                WindowsIdentity identity = WindowsIdentity.GetCurrent();
                WindowsPrincipal principal = new WindowsPrincipal(identity);
                bool result = principal.IsInRole(WindowsBuiltInRole.Administrator);
                return result;
            }
        }
    }
}
Interpose answered 21/12, 2010 at 8:49 Comment(6)
Works if the account to run as is a local administrator but if you use the domain administrator the variable isProcessAdmin returns false. But UAC accepts the Domain Administrator as valid when elevating privileges (create folder in windows, run as administrator, etc)...How can i modify your function so it takes into account that case too?Chlorenchyma
You might also want to consider that if the account is the built in administrator then UAC is elevated by default so IsProcessElevated will return false in this case (because IsUacEnabled is true and elevationResult is TokenElevationTypeDefault) even though the process runs with in elevated mode without having prompted the user. Or in other words the account is elevated and the process runs in the default elevation type.Newlywed
This code requires the following using statements: using System.Diagnostics; using System.Runtime.InteropServices; using System.Security.Principal; It also seems to be mirrored here.Tryck
This got me an exception at Windows 8, at Marshal.SizeOf((int)elevationResult) I am not sure why yet. Exception message is: Method not found. At: Int32 System.Runtime.InteropServices.Marshal.SizeOf(!!0).Vance
What about TokenElevationTypeLimited ? Should it not be considered to set isProcessAdmin to true ?Gwenora
This code works pretty well with Visual Studio Setup project custom actions.I am trying to move to InstallShield installer basic msi project and calling the same above code gives me different values. elevationResult value is different with VS vs. IS installers when running on same machine with same user. VS Installer: elevationResult value is TokenElevationTypeDefault and IsAdministrator is true. IS Installer: elevationResult value is TokenElevationTypeFull and IsAdministrator is false. Anyone observed same issue? Any ideas why different behavior?Rhachis
B
39

(new answer six years after the question was asked)

Disclaimer: This is just something that happened to work on my particular OS with my particular settings with my particular user:

using System.Security.Principal;

// ...

    static bool IsElevated
    {
      get
      {
        return WindowsIdentity.GetCurrent().Owner
          .IsWellKnown(WellKnownSidType.BuiltinAdministratorsSid);
      }
    }

So when I run this "Run as administrator", the property get accessor returns true. When running normally (even if my user "is" administrator, just not running this particular application "as administrator"), it returns false.

This seems much simpler than many other answers.

I have no idea if there are cases where this fails.

PS! This also seems OK:

    static bool IsElevated
    {
      get
      {
        var id = WindowsIdentity.GetCurrent();
        return id.Owner != id.User;
      }
    }
Bonnibelle answered 6/8, 2015 at 12:53 Comment(8)
Thanks for this! - I used this in PowerShell [Security.Principal.WindowsIdentity]::GetCurrent().Owner.IsWellKnown([System.Security.Principal.WellKnownSidType]::BuiltinAdministratorsSid)Katusha
When having set notifications to 'never show any notification', this will return true. Perhaps in certain scenario's where you really need to run the software as administrator it can give a false indication.Vance
This will not distinguish between "semi-unelevated" process and a properly-unelevated process: it's possible that IsElevated will return false but the process may still be running with High integrity level. A genuinely non-elevated process has a medium integrity level. This is probably irrelevant for 99% of the applications, but it's worth mentioning because tools like Process Hacker might still declare such a process to be elevated. A "semi-unelevated" process is not something you'd see normally; it might occur when someone fails to launch an unelevated child process correctly.Mood
Whats "running with High integrity level"?Georginageorgine
@Georginageorgine that's too big of a question to answer in comments, but see here and here.Mood
Thanks @RomanStarkov and Jeppe, 20+ years in the business and I hadn't heard the term before.Georginageorgine
How to check for other process (by name) , i.e. if other_process.exe running in admin or not?Persona
This isn't really right, the owner of a security token generally will be admin if it's an elevated token but it doesn't actually have to be. Also you can have a non-elevated security token owned by admin. The true test of whether a token is elevated is whether it has the admin SID in list of granted access SIDs in the ACLs because that's what will actually make it pass the ACL test.Vibrate
A
23

Here is a modified version of this answer to include things like the proper disposing of resources and handling of Domain Administrators, as well as handle the exception in Marshal.SizeOf().

public static class UacHelper
{
    private const string uacRegistryKey = "Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System";
    private const string uacRegistryValue = "EnableLUA";

    private static uint STANDARD_RIGHTS_READ = 0x00020000;
    private static uint TOKEN_QUERY = 0x0008;
    private static uint TOKEN_READ = (STANDARD_RIGHTS_READ | TOKEN_QUERY);

    [DllImport("advapi32.dll", SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    static extern bool OpenProcessToken(IntPtr ProcessHandle, UInt32 DesiredAccess, out IntPtr TokenHandle);

    [DllImport("kernel32.dll", SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    static extern bool CloseHandle(IntPtr hObject);

    [DllImport("advapi32.dll", SetLastError = true)]
    public static extern bool GetTokenInformation(IntPtr TokenHandle, TOKEN_INFORMATION_CLASS TokenInformationClass, IntPtr TokenInformation, uint TokenInformationLength, out uint ReturnLength);

    public enum TOKEN_INFORMATION_CLASS
    {
        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
    }

    public enum TOKEN_ELEVATION_TYPE
    {
        TokenElevationTypeDefault = 1,
        TokenElevationTypeFull,
        TokenElevationTypeLimited
    }

    public static bool IsUacEnabled
    {
        get
        {
            using (RegistryKey uacKey = Registry.LocalMachine.OpenSubKey(uacRegistryKey, false))
            {
                bool result = uacKey.GetValue(uacRegistryValue).Equals(1);
                return result;
            }
        }
    }

    public static bool IsProcessElevated
    {
        get
        {
            if (IsUacEnabled)
            {
                IntPtr tokenHandle = IntPtr.Zero;
                if (!OpenProcessToken(Process.GetCurrentProcess().Handle, TOKEN_READ, out tokenHandle))
                {
                    throw new ApplicationException("Could not get process token.  Win32 Error Code: " +
                                                   Marshal.GetLastWin32Error());
                }

                try
                {
                    TOKEN_ELEVATION_TYPE elevationResult = TOKEN_ELEVATION_TYPE.TokenElevationTypeDefault;

                    int elevationResultSize = Marshal.SizeOf(Enum.GetUnderlyingType(typeof(TOKEN_ELEVATION_TYPE)));
                    uint returnedSize = 0;

                    IntPtr elevationTypePtr = Marshal.AllocHGlobal(elevationResultSize);
                    try
                    {
                        bool success = GetTokenInformation(tokenHandle, TOKEN_INFORMATION_CLASS.TokenElevationType,
                                                           elevationTypePtr, (uint) elevationResultSize,
                                                           out returnedSize);
                        if (success)
                        {
                            elevationResult = (TOKEN_ELEVATION_TYPE) Marshal.ReadInt32(elevationTypePtr);
                            bool isProcessAdmin = elevationResult == TOKEN_ELEVATION_TYPE.TokenElevationTypeFull;
                            return isProcessAdmin;
                        }
                        else
                        {
                            throw new ApplicationException("Unable to determine the current elevation.");
                        }
                    }
                    finally
                    {
                        if (elevationTypePtr != IntPtr.Zero)
                            Marshal.FreeHGlobal(elevationTypePtr);
                    }
                }
                finally
                {
                    if (tokenHandle != IntPtr.Zero)
                        CloseHandle(tokenHandle);
                }
            }
            else
            {
                WindowsIdentity identity = WindowsIdentity.GetCurrent();
                WindowsPrincipal principal = new WindowsPrincipal(identity);
                bool result = principal.IsInRole(WindowsBuiltInRole.Administrator) 
                           || principal.IsInRole(0x200); //Domain Administrator
                return result;
            }
        }
    }
}
Antrorse answered 2/8, 2009 at 23:49 Comment(10)
It all depends on which user you are running the service as. Are you trying to detect if the service is running as Local System, Local Service, Network Service, or a windows user? Detecting "administrative status" wont work for telling the difference between Local System and Local Service, you need to test for that by checking directly which user is running the process.Antrorse
This got me an exception at Windows 8, at Marshal.SizeOf((int)elevationResult) I am not sure why yet. Exception message is: Method not found. At: Int32 System.Runtime.InteropServices.Marshal.SizeOf(!!0).Vance
@RageCompex are you using a restricted platform like a universal app or Unity3d?Antrorse
Well it is a customer that I receive the error from, automatic logging, All I know is that it doesn't work on his/her pc. All I know until now is that it is a Windows 8, 64x bit, 2 processors. I suspect it is due old .NET version, Marshal.SizeOf is supported since 4.5.1 I can read elsewhere..Vance
@RageCompex its been available sense .NET 1.1. Perhaps you have a typo?Antrorse
Yea, thats what I thought :( No it is working correctly on mine and about 100 others... full stack trace: pastebin.com/ebSWARfuVance
Ah, you are compiling with 4.5.1 because of that it is trying to use this overload but the user does not have 4.5.1 installed. Try replacing it with Marshal.SizeOf(typeof(TOKEN_ELEVATION_TYPE)),Antrorse
Thanks @ScottChamberlain The user updated to 4.5.2 (which I target) and it solved the problem. This solution is probably good for backwards compatibility.Vance
@ScottChamberlain int elevationResultSize = Marshal.SizeOf(typeof(TOKEN_ELEVATION_TYPE)) throws an ArgumentException on 32 Bit application .NET 4.0, int elevationResultSize = Marshal.SizeOf((int)elevationResult) worked, however.Brusque
This answer does not detect elevation on Windows Server 2019 on an administrator account. However, this answer does: https://mcmap.net/q/233989/-detect-if-running-as-administrator-with-or-without-elevated-privilegesBroadspectrum
M
16

NB: This is an old answer which was more relevant when the question was originally written and was how you could do this in managed c++ (the .net version of c++), as it was much harder to solve this in C# back then. You build this in a separate assembly and reference it in your c# project as normal

The CodePlex project UAChelper has code that checks on elevation in UserAccountControl.cpp UserAccountControl::IsUserAdmin, which checks if UAC is enabled and then checks if the process is elevated.

bool UserAccountControl::IsCurrentProcessElevated::get()
{
    return GetProcessTokenElevationType() == TokenElevationTypeFull;    //elevated
}

from the function:

int UserAccountControl::GetProcessTokenElevationType()
{
    HANDLE hToken;
    try
    {
        if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
            throw gcnew Win32Exception(GetLastError());

        TOKEN_ELEVATION_TYPE elevationType;
        DWORD dwSize;
        if (!GetTokenInformation(hToken, TokenElevationType, &elevationType, sizeof(elevationType), &dwSize))
            throw gcnew Win32Exception(GetLastError());

        return elevationType;
    }
    finally
    {
        CloseHandle(hToken);
    }
}
Millrun answered 3/8, 2009 at 0:5 Comment(3)
This appears to be c++ code, not the requested c# code.Candlenut
@JohnLord This is managed C++ (.net) and not plain c++. I believe that when I wrote obtained answer 14 years ago, doing it in C# was not as easy/possible as it is now. I can't be sure anymore as I don't have the tooling of the period any more. But your point is fair.Millrun
well i didn't downvote or anything. However, if your solution is managed c, you should explain how to include that in the c# project. I've been programming in c# 5 years, and i don't know how to do that. I think you have to include it as a separate project but not sure.Candlenut
A
11

In .net Framwork 4.5 I found another method that works for me. In relation to the following script that can be found here here (in German)

 rem --- Admintest.bat ---
 whoami /groups | find "S-1-5-32-544" > nul
 if errorlevel 1 goto ende
 echo Benutzer %username% ist lokaler Administrator.
 :ende

In C# it looks like this:

    private bool IsAdmin
    {
        get
        {
            WindowsIdentity identity = WindowsIdentity.GetCurrent();
            if (identity != null)
            {
               WindowsPrincipal principal = new WindowsPrincipal(identity);
               List<Claim> list = new List<Claim>(principal.UserClaims);
               Claim c = list.Find(p => p.Value.Contains("S-1-5-32-544"));
               if (c != null)
                  return true;
            }
            return false;
        }
    }

But in .net < 4.5 the WindowsPrincipal class does not contain the UserClaims property and I found no way to get this information.

Alfredalfreda answered 22/7, 2014 at 12:50 Comment(3)
FYI: Only determining if account is administrator, not if the app is elevatedVance
To do check if a user is a member of S-1-5-32-544 (Administrators group) in .Net < 4.5, you can just use the code in the original question. The principal will only be a member of the Administrators group if the process is running elevated and the user is in the group. If the process is not elevated the principal will not be in the group.Siloxane
Nice answer, short and efficient, I gave you a +1 for it. N.B. I made this a property in my code (private bool IsAdmin{ get { ... } }), then you don't need the brackets if you invoke IsAdmin.Sculptress
J
5

Using TokenElevationType would work, but if you PInvoke CheckTokenMembership() against the admin group SID, your code would also work when UAC is off and on 2000/XP/2003 and will also handle deny SID's.

There is also a IsUserAnAdmin() function that does the CheckTokenMembership check for you, but MSDN says it might not be there forever

Jariah answered 4/8, 2009 at 22:40 Comment(7)
I found CheckTokenMembership insufficient when subject to UAC - github.com/chocolatey/choco/blob/… returns false. Check the code (I'm replacing it) and take a look at the output from Win2012R2 - i.imgur.com/gX3JP0W.pngDelbert
@Delbert It depends on what you really want to know; is the user a elevated administrator right now or can they elevate if required. You can for example check TOKEN_ELEVATION_TYPE and end up with something like: bool is_or_can_elevate() { return process_is_elevated() || TokenElevationTypeLimited == get_current_token_elevation_type(); }. Another issue is that the definition of elevated is not the same everywhere, you can have a console window with the "Administrator: " prefix and at the same time be below High integrity level! TokenElevation does not always match TokenIntegrityLevel.Jariah
Fun times. I want to know if I have an elevated process separate from whether the user is an administrator. Here's where I ended up. Let me know where I should go if it is wrong - github.com/chocolatey/choco/issues/77#issuecomment-73523774 and github.com/chocolatey/choco/commit/…Delbert
@Delbert is_processes_elevated() { return CheckTokenMembership/IsInRole || TokenElevation/TokenIntegrityLevel>=0x3000; } CheckTokenMembership or IsInRole for < Vista and Vista+ with UAC off. TokenElevation or TokenIntegrityLevel>=0x3000 depending on exactly how you want to detect the elevation. I believe conhost.exe uses TokenElevation but it is broken IMHO and you should check the actual level... (You need special tools to generate a token that fools TokenElevation though) See also: windowssucks.wordpress.com/2011/02/07/uac-are-you-high/#Jariah
...and even that is sort of wrong, in theory it is possible to have a elevated token and not be in the administrators group. So if you only want people in the administrators group and make sure that they are elevated you should perform the CheckTokenMembership/IsInRole check and then the Token* check should fail (No UAC) or it's value should indicate elevation... This of course depends on what the you actually want to access. You might need to be system/admin and elevated, or just elevated, it depends on the ACL.Jariah
Yep, if you read my comments in there you can see I mention that. We only use the detection for informational purposes now.Delbert
Reading over that code, we check a user first based on a quick win (IsInRole), then switch over to the token elevation. In most cases we are going to catch administrators who are not elevated but in some minor cases we may catch others as well. We'll generate a warning when is_admin and is_process_elevated don't match.Delbert
C
5

This Answer has a few problems. First, it doesn't get any System processes that run as Admin (for example under NT-Authority/SYSTEM). The code example below fixes all problems (detects, LocalAdmins, DomainAdmins, and LocalSystemAdmins)

If you just want the current Process, replace pHandle with Process.GetCurrentProcess().Handle

NOTE: You must have certain privileges to run it. (Every AdminProcess has them but needs to active them first, Services have them activated by default)

internal static bool IsProcessElevatedEx(this IntPtr pHandle) {

        var token = IntPtr.Zero;
        if (!OpenProcessToken(pHandle, MAXIMUM_ALLOWED, ref token))
                throw new Win32Exception(Marshal.GetLastWin32Error(), "OpenProcessToken failed");

        WindowsIdentity identity = new WindowsIdentity(token);
        WindowsPrincipal principal = new WindowsPrincipal(identity);
        bool result = principal.IsInRole(WindowsBuiltInRole.Administrator)
                   || principal.IsInRole(0x200); //Domain Administrator
        CloseHandle(token);
        return result;
}
Churchless answered 9/3, 2019 at 16:45 Comment(0)
R
3

I think there is one more issue. I checked solutions provided by you and have to say that in the installation of Windows 7 and logged on as an admin the check does not work. Windows never returns information that process runs in an elevated mode. So the sequence:

if (IsUacEnabled)
    return IsProcessInElevatedMode();
return IsUserAdmin();

does not returns true when logged as an Administrator but the process has all privileges to perform system operations (e.g. stop system services). The working sequence is:

if (IsUserAdmin())
    return true;

if (IsUacEnabled)
    return IsProcessInElevatedMode();

return false;

You should first check if the process is run in Administrator context. Additional info:

IsUacEnabled() - checks if the UAC has been enabled in the system (Windows)
IsProcessInElevatedMode() - checks if the process is run in an elevated mode
IsUserAdmin() - checks if the current user has an Administrtor role

All of that methods has been described in previous posts.

Rastus answered 14/1, 2015 at 15:8 Comment(1)
This is not an answer but arguably commentary on another postWhitt
D
1

Using UACHelper nuget package: https://www.nuget.org/packages/UACHelper/

if (UACHelper.IsElevated)
    // something
else
    // something else

There are a lot of other properties that can be used to detect if the user is, in fact, an administrator, or if the process is running under UAC virtualization, or if the desktop owner is the process owner. (Run as from limited account)

Check the read me for more information.

Denouement answered 8/7, 2018 at 9:44 Comment(0)
T
0

I am using this code and it works well:


bool runningAsAdmin = WindowsIdentity.GetCurrent().Owner.IsWellKnown(WellKnownSidType.BuiltinAdministratorsSid);

*Admin is part of the Build-In Administrators group.

"A user account for the system administrator. This account is the first account created during operating system installation. The account cannot be deleted or locked out. It is a member of the Administrators group and cannot be removed from that group." -- https://ss64.com/nt/syntax-security_groups.html

Tidewaiter answered 26/11, 2020 at 0:30 Comment(1)
Technically this relies on an implementation detail. The fact, that the "owner" field of the token points to the built-in Administrators group. However, no one's to say this implementation detail is there to stay and the appropriate method is to check whether the current security context is elevated or not!Chaves
C
0

Since .NET 8.0 (released November 2023) there is a new easy way using a static property of the environment class:

if (System.Environment.IsPrivilegedProcess) {
    // current process has admin permissions
} else {
    // current process has no admin permissions
}

This also works on UNIX based systems.

https://learn.microsoft.com/en-us/dotnet/api/system.environment.isprivilegedprocess?view=net-8.0

Critic answered 13/7, 2024 at 10:47 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.