How to Determine if a User is a Member of the Administrators Group with UAC Enabled on Windows Vista


User Account Control (UAC) on Windows Vista changes the paradigm of being an administrator on a Microsoft Windows operating system. Rather than wielding full administrative privileges all of the time, the token is “split” and there are two of them. If you run an application normally, it is given the token that has fewer privileges (a “standard user” token, if you will, although the Administrators group is still present and set to “deny only” so securable objects that an administrator is explicitly forbidden from accessing will still be denied to this user). If you create a process elevated, you are prompted to approve the elevation, after which the process is provided with an “unfiltered” token that grants this application full administrator credentials.

This is a huge win for security. However, it does break some of the paradigms that you may be used to using when developing applications. One example is checking to see if the user is an administrator explicitly. Most of the tools that help identify more LUA bugs (and these tools are becoming pretty indispensible now that pretty much everybody is running as a standard user the majority of the time) will flag this as a potential LUA bug. You see, this is something that can be done for good (you are checking to see if the user is an administrator to determine whether or not you want to offer them the option of launching another process elevated to provide additional functionality), and it is something that can be done for evil (you are checking to see if the user is an administrator, because it is easier just to fail than to fix the LUA bug). For now, let’s assume that you are using this power for good.

If you happen to be using the handy shell32 API IsUserAnAdmin, you will find that it will return true if the process is elevated, and false if it is not. Note that the Boolean return value doesn’t provide you with any information that will help you determine if the user CAN elevate – it just tells you if you already have. What can you do if you want to know if the user CAN elevate, whether or not they already have?

The GetTokenInformation API provides a new ability to return a TokenElevationType structure. As of Windows Vista RC1, I do not see this documented in the Windows SDK, but you can find it in the Windows header files (winbase.h and winnt.h). So, I whipped together a little sample that you can run from the command line to determine not only whether the current process is elevated, but also whether the user happens to be a member of the administrators group:

 

#include <windows.h>
#include <stdio.h>

int main() {
  HANDLE hToken;
  TOKEN_ELEVATION_TYPE elevationType;
  DWORD dwSize;

  OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken);
  GetTokenInformation(hToken, TokenElevationType, &elevationType, sizeof(elevationType), &dwSize);

  switch (elevationType) {
    case TokenElevationTypeDefault:
      wprintf(TEXT(“nTokenElevationTypeDefault – User is not using a split token.n”));
      break;
    case TokenElevationTypeFull:
      wprintf(TEXT(“nTokenElevationTypeFull – User has a split token, and the process is running elevated.n”));
      break;
    case TokenElevationTypeLimited:
      wprintf(TEXT(“nTokenElevationTypeLimited – User has a split token, but the process is not running elevated.n”));
      break;
  }

  if (hToken) {
    CloseHandle(hToken);
  }
}

 

As you can see, it’s fairly straightforward. (I have elided error handling and return value checking for clarity.) If you are looking to use this information for good, I hope this helps. If you are looking to use this information for evil, I hope you don’t find this post!

Update: Note that this technique detects if the token is split or not. In the vast majority of situations, this will determine whether the user is running as an administrator. However, there are other user types with advanced permissions which may generate a split token during an interactive login (for example, the Network Configuration Operators group). If you are using one of these advanced permission groups, this technique will determine the elevation type, and not the presence (or absence) of the administrator credentials.

Updated 03-March-2010

Apparently my previous update didn’t scare enough people away from this approach, so here goes: be very afraid of this approach. It will correctly tell you if you have a split token, but that doesn’t necessarily mean anything useful. For example, what happens if you disable UAC? You won’t have a split token. You’d get TokenElevationTypeDefault. What happens if you are logged in as the .Administrators account? Same thing. Neither one means you’re a standard user, which is a common mistake by misapplying the above logic for the “typical” case. What about if you happen to have one, and only one, super privilege, and you elevated to get that into your token? Then you’d have TokenElevationTypeFull – which is frequently interpreted as meaning you’re an admin.

The *right* thing to do is not care. If you think you need admin rights, then manifest your binary, and ask for them. Does the current user’s capabilities matter? If they have a second admin account, then why does it matter if their current one is a standard user?

