Archive: Determine if Admin rights are needed (UAC)


Determine if Admin rights are needed (UAC)
Hello all,

I have an installer which can be used for "global", as well as "local" installations. By "global" I mean installing to system locations, creating shortcuts, etc...
While "local" installation is essentially just extracting into a directory (INSTDIR).

The thing is, I don't know how to determine if it needs administrative privileges when doing a "local" installation. For example, doing an installation to "C:\program files\..." would require admin privileges, while installing to a user's personal directory wouldn't.

Right now I just have "RequestExecutionLevel admin", but it doesn't allow ordinary users to do a "local" install to their directories.

I know there's a UAC plugin ( http://nsis.sourceforge.net/UAC_plug-in ), but there's almost no documentation there and I have no idea how to use it to achieve my goal.

Any help would be really appreciated.
Thanks in advance!


You looking for something like this? http://nsis.sourceforge.net/Docs/MultiUser/Readme.html


Here's what I use, based on a trick shown to me by Anders:

Function TestFileAccess
;ACCESS_MASK = FILE_GENERIC_READ | FILE_GENERIC_WRITE | FILE_GENERIC_EXECUTE | DELETE
; = STANDARD_RIGHTS_READ | FILE_READ_DATA | FILE_READ_ATTRIBUTES | FILE_READ_EA | SYNCHRONIZE |
; STANDARD_RIGHTS_WRITE | FILE_WRITE_DATA | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA | FILE_APPEND_DATA |
; STANDARD_RIGHTS_EXECUTE | FILE_EXECUTE | DELETE
System::Call "kernel32::GetFileAttributes(t `$INSTDIR`)i .r1"
IntOp $1 $1 & 0x0010
${If} $1 != 0
${AndIf} ${FileExists} "$INSTDIR"
;Instdir exists and is a directory.
; Check instdir for 0x1301BB (with DELETE)
; We need delete privs: Instdir may have been created by another user, thus not giving us delete access.
System::Call "kernel32::CreateFile(t `$INSTDIR`,i 0x1301BB,i 3,i 0,i 3,i 0x02000000,i 0)i .r1"
System::Call "kernel32::CloseHandle(ir1)"
${Else}
;Instdir either doesn't exist, or doesn't exist as a directory yet.
;Search for an instdir\..\..\.. directory that actually exists
push $2
push $3
${RemoveTrailingBackslashSpaces} $2 $INSTDIR
${Do}
System::Call "kernel32::GetFileAttributes(t `$2`)i .r1"
IntOp $1 $1 & 0x0010
${If} $1 != 0
${AndIf} ${FileExists} "$2"
;It exists and is a directory.
${ExitDo}
${EndIf}
${CutBeforeLastBackslash} $2 $2
StrLen $3 $2
${If} $3 == 2
${ExitDo}
${EndIf}
${Loop}
; Check instdir\..\..\.. for 0x1201BB (without DELETE).
; We don't need delete privs: The user creating a dir is always owner, so as long as we
; create instdir at user level we'll get delete access automatically.
System::Call "kernel32::CreateFile(t `$2`,i 0x1201BB,i 3,i 0,i 3,i 0x02000000,i 0)i .r1"
System::Call "kernel32::CloseHandle(ir1)"
pop $3
pop $2
${EndIf}
FunctionEnd

Function YourFunction
Call TestFileAccess
${If} $1 == -1
;We don't have access rights. Pop an error.
${EndIf}
FunctionEnd


The point of interest is the CreateFile/CloseHandle command. It does not actually create anything, but it will return a positive handle ID if the user calling it has enough privileges. If not, it'll return -1. Oh, and I pasted those two macros I used in here: http://nsis.pastebin.com/fc93d069

Thank you both for your replies.

The MultiUser stuff - I think I read somewhere that it's not compatible with UAC. Also, I don't quite see the thing I'm looking for there (maybe I'm looking at it the wrong way).

MSG, thanks a lot for your code.
One question though - is this code known to work with WOW emulation on 64-bit windows? I know windows does a lot of behind-the-scenes path substitution in that mode, and e.g. "C:\windows\system32"'s parent directory may not always be "C:\windows". (I wrote that as an example - it might work for these directories, but for others it might not).


One question though - is this code known to work with WOW emulation on 64-bit windows? I know windows does a lot of behind-the-scenes path substitution in that mode, and e.g. "C:\windows\system32"'s parent directory may not always be "C:\windows". (I wrote that as an example - it might work for these directories, but for others it might not).
Hmm... That's an interesting question. I was going to say that, because the redirection happens behind the scenes, the above code should not be affected by it. "$INSTDIR\..\.." could never become some strange virtualstore directory, because $INSTDIR is just a string, and that string does not contain virtualstore.
But then I realized I have no idea when exactly the path gets expanded to its physical location. If "$PROGRAMFILES\Test\.." is expanded to $PROGRAMFILES before it's sent to win32 api, there's no problem. But if it's the api commands that expand the path... It might get tricky.

Then again, although I don't know how to find out which of the two actually happens, I haven't encountered any problems with the code. But perhaps some programmer could help out to clarify?

x64.nsh has functions to turn redirection on and off


Oh, right, he was asking about WOW64. What about permissions-related virtualization, though?


There seem to be a lot of cases when windows decides to rewrite the paths. I think this breaks the parent directory / subdirectory relationships based on path string parsing.

There's this: http://support.microsoft.com/kb/927387

And there's also WOW64. For WOW64 I think the most correct way would be to get a real path from our virtualized path, and then use it in the above checks. Simply disabling the path translation is incorrect IMHO.

I found the question asking about how to do this ( http://stackoverflow.com/questions/942110/translating-32-bit-paths-to-their-wow64-equivalents ), but it remains unanswered.