Check if the current user is an Administrator
Author: CancerFace (talk, contrib) |
NSIS forum thread
Description
I wanted to have the option of checking if a user belongs to the adminisrator's group without using a special plugin. This can be achieved using API calls with the System plugin.
There are 3 alternative implementations (2 macros and 1 function).
Macros
The first macro works in Windows 2000 and later with correct UAC handling while the second one also works in NT but does not support UAC or restricted tokens.
Usage
Use the following code in your NSIS installer to find out if the user invoking it belongs to the administrator's group:
!insertmacro IsUserAdmin $0 ; $0 = 1 if the user belongs to the administrator's group ; $0 = 0 if not ; $0 = -1 if there was an error (only for the 1st Macro)
Macro 1 (2k/XP/2k3)
!macro IsUserAdmin RESULT !define Index "Line${__LINE__}" StrCpy ${RESULT} 0 System::Call '*(&i1 0,&i4 0,&i1 5)i.r0' System::Call 'advapi32::AllocateAndInitializeSid(i r0,i 2,i 32,i 544,i 0,i 0,i 0,i 0,i 0, \ i 0,*i .R0)i.r5' System::Free $0 System::Call 'advapi32::CheckTokenMembership(i n,i R0,*i .R1)i.r5' StrCmp $5 0 ${Index}_Error StrCpy ${RESULT} $R1 Goto ${Index}_End ${Index}_Error: StrCpy ${RESULT} -1 ${Index}_End: System::Call 'advapi32::FreeSid(i R0)i.r5' !undef Index !macroend
Macro 2 (NT/2k/XP/2k3) - doesn't work!
!define TOKEN_READ 0x00020008 !define TokenGroups 2 !macro IsUserAdmin RESULT !define Index "Line${__LINE__}" StrCpy ${RESULT} 0 # Construct the SID for the Admin group - (S-1-5-32-544 for administrators) put in $R4 System::Call "*(&i1 0,&i4 0,&i1 5)i.r0" System::Call "advapi32::AllocateAndInitializeSid(i r0,i 2,i 32,i 544,i 0,i 0,i 0,i 0,i 0,\ i 0,*i .R4)i.r5" System::Free $0 # Get a psuedo-handle of the current process and place it on R0 System::Call 'kernel32::GetCurrentProcess()i.R0' # Open the Token from the psuedo process and place the handle on R1 System::Call 'advapi32::OpenProcessToken(i R0,i ${TOKEN_READ},*i .R1)i.R9' # Get info from the token and place it in $R2 (the size goes to $R3) System::Call 'advapi32::GetTokenInformation(i R1,i ${TokenGroups},*i .R2,i 0,*i .R3)i.R9' System::Alloc $R3 Pop $R2 System::Call 'advapi32::GetTokenInformation(i R1,i ${TokenGroups},i R2,i $R3,*i .R3)i.R9' # Check how many TOKEN_GROUPS elements are in $R2 (place the number in $R5) System::Call '*$R2(i.R5,i.R6)' # Compare the SID structures StrCpy $1 0 ${Index}_Start: StrCmp $1 $R5 ${Index}_Stop System::Call 'advapi32::EqualSid(i R4,i R6)i.R9' StrCmp $R9 "" ${Index}_Increment StrCmp $R9 0 +1 +3 StrCpy ${RESULT} 0 Goto ${Index}_Increment StrCpy ${RESULT} 1 System::Call 'advapi32::FreeSid(i R6)i.R8' Goto ${Index}_Stop ${Index}_Increment: System::Call 'advapi32::GetLengthSid(i R6)i.R9' System::Call 'advapi32::FreeSid(i R6)i.R8' IntOp $R6 $R6 + $R9 IntOp $1 $1 + 1 Goto ${Index}_Start ${Index}_Stop: # Close the token handle System::Call 'kernel32::CloseHandle(i R1)i.R9' # cleanup System::Call 'advapi32::FreeSid(i R4)i.r5' System::Free $R2 System::Free 0 !undef Index !macroend
Macro 3 (Windows 2k or above)
I found that a Windows API can get the information of the specified user name, it's more easier than the above ways, I write it and so add it here. Any question please email jiake(at)vip.qq.com.
!define USER_PRIV_GUEST 0 !define USER_PRIV_USER 1 !define USER_PRIV_ADMIN 2 !include "Util.nsh" !define GetUserLevel "!insertmacro Call_GetUserLevel" !macro Call_GetUserLevel _level _username !verbose push !verbose 3 Push ${_username} ${CallArtificialFunction} Func_GetUserLevel Pop ${_level} !verbose pop !macroend !macro Func_GetUserLevel Exch $R0 Push $R1 System::Call "*(i)i.R1" System::Call "netapi32::NetUserGetInfo(in,wR0,i1,iR1)i.R0" IntCmp $R0 0 +3 Push error Goto lbl_end System::Call "*$R1(i.R0)" System::Call "*$R0(w,w,i,i.s)" System::Call "netapi32::NetApiBufferFree(iR0)" lbl_end: System::Free $R1 Exch Pop $R1 Exch Pop $R0 !macroend
Usage example:
; ${GetUserLevel} VAR_USER_LEVEL_OUT STRING_OR_VAR_USERNAME ; STRING_OR_VAR_USERNAME: ; A valid username. ; VAR_USER_LEVEL_OUT: ; A variable to store the result, it can be: ; ${USER_PRIV_GUEST} (0) A guest account. ; ${USER_PRIV_USER} (1) A limited account. ; ${USER_PRIV_ADMIN} (2) An administrator account. ; error An error ocurred while calling. ReadEnvStr $R0 UserName ${GetUserLevel} $0 $R0 MessageBox MB_OK "The user $R0's level is $0." ${GetUserLevel} $0 Administrator MessageBox MB_OK "The user Administrator's level is $0."
Function (by Afrow UK)
Based on this code
Note: After testing in the field, this function does not always work. Use the other function or use the included NSIS UserInfo plug-in (UserInfo::GetAccountType).
!define SC_MANAGER_LOCK 8 !define ERROR_SERVICE_DATABASE_LOCKED 1055 Function IsUserAdmin Push $R0 Push $R1 Push $R2 StrCpy $R0 0 System::Call `advapi32::OpenSCManager(i 0, i 0, i ${SC_MANAGER_LOCK}) i.R1` ${If} $R1 != 0 System::Call `advapi32::LockServiceDatabase(i R1) i.R2` ${If} $R2 != 0 System::Call `advapi32::UnlockServiceDatabase(i R2)` StrCpy $R0 1 ${Else} System::Call `kernel32::GetLastError() i.R2` ${If} $R2 == ${ERROR_SERVICE_DATABASE_LOCKED} StrCpy $R0 1 ${EndIf} ${EndIf} System::Call `advapi32::CloseServiceHandle(i R1)` ${EndIf} Pop $R2 Pop $R1 Exch $R0 FunctionEnd
Usage
Call IsUserAdmin Pop $0 ; $0 = 1 if the user belongs to the administrator's group ; $0 = 0 if not
Resources and Links
- NSIS forum thread
- XtInfo Plugin
API Functions used:
- AllocateAndInitializeSid
- GetCurrentProcess
- OpenProcessToken
- GetTokenInformation
- EqualSid
- FreeSid
- GetLengthSid
- CloseHandle