2
votes

I have an NSIS installer script that generates an Uninstaller. The Uninstaller, when created, requires elevated permissions in order to be executed. Certain requirements make it such that I need to be able to run the uninstaller as any user without the elevated permission level. None of the other files that are generated are set with elevated permissions, not even the application executable itself. Is there any way to set the permission level to any user? Here is my NSIS script. I have removed a lot from the script so that the application stays anonymous, but have left everything that I think is relevant

Function .onInit
    UserInfo::GetAccountType
    pop $0
    ${If} $0 != "admin"
        MessageBox mb_iconstop "Administrator rights required!"
        SetErrorLevel 740 ;ERROR_ELEVATION_REQUIRED
        Quit
    ${EndIf}

    ${IfNot} ${AtLeastWin7}
      MessageBox MB_OK "Application requires at minimum Windows 7 as the installed operating system. Exiting installation..."
      Quit
    ${EndIf}

    IntOp $0 ${SF_SELECTED} | ${SF_RO}
    SectionSetFlags ${SecApp} $0
FunctionEnd

; sections
Section "AppSection" SecApp
    ... installer stuff
    WriteRegStr HKCU "${AppRegistryPath}" \
                     "UninstallString" "$\"$INSTDIR\Uninstall.exe$\""

    WriteUninstaller "$INSTDIR\Uninstall.exe"
    ... more installer stuff
SectionEnd

Section "Uninstall"
    ; code that terminates the running application

    ; code that removes a firewall rule

    DetailPrint "Removing files and directories"
    Delete "$INSTDIR\*"
    Delete "$INSTDIR\x86\*"
    Delete "$INSTDIR\x64\*"
    Delete "$INSTDIR\fonts\*"
    RMDir "$INSTDIR\x86"
    RMDir "$INSTDIR\x64"
    RMDir "$INSTDIR\fonts"
    RMDir "$INSTDIR"

    DetailPrint "Removing registry values"
    DeleteRegKey HKCU "${AppRegistryPath}"
    DeleteRegKey HKCU "${AppPath}"
    DeleteRegKey HKLM "${AppRegistryPath}"
    DeleteRegKey HKLM "${AppPath}"
SectionEnd
3

3 Answers

1
votes

The uninstaller uses the same manifest as the installer and the UAC part of the manifest is set by RequestExecutionLevel.

You could try using RequestExecutionLevel highest, it will only elevate administrators, normal users will run the application normally without any prompts. Use UserInfo::GetAccountType in un.onInit and abort with a error message if you need to be elevated.

You can also re-launch yourself and request elevation with ExecShell "RunAs" '"$ExePath"' (you still need to verify with UserInfo::GetAccountType).

1
votes

Look into the UAC plugin. Among other things, it allows you to control when in your script elevation is requested, so you can have the uninstall section skip over doing that.

1
votes

With Windows Vista or above, the correct way to mark a program is to embed an application manifest within your application that tells the operating system what the application needs. There are attributes within this application manifest that permit developers to specify their programs level of execution or requested execution level.

The request level options are as follows:

  • As Invoker – The application runs with the same access token as the parent process. (Recommended for standard user applications)
  • Highest Available – The application runs with the highest privileges the current user can obtain. (Recommended for mixed-mode applications)
  • Require Administrator – The application runs only for administrators and requires that the application be launched with the full access token of an administrator. (Recommended for administrator only applications)
  • No Execution Level Information – The application does not have an embeded request execution level manifest.

Unless an application is designed to be run exclusively by system administrators, it should be run with the least privileges possible.

No Execution Level

On Windows Vista and above, when no execution level information is set in the application's manifest and the application is not elevated previously the application runs in legacy mode for backwards compatibility support. In this mode the operating system uses a virtualization mechanism for the file system and registry to access. This means that its attempt to create or change files in restricted folder locations or to write in registry restricted hives is redirected (reflected) towards a "per-user" accessible location. See VirtualStore for more information on how this applies here.

More information on this subject can be found here: Windows User Account Control


On the page I linked above (Windows User Account Control) is a link in which you can download some manifests with these request execution level attributes inside xml files. In your case I'd edit one of them with your desired level of execution. Using the following snippet of code this can be done:

Section "Uninstall"
    !define RequestLevel User
    !define ResHacker    `${NSISDIR}\Contrib\Manifests\ResHacker.exe`
    !define ManifDir     `${NSISDIR}\Contrib\Manifests`
    !define Manifest     `NSIS_2.46_Win8`
    !packhdr             `$%TEMP%\exehead.tmp` `"${Reshacker}" -addoverwrite "%TEMP%\exehead.tmp", "%TEMP%\exehead.tmp", "${ManifDir}\${Manifest}_${RequestLevel}.manifest", 24,1,1033`
    
    # The rest of your code...
SectionEnd

All the required files you need is in the downloadable zip file at the bottom of the aforementioned webpage. How to use this is also explained in greater detail.

NOTE: Although I agree with Anders as you need elevated privileges to delete HKLM keys, mess with the firewall settings, etc.