And you certainly don’t want to go looking for the Administrators ACE in the token in order to authorize something. Because, you know, it has a flag that says “Deny Only” on it. If you go granting access, then that’s directly violating the “Deny Only” intent. Kind of like saying, “oh, I see you’re on the no gambling list – come on into the casino…”

In other words, despite the fact that I wrote this (back in my youth), unless you know precisely what it is doing, please don’t use this. Because it probably doesn’t do what you think it does.

Comments (36)

  1. DanielMoth says:

    Nice… I have to ask though, what is the scenario where you need to distinguish between an admin user that can elevate and a non-admin user that cannot?

    At the end of the day, you need to know if the process is not elevated right now, so you can show shields where appropriate and take elevation actions behind those shield presses (i.e. even if they can’t elevate, the elevation action will result in the UAC dialog that by default requires entering the admin username and pwd). So I don’t see the scenario where I need to distinguish… but I am sure you’ll enlighten me 🙂 Cheers.

  2. Björn says:

    Daniel, if I understand the sample (and your question) right, nTokenElevationTypeLimited and nTokenElevationTypeFull define administrators that can elevate (or are elevated). A return value of TokenElevationTypeDefault should result in the UAC dialog to enter administrator credentials.

  3. cjacks says:

    Some developers have expressed an interest in being able to offer an application update only if the user is able to obtain admin privileges. In a standard user enterprise, where the nearest administrator may be several floors or buildings away, prompting for elevation to install an application update isn’t that helpful.

    It also may be useful when doing lower level work. For example, when you manifest an application for highestAvailable, internally we have to do a similar check. If you can become an admin, then we launch prompting for credentials. If you can’t, then we just launch as standard user.

    Yes, there are a host of scenarios where using this knowledge can be harmful, but there are still some where it is useful.

  4. Stefan says:

    Is there a nice managed code .NET API to to this very same thing?

  5. cjacks says:

    There is not a managed code API to achieve this – this function is new in Windows Vista, so no wrapper from the 2.0 framework (which was released a while ago) is going to be aware of this API. So, the answer is to use p/invoke to access these functions.

  6. vshashi says:

    Once a process knows that it CAN be elevated how does it really elevate itself and perform an Admin task. Can we expect a code-snippet/APIs for the same.

    Lets say the process whats to spawn/launch a child with an elevated token.

    Thanks,

    Shashi.

  7. cjacks says:

    You never elevate a process in place. What you would need to do is ShellExecute another process that is manifested as requireAdministrator, and package all of your administrator functionality into this exe.

  8. vshashi says:

    I was tring to modify the hToken in your code by using AdjustTokenGroups() and enable the Admin Group such that the token can be used in a CreateProcessAsUser() and this created process be launched elevated.

    Is this the right approach.

    Thanks,

    Shashi

  9. cjacks says:

    No need to muck around adjusting token groups manually. In fact, this won’t get you a full admin token. We do more than just set the deny-only flag on the Administrator ACE. We also set the IL to medium instead of high, and we strip out a number of privileges.

    If you are launching a particular admin process, just manifest that process to requireAdministrator. If you want to launch a particular exe elevated just that time, then call ShellExecuteEx and specify the verb as "runas."

  10. Dylan Bourque says:

    I have a question about a similar issue.  Is there a way to programmatically check if UAC is enabled/disabled?  As a short term fix for an upcoming product release, I have been asked to check for UAC when running on Vista and to MessageBox() the user and exit the application if UAC is enabled.  There doesn’t seem to be any documented method of checking and I thought maybe you could help.

    Thanks in advance,

    Dylan Bourque

    dbourque-at-lewis-dot-com

    Lewis Computer Services, Inc.

  11. cjacks says:

    Does your application not work if UAC is enabled at all, or does it just not work if the user is not an administrator? TokenElevationTypeDefault && IsUserAnAdmin == True indicates that UAC is turned off, but I would think you’d be doing your users a disservice by requiring that they disable an important security feature of the OS to run your software. Would a check for IsUserAnAdmin be sufficient to overcome any issues at the moment? Or could you just manifest the exe to requireAdministrator until you eradicate all of the LUA Bugs? (Standard User Analyzer from ACT 5.0 can help with this.) I’d love to understand more about your scenario here.

  12. Dylan Bourque says:

    Thanks for the reply.  This is intended (according to or PM) to be a short term band-aid.  We have a fair number of LUA issues that we won’t have time to address before our next release, which will be in March.  Also, the application in question is slated for a major overhaul as part of our next release, so there isn’t really any incentive to put a lot of effort into the current codebase.

    We have several new client installs lined up in the next several months, all of which will more than likely be buying new laptops with Vista pre-installed.  For the short term, instead of updating the application to work correctly under UAC, our PM decided to require that they disable UAC if they are running under Vista.

    I have been lobbying here to either ask the new clients to buy XP laptops and wait on Vista until our next release or to manifest the exe, but I’m too low on the totem pole to force the issue.

    Thanks again for you help,

    Dylan Bourque

    dbourque-at-lewis-dot-com

    Lewis Computer Services, Inc.

  13. cjacks says:

    (Followed up offline.)

  14. Daniel Moth says:

    Programmatically determine if UAC is enabled

  15. The moth says, This is a question I get often: "How can I determine if User Account Control is on or

  16. Syam says:

    Hi,

    Thanks for a good article.

    I have only one small task in my application that needs admin privilege.

    Is it a must to compile that small piece of code into a separate exe and launch it with the required privilege? Can I elevate the process for just that one task and bring it back to standard user privilege? Is it possible?

  17. cjacks says:

    You can either separate your functionality into a separate exe, or you can use an out-of-process COM component. For something very small, you may want to think about using COM. See: http://msdn2.microsoft.com/en-us/library/ms679687.aspx.

    You can’t just elevate the same process for a little while. If we had an elevated token in the process, if even for a little while, any thread in the process could call DuplicateToken(Ex). It’s like giving your house key to a non-trusted party who happens to have the equipment to instantly make a copy of your key – once it’s in the process, the process is no longer locked down, and any code could potentially start using that token.

  18. Morgan says:

    Is there a way to detect if the login/interactive user is different from the effective/elevated user of the current process?  Another way of asking this is if I can tell if, upon starting an elevated process, did the HKCU registry key "switch" to represent a different user (it used to point to a normal user, but now points to an admin user for this process)?

    Whether or not the login user is the "same user" as the elevated user, your function above seems to return TokenElevationTypeFull, but I need to differentiate those two cases that are both detected as TokenElevationTypeFull.

  19. cjacks says:

    Morgan,

    My first question is – why do you need to know this? If you are trying to write to HKCU, can you just do this from a non-elevated context, separating out the bits that require elevated rights into a separate component so you only execute the minimum amount of code necessary into an elevated process? Are there other reasons why you would need to know if the elevated user is different than the launching user?

    One thing I caution strongly against is failing if you are a different user, because you don’t want to fail for somebody who elevates to a different identity, since this would break the scenario of somebody running the best practice of running full time as standard user, and keeping a separate admin account available for when elevation is required.

    To address your question specifically, you could always use the WTS APIs to determine the interactive user. The WTSQuerySessionInformation API will let you query for WTSUserName and determine who the logged in user is for the session you specify (which you can query for using the WTS APIs as well).

  20. Jason says:

    Thanks for the code sample …. for those that cannot fathom why you would need to determine the current UAC state, we have a very simple reason — automated testing.  We have a large testing infrastructure (hundreds of machines) that has an enormous number of tests (tens of thousands) which are run against the software as it’s modified and enhanced.  We have parts of the software that legitimately require elevated permissions, but the vast majority of the product does not.  So we need to set up some hosts with UAC on (to verify no one introduces something that would require admin privs), but have to keep some hosts with it on to test the parts of the software that need to do things at the higher level.  Since the tests are automated, we don’t have means of clicking the button.  We check the machine setups between test runs to make sure no settings have changed, and we need to know if UAC is on or off to assure our testing is being done correctly.

  21. cjacks says:

    Jason – another idea might be to just change the UAC policy for your lab machines to silently elevate, rather than turning it off entirely. That way, you’re still running with UAC enabled as if the user were doing that, but getting elevated without requiring somebody to actually click on an approval dialog or provide creds. Just a thought.

  22. Jason says:

    cjacks — we are attempting to determine if we are doing something in our code that would require admin privelages (where we didn’t expect that it would).  We are attempting to test our software in an environment that is as close to "out of the box" as possible — which means UAC on.

    With the "silent elevation", is it possible to allow a test to fail as a result of the elevation?  This would allow us to continue our testing routines and deal with the failure after the test run has completed.  Right now when we hit the UAC prompt, testing is dead.

    Also, we are having a devil of a time compiling this code to work and examine it.  We have VS2005 with the Vista packs installed, but can’t seem to get it to work.  Can you detail your configuration?  

  23. cjacks says:

    Hi Jason,

    If you’re trying to sort out why an application may be failing as non-admin, I’d take a look at Standard User Analyzer (part of the Application Compatibility Toolkit 5.0) – this will pinpoint exactly what your code is doing that requires admin rights and help you address that. Once you erradicate the LUA bugs, you can just manifest to run asInvoker, and you’ll never have us elevate it for you because our heuristics determine that you might need elevation.

    Running UAC on is the best thing, since that’s what the overwhelming majority of our testing is done on and the majority of users will see.

    If you want to see both failure and success, the group policy for UAC allows you to either silently elevate (not really ever a good idea for an actual user, but a good solution for your tests) or automatically fail (many enterprises will want this if they are running a standard user desktop, since the average user won’t have creds to provide when we prompt). You could try both policies – one will fail your test since elevation won’t happen, the other will let it happen without you having to drive it.

    To compile my code, you’ll need to have the Vista SDK installed, and point to it in the headers and libraries when you configure your project. Once you have that, you can also try out the new goodies in Vista since you’ll have all of the new headers, if you want to do a "light up on Vista" type of thing (yes, you’ll typically want to branch so you can support down-level, but if your code factors out the UI stuff you can keep it fairly clean).

    Feel free to ping me on the "email me" link if you need deeper assistance, and I’d be happy to go deeper on anything that is confusing.

    Thanks,

    Chris

  24. Saraswathi says:

    I tried to use the same piece of code , But GetTokenInformation() is returning the Error as Invalid Parameter.. Did anyone get the same error?

    Can Anyone pls help me in this.. Thanks in advance.

  25. Shashi says:

    You may want to check the following…

    Do you have the latest header files? Windows Vista SDK?

    Check the search order of the directories for include files.

    If not please provide the code so that we understand the case better

  26. Saraswathi says:

    Thanks a lot , after adding the latest header file that error has gone..

    But Now that getTokenInformation is always returning TokenElevationTypeDefault whether the user is admin or non admin.. I created one small test application with these piece of code. And I ran that code. Its returning TokenElevationTypeDefault for admin and other users and for both UAC enabled and UAC Disabled.. what may be the problem? Thanks

  27. Data Backup says:

    My app is a data backup program, and is manifested as ‘highestAvailable’.  It needs this to utilize the volume shadow service for backing up locked/open files.  Unfortunately, this means that under Vista when UAC is on, my app doesn’t receive WM_DROPFILES messages.  I haven’t seen it documented by MS, but apparently this is because these messages are disabled between applications with different security levels.  I’m thinking of just checking to see if UAC is on, and if so, I won’t call the DragAcceptFiles(), and simply won’t support drag/drop in this case.  I have 2 questions:

    1) Can anyone offer a better solution?

    2) If not, how to you programatically determine if UAC is on/off?

    Thanks,

    Mark

  28. cjacks says:

    Call the ChangeWindowMessageFilter function to allow whatever messages you want to recieve through the UIPI filters. Then, make sure you are particularly vigilant about scrubbing this input, as it could potentially come from an untrusted source.

  29. Chris Jackson says:

    Still app doesn’t receive  WM_DROPFILES. But ChangeWindowMessageFilter returns "true".

  30. ananda84 says:

    Is it possible to use GetTokenInformation and find out if a process is getting virtualized or not?

  31. Gautam says:

    Can such a detection be done through Javascript?

  32. cjacks says:

    Gautam,

    What’s the scenario in which this is helpful to you? Just trying to figure out what the best way to get that information is.

    Chris

  33. Josef Engel says:

    under Windows Server 2008 the answer of GetTokenInformation is always TokenElevationTypeDefault independent from the user.

  34. cjacks says:

    No it isn’t.