Generally finding out if a user has the permissions to do something involves trying to do that thing, and handling the access denied (error 5) exception that results.
What if you wanted to know if a user is an admin though prior to doing some admin task via code? Normally this would be as easy as checking if the user is a member of the built in administrators group in Windows, however with Vista and Windows 7, the OS uses a split token so your user account runs with standard user permissions, but can elevate as needed to full admin rights.
When a standard user does the same thing, and invokes some process that requires administrative rights, they will be prompted with a user id/password dialog, and must enter the credentials of an account that is an admin member on the system.
My reason for having to look at this was due to a program where a process running as a standard user downloads some update files to the user's temp folder, and then launches an application as administrator to process those files. If the standard user gets a credentials dialog, and enters in alternate admin credentials, then the user profile changes, and the temp directory is now the admins temp directory, not the standard user's. This created lots of problems, and while there were a few options for a resolution, most were going to be messy, and I opted to go for the following approach:
When the user goes to download update files from the software, determine if they are an admin, and if not, determine if they CAN be an admin. If they can, then I know they will get the regular click through elevation prompt, and the profile will remain the same. However if they are not an admin, and they can not elevate to an admin, I can void the download, and inform the user the main download process should be invoked by an administrator. This will cause the temp files to be in the correct place, as well as some other environment variables that the program uses, and the update will succeed.
So that was my reason for needing it, but I know that several other reasons could exist for wanting to know if a Vista/7 user is an admin, but not running currently as one.
Ok so you probably came here for code and not to listen to my coding issues, so lets get down to it.
Here is the entire class file. It consists of just a few API calls, as well as 3 public functions for you to consume.
IsRunningAsAdmin(): Does the standard check to simply determine if the user is in the administrators group. Will return false for admins running the process with reduced rights in Vista/7.
CanElevateToAdmin(): Uses the Windows API calls to determine if the user can elevate, or is elevated currently.
RunningStandardButCanElevate(): Just a combination of the two above methods, as a shortcut to determine if the user can elevate to admin, but currently is not.
As you can probably tell by the method names, they are all boolean methods.
One thing to note, is that the CanElevateToAdmin method will check the OS version, and only do the API calls when the major version is greater than 5. (5 being Windows XP, 6+ being Vista and up) Otherwise it simply does the standard admin check.
Sometimes the code formatting may look a little odd, it is just to avoid weird wrapping in the blog post. 
Imports System.Runtime.InteropServices
Class AdminCheck
#Region "CONSTANTS"
Const TOKEN_QUERY As UInt32 = &H8
Const INT_SIZE As Integer = 4
#End Region
#Region "ENUMERATIONS"
Private Enum TOKEN_ELEVATION_TYPE
TokenElevationTypeDefault = 1
TokenElevationTypeFull
TokenElevationTypeLimited
End Enum
Private Enum TOKEN_INFO_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
' MaxTokenInfoClass should always be the last enum
End Enum
#End Region
#Region "WIN API FUNCTIONS"
<DllImport("kernel32.dll")> _
Private Shared Function GetCurrentProcess() As IntPtr
End Function
<DllImport("advapi32.dll", SetLastError:=True)> _
Private Shared Function OpenProcessToken(ByVal ProcessHandle As IntPtr, _
ByVal DesiredAccess As UInt32, _
ByRef TokenHandle As IntPtr) As Boolean
End Function
<DllImport("advapi32.dll", SetLastError:=True)> _
Private Shared Function GetTokenInformation( _
ByVal TokenHandle As IntPtr, _
ByVal TokenInformationClass As TOKEN_INFO_CLASS, _
ByVal TokenInformation As IntPtr, _
ByVal TokenInformationLength As Integer, _
ByRef ReturnLength As UInteger) As Boolean
End Function
#End Region
#Region "PUBLIC METHODS"
''' <summary>
''' RETURNS TRUE WHEN THE CURRENT USER IS A MEMBER OF THE
''' ADMINISTRATORS GROUP AND IS ALSO RUNNING THE PROCESS
''' ELEVATED AS AN ADMINISTRATOR, OTHERWISE RETURNS FALSE.
''' </summary>
''' <returns>Boolean</returns>
Public Shared Function IsRunningAsAdmin() As Boolean
Return My.User.IsInRole(ApplicationServices.BuiltInRole.Administrator)
End Function
''' <summary>
''' RETURNS TRUE WHEN THE CURRENT USER CAN ELEVATE TO ADMINISTRATOR RIGHTS
''' OR ALREADY HAD ELEVATED TO ADMINISTRATOR RIGHTS,
''' OTHERWISE RETURNS FALSE
''' </summary>
''' <returns>Boolean</returns>
Public Shared Function CanElevateToAdmin() As Boolean
'DETERMINE IF THE CURRENT USER IS ALREADY RUNNING WITH ADMIN RIGHTS
Dim IsAdmin = IsRunningAsAdmin()
'EXIT OUT NOW IF THE USER IS AN ADMIN, RETURNING TRUE
'THERE IS NO ELEVATION NEEDED IF THE USER IS ALREADY
'RUNNING WITH ADMIN RIGHTS
If IsAdmin Then Return True
'IF VISTA OR HIGHER, CHECK FOR SPLIT TOKEN
If Environment.OSVersion.Version.Major > 5 Then
Try
Dim myToken As IntPtr
Dim elevationType As TOKEN_ELEVATION_TYPE
Dim dwSize As UInteger
Dim pElevationType As IntPtr = Marshal.AllocHGlobal(INT_SIZE)
'GET A TOKEN REFERENCE FOR THE USER RUNNING THIS PROCESS
OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, myToken)
'GET THE ELEVATION INFORMATION FOR THIS TOKEN
GetTokenInformation(myToken, _
TOKEN_INFO_CLASS.TokenElevationType, _
pElevationType, _
INT_SIZE, _
dwSize)
'CAST THE RESULT TO ENUM TYPE
elevationType = DirectCast( _
Marshal.ReadInt32(pElevationType), _
TOKEN_ELEVATION_TYPE)
'FREE ALLOCATED UNMANAGED MEMORY
Marshal.FreeHGlobal(pElevationType)
'DETERMINE THE RESULT OF THE ELEVATION CHECK
'==============================================
'TokenElevationTypeFull - User has a split token,
'and the process is running elevated
'TokenElevationTypeLimited - User has a split token,
'but the process is not running elevated
'TokenElevationTypeDefault - User is not using a split token
'==============================================
Return (elevationType = _
TOKEN_ELEVATION_TYPE.TokenElevationTypeLimited) OrElse _
(elevationType = _
TOKEN_ELEVATION_TYPE.TokenElevationTypeFull)
Catch ex As Exception
'LOG/HANDLE ERROR
'RETURN FALSE IN EVENT OF ERROR
Return False
End Try
Else
'PRIOR TO VISTA, ONLY CHECK
'NEEDED IS IF THE USER IS IN THE ADMIN GROUP
Return IsAdmin
End If
End Function
''' <summary>
''' RETURNS TRUE WHEN USER IS RUNNING AS A STANDARD USER CURRENTLY
''' BUT CAN ELEVATE TO ADMIN RIGHTS WITH THEIR OWN CREDENTIALS
''' OTHERWISE RETURNS FALSE
''' </summary>
''' <returns>Boolean</returns>
Public Shared Function RunningStandardButCanElevate() As Boolean
Return CanElevateToAdmin() And (Not IsRunningAsAdmin())
End Function
#End Region
End Class
Hopefully this will come in handy for anyone else that may be looking to determine this information. I did a good amount of searching around the web, and wasn't able to find a good example of this, especially in Visual Basic.
Thanks to Rob Teixeira for pointing me to GetTokenInformation API function, which enabled me to ultimately solve this issue.
Posted
Jul 29 2009, 08:20 PM
by
Matthew Kleinwaks