Check if the current user is an Administrator

From NSIS Wiki
Jump to navigationJump to search
Author: CancerFace (talk, contrib)


Note: NSIS v2.00 added the UserInfo plug-in, it allows you to detect the user type without any of the code on this page.

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

API Functions used